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
+
+
continue;
else if (pathname)
{
- if (is_directory (pathname))
+ if (file_isdir (pathname))
{
#ifdef EISDIR
builtin_error ("%s: %s", pathname, strerror (EISDIR));
.\" 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.
.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
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
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
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})
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
/*
* 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
*
*
*/
/*
- 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
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)
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':
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;
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);
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 (qflag == 0) {
+ if (vflag)
+ printf ("%s -> ", p);
+ printf("%s\n", r);
}
- if (Sflag)
+ if (sflag)
free (r);
}
return es;
"",
"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
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 */
};
/* 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.
/* 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 *));
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);
}
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))
{
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);
}
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);
}
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)
}
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;
if (xpath != path)
free (xpath);
+ if (rflagsp)
+ *rflagsp = status;
+
if ((status & FS_EXISTS) == 0)
{
free (full_path);
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);
/* 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;
if (full_path)
{
+ if (rflagsp)
+ *rflagsp = rflags;
FREE (file_to_lose_on);
return (full_path);
}
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;
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));
}
#include "history.h"
#include "rlprivate.h"
+#include "histlib.h"
#include "rlshell.h"
#include "xmalloc.h"
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)
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;
}
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);
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
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
+# 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 a\ 1b )
recho $( echo \\ 1 )
recho $( echo \\\ 1 )
LC_CTYPE=C
-od -c <<EOF
+od -c <<EOF | _intl_normalize_spaces
a$'\01'b
a\ 1b
EOF
-od -c <<EOF
+od -c <<EOF | _intl_normalize_spaces
${none-a$'\01'b}
${none-a\ 1b}
EOF
V=Aa$'\01'b
-od -c <<EOF
+od -c <<EOF | _intl_normalize_spaces
${V%a$'\01'b}
${V%a\ 1b}
EOF
-
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 ();