efficiency change to command hashing; fix pointer aliasing problem with readline history-search-backward

This commit is contained in:
Chet Ramey
2022-03-01 09:32:15 -05:00
parent e7a56619a2
commit 6c4a9a5cb7
12 changed files with 166 additions and 94 deletions
+41
View File
@@ -3245,3 +3245,44 @@ parse.y
into this call to the parser (PST_REGEXP, PST_EXTPAT, PST_CONDCMD,
PST_CONDEXPR for now). Fixes bug reported by konsolebox
<konsolebox@gmail.com>
2/23
----
findcmd.c,builtins/hash.def
- replace calls to is_directory with file_isdir, which only performs a
stat and doesn't do the eaccess call to check for an executable file
findcmd.c
- find_in_path_element: takes a new RFLAGSP argument, an int * where
the status flags for the returned pathname are returned; saves
additional calls to stat/eaccess
- search_for_command: get the returned flags from
find_user_command_in_path so we don't need any additional calls to
file_status after we find the command in $PATH
2/24
----
doc/{bash.1,bashref.texi}
- FUNCTIONS: some small changes to the description of local variables
and dynamic scoping, with emphasis on how that affects `unset'
behavior. Inspired by a discussion with
Christoph Anton Mitterer <calestyo@scientia.net>
2/25
----
examples/loadables/realpath.c
- renamed -s option to -q to align with other versions
- perform array assignment for `-a varname' even if -q option supplied
- renamed -S option to -s for Linux compatibility
2/28
----
lib/readline/misc.c
- _rl_free_saved_history_line: call rl_free_undo_list, saving and
setting rl_undo_list to the saved history line's data, so the right
call to _hs_replace_history_data happens and we don't end up with
a pointer aliasing problem. Fixes core dump reported by
Andreas Schwab <schwab@linux-m68k.org>, but does not make his
scenario equivalent to incremental search
+1 -1
View File
@@ -177,7 +177,7 @@ hash_builtin (list)
continue;
else if (pathname)
{
if (is_directory (pathname))
if (file_isdir (pathname))
{
#ifdef EISDIR
builtin_error ("%s: %s", pathname, strerror (EISDIR));
+17 -4
View File
@@ -5,12 +5,12 @@
.\" Case Western Reserve University
.\" chet.ramey@case.edu
.\"
.\" Last Change: Thu Feb 10 11:04:52 EST 2022
.\" Last Change: Thu Feb 24 14:43:14 EST 2022
.\"
.\" bash_builtins, strip all but Built-Ins section
.if \n(zZ=1 .ig zZ
.if \n(zY=1 .ig zY
.TH BASH 1 "2022 February 10" "GNU Bash 5.2"
.TH BASH 1 "2022 February 24" "GNU Bash 5.2"
.\"
.\" There's some problem with having a `@'
.\" in a tagged paragraph with the BSD man macros.
@@ -4495,11 +4495,22 @@ been enabled.
.PP
Variables local to the function may be declared with the
.B local
builtin command. Ordinarily, variables and their values
builtin command (\fIlocal variables\fP).
Ordinarily, variables and their values
are shared between the function and its caller.
If a variable is declared \fBlocal\fP, the variable's visible scope
is restricted to that function and its children (including the functions
it calls).
.PP
In the following description, the \fIcurrent scope\fP is a currently-
executing function.
Previous scopes consist of that function's caller and so on,
back to the "global" scope, where the shell is not executing
any shell function.
Consequently, a local variable at the current scope is a variable
declared using the \fBlocal\fP or \fBdeclare\fP builtins in the
function that is currently executing.
.PP
Local variables "shadow" variables with the same name declared at
previous scopes.
For instance, a local variable declared in a function
@@ -4530,11 +4541,13 @@ variable is local to the current scope, \fBunset\fP will unset it;
otherwise the unset will refer to the variable found in any calling scope
as described above.
If a variable at the current local scope is unset, it will remain so
(appearing as unset)
until it is reset in that scope or until the function returns.
Once the function returns, any instance of the variable at a previous
scope will become visible.
If the unset acts on a variable at a previous scope, any instance of a
variable with that name that had been shadowed will become visible.
variable with that name that had been shadowed will become visible
(see below how the \fBlocalvar_unset\fP shell option changes this behavior).
.PP
The \fBFUNCNEST\fP variable, if set to a numeric value greater
than 0, defines a maximum function nesting level. Function
+16 -2
View File
@@ -1600,10 +1600,22 @@ return status is the exit status of the last command executed
before the @code{return}.
Variables local to the function may be declared with the
@code{local} builtin. These variables are visible only to
@code{local} builtin (@dfn{local variables}).
Ordinarily, variables and their values
are shared between a function and its caller.
These variables are visible only to
the function and the commands it invokes. This is particularly
important when a shell function calls other functions.
In the following description, the @dfn{current scope} is a currently-
executing function.
Previous scopes consist of that function's caller and so on,
back to the "global" scope, where the shell is not executing
any shell function.
Consequently, a local variable at the current local scope is a variable
declared using the @code{local} or @code{declare} builtins in the
function that is currently executing.
Local variables "shadow" variables with the same name declared at
previous scopes. For instance, a local variable declared in a function
hides a global variable of the same name: references and assignments
@@ -1656,11 +1668,13 @@ variable is local to the current scope, @code{unset} will unset it;
otherwise the unset will refer to the variable found in any calling scope
as described above.
If a variable at the current local scope is unset, it will remain so
(appearing as unset)
until it is reset in that scope or until the function returns.
Once the function returns, any instance of the variable at a previous
scope will become visible.
If the unset acts on a variable at a previous scope, any instance of a
variable with that name that had been shadowed will become visible.
variable with that name that had been shadowed will become visible
(see below how @code{localvar_unset}shell option changes this behavior).
Function names and definitions may be listed with the
@option{-f} option to the @code{declare} (@code{typeset})
+2 -2
View File
@@ -2,10 +2,10 @@
Copyright (C) 1988-2022 Free Software Foundation, Inc.
@end ignore
@set LASTCHANGE Sat Feb 5 18:20:58 EST 2022
@set LASTCHANGE Thu Feb 24 14:43:35 EST 2022
@set EDITION 5.2
@set VERSION 5.2
@set UPDATED 5 February 2022
@set UPDATED 24 February 2022
@set UPDATED-MONTH February 2022
+29 -28
View File
@@ -1,13 +1,13 @@
/*
* realpath -- canonicalize pathnames, resolving symlinks
*
* usage: realpath [-csv] [-a name] pathname [pathname...]
* usage: realpath [-cqsv] [-a name] pathname [pathname...]
*
* options: -a name assign each canonicalized pathname to indexed array
* variable NAME
* -c check whether or not each resolved path exists
* -s no output, exit status determines whether path is valid
* -S strip . and .. from the pathname only, no symlink resolution
* -q no output, exit status determines whether path is valid
* -s strip . and .. from the pathname only, no symlink resolution
* -v produce verbose output
*
*
@@ -22,7 +22,7 @@
*/
/*
Copyright (C) 1999-2009,2021 Free Software Foundation, Inc.
Copyright (C) 1999-2009,2021,2022 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
@@ -66,7 +66,7 @@ extern char *sh_realpath();
int
realpath_builtin(WORD_LIST *list)
{
int opt, cflag, vflag, sflag, Sflag, aflag, es;
int opt, cflag, vflag, qflag, sflag, aflag, es;
char *r, realbuf[PATH_MAX], *p, *newpath;
struct stat sb;
#if defined (ARRAY_VARS)
@@ -80,14 +80,14 @@ realpath_builtin(WORD_LIST *list)
return (EX_USAGE);
}
vflag = cflag = sflag = aflag = Sflag = 0;
vflag = cflag = qflag = aflag = sflag = 0;
#if defined (ARRAY_VARS)
aname = NULL;
v = NULL;
ind = 0;
#endif
reset_internal_getopt();
while ((opt = internal_getopt (list, "a:Scsv")) != -1) {
while ((opt = internal_getopt (list, "a:cqsv")) != -1) {
switch (opt) {
#if defined (ARRAY_VARS)
case 'a':
@@ -98,12 +98,12 @@ realpath_builtin(WORD_LIST *list)
case 'c':
cflag = 1;
break;
case 'q':
qflag = 1;
break;
case 's':
sflag = 1;
break;
case 'S':
Sflag = 1;
break;
case 'v':
vflag = 1;
break;
@@ -146,7 +146,7 @@ realpath_builtin(WORD_LIST *list)
for (es = EXECUTION_SUCCESS; list; list = list->next) {
p = list->word->word;
if (Sflag) {
if (sflag) {
/* sh_canonpath doesn't convert to absolute pathnames */
newpath = make_absolute(p, get_string_value("PWD"));
r = sh_canonpath(newpath, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
@@ -155,27 +155,26 @@ realpath_builtin(WORD_LIST *list)
r = sh_realpath(p, realbuf);
if (r == 0) {
es = EXECUTION_FAILURE;
if (sflag == 0)
if (qflag == 0)
builtin_error("%s: cannot resolve: %s", p, strerror(errno));
continue;
}
if (cflag && (stat(r, &sb) < 0)) {
es = EXECUTION_FAILURE;
if (sflag == 0)
if (qflag == 0)
builtin_error("%s: %s", p, strerror(errno));
continue;
}
if (sflag == 0) {
if (aflag) {
bind_array_element (v, ind, r, 0);
ind++;
} else {
if (vflag)
printf ("%s -> ", p);
printf("%s\n", r);
}
if (aflag) {
bind_array_element (v, ind, r, 0);
ind++;
}
if (Sflag)
if (qflag == 0) {
if (vflag)
printf ("%s -> ", p);
printf("%s\n", r);
}
if (sflag)
free (r);
}
return es;
@@ -186,11 +185,13 @@ char *realpath_doc[] = {
"",
"Display the canonicalized version of each PATHNAME argument, resolving",
"symbolic links.",
"If the -S option is supplied, canonicalize . and .. pathname components",
"without resolving symbolic links.",
"The -a option stores each canonicalized PATHNAME argument into the indexed",
"array VARNAME.",
"The -c option checks whether or not each resolved name exists.",
"The -s option produces no output; the exit status determines the",
"validity of each PATHNAME.",
"The -q option produces no output; the exit status determines the",
"validity of each PATHNAME, but any array assignment is still performed.",
"If the -s option is supplied, canonicalize . and .. pathname components",
"without resolving symbolic links.",
"The -v option produces verbose output.",
"The exit status is 0 if each PATHNAME was resolved; non-zero otherwise.",
(char *)NULL
@@ -201,6 +202,6 @@ struct builtin realpath_struct = {
realpath_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
realpath_doc, /* array of long documentation strings */
"realpath [-Scsv] pathname [pathname...]", /* usage synopsis */
"realpath [-a varname] [-cqsv] pathname [pathname...]", /* usage synopsis */
0 /* reserved for internal use */
};
+25 -19
View File
@@ -1,6 +1,6 @@
/* findcmd.c -- Functions to search for commands by name. */
/* Copyright (C) 1997-2021 Free Software Foundation, Inc.
/* Copyright (C) 1997-2022 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
@@ -54,8 +54,8 @@ extern int errno;
/* Static functions defined and used in this file. */
static char *_find_user_command_internal PARAMS((const char *, int));
static char *find_user_command_internal PARAMS((const char *, int));
static char *find_user_command_in_path PARAMS((const char *, char *, int));
static char *find_in_path_element PARAMS((const char *, char *, int, int, struct stat *));
static char *find_user_command_in_path PARAMS((const char *, char *, int, int *));
static char *find_in_path_element PARAMS((const char *, char *, int, int, struct stat *, int *));
static char *find_absolute_program PARAMS((const char *, int));
static char *get_next_path_element PARAMS((char *, int *));
@@ -274,7 +274,7 @@ _find_user_command_internal (name, flags)
if (path_list == 0 || *path_list == '\0')
return (savestring (name));
cmd = find_user_command_in_path (name, path_list, flags);
cmd = find_user_command_in_path (name, path_list, flags, (int *)0);
return (cmd);
}
@@ -384,7 +384,7 @@ search_for_command (pathname, flags)
else
path_list = 0;
command = find_user_command_in_path (pathname, path_list, FS_EXEC_PREFERRED|FS_NODIRS);
command = find_user_command_in_path (pathname, path_list, FS_EXEC_PREFERRED|FS_NODIRS, &st);
if (command && hashing_enabled && temp_path == 0 && (flags & CMDSRCH_HASH))
{
@@ -393,7 +393,6 @@ search_for_command (pathname, flags)
table unless it's an executable file in the current directory. */
if (STREQ (command, pathname))
{
st = file_status (command);
if (st & FS_EXECABLE)
phash_insert ((char *)pathname, command, dot_found_in_search, 1);
}
@@ -401,7 +400,6 @@ search_for_command (pathname, flags)
to the hash table. */
else if (posixly_correct || check_hashed_filenames)
{
st = file_status (command);
if (st & FS_EXECABLE)
phash_insert ((char *)pathname, command, dot_found_in_search, 1);
}
@@ -469,8 +467,7 @@ user_command_matches (name, flags, state)
if (path_element == 0)
break;
match = find_in_path_element (name, path_element, flags, name_len, &dotinfo);
match = find_in_path_element (name, path_element, flags, name_len, &dotinfo, (int *)0);
free (path_element);
if (match == 0)
@@ -523,11 +520,12 @@ find_absolute_program (name, flags)
}
static char *
find_in_path_element (name, path, flags, name_len, dotinfop)
find_in_path_element (name, path, flags, name_len, dotinfop, rflagsp)
const char *name;
char *path;
int flags, name_len;
struct stat *dotinfop;
int *rflagsp;
{
int status;
char *full_path, *xpath;
@@ -548,6 +546,9 @@ find_in_path_element (name, path, flags, name_len, dotinfop)
if (xpath != path)
free (xpath);
if (rflagsp)
*rflagsp = status;
if ((status & FS_EXISTS) == 0)
{
free (full_path);
@@ -606,19 +607,22 @@ find_in_path_element (name, path, flags, name_len, dotinfop)
FS_NODIRS: Don't find any directories.
*/
static char *
find_user_command_in_path (name, path_list, flags)
find_user_command_in_path (name, path_list, flags, rflagsp)
const char *name;
char *path_list;
int flags;
int flags, *rflagsp;
{
char *full_path, *path;
int path_index, name_len;
int path_index, name_len, rflags;
struct stat dotinfo;
/* We haven't started looking, so we certainly haven't seen
a `.' as the directory path yet. */
dot_found_in_search = 0;
if (rflagsp)
*rflagsp = 0;
if (absolute_program (name))
{
full_path = find_absolute_program (name, flags);
@@ -645,12 +649,12 @@ find_user_command_in_path (name, path_list, flags)
/* Side effects: sets dot_found_in_search, possibly sets
file_to_lose_on. */
full_path = find_in_path_element (name, path, flags, name_len, &dotinfo);
full_path = find_in_path_element (name, path, flags, name_len, &dotinfo, &rflags);
free (path);
/* This should really be in find_in_path_element, but there isn't the
right combination of flags. */
if (full_path && is_directory (full_path))
/* We use the file status flag bits to check whether full_path is a
directory, which we reject here. */
if (full_path && (rflags & FS_DIRECTORY))
{
free (full_path);
continue;
@@ -658,6 +662,8 @@ find_user_command_in_path (name, path_list, flags)
if (full_path)
{
if (rflagsp)
*rflagsp = rflags;
FREE (file_to_lose_on);
return (full_path);
}
@@ -669,7 +675,7 @@ find_user_command_in_path (name, path_list, flags)
search would accept a non-executable as a last resort. If the
caller specified FS_NODIRS, and file_to_lose_on is a directory,
return NULL. */
if (file_to_lose_on && (flags & FS_NODIRS) && is_directory (file_to_lose_on))
if (file_to_lose_on && (flags & FS_NODIRS) && file_isdir (file_to_lose_on))
{
free (file_to_lose_on);
file_to_lose_on = (char *)NULL;
@@ -686,5 +692,5 @@ find_in_path (name, path_list, flags)
char *path_list;
int flags;
{
return (find_user_command_in_path (name, path_list, flags));
return (find_user_command_in_path (name, path_list, flags, (int *)0));
}
+9 -1
View File
@@ -50,6 +50,7 @@
#include "history.h"
#include "rlprivate.h"
#include "histlib.h"
#include "rlshell.h"
#include "xmalloc.h"
@@ -381,6 +382,8 @@ rl_maybe_save_line (void)
int
_rl_free_saved_history_line (void)
{
UNDO_LIST *orig;
if (_rl_saved_line_for_history)
{
if (rl_undo_list && rl_undo_list == (UNDO_LIST *)_rl_saved_line_for_history->data)
@@ -390,7 +393,12 @@ _rl_free_saved_history_line (void)
callers that know this is _rl_saved_line_for_history can know that
it's an undo list. */
if (_rl_saved_line_for_history->data)
_rl_free_undo_list ((UNDO_LIST *)_rl_saved_line_for_history->data);
{
orig = rl_undo_list;
rl_undo_list = _rl_saved_line_for_history->data;
rl_free_undo_list ();
rl_undo_list = orig;
}
_rl_free_history_entry (_rl_saved_line_for_history);
_rl_saved_line_for_history = (HIST_ENTRY *)NULL;
}
+5 -5
View File
@@ -88,6 +88,11 @@ make_history_line_current (HIST_ENTRY *entry)
if (rl_undo_list && rl_undo_list != (UNDO_LIST *)entry->data)
rl_free_undo_list ();
/* This will need to free the saved undo list associated with the original
(pre-search) line buffer. */
if (_rl_saved_line_for_history)
_rl_free_saved_history_line ();
/* Now we create a new undo list with a single insert for this text.
WE DON'T CHANGE THE ORIGINAL HISTORY ENTRY UNDO LIST */
_rl_replace_text (entry->line, 0, rl_end);
@@ -100,11 +105,6 @@ make_history_line_current (HIST_ENTRY *entry)
current editing buffer. */
rl_free_undo_list ();
#endif
/* This will need to free the saved undo list associated with the original
(pre-search) line buffer. */
if (_rl_saved_line_for_history)
_rl_free_saved_history_line ();
}
/* Search the history list for STRING starting at absolute history position
+3 -3
View File
@@ -72,9 +72,9 @@ argv[1] = <a^A)b>
argv[1] = <a^Ab>
argv[1] = <^A>
argv[1] = <\^A>
0000000 a $ ' \ 0 1 ' b \n a 001 b \n
0000000 a $ ' \ 0 1 ' b \n a 001 b \n
0000015
0000000 a $ ' \ 0 1 ' b \n a 001 b \n
0000000 a $ ' \ 0 1 ' b \n a 001 b \n
0000015
0000000 A \n A \n
0000000 A \n A \n
0000004
+18 -4
View File
@@ -1,22 +1,36 @@
# 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 <http://www.gnu.org/licenses/>.
#
. test-glue-functions
recho $( echo a$'\01)'b )
recho $( echo ab )
recho $( echo \ )
recho $( echo \\ )
LC_CTYPE=C
od -c <<EOF
od -c <<EOF | _intl_normalize_spaces
a$'\01'b
ab
EOF
od -c <<EOF
od -c <<EOF | _intl_normalize_spaces
${none-a$'\01'b}
${none-ab}
EOF
V=Aa$'\01'b
od -c <<EOF
od -c <<EOF | _intl_normalize_spaces
${V%a$'\01'b}
${V%ab}
EOF
-25
View File
@@ -691,31 +691,6 @@ initialize_shell_variables (env, privmode)
rl_prefer_env_winsize = 1;
#endif /* READLINE && STRICT_POSIX */
/*
* 24 October 2001
*
* I'm tired of the arguing and bug reports. Bash now leaves SSH_CLIENT
* and SSH2_CLIENT alone. I'm going to rely on the shell_level check in
* isnetconn() to avoid running the startup files more often than wanted.
* That will, of course, only work if the user's login shell is bash, so
* I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
* in config-top.h.
*/
#if 0
temp_var = find_variable ("SSH_CLIENT");
if (temp_var && imported_p (temp_var))
{
VUNSETATTR (temp_var, att_exported);
array_needs_making = 1;
}
temp_var = find_variable ("SSH2_CLIENT");
if (temp_var && imported_p (temp_var))
{
VUNSETATTR (temp_var, att_exported);
array_needs_making = 1;
}
#endif
/* Get the user's real and effective user ids. */
uidset ();