diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index ac14de65..b1d47e03 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -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 + + 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 + + 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 , but does not make his + scenario equivalent to incremental search + + diff --git a/builtins/hash.def b/builtins/hash.def index b65f42ee..03ae5474 100644 --- a/builtins/hash.def +++ b/builtins/hash.def @@ -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)); diff --git a/doc/bash.1 b/doc/bash.1 index 02f24b24..d7380c71 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -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 diff --git a/doc/bashref.texi b/doc/bashref.texi index a504ab64..94f97ab1 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -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}) diff --git a/doc/version.texi b/doc/version.texi index b6af0f02..78e22b8e 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -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 diff --git a/examples/loadables/realpath.c b/examples/loadables/realpath.c index 81c96aa1..ef836713 100644 --- a/examples/loadables/realpath.c +++ b/examples/loadables/realpath.c @@ -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 */ }; diff --git a/findcmd.c b/findcmd.c index 30e55954..0bf20796 100644 --- a/findcmd.c +++ b/findcmd.c @@ -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)); } diff --git a/lib/readline/misc.c b/lib/readline/misc.c index 622ebec5..96d37b8d 100644 --- a/lib/readline/misc.c +++ b/lib/readline/misc.c @@ -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; } diff --git a/lib/readline/search.c b/lib/readline/search.c index 89f5961a..c67519a4 100644 --- a/lib/readline/search.c +++ b/lib/readline/search.c @@ -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 diff --git a/tests/nquote.right b/tests/nquote.right index 918aa429..31c35c3b 100644 --- a/tests/nquote.right +++ b/tests/nquote.right @@ -72,9 +72,9 @@ argv[1] = argv[1] = 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 diff --git a/tests/nquote5.sub b/tests/nquote5.sub index 8a2870e4..97cbadc4 100644 --- a/tests/nquote5.sub +++ b/tests/nquote5.sub @@ -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 . +# +. test-glue-functions + recho $( echo a$'\01)'b ) recho $( echo ab ) recho $( echo \ ) recho $( echo \\ ) LC_CTYPE=C -od -c <