From f2f545ad7ba395cc4dcad49eae144ccc7c0e68ec Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Mon, 6 Oct 2025 15:29:41 -0400 Subject: [PATCH] fix for %P in TIMEFORMAT; make updating variables that aren't subject to allexport smoother; fix spurious compiler warning about realloc; efficiency improvement for command timing; fix issue with read builtin and failure to set terminal attributes --- CWRU/CWRU.chlog | 49 ++++++++++++++++++++++++++++++++ builtins/read.def | 6 ++-- builtins/set.def | 19 ++++--------- builtins/shopt.def | 15 +++------- doc/bash.1 | 4 +++ doc/bashref.texi | 9 ++++-- execute_cmd.c | 69 +++++++++++++++++++++------------------------- include/typemax.h | 4 +++ parse.y | 14 +++++----- subst.h | 3 +- variables.c | 6 ++-- 11 files changed, 118 insertions(+), 80 deletions(-) diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 4114d6770..e7c491311 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -11862,3 +11862,52 @@ parse.y - reset_parser: reset shell_eof_token to 0, rely on callers to restore it if they need to Report from Grisha Levit + +execute_cmd.c + - print_formatted_time: when processing %P, sum_frac should be in + microseconds; missed when making max precision 6 back in 6/2023 + Report from pourko2@tutamail.com + + 9/30 + ---- +subst.h + - ASS_NOEXPORT: don't export the variable being assigned even if + `allexport' is enabled + +builtins/set.def + - set_shellopts: pass ASS_NOEXPORT so we don't have to mess with + unsetting the export attribute + +builtins/shopt.def + - set_bashopts: pass ASS_NOEXPORT so we don't have to mess with + unsetting the export attribute + +variables.c + - bind_variable_internal,bind_variable_value: if ASS_NOEXPORT is set in + the flags argument, don't export the variable even if `allexport' + (mark_modified_vars) is set. + Patch from Martin D Kealey + + 10/1 + ---- +include/typemax.h + - PTRDIFF_MAX: add default max value for ptrdiff_t if the system + doesn't provide one + +parse.y + - SIZE_MAX -> PTRDIFF_MAX to avoid spurious compiler warning + Patch from Martin D Kealey and + Grisha Levit + +execute_cmd.c + - time_command: don't bother with time zone info in gettimeofday() + - time_command: don't bother trying to execute a null command in + posix mode, since we just want timing information since the shell + started + + 10/3 + ---- +builtins/read.def + - read_builtin: don't use i as a return value for functions and then + as an index into input_string without resetting it + Report and patch from Grisha Levit diff --git a/builtins/read.def b/builtins/read.def index 4ad36325d..cb0d30538 100644 --- a/builtins/read.def +++ b/builtins/read.def @@ -622,8 +622,7 @@ read_builtin (WORD_LIST *list) termsave.attrs = ttattrs; ttset = ttattrs; - i = silent ? ttfd_cbreak (fd, &ttset) : ttfd_onechar (fd, &ttset); - if (i < 0) + if ((silent ? ttfd_cbreak (fd, &ttset) : ttfd_onechar (fd, &ttset)) < 0) sh_ttyerror (1); tty_modified = 1; add_unwind_protect (uw_ttyrestore, &termsave); @@ -639,8 +638,7 @@ read_builtin (WORD_LIST *list) termsave.attrs = ttattrs; ttset = ttattrs; - i = ttfd_noecho (fd, &ttset); /* ttnoecho (); */ - if (i < 0) + if (ttfd_noecho (fd, &ttset) < 0) sh_ttyerror (1); tty_modified = 1; diff --git a/builtins/set.def b/builtins/set.def index 13630c22d..e8597d4a0 100644 --- a/builtins/set.def +++ b/builtins/set.def @@ -532,7 +532,7 @@ set_shellopts (void) { char *value; char tflag[N_O_OPTIONS]; - int i, *ip, exported; + int i, *ip; size_t vsize, vptr; SHELL_VAR *v; @@ -571,21 +571,12 @@ set_shellopts (void) vptr--; /* cut off trailing colon */ value[vptr] = '\0'; - v = find_variable ("SHELLOPTS"); + /* ASS_FORCE so we don't have to temporarily turn off readonly; ASS_NOEXPORT + so we don't have to work around allexport. */ + v = bind_variable ("SHELLOPTS", value, ASS_FORCE|ASS_NOEXPORT); - /* Note whether or not the variable was exported so we can adjust after the - assignment. */ - exported = v ? exported_p (v) : 0; - - /* ASS_FORCE so we don't have to temporarily turn off readonly */ - v = bind_variable ("SHELLOPTS", value, ASS_FORCE); - - /* Turn the read-only attribute back on, and turn off the export attribute - if it was set implicitly by mark_modified_vars and SHELLOPTS was not - exported before we bound the new value. */ + /* Turn the read-only attribute back on. */ VSETATTR (v, att_readonly); - if (mark_modified_vars && exported == 0 && exported_p (v)) - VUNSETATTR (v, att_exported); free (value); } diff --git a/builtins/shopt.def b/builtins/shopt.def index cf6f6be12..6ecc087f8 100644 --- a/builtins/shopt.def +++ b/builtins/shopt.def @@ -823,7 +823,7 @@ set_bashopts (void) { char *value; char tflag[N_SHOPT_OPTIONS]; - int i, exported; + int i; size_t vsize, vptr; SHELL_VAR *v; @@ -853,21 +853,14 @@ set_bashopts (void) vptr--; /* cut off trailing colon */ value[vptr] = '\0'; - v = find_variable ("BASHOPTS"); - - /* Note whether or not the variable was exported so we can adjust after the - assignment. */ - exported = v ? exported_p (v) : 0; - - /* ASS_FORCE so we don't have to temporarily turn off readonly */ - v = bind_variable ("BASHOPTS", value, ASS_FORCE); + /* ASS_FORCE so we don't have to temporarily turn off readonly; ASS_NOEXPORT + so we don't have to work around allexport. */ + v = bind_variable ("BASHOPTS", value, ASS_FORCE|ASS_NOEXPORT); /* Turn the read-only attribute back on, and turn off the export attribute if it was set implicitly by mark_modified_vars and SHELLOPTS was not exported before we bound the new value. */ VSETATTR (v, att_readonly); - if (mark_modified_vars && exported == 0 && exported_p (v)) - VUNSETATTR (v, att_exported); free (value); } diff --git a/doc/bash.1 b/doc/bash.1 index f82c130c2..c68729126 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -2513,6 +2513,10 @@ A filename whose suffix matches one of the entries in is excluded from the list of matched filenames. A sample value is .Q .o:\*~ . +Since tilde expansion takes place after +.Q : +in assignment statements, +make sure to quote assignments appropriately to avoid it as appropriate. .TP .B FUNCNEST If set to a numeric value greater than 0, defines a maximum function diff --git a/doc/bashref.texi b/doc/bashref.texi index 0ac623d1b..600c80286 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -7174,8 +7174,13 @@ A colon-separated list of suffixes to ignore when performing filename completion. A filename whose suffix matches one of the entries in @env{FIGNORE} -is excluded from the list of matched filenames. A sample -value is @samp{.o:~} +is excluded from the list of matched filenames. +A sample value is +@samp{.o:~}. +Since tilde expansion takes place after +@samp{:} +in assignment statements, +make sure to quote assignments appropriately to avoid it as appropriate. @item FUNCNAME An array variable containing the names of all shell functions diff --git a/execute_cmd.c b/execute_cmd.c index 3a1c169ed..789a8b09a 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -1408,7 +1408,7 @@ print_formatted_time (FILE *fp, char *format, cpu = 10000; #endif sum = cpu / 100; - sum_frac = (cpu % 100) * 10; + sum_frac = (cpu % 100) * 10000; /* convert to microseconds */ len = mkfmt (ts, 2, 0, sum, sum_frac); RESIZE_MALLOCED_BUFFER (str, sindex, len, ssize, 64); strcpy (str + sindex, ts); @@ -1468,9 +1468,6 @@ time_command (COMMAND *command, int asynchronous, int pipe_in, int pipe_out, str #if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY) struct timeval real, user, sys; struct timeval before, after; -# if defined (HAVE_STRUCT_TIMEZONE) - struct timezone dtz; /* posix doesn't define this */ -# endif struct rusage selfb, selfa, kidsb, kidsa; /* a = after, b = before */ #else # if defined (HAVE_TIMES) @@ -1479,24 +1476,28 @@ time_command (COMMAND *command, int asynchronous, int pipe_in, int pipe_out, str # endif #endif + rv = EXECUTION_SUCCESS; /* suppress uninitialized use warnings */ + + rs = us = ss = 0; + rsf = usf = ssf = 0; + cpu = 0; + + old_subshell = subshell_environment; + posix_time = command && (command->flags & CMD_TIME_POSIX); + nullcmd = (command == 0) || (command->type == cm_simple && command->value.Simple->words == 0 && command->value.Simple->redirects == 0); + #if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY) -# if defined (HAVE_STRUCT_TIMEZONE) - gettimeofday (&before, &dtz); -# else - gettimeofday (&before, NULL); -# endif /* !HAVE_STRUCT_TIMEZONE */ getrusage (RUSAGE_SELF, &selfb); getrusage (RUSAGE_CHILDREN, &kidsb); + gettimeofday (&before, NULL); #else # if defined (HAVE_TIMES) tbefore = times (&before); # endif #endif - old_subshell = subshell_environment; - posix_time = command && (command->flags & CMD_TIME_POSIX); - - nullcmd = (command == 0) || (command->type == cm_simple && command->value.Simple->words == 0 && command->value.Simple->redirects == 0); + /* In posix mode, `time' without argument is equivalent to `times', but + obeys TIMEFORMAT. This is from POSIX interp 267 */ if (posixly_correct && nullcmd) { #if defined (HAVE_GETRUSAGE) @@ -1508,35 +1509,27 @@ time_command (COMMAND *command, int asynchronous, int pipe_in, int pipe_out, str tbefore = shell_start_time * get_clk_tck (); #endif } + else + { + old_flags = command->flags; + COPY_PROCENV (top_level, save_top_level); + command->flags &= ~(CMD_TIME_PIPELINE|CMD_TIME_POSIX); + code = setjmp_nosigs (top_level); + if (code == NOT_JUMPED) + rv = execute_command_internal (command, asynchronous, pipe_in, pipe_out, fds_to_close); + COPY_PROCENV (save_top_level, top_level); + if (code == NOT_JUMPED) + command->flags = old_flags; - rv = EXECUTION_SUCCESS; /* suppress uninitialized use warnings */ - old_flags = command->flags; - COPY_PROCENV (top_level, save_top_level); - command->flags &= ~(CMD_TIME_PIPELINE|CMD_TIME_POSIX); - code = setjmp_nosigs (top_level); - if (code == NOT_JUMPED) - rv = execute_command_internal (command, asynchronous, pipe_in, pipe_out, fds_to_close); - COPY_PROCENV (save_top_level, top_level); - - if (code == NOT_JUMPED) - command->flags = old_flags; - - /* If we're jumping in a different subshell environment than we started, - don't bother printing timing stats, just keep longjmping back to the - original top level. */ - if (code != NOT_JUMPED && subshell_environment && subshell_environment != old_subshell) - sh_longjmp (top_level, code); - - rs = us = ss = 0; - rsf = usf = ssf = 0; - cpu = 0; + /* If we're jumping in a different subshell environment than we started, + don't bother printing timing stats, just keep longjmping back to the + original top level. */ + if (code != NOT_JUMPED && subshell_environment && subshell_environment != old_subshell) + sh_longjmp (top_level, code); + } #if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY) -# if defined (HAVE_STRUCT_TIMEZONE) - gettimeofday (&after, &dtz); -# else gettimeofday (&after, NULL); -# endif /* !HAVE_STRUCT_TIMEZONE */ getrusage (RUSAGE_SELF, &selfa); getrusage (RUSAGE_CHILDREN, &kidsa); diff --git a/include/typemax.h b/include/typemax.h index e3b98f472..467a4ac8b 100644 --- a/include/typemax.h +++ b/include/typemax.h @@ -119,6 +119,10 @@ static const unsigned long long int maxquad = ULLONG_MAX; # define SIZE_MAX ((size_t) ~(size_t)0) #endif +#ifndef PTRDIFF_MAX +# define PTRDIFF_MAX TYPE_MAXIMUM(ptrdiff_t) +#endif + #ifndef sh_imaxabs # define sh_imaxabs(x) (((x) >= 0) ? (x) : -(x)) #endif diff --git a/parse.y b/parse.y index 615c3fe02..ab042dafb 100644 --- a/parse.y +++ b/parse.y @@ -47,7 +47,7 @@ #include "shell.h" #include "execute_cmd.h" -#include "typemax.h" /* SIZE_MAX if needed */ +#include "typemax.h" /* PTRDIFF_MAX if needed */ #include "trap.h" #include "flags.h" #include "parser.h" @@ -2579,21 +2579,21 @@ shell_getc (int remove_quoted_newline) /* If we can't put 256 bytes more into the buffer, allocate everything we can and fill it as full as we can. */ /* XXX - we ignore rest of line using `truncating' flag */ - if (shell_input_line_size > (SIZE_MAX - 256)) + if (shell_input_line_size > (PTRDIFF_MAX - 256)) { size_t n; - n = SIZE_MAX - i; /* how much more can we put into the buffer? */ + n = PTRDIFF_MAX - i; /* how much more can we put into the buffer? */ if (n <= 2) /* we have to save 1 for the newline added below */ { if (truncating == 0) - internal_warning(_("shell_getc: shell_input_line_size (%zu) exceeds SIZE_MAX (%lu): line truncated"), shell_input_line_size, (unsigned long)SIZE_MAX); + internal_warning(_("shell_getc: shell_input_line_size (%zu) exceeds PTRDIFF_MAX (%lu): line truncated"), shell_input_line_size, (unsigned long)PTRDIFF_MAX); shell_input_line[i] = '\0'; truncating = 1; } - if (shell_input_line_size < SIZE_MAX) + if (shell_input_line_size < PTRDIFF_MAX) { - shell_input_line_size = SIZE_MAX; + shell_input_line_size = PTRDIFF_MAX; shell_input_line = xrealloc (shell_input_line, shell_input_line_size); } } @@ -2735,7 +2735,7 @@ shell_getc (int remove_quoted_newline) not already end in an EOF character. */ if (shell_input_line_terminator != EOF && shell_input_line_terminator != READERR) { - if (shell_input_line_size + 3 < SIZE_MAX && (shell_input_line_len+3 > shell_input_line_size)) + if (shell_input_line_size + 3 < PTRDIFF_MAX && (shell_input_line_len+3 > shell_input_line_size)) shell_input_line = (char *)xrealloc (shell_input_line, 1 + (shell_input_line_size += 2)); diff --git a/subst.h b/subst.h index b412c66a5..c9f155a8b 100644 --- a/subst.h +++ b/subst.h @@ -45,7 +45,7 @@ /* Flag values controlling how assignment statements are treated. */ #define ASS_APPEND 0x0001 /* a+=b */ -#define ASS_MKLOCAL 0x0002 +#define ASS_MKLOCAL 0x0002 /* make a local variable */ #define ASS_MKASSOC 0x0004 #define ASS_MKGLOBAL 0x0008 /* force global assignment */ #define ASS_NAMEREF 0x0010 /* assigning to nameref variable */ @@ -59,6 +59,7 @@ #define ASS_ONEWORD 0x1000 /* don't check array subscripts, assume higher level has done that */ #define ASS_NOTEMPENV 0x2000 /* don't assign into temporary environment */ #define ASS_XTRACE 0x4000 /* print trace after compound assignment expansion */ +#define ASS_NOEXPORT 0x8000 /* don't export even if allexport is set */ /* Flags for the string extraction functions. */ #define SX_NOALLOC 0x0001 /* just skip; don't return substring */ diff --git a/variables.c b/variables.c index 2e4183230..27fec3269 100644 --- a/variables.c +++ b/variables.c @@ -3166,7 +3166,7 @@ assign_value: INVALIDATE_EXPORTSTR (entry); optimized_assignment (entry, value, aflags); - if (mark_modified_vars) + if (mark_modified_vars && (aflags & ASS_NOEXPORT) == 0) VSETATTR (entry, att_exported); if (exported_p (entry)) @@ -3208,7 +3208,7 @@ assign_value: } } - if (mark_modified_vars) + if (mark_modified_vars && (aflags & ASS_NOEXPORT) == 0) VSETATTR (entry, att_exported); if (exported_p (entry)) @@ -3363,7 +3363,7 @@ bind_variable_value (SHELL_VAR *var, char *value, int aflags) INVALIDATE_EXPORTSTR (var); - if (mark_modified_vars) + if (mark_modified_vars && (aflags & ASS_NOEXPORT) == 0) VSETATTR (var, att_exported); if (exported_p (var)) -- 2.47.3