From 553a7d66e468df13912329b6a05891937644a00b Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Thu, 3 Nov 2016 17:28:23 -0400 Subject: [PATCH] commit bash-20161028 snapshot --- CWRU/CWRU.chlog | 127 ++++++++++ MANIFEST | 2 + array.c | 41 +--- array.h | 1 + arrayfunc.c | 6 +- bashhist.c | 28 ++- bashintl.h | 4 + builtins/evalstring.c | 6 - builtins/pushd.def | 7 +- doc/bash.1 | 47 +++- doc/bashref.texi | 30 +++ doc/version.texi | 6 +- examples/loadables/Makefile.in | 10 +- examples/loadables/README | 1 + examples/loadables/cat.c | 2 + examples/loadables/rm.c | 175 ++++++++++++++ examples/loadables/stat.c | 430 +++++++++++++++++++++++++++++++++ execute_cmd.c | 15 +- externs.h | 3 + jobs.c | 15 ++ jobs.h | 1 + lib/glob/sm_loop.c | 9 + lib/malloc/malloc.c | 28 +++ lib/readline/colors.c | 18 +- lib/readline/histfile.c | 4 + locale.c | 18 ++ parse.y | 11 +- pathexp.c | 10 + subst.c | 30 ++- variables.c | 66 +++++ 30 files changed, 1077 insertions(+), 74 deletions(-) create mode 100644 examples/loadables/rm.c create mode 100644 examples/loadables/stat.c diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 73197636..59daed23 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -12013,3 +12013,130 @@ variables.c doc/{bash.1,bashref.texi} - BASHPID: note that assignments are ignored and unsetting BASHPID causes it to lose its special properties + + 10/28 + ----- +builtins/pushd.def + - popd_builtin: make sure to check the normalized stack offset + (i.e., negatives counting back from the end of the stack) is within + bounds before trying to free that stack entry. Report from + Fernando Muñoz + +lib/readline/histfile.c + - chown: protect calls with HAVE_CHOWN. Fixes for MinGW{64,32} from + J. Peter Mugaas + +lib/readline/colors.c + - S_ISDIR: add define if missing + - S_ISUID, S_ISGID, S_ISLNK, S_ISSOCK: don't use unless defined. + Fixes for MinGW{64,32} from J. Peter Mugaas + +bashhist.c + - current_command_line_comment: set to the most recent line in a + possibly multi-line command that contains a shell comment; set in + maybe_add_history and bash_add_history to either the current + command number or -2 + - maybe_add_history: set current_command_line_comment for first line + of command + - bash_add_history: chars_to_add set to "\n" if the current command line + is one greater than the previous line containing a comment + (current_command_line_comment). Fixes complaint from back in + January, 2016 from Dave Rutherford + +array.h + - lastref: move last-referenced pointer into each array struct, so all + arrays can have reference locality + +array.c + - array_create: set lastref element to 0 + - array_copy: if copying the lastref in array a, set the new lastref + in the copy to the same element + - IS_LASTREF, LASTREF_START, LASTREF, INVALIDATE_LASTREF, SET_LASTREF, + UNSET_LASTREF: change to use new array `lastref' member + +pathexp.c + - quote_string_for_globbing: if quoting for a regexp, make sure to + skip and copy a leading `^' in a bracket expression, and skip and + copy a subsequent `]' (after an optional `^'), so that leading + bracket doesn't close the bracket expression. Report from + Stephane Chazelas + + 10/29 + ----- +subst.c + - extract_delimited_string: add calls to CHECK_STRING_OVERRUN for + $( inside $((, nested OPENERs, nested ALT_OPENERs, and backquotes + - skip_to_delim: add calls to CHECK_STRING_OVERRUN for $(, ${, + process substitution + - extract_dollar_brace_string: add calls to CHECK_STRING_OVERRUN for + $( + - extract_dollar_brace_string: use skipsubscript to skip over the + array subscript in ${var[sub]} (non-quoted case uses string_extract, + which already uses skipsubscript). Tagged for bash-5.0 + + 10/30 + ----- +variables.c + - EPOCHSECONDS: new dynamic variable, time in seconds since Unix + epoch; assignments are ignored. Can be used on systems where + strftime() doesn't support '%s' + - EPOCHREALTIME: new dynamic variable, time in seconds since Unix + epoch with microsecond granularity + +doc/{bash.1,bashref.texi} + - EPOCHSECONDS documentation + - EPOCHREALTIME documentation + +execute_cmd.c + - decpoint: moved to locale.c, renamed locale_decpoint; changed callers + +{bashintl,externs}.h + - locale_decpoint: extern declaration or #define if support for + localeconv() not there + + 10/31 + ----- +lib/malloc/malloc.c + - posix_memalign: add new posix-mandated interface + +examples/loadables/rm.c + - rm: minimal loadable builtin, removes files and directories, only + handles -r and -f options. Original from Tim Ruehsen + , heavily rewritten for inclusion as loadable + +examples/loadables/Makefile.in + - rm: add rules to build rm as one of the standard targets + +examples/loadables/stat.c + - stat: new loadable builtin that takes a filename and loads the info + returned by stat(2) into an associative array specified by the -A + argument (default STAT) + + 11/1 + ---- +variables.c + - BASH_ARGV0: new dynamic variable, returns $0 on reference and sets + $0 on assignment. From a suggestion from Rocky Bernstein + a few years ago + +doc/{bash.1,bashref.texi} + - BASH_ARGV0: document + + 11/2 + ---- +lib/glob/sm_loop.c + - parse_collsym: make sure to not return an out-of-bounds read if a + collating symbol is unterminated. Fixes OOB read reported by + op7ic \x00 + - brackmatch: after incrementing p, before checking whether it's a + character range, check whether *p was NULL before the increment + and short-circuit the bracket expression if it is + +jobs.c + - discard_last_procsub_child: new function, safely discards + last_procsub_child and sets it to NULL + +subst.c + - process_substitute: call discard_last_procsub_child instead of + calling discard_pipeline directly. Fixes bug reported by + Christian Weisgerber diff --git a/MANIFEST b/MANIFEST index a306b6ff..de5e184b 100644 --- a/MANIFEST +++ b/MANIFEST @@ -681,6 +681,7 @@ examples/loadables/dirname.c f examples/loadables/tty.c f examples/loadables/pathchk.c f examples/loadables/tee.c f +examples/loadables/rm.c f examples/loadables/rmdir.c f examples/loadables/head.c f examples/loadables/printenv.c f @@ -693,6 +694,7 @@ examples/loadables/mkdir.c f examples/loadables/ln.c f examples/loadables/mypid.c f examples/loadables/unlink.c f +examples/loadables/stat.c f examples/loadables/perl/Makefile.in f examples/loadables/perl/README f examples/loadables/perl/bperl.c f diff --git a/array.c b/array.c index 76ab5b6c..040fb792 100644 --- a/array.c +++ b/array.c @@ -63,39 +63,17 @@ static char *array_to_string_internal __P((ARRAY_ELEMENT *, ARRAY_ELEMENT *, char *, int)); -/* lastref should be moved into the array structure so each array can be - optimized separately */ - -static ARRAY *lastarray = 0; -static ARRAY_ELEMENT *lastref = 0; - -#define IS_LASTREF(a) (lastarray && (a) == lastarray) +#define IS_LASTREF(a) (a->lastref) #define LASTREF_START(a, i) \ - (IS_LASTREF(a) && i >= element_index(lastref)) ? lastref \ - : element_forw(a->head) + (IS_LASTREF(a) && i >= element_index(a->lastref)) ? a->lastref \ + : element_forw(a->head) -#define LASTREF(a) IS_LASTREF(a) ? lastref : element_forw(a->head) +#define LASTREF(a) (a->lastref ? a->lastref : element_forw(a->head)) -#define INVALIDATE_LASTREF(a) \ -do { \ - if ((a) == lastarray) { \ - lastarray = 0; \ - lastref = 0; \ - } \ -} while (0) - -#define SET_LASTREF(a, e) \ -do { \ - lastarray = (a); \ - lastref = (e); \ -} while (0) - -#define UNSET_LASTREF(a) \ -do { \ - lastarray = 0; \ - lastref = 0; \ -} while (0) +#define INVALIDATE_LASTREF(a) a->lastref = 0 +#define SET_LASTREF(a, e) a->lastref = (e) +#define UNSET_LASTREF(a) a->lastref = 0; ARRAY * array_create() @@ -107,6 +85,7 @@ array_create() r->type = array_indexed; r->max_index = -1; r->num_elements = 0; + r->lastref = (ARRAY_ELEMENT *)0; head = array_create_element(-1, (char *)NULL); /* dummy head */ head->prev = head->next = head; r->head = head; @@ -159,6 +138,8 @@ ARRAY *a; for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) { new = array_create_element(element_index(ae), element_value(ae)); ADD_BEFORE(a1->head, new); + if (ae == LASTREF(a)) + SET_LASTREF(a1, new); } return(a1); } @@ -722,7 +703,7 @@ arrayind_t i; if (a == 0 || array_empty(a)) return((ARRAY_ELEMENT *) NULL); if (i > array_max_index(a) || i < array_first_index(a)) - return((char *)NULL); /* Keep roving pointer into array to optimize sequential access */ + return((ARRAY_ELEMENT *)NULL); /* Keep roving pointer into array to optimize sequential access */ start = LASTREF(a); /* Use same strategy as array_reference to avoid paying large penalty for semi-random assignment pattern. */ diff --git a/array.h b/array.h index d79fc839..4f4d65be 100644 --- a/array.h +++ b/array.h @@ -33,6 +33,7 @@ typedef struct array { enum atype type; arrayind_t max_index; int num_elements; + struct array_element *lastref; struct array_element *head; } ARRAY; diff --git a/arrayfunc.c b/arrayfunc.c index 309acff8..786b8ec6 100644 --- a/arrayfunc.c +++ b/arrayfunc.c @@ -1086,15 +1086,17 @@ array_value_internal (s, quoted, flags, rtype, indp) return ((char *) NULL); } + /* Caller of array_value takes care of inspecting rtype and duplicating + retval if rtype == 0, so this is not a memory leak */ if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) { temp = string_list_dollar_star (l); - retval = quote_string (temp); /* XXX - leak here */ + retval = quote_string (temp); free (temp); } else /* ${name[@]} or unquoted ${name[*]} */ /* XXX - bash-4.4/bash-5.0 test AV_ASSIGNRHS and pass PF_ASSIGNRHS */ - retval = string_list_dollar_at (l, quoted, (flags & AV_ASSIGNRHS) ? PF_ASSIGNRHS : 0); /* XXX - leak here */ + retval = string_list_dollar_at (l, quoted, (flags & AV_ASSIGNRHS) ? PF_ASSIGNRHS : 0); dispose_words (l); } diff --git a/bashhist.c b/bashhist.c index 9979f99a..829952b4 100644 --- a/bashhist.c +++ b/bashhist.c @@ -140,6 +140,11 @@ int command_oriented_history = 1; the history-manipluating builtins can see it. */ int current_command_first_line_saved = 0; +/* Set to the number of the most recent line of a possibly-multi-line command + that contains a shell comment. Used by bash_add_history() to determine + whether to add a newline or a semicolon. */ +int current_command_line_comment = 0; + /* Non-zero means to store newlines in the history list when using command_oriented_history rather than trying to use semicolons. */ int literal_history; @@ -597,16 +602,24 @@ pre_process_line (line, print_changes, addit) } /* Return 1 if the first non-whitespace character in LINE is a `#', indicating - * that the line is a shell comment. */ + that the line is a shell comment. Return 2 if there is a comment after the + first non-whitespace character. Return 0 if the line does not contain a + comment. */ static int shell_comment (line) char *line; { char *p; + int n; + if (line == 0) + return 0; for (p = line; p && *p && whitespace (*p); p++) ; - return (p && *p == '#'); + if (p && *p == '#') + return 1; + n = skip_to_delim (line, p - line, "#", SD_NOJMP|SD_GLOB|SD_EXTGLOB|SD_COMPLETE); + return (line[n] == '#') ? 2 : 0; } #ifdef INCLUDE_UNUSED @@ -708,13 +721,14 @@ maybe_add_history (line) if (current_command_line_count > 1) { if (current_command_first_line_saved && - ((parser_state & PST_HEREDOC) || literal_history || dstack.delimiter_depth != 0 || shell_comment (line) == 0)) + ((parser_state & PST_HEREDOC) || literal_history || dstack.delimiter_depth != 0 || shell_comment (line) != 1)) bash_add_history (line); return; } /* This is the first line of a (possible multi-line) command. Note whether or not we should save the first line and remember it. */ + current_command_line_comment = shell_comment (line) ? current_command_line_count : -2; current_command_first_line_saved = check_add_history (line, 0); } @@ -803,12 +817,18 @@ bash_add_history (line) so we have to duplicate some of what that function does here. */ if ((parser_state & PST_HEREDOC) && literal_history && current_command_line_count > 2 && line[strlen (line) - 1] == '\n') chars_to_add = ""; + else if (current_command_line_count == current_command_line_comment+1) + chars_to_add = "\n"; + else if (literal_history) + chars_to_add = "\n"; else - chars_to_add = literal_history ? "\n" : history_delimiting_chars (line); + chars_to_add = history_delimiting_chars (line); using_history (); current = previous_history (); + current_command_line_comment = shell_comment (line) ? current_command_line_count : -2; + if (current) { /* If the previous line ended with an escaped newline (escaped diff --git a/bashintl.h b/bashintl.h index 7e6b83fc..dd326833 100644 --- a/bashintl.h +++ b/bashintl.h @@ -47,4 +47,8 @@ # define setlocale(cat, loc) #endif +#if !defined (HAVE_LOCALE_H) || !defined (HAVE_LOCALECONV) +# define locale_decpoint() '.' +#endif + #endif /* !_BASHINTL_H_ */ diff --git a/builtins/evalstring.c b/builtins/evalstring.c index 78caaffb..7e1487e3 100644 --- a/builtins/evalstring.c +++ b/builtins/evalstring.c @@ -139,19 +139,13 @@ optimize_subshell_command (command) ((command->flags & CMD_TIME_PIPELINE) == 0) && ((command->flags & CMD_INVERT_RETURN) == 0)) { -itrace("optimize_subshell_command: optimizing simple command"); command->flags |= CMD_NO_FORK; command->value.Simple->flags |= CMD_NO_FORK; } else if (command->type == cm_connection && (command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR)) -{ -itrace("optimize_subshell_command: attempting to optimize connection"); optimize_subshell_command (command->value.Connection->second); } - else - itrace("optimize_subshell_command: command type = %d", command->type); -} /* How to force parse_and_execute () to clean up after itself. */ void diff --git a/builtins/pushd.def b/builtins/pushd.def index 82653c47..6579e4c8 100644 --- a/builtins/pushd.def +++ b/builtins/pushd.def @@ -365,7 +365,7 @@ popd_builtin (list) break; } - if (which > directory_list_offset || (directory_list_offset == 0 && which == 0)) + if (which > directory_list_offset || (which < -directory_list_offset) || (directory_list_offset == 0 && which == 0)) { pushd_error (directory_list_offset, which_word ? which_word : ""); return (EXECUTION_FAILURE); @@ -387,6 +387,11 @@ popd_builtin (list) remove that directory from the list and shift the remainder of the list into place. */ i = (direction == '+') ? directory_list_offset - which : which; + if (i < 0 || i > directory_list_offset) + { + pushd_error (directory_list_offset, which_word ? which_word : ""); + return (EXECUTION_FAILURE); + } free (pushd_directory_list[i]); directory_list_offset--; diff --git a/doc/bash.1 b/doc/bash.1 index 205b2b3b..9e043cd5 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -5,12 +5,12 @@ .\" Case Western Reserve University .\" chet.ramey@case.edu .\" -.\" Last Change: Tue Sep 27 14:02:28 EDT 2016 +.\" Last Change: Tue Nov 1 16:05:17 EDT 2016 .\" .\" bash_builtins, strip all but Built-Ins section .if \n(zZ=1 .ig zZ .if \n(zY=1 .ig zY -.TH BASH 1 "2016 September 27" "GNU Bash 4.4" +.TH BASH 1 "2016 November 1" "GNU Bash 4.4" .\" .\" There's some problem with having a `@' .\" in a tagged paragraph with the BSD man macros. @@ -1509,6 +1509,19 @@ option to the .B shopt builtin below) .TP +.B BASH_ARGV0 +When referenced, this variable expands to the name of the shell or shell +script (identical to +.BR $0 ; +see the description of special parameter 0 above). +Assignment to +.B BASH_ARGV0 +causes the value assigned to also be assigned to \fB$0\fP. +If +.B BASH_ARGV0 +is unset, it loses its special properties, even if it is +subsequently reset. +.TP .B BASH_CMDS An associative array variable whose members correspond to the internal hash table of commands as maintained by the \fBhash\fP builtin. @@ -1695,6 +1708,33 @@ If is unset, it loses its special properties, even if it is subsequently reset. .TP +.B EPOCHREALTIME +Each time this parameter is referenced, it expands to the number of seconds +since the Unix Epoch (see \fItime\fP\fR(3)\fP) as a floating point value +with micro-second granularity. +Assignments to +.SM +.B EPOCHREALTIME +are ignored. +If +.SM +.B EPOCHREALTIME +is unset, it loses its special properties, even if it is +subsequently reset. +.TP +.B EPOCHSECONDS +Each time this parameter is referenced, it expands to the number of seconds +since the Unix Epoch (see \fItime\fP\fR(3)\fP). +Assignments to +.SM +.B EPOCHSECONDS +are ignored. +If +.SM +.B EPOCHSECONDS +is unset, it loses its special properties, even if it is +subsequently reset. +.TP .B EUID Expands to the effective user ID of the current user, initialized at shell startup. This variable is readonly. @@ -3704,6 +3744,9 @@ than or equal to 10 and assign it to \fIvarname\fP. If >&- or <&- is preceded by {\fIvarname\fP}, the value of \fIvarname\fP defines the file descriptor to close. +If {\fIvarname\fP} is supplied, the redirection persists beyond +the scope of the command, allowing the shell programmer to manage +the file descriptor himself. .PP In the following descriptions, if the file descriptor number is omitted, and the first character of the redirection operator is diff --git a/doc/bashref.texi b/doc/bashref.texi index 9c7c0c73..7072cdff 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -2567,6 +2567,9 @@ In this case, for each redirection operator except than 10 and assign it to @{@var{varname}@}. If >&- or <&- is preceded by @{@var{varname}@}, the value of @var{varname} defines the file descriptor to close. +If @{@var{varname}@} is supplied, the redirection persists beyond +the scope of the command, allowing the shell programmer to manage +the file descriptor himself. In the following descriptions, if the file descriptor number is omitted, and the first character of the redirection operator is @@ -5519,6 +5522,16 @@ The shell sets @code{BASH_ARGV} only when in extended debugging mode for a description of the @code{extdebug} option to the @code{shopt} builtin). +@item BASH_ARGV0 +When referenced, this variable expands to the name of the shell or shell +script (identical to @code{$0}; @xref{Special Parameters}, +for the description of special parameter 0). +Assignment to @code{BASH_ARGV0} +causes the value assigned to also be assigned to @code{$0}. +If @code{BASH_ARGV0} +is unset, it loses its special properties, even if it is +subsequently reset. + @item BASH_CMDS An associative array variable whose members correspond to the internal hash table of commands as maintained by the @code{hash} builtin @@ -5734,6 +5747,23 @@ Emacs shell buffer and disables line editing. Similar to @code{BASH_ENV}; used when the shell is invoked in @sc{posix} Mode (@pxref{Bash POSIX Mode}). +@item EPOCHREALTIME +Each time this parameter is referenced, it expands to the number of seconds +since the Unix Epoch as a floating point value with micro-second granularity +(see the documentation for the C library function @var{time} for the +definition of Epoch). +Assignments to @env{EPOCHREALTIME} are ignored. +If @env{EPOCHREALTIME} is unset, it loses its special properties, even if +it is subsequently reset. + +@item EPOCHSECONDS +Each time this parameter is referenced, it expands to the number of seconds +since the Unix Epoch (see the documentation for the C library function +@var{time} for the definition of Epoch). +Assignments to @env{EPOCHSECONDS} are ignored. +If @env{EPOCHSECONDS} is unset, it loses its special properties, even if +it is subsequently reset. + @item EUID The numeric effective user id of the current user. This variable is readonly. diff --git a/doc/version.texi b/doc/version.texi index c2d30ce6..9e99170e 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -2,10 +2,10 @@ Copyright (C) 1988-2016 Free Software Foundation, Inc. @end ignore -@set LASTCHANGE Tue Sep 27 14:02:09 EDT 2016 +@set LASTCHANGE Tue Nov 1 16:08:43 EDT 2016 @set EDITION 4.4 @set VERSION 4.4 -@set UPDATED 27 September 2016 -@set UPDATED-MONTH September 2016 +@set UPDATED 1 November 2016 +@set UPDATED-MONTH November 2016 diff --git a/examples/loadables/Makefile.in b/examples/loadables/Makefile.in index ec305cda..709f701e 100644 --- a/examples/loadables/Makefile.in +++ b/examples/loadables/Makefile.in @@ -103,7 +103,7 @@ INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins -I${srcdir} \ ALLPROG = print truefalse sleep finfo logname basename dirname \ tty pathchk tee head mkdir rmdir printenv id whoami \ uname sync push ln unlink realpath strftime mypid setpgid -OTHERPROG = necho hello cat pushd +OTHERPROG = necho hello cat pushd stat rm all: $(SHOBJ_STATUS) @@ -142,6 +142,9 @@ finfo: finfo.o cat: cat.o $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ cat.o $(SHOBJ_LIBS) +rm: rm.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ rm.o $(SHOBJ_LIBS) + logname: logname.o $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ logname.o $(SHOBJ_LIBS) @@ -202,10 +205,12 @@ strftime: strftime.o mypid: mypid.o $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mypid.o $(SHOBJ_LIBS) - setpgid: setpgid.o $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ setpgid.o $(SHOBJ_LIBS) +stat: stat.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ stat.o $(SHOBJ_LIBS) + # pushd is a special case. We use the same source that the builtin version # uses, with special compilation options. # @@ -283,3 +288,4 @@ mkdir.o: mkdir.c realpath.o: realpath.c strftime.o: strftime.c setpgid.o: setpgid.c +stat.o: stat.c diff --git a/examples/loadables/README b/examples/loadables/README index 2eae9cc6..318d0013 100644 --- a/examples/loadables/README +++ b/examples/loadables/README @@ -52,6 +52,7 @@ printenv.c Minimal builtin clone of BSD printenv(1). push.c Anyone remember TOPS-20? README README realpath.c Canonicalize pathnames, resolving symlinks. +rm.c Remove files and directories. rmdir.c Remove directory. sleep.c sleep for fractions of a second. strftime.c Loadable builtin interface to strftime(3). diff --git a/examples/loadables/cat.c b/examples/loadables/cat.c index 1ce2e2dc..be99c4cd 100644 --- a/examples/loadables/cat.c +++ b/examples/loadables/cat.c @@ -56,6 +56,7 @@ int fd; return 0; } +int cat_main (argc, argv) int argc; char **argv; @@ -88,6 +89,7 @@ char **argv; return (r); } +int cat_builtin(list) WORD_LIST *list; { diff --git a/examples/loadables/rm.c b/examples/loadables/rm.c new file mode 100644 index 00000000..6940befa --- /dev/null +++ b/examples/loadables/rm.c @@ -0,0 +1,175 @@ +/* rm - remove files and directories with -r */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 2016 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash 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. + + Bash 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 Bash. If not, see . +*/ + +#include "config.h" + +#include +#include +#include +#include "builtins.h" +#include "shell.h" +#include "common.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif + +static int rm_file(const char *fname); + +static int force, recursive; + +static int +_remove_directory(const char *dirname) +{ + DIR *dir; + struct dirent *dp; + size_t dirlen; + int err; + + dirlen = strlen (dirname); + err = 0; + + if ((dir = opendir(dirname))) + { + while ((dp = readdir(dir))) + { +#ifdef __GNUC__ + char fname[dirlen + 1 + strlen (dp->d_name) + 1]; +#else + char *fname; + int fnsize; +#endif + + if (*dp->d_name == '.' && (dp->d_name[1] == 0 || (dp->d_name[1] == '.' && dp->d_name[2] == 0))) + continue; + +#ifdef __GNUC__ + snprintf(fname, sizeof (fname), "%s/%s", dirname, dp->d_name); +#else + fnsize = dirlen + 1 + strlen (dp->d_name) + 1; + fname = xmalloc (fnsize); + snprintf(fname, fnsize, "%s/%s", dirname, dp->d_name); +#endif + + if (rm_file (fname) && force == 0) + err = 1; +#ifndef __GNUC__ + free (fname); +#endif + } + + closedir(dir); + + if (err == 0 && rmdir (dirname) && force == 0) + err = 1; + } + else if (force == 0) + err = 1; + + if (err) + builtin_error ("%s: %s", dirname, strerror (errno)); + + return err; +} + +static int +rm_file(const char *fname) +{ + if (unlink (fname) == 0) + return 0; + + /* If FNAME is a directory glibc returns EISDIR but correct POSIX value + would be EPERM. If we get that error and FNAME is a directory and -r + was supplied, recursively remove the directory and its contents */ + if ((errno == EISDIR || errno == EPERM) && recursive && file_isdir (fname)) + return _remove_directory(fname); + else if (force) + return 0; + + builtin_error ("%s: %s", fname, strerror (errno)); + return 1; +} + +int +rm_builtin (list) + WORD_LIST *list; +{ + const char *name; + WORD_LIST *l; + int rval, opt; + + recursive = force = 0; + rval = EXECUTION_SUCCESS; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "Rrf")) != -1) + { + switch (opt) + { + case 'R': + case 'r': + recursive = 1; + break; + case 'f': + force = 1; + break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) + { + builtin_usage (); + return (EXECUTION_FAILURE); + } + + for (l = list; l; l = l->next) + { + if (rm_file(l->word->word) && force == 0) + rval = EXECUTION_FAILURE; + } + + return rval; +} + +char *rm_doc[] = { + "Remove files.", + "", + "rm removes the files specified as arguments.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin rm_struct = { + "rm", /* builtin name */ + rm_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + rm_doc, /* array of long documentation strings. */ + "rm [-rf] file ...", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/stat.c b/examples/loadables/stat.c new file mode 100644 index 00000000..52b9580f --- /dev/null +++ b/examples/loadables/stat.c @@ -0,0 +1,430 @@ +/* stat - load up an associative array with stat information about a file */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 2016 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash 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. + + Bash 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 Bash. If not, see . +*/ + +#include + +#if defined (HAVE_UNISTD_H) +# include +#endif + +#include + +#include +#include "posixstat.h" +#include +#include +#include +#include +#include "posixtime.h" + +#include "bashansi.h" +#include "shell.h" +#include "builtins.h" +#include "common.h" +#include "bashgetopt.h" + +#ifndef errno +extern int errno; +#endif + +#define ST_NAME 0 +#define ST_DEV 1 +#define ST_INO 2 +#define ST_MODE 3 +#define ST_NLINK 4 +#define ST_UID 5 +#define ST_GID 6 +#define ST_RDEV 7 +#define ST_SIZE 8 +#define ST_ATIME 9 +#define ST_MTIME 10 +#define ST_CTIME 11 +#define ST_BLKSIZE 12 +#define ST_BLOCKS 13 +#define ST_CHASELINK 14 +#define ST_PERMS 15 + +#define ST_END 16 + +static char *arraysubs[] = + { + "name", "device", "inode", "type", "nlink", "uid", "gid", "rdev", + "size", "atime", "mtime", "ctime", "blksize", "blocks", "link", "perms", + 0 + }; + +static int +getstat (fname, flags, sp) + const char *fname; + int flags; + struct stat *sp; +{ + intmax_t lfd; + int fd, r; + + if (strncmp (fname, "/dev/fd/", 8) == 0) + { + if ((legal_number(fname + 8, &lfd) == 0) || (int)lfd != lfd) + { + errno = EINVAL; + return -1; + } + fd = lfd; + r = fstat(fd, sp); + } +#ifdef HAVE_LSTAT + else if (flags & 1) + r = lstat(fname, sp); +#endif + else + r = stat(fname, sp); + + return r; +} + +static char * +statlink (fname, sp) + char *fname; + struct stat *sp; +{ +#if defined (HAVE_READLINK) + char linkbuf[PATH_MAX]; + int n; + + if (fname && S_ISLNK (sp->st_mode) && (n = readlink (fname, linkbuf, PATH_MAX)) > 0) + { + linkbuf[n] = '\0'; + return (savestring (linkbuf)); + } + else +#endif + return (savestring (fname)); +} + +static char * +octalperms (m) + int m; +{ + int operms; + char *ret; + + operms = 0; + + if (m & S_IRUSR) + operms |= 0400; + if (m & S_IWUSR) + operms |= 0200; + if (m & S_IXUSR) + operms |= 0100; + + if (m & S_IRGRP) + operms |= 0040; + if (m & S_IWGRP) + operms |= 0020; + if (m & S_IXGRP) + operms |= 0010; + + if (m & S_IROTH) + operms |= 0004; + if (m & S_IWOTH) + operms |= 0002; + if (m & S_IXOTH) + operms |= 0001; + + if (m & S_ISUID) + operms |= 04000; + if (m & S_ISGID) + operms |= 02000; + if (m & S_ISVTX) + operms |= 01000; + + ret = (char *)xmalloc (16); + snprintf (ret, 16, "%04o", operms); + return ret; +} + +static char * +statperms (m) + int m; +{ + char ubits[4], gbits[4], obits[4]; /* u=rwx,g=rwx,o=rwx */ + int i; + char *ret; + + i = 0; + if (m & S_IRUSR) + ubits[i++] = 'r'; + if (m & S_IWUSR) + ubits[i++] = 'w'; + if (m & S_IXUSR) + ubits[i++] = 'x'; + ubits[i] = '\0'; + + i = 0; + if (m & S_IRGRP) + gbits[i++] = 'r'; + if (m & S_IWGRP) + gbits[i++] = 'w'; + if (m & S_IXGRP) + gbits[i++] = 'x'; + gbits[i] = '\0'; + + i = 0; + if (m & S_IROTH) + obits[i++] = 'r'; + if (m & S_IWOTH) + obits[i++] = 'w'; + if (m & S_IXOTH) + obits[i++] = 'x'; + obits[i] = '\0'; + + if (m & S_ISUID) + ubits[2] = (m & S_IXUSR) ? 's' : 'S'; + if (m & S_ISGID) + gbits[2] = (m & S_IXGRP) ? 's' : 'S'; + if (m & S_ISVTX) + obits[2] = (m & S_IXOTH) ? 't' : 'T'; + + ret = (char *)xmalloc (32); + snprintf (ret, 32, "u=%s,g=%s,o=%s", ubits, gbits, obits); + return ret; +} + +static char * +statmode(mode) + int mode; +{ + char *modestr, *m; + + modestr = m = (char *)xmalloc (8); + if (S_ISBLK (mode)) + *m++ = 'b'; + if (S_ISCHR (mode)) + *m++ = 'c'; + if (S_ISDIR (mode)) + *m++ = 'd'; + if (S_ISREG(mode)) + *m++ = '-'; + if (S_ISFIFO(mode)) + *m++ = 'p'; + if (S_ISLNK(mode)) + *m++ = 'l'; + if (S_ISSOCK(mode)) + *m++ = 's'; + +#ifdef S_ISDOOR + if (S_ISDOOR (mode)) + *m++ = 'D'; +#endif +#ifdef S_ISWHT + if (S_ISWHT(mode)) + *m++ = 'W'; +#endif +#ifdef S_ISNWK + if (S_ISNWK(mode)) + *m++ = 'n'; +#endif +#ifdef S_ISMPC + if (S_ISMPC (mode)) + *m++ = 'm'; +#endif + + *m = '\0'; + return (modestr); +} + +static char * +stattime (t) + time_t t; +{ + char *tbuf, *ret; + size_t tlen; + + tbuf = ctime (&t); + tlen = strlen (tbuf); + ret = savestring (tbuf); + ret[tlen-1] = '\0'; + return ret; +} + +static char * +statval (which, fname, flags, sp) + int which; + char *fname; + int flags; + struct stat *sp; +{ + int temp; + + switch (which) + { + case ST_NAME: + return savestring (fname); + case ST_DEV: + return itos (sp->st_dev); + case ST_INO: + return itos (sp->st_ino); + case ST_MODE: + return (statmode (sp->st_mode)); + case ST_NLINK: + return itos (sp->st_nlink); + case ST_UID: + return itos (sp->st_uid); + case ST_GID: + return itos (sp->st_gid); + case ST_RDEV: + return itos (sp->st_rdev); + case ST_SIZE: + return itos (sp->st_size); + case ST_ATIME: + return ((flags & 2) ? stattime (sp->st_atime) : itos (sp->st_atime)); + case ST_MTIME: + return ((flags & 2) ? stattime (sp->st_mtime) : itos (sp->st_mtime)); + case ST_CTIME: + return ((flags & 2) ? stattime (sp->st_ctime) : itos (sp->st_ctime)); + case ST_BLKSIZE: + return itos (sp->st_blksize); + case ST_BLOCKS: + return itos (sp->st_blocks); + case ST_CHASELINK: + return (statlink (fname, sp)); + case ST_PERMS: + temp = sp->st_mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID); + return (flags & 2) ? statperms (temp) : octalperms (temp); + default: + return savestring ("42"); + } +} + +static int +loadstat (vname, var, fname, flags, sp) + char *vname; + SHELL_VAR *var; + char *fname; + int flags; + struct stat *sp; +{ + int i; + char *key, *value; + SHELL_VAR *v; + + for (i = 0; arraysubs[i]; i++) + { + key = savestring (arraysubs[i]); + value = statval (i, fname, flags, sp); + v = bind_assoc_variable (var, vname, key, value, ASS_FORCE); + } + return 0; +} + +int +stat_builtin (list) + WORD_LIST *list; +{ + int opt, flags; + char *aname, *fname; + struct stat st; + SHELL_VAR *v; + + aname = "STAT"; + flags = 0; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "A:Ll")) != -1) + { + switch (opt) + { + case 'A': + aname = list_optarg; + break; + case 'L': + flags |= 1; /* operate on links rather than resolving them */ + break; + case 'l': + flags |= 2; + break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + + list = loptend; + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + fname = list->word->word; + + if (getstat (fname, flags, &st) < 0) + { + builtin_error ("%s: cannot stat: %s", fname, strerror (errno)); + return (EXECUTION_FAILURE); + } + + unbind_variable (aname); + v = make_new_assoc_variable (aname); + if (v == 0) + { + builtin_error ("%s: cannot create variable", aname); + return (EXECUTION_FAILURE); + } + if (loadstat (aname, v, fname, flags, &st) < 0) + { + builtin_error ("%s: cannot assign file status information", aname); + unbind_variable (aname); + return (EXECUTION_FAILURE); + } + + return (EXECUTION_SUCCESS); +} + +/* An array of strings forming the `long' documentation for a builtin xxx, + which is printed by `help xxx'. It must end with a NULL. By convention, + the first line is a short description. */ +char *stat_doc[] = { + "Load an associative array with file status information.", + "", + "Take a filename and load the status information returned by a", + "stat(2) call on that file into the associative array specified", + "by the -A option. The default array name is STAT. If the -L", + "option is supplied, stat does not resolve symbolic links and", + "reports information about the link itself. The -l option results", + "in longer-form listings for some of the fields. The exit status is 0", + "unless the stat fails or assigning the array is unsuccessful.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. The flags must include BUILTIN_ENABLED so the + builtin can be used. */ +struct builtin stat_struct = { + "stat", /* builtin name */ + stat_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + stat_doc, /* array of long documentation strings. */ + "stat [-lL] [-A aname] file", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/execute_cmd.c b/execute_cmd.c index 935ffe70..ced48fc7 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -1110,19 +1110,6 @@ extern int timeval_to_cpu __P((struct timeval *, struct timeval *, struct timeva static const int precs[] = { 0, 100, 10, 1 }; -#if defined (HAVE_LOCALE_H) && defined (HAVE_LOCALECONV) -static int -decpoint () -{ - struct lconv *lv; - - lv = localeconv (); - return (lv && lv->decimal_point && lv->decimal_point[0]) ? lv->decimal_point[0] : '.'; -} -#else -# define decpoint() '.' -#endif - /* Expand one `%'-prefixed escape sequence from a time format string. */ static int mkfmt (buf, prec, lng, sec, sec_fraction) @@ -1167,7 +1154,7 @@ mkfmt (buf, prec, lng, sec, sec_fraction) and 999. */ if (prec != 0) { - buf[ind++] = decpoint (); + buf[ind++] = locale_decpoint (); for (aind = 1; aind <= prec; aind++) { buf[ind++] = (sec_fraction / precs[aind]) + '0'; diff --git a/externs.h b/externs.h index fa0603d3..0ad223c6 100644 --- a/externs.h +++ b/externs.h @@ -137,6 +137,9 @@ extern char *get_locale_var __P((char *)); extern char *localetrans __P((char *, int, int *)); extern char *mk_msgstr __P((char *, int *)); extern char *localeexpand __P((char *, int, int, int, int *)); +#ifndef locale_decpoint +extern int locale_decpoint __P((void)); +#endif /* Declarations for functions defined in list.c. */ extern void list_walk __P((GENERIC_LIST *, sh_glist_func_t *)); diff --git a/jobs.c b/jobs.c index 537e992a..cd863e29 100644 --- a/jobs.c +++ b/jobs.c @@ -453,6 +453,21 @@ cleanup_the_pipeline () discard_pipeline (disposer); } +void +discard_last_procsub_child () +{ + PROCESS *disposer; + sigset_t set, oset; + + BLOCK_CHILD (set, oset); + disposer = last_procsub_child; + last_procsub_child = (PROCESS *)NULL; + UNBLOCK_CHILD (oset); + + if (disposer) + discard_pipeline (disposer); +} + struct pipeline_saver * alloc_pipeline_saver () { diff --git a/jobs.h b/jobs.h index 4ba3513c..6df0607e 100644 --- a/jobs.h +++ b/jobs.h @@ -190,6 +190,7 @@ extern JOB **jobs; extern void making_children __P((void)); extern void stop_making_children __P((void)); extern void cleanup_the_pipeline __P((void)); +extern void discard_last_procsub_child __P((void)); extern void save_pipeline __P((int)); extern PROCESS *restore_pipeline __P((int)); extern void start_pipeline __P((void)); diff --git a/lib/glob/sm_loop.c b/lib/glob/sm_loop.c index c3a2aa37..65179e24 100644 --- a/lib/glob/sm_loop.c +++ b/lib/glob/sm_loop.c @@ -330,6 +330,12 @@ PARSE_COLLSYM (p, vp) for (pc = 0; p[pc]; pc++) if (p[pc] == L('.') && p[pc+1] == L(']')) break; + if (p[pc] == 0) + { + if (vp) + *vp = INVALID; + return (p + pc); + } val = COLLSYM (p, pc); if (vp) *vp = val; @@ -483,6 +489,9 @@ BRACKMATCH (p, test, flags) c = *p++; c = FOLD (c); + if (c == L('\0')) + return ((test == L('[')) ? savep : (CHAR *)0); + if ((flags & FNM_PATHNAME) && c == L('/')) /* [/] can never match when matching a pathname. */ return (CHAR *)0; diff --git a/lib/malloc/malloc.c b/lib/malloc/malloc.c index 68fb5e79..2cff99dd 100644 --- a/lib/malloc/malloc.c +++ b/lib/malloc/malloc.c @@ -100,6 +100,12 @@ # include "watch.h" #endif +#ifdef powerof2 +# undef powerof2 +#endif +/* Could also use (((x) & -(x)) == (x)) */ +#define powerof2(x) ((((x) - 1) & (x)) == 0) + /* System-specific omissions. */ #ifdef HPUX # define NO_VALLOC @@ -1125,6 +1131,28 @@ internal_memalign (alignment, size, file, line, flags) return aligned; } +int +posix_memalign (memptr, alignment, size) + void **memptr; + size_t alignment, size; +{ + void *mem; + + /* Perform posix-mandated error checking here */ + if ((alignment % sizeof (void *) != 0) || alignment == 0) + return EINVAL; + else if (powerof2 (alignment) == 0) + return EINVAL; + + mem = internal_memalign (alignment, size, (char *)0, 0, 0); + if (mem != 0) + { + *memptr = mem; + return 0; + } + return ENOMEM; +} + #if !defined (NO_VALLOC) /* This runs into trouble with getpagesize on HPUX, and Multimax machines. Patching out seems cleaner than the ugly fix needed. */ diff --git a/lib/readline/colors.c b/lib/readline/colors.c index 7859f5d2..5d450ee0 100644 --- a/lib/readline/colors.c +++ b/lib/readline/colors.c @@ -37,6 +37,10 @@ #include "posixstat.h" // stat related macros (S_ISREG, ...) #include // S_ISUID +#ifndef S_ISDIR +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + // strlen() #if defined (HAVE_STRING_H) # include @@ -183,11 +187,17 @@ _rl_print_color_indicator (const char *f) { colored_filetype = C_FILE; +#if defined (S_ISUID) if ((mode & S_ISUID) != 0 && is_colored (C_SETUID)) colored_filetype = C_SETUID; - else if ((mode & S_ISGID) != 0 && is_colored (C_SETGID)) + else +#endif +#if defined (S_ISGID) + if ((mode & S_ISGID) != 0 && is_colored (C_SETGID)) colored_filetype = C_SETGID; - else if (is_colored (C_CAP) && 0) //f->has_capability) + else +#endif + if (is_colored (C_CAP) && 0) //f->has_capability) colored_filetype = C_CAP; else if ((mode & S_IXUGO) != 0 && is_colored (C_EXEC)) colored_filetype = C_EXEC; @@ -211,12 +221,16 @@ _rl_print_color_indicator (const char *f) colored_filetype = C_STICKY; #endif } +#if defined (S_ISLNK) else if (S_ISLNK (mode)) colored_filetype = C_LINK; +#endif else if (S_ISFIFO (mode)) colored_filetype = C_FIFO; +#if defined (S_ISSOCK) else if (S_ISSOCK (mode)) colored_filetype = C_SOCK; +#endif else if (S_ISBLK (mode)) colored_filetype = C_BLK; else if (S_ISCHR (mode)) diff --git a/lib/readline/histfile.c b/lib/readline/histfile.c index 5b057cde..2e69c583 100644 --- a/lib/readline/histfile.c +++ b/lib/readline/histfile.c @@ -606,12 +606,14 @@ history_truncate_file (fname, lines) history_lines_written_to_file = 0; } +#if defined (HAVE_CHOWN) /* Make sure the new filename is owned by the same user as the old. If one user is running this, it's a no-op. If the shell is running after sudo with a shared history file, we don't want to leave the history file owned by root. */ if (rv == 0 && exists) r = chown (filename, finfo.st_uid, finfo.st_gid); +#endif xfree (filename); FREE (tempname); @@ -753,12 +755,14 @@ mmap_error: history_lines_written_to_file = 0; } +#if defined (HAVE_CHOWN) /* Make sure the new filename is owned by the same user as the old. If one user is running this, it's a no-op. If the shell is running after sudo with a shared history file, we don't want to leave the history file owned by root. */ if (rv == 0 && exists) mode = chown (histname, finfo.st_uid, finfo.st_gid); +#endif FREE (histname); FREE (tempname); diff --git a/locale.c b/locale.c index a199cdf3..d2c31013 100644 --- a/locale.c +++ b/locale.c @@ -561,3 +561,21 @@ locale_isutf8 (lspec) return (strstr (lspec, "UTF-8") || strstr (lspec, "utf8")); #endif } + +#if defined (HAVE_LOCALECONV) +int +locale_decpoint () +{ + struct lconv *lv; + + lv = localeconv (); + return (lv && lv->decimal_point && lv->decimal_point[0]) ? lv->decimal_point[0] : '.'; +} +#else +# undef locale_decpoint +int +locale_decpoint () +{ + return '.'; +} +#endif diff --git a/parse.y b/parse.y index 7c81f3c1..389a5ac0 100644 --- a/parse.y +++ b/parse.y @@ -2816,6 +2816,9 @@ mk_alexpansion (s) strcpy (r, s); /* If the last character in the alias is a newline, don't add a trailing space to the expansion. Works with shell_getc above. */ + /* Need to do something about the case where the alias expansion contains + an unmatched quoted string, since appending this space affects the + subsequent output. */ if (r[l - 1] != ' ' && r[l - 1] != '\n' && shellmeta(r[l - 1]) == 0) r[l++] = ' '; r[l] = '\0'; @@ -2839,9 +2842,11 @@ alias_expand_token (tokstr) return (NO_EXPANSION); /* mk_alexpansion puts an extra space on the end of the alias expansion, - so the lookahead by the parser works right. If this gets changed, - make sure the code in shell_getc that deals with reaching the end of - an expanded alias is changed with it. */ + so the lookahead by the parser works right (the alias needs to remain + `in use' while parsing its last word to avoid alias recursion for + something like "alias echo=echo"). If this gets changed, make sure + the code in shell_getc that deals with reaching the end of an + expanded alias is changed with it. */ expanded = ap ? mk_alexpansion (ap->value) : (char *)NULL; if (expanded) diff --git a/pathexp.c b/pathexp.c index 21fc148d..380d507e 100644 --- a/pathexp.c +++ b/pathexp.c @@ -230,6 +230,16 @@ quote_string_for_globbing (pathname, qflags) savej = j; savei = i; c = pathname[i++]; /* c == char after open bracket */ + if (c == '^') /* ignore pattern negation */ + { + temp[j++] = c; + c = pathname[i++]; + } + if (c == ']') /* ignore right bracket if first char */ + { + temp[j++] = c; + c = pathname[i++]; + } do { if (c == 0) diff --git a/subst.c b/subst.c index be767f2f..d9d7966c 100644 --- a/subst.c +++ b/subst.c @@ -1383,6 +1383,7 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags) { si = i + 2; t = extract_command_subst (string, &si, flags|SX_NOALLOC); + CHECK_STRING_OVERRUN (i, si, slen, c); i = si + 1; continue; } @@ -1392,6 +1393,7 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags) { si = i + len_opener; t = extract_delimited_string (string, &si, opener, alt_opener, closer, flags|SX_NOALLOC); + CHECK_STRING_OVERRUN (i, si, slen, c); i = si + 1; continue; } @@ -1401,6 +1403,7 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags) { si = i + len_alt_opener; t = extract_delimited_string (string, &si, alt_opener, alt_opener, closer, flags|SX_NOALLOC); + CHECK_STRING_OVERRUN (i, si, slen, c); i = si + 1; continue; } @@ -1420,6 +1423,7 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags) { si = i + 1; t = string_extract (string, &si, "`", flags|SX_NOALLOC); + CHECK_STRING_OVERRUN (i, si, slen, c); i = si + 1; continue; } @@ -1473,6 +1477,8 @@ extract_delimited_string (string, sindex, opener, alt_opener, closer, flags) it should point to just after the first `{' found. On exit, SINDEX gets the position of the matching `}'. QUOTED is non-zero if this occurs inside double quotes. */ +/* XXX -- should this use skipsubscript to handle ${word[sub]}? (only do it + if dolbrace_state == DOLBRACE_PARAM) */ /* XXX -- this is very similar to extract_delimited_string -- XXX */ static char * extract_dollar_brace_string (string, sindex, quoted, flags) @@ -1539,7 +1545,7 @@ extract_dollar_brace_string (string, sindex, quoted, flags) t = string_extract (string, &si, "`", flags|SX_NOALLOC); CHECK_STRING_OVERRUN (i, si, slen, c); - + i = si + 1; continue; } @@ -1550,6 +1556,9 @@ extract_dollar_brace_string (string, sindex, quoted, flags) { si = i + 2; t = extract_command_subst (string, &si, flags|SX_NOALLOC); + + CHECK_STRING_OVERRUN (i, si, slen, c); + i = si + 1; continue; } @@ -1577,6 +1586,17 @@ extract_dollar_brace_string (string, sindex, quoted, flags) continue; } +#if defined (ARRAY_VARS) + /* XXX - bash-5.0 */ + if (c == '[' && dolbrace_state == DOLBRACE_PARAM) + { + si = skipsubscript (string, i, 0); + CHECK_STRING_OVERRUN (i, si, slen, c); + if (string[si] == ']') + c = string[i = si]; + } +#endif + /* move past this character, which was not special. */ ADVANCE_CHAR (string, slen, i); @@ -1905,6 +1925,7 @@ skip_to_delim (string, start, delims, flags) temp = extract_delimited_string (string, &si, "$(", "(", ")", SX_NOALLOC|SX_COMMAND); /* ) */ else temp = extract_dollar_brace_string (string, &si, 0, SX_NOALLOC); + CHECK_STRING_OVERRUN (i, si, slen, c); i = si; if (string[i] == '\0') /* don't increment i past EOS in loop */ break; @@ -1923,6 +1944,7 @@ skip_to_delim (string, start, delims, flags) temp = extract_process_subst (string, (c == '<') ? "<(" : ">(", &si, 0); free (temp); /* XXX - not using SX_NOALLOC here yet */ #endif + CHECK_STRING_OVERRUN (i, si, slen, c); i = si; if (string[i] == '\0') break; @@ -1942,6 +1964,7 @@ skip_to_delim (string, start, delims, flags) open[2] = '\0'; temp = extract_delimited_string (string, &si, open, "(", ")", SX_NOALLOC); /* ) */ + CHECK_STRING_OVERRUN (i, si, slen, c); i = si; if (string[i] == '\0') /* don't increment i past EOS in loop */ break; @@ -5810,10 +5833,7 @@ process_substitute (string, open_for_read_in_child) { #if defined (JOB_CONTROL) if (last_procsub_child) - { - discard_pipeline (last_procsub_child); - last_procsub_child = (PROCESS *)NULL; - } + discard_last_procsub_child (); last_procsub_child = restore_pipeline (0); #endif diff --git a/variables.c b/variables.c index cc383fde..63f3f201 100644 --- a/variables.c +++ b/variables.c @@ -221,6 +221,9 @@ static SHELL_VAR *get_lineno __P((SHELL_VAR *)); static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t, char *)); static SHELL_VAR *get_subshell __P((SHELL_VAR *)); +static SHELL_VAR *get_epochseconds __P((SHELL_VAR *)); +static SHELL_VAR *get_epochrealtime __P((SHELL_VAR *)); + static SHELL_VAR *get_bashpid __P((SHELL_VAR *)); #if defined (HISTORY) @@ -1451,6 +1454,40 @@ get_subshell (var) return (var); } +static SHELL_VAR * +get_epochseconds (var) + SHELL_VAR *var; +{ + intmax_t now; + char *p; + + now = NOW; + p = itos (now); + + FREE (value_cell (var)); + var_setvalue (var, p); + return (var); +} + +static SHELL_VAR * +get_epochrealtime (var) + SHELL_VAR *var; +{ + char buf[32]; + char *p; + struct timeval tv; + + gettimeofday (&tv, NULL); + snprintf (buf, sizeof (buf), "%u%c%06u", (unsigned)tv.tv_sec, + locale_decpoint (), + (unsigned)tv.tv_usec); + + p = savestring (buf); + FREE (value_cell (var)); + var_setvalue (var, p); + return (var); +} + static SHELL_VAR * get_bashpid (var) SHELL_VAR *var; @@ -1467,6 +1504,30 @@ get_bashpid (var) return (var); } +static SHELL_VAR * +get_bash_argv0 (var) + SHELL_VAR *var; +{ + char *p; + + p = savestring (dollar_vars[0]); + FREE (value_cell (var)); + var_setvalue (var, p); + return var; +} + +static SHELL_VAR * +assign_bash_argv0 (var, value, unused, key) + SHELL_VAR *var; + char *value; + arrayind_t unused; + char *key; +{ + FREE (dollar_vars[0]); + dollar_vars[0] = savestring (value); + return var; +} + static SHELL_VAR * get_bash_command (var) SHELL_VAR *var; @@ -1758,6 +1819,8 @@ initialize_dynamic_variables () v = init_seconds_var (); + INIT_DYNAMIC_VAR ("BASH_ARGV0", (char *)NULL, get_bash_argv0, assign_bash_argv0); + INIT_DYNAMIC_VAR ("BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL); INIT_DYNAMIC_VAR ("BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell); @@ -1769,6 +1832,9 @@ initialize_dynamic_variables () INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign); VSETATTR (v, att_integer); + INIT_DYNAMIC_VAR ("EPOCHSECONDS", (char *)NULL, get_epochseconds, null_assign); + INIT_DYNAMIC_VAR ("EPOCHREALTIME", (char *)NULL, get_epochrealtime, null_assign); + #if defined (HISTORY) INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL); VSETATTR (v, att_integer);