-----
builtins/wait.def
- wait_builtin: don't assign the variable given with -p if there are no
- jobs to wait for. Report and fix from OÄuz <oguzismailuysal@gmail.com>
+ jobs to wait for.
+ Report and fix from OÄuz <oguzismailuysal@gmail.com>
arrayfunc.c
- kvpair_assignment_p: return non-zero if argument L appears to be a
subst.c
- expand_oneword: detect whether VALUE appears to be a key-value
pair compound assignment and call the appropriate function to expand
- each word in the resulting list. Fixes inconsistency reported by
- oguzismailuysal@gmail.com
+ each word in the resulting list.
+ Fixes inconsistency reported by oguzismailuysal@gmail.com
12/12
-----
array variable that was the value of a nameref (used in the original
assignment), don't call make_variable_value on the value, since that
messes up +=. Just call assign_array_element and let that take care
- of calling make_variable_value appropriately. Fixes bug reported by
- Oguz <oguzismailuysal@gmail.com>
+ of calling make_variable_value appropriately.
+ Fixes bug reported by Oguz <oguzismailuysal@gmail.com>
1/14
----
subst.c
- param_expand: if a nameref expands to array[@] or array[*], make sure
to call chk_atstar so the right variables are set to split the
- result. Report from Oguz <oguzismailuysal@gmail.com>
+ result.
+ Report from Oguz <oguzismailuysal@gmail.com>
1/22
----
oslib.c
- bcopy: update function declaration to add `const'
+
+execute_cmd.c
+ - mkfmt: fix rounding of fractional part of the printed value to
+ increment the seconds value if necessary (e.g., 0.9848 -> 1.0 if
+ precision == 1)
+ - mkfmt: compute the seconds and seconds fraction before writing
+ anything into the buffer in case we increment the seconds
+ Report and patch from Grisha Levit <grishalevit@gmail.com>
+
+subst.h
+ - SD_QUOTEDSTR: tell skip_to_delim that we're skipping over a quoted
+ string; *delims is the close quote
+
+subst.c
+ - skip_to_delim: understand SD_QUOTEDSTR and call skip_single_quoted
+ or skip_double_quoted immediately as appropriate
+
+bashline.c
+ - bash_forward_shellword: use SD_QUOTEDSTR in the call to skip_to_delim
+ Fixes bug reported by Grisha Levit <grishalevit@gmail.com>
+
+ 10/20
+ -----
+execute_cmd.c
+ - mkfmt: round when the precision is 0 like with other precision values
+ Report and patch from Grisha Levit <grishalevit@gmail.com>
+
+variables.c
+ - make_local_array_variable,make_local_assoc_variable: if we're
+ converting an unset nameref into an array, warn about it and turn
+ off the nameref attribute
+ Report from Grisha Levit <grishalevit@gmail.com>
+
+builtins/mkbuiltins.c
+ - write_documentation: change to use modern C-style strings, where
+ adjacent strings are effectively concatenated
+ - write_documentation: don't add indent to otherwise blank lines
+ Original patch from Martin D Kealey <martin@kurahaupo.gen.nz>
+
+ 10/21
+ -----
+builtins/read.def
+ - read_builtin: since referring to a pointer after realloc may have
+ freed it is, in theory, undefined behavior, redo the unwind-protect
+ every time we increase the size of input_string. This updates the
+ CHERI-only change of 10/6
+ From https://savannah.gnu.org/bugs/?67586
+
+ 10/22
+ -----
+arrayfunc.c
+ - make_array_variable_value,assign_array_element_internal: set
+ att_assigning while evaluating the value to be assigned so the rest
+ of the shell knows not to unset the variable
+
+variables.c
+ - bind_variable_internal,bind_variable_value: set att_assigning
+ around calls to make_variable_value to avoid unsetting the variable
+ - makunbound: if we are assigning to the variable being unset, just
+ empty it out and set it to invisible, assuming the variable will be
+ marked as visible after assignment
+ Report from Oguz <oguzismailuysal@gmail.com> back on 8/8/2025
+
dispose_variable (dentry);
}
else
- newval = make_variable_value (entry, value, flags);
+ {
+ if (entry)
+ VSETATTR (entry, att_assigning);
+ newval = make_variable_value (entry, value, flags);
+ if (entry)
+ VUNSETATTR (entry, att_assigning);
+ }
return newval;
}
(*entry->assign_func) (entry, newval, 0, key);
FREE (key);
}
- else
+ else if (assoc_p (entry))
assoc_insert (hash, key, newval);
FREE (newval);
(*entry->assign_func) (entry, newval, ind, key);
else if (assoc_p (entry))
assoc_insert (assoc_cell (entry), key, newval);
- else
+ else if (array_p (entry))
array_insert (array_cell (entry), ind, newval);
FREE (newval);
int avflags;
avflags = convert_assign_flags_to_arrayval_flags (flags);
+ if (entry)
+ VSETATTR (entry, att_assigning);
ind = array_expand_index (entry, sub, sublen, avflags);
+ if (entry)
+ VUNSETATTR (entry, att_assigning);
/* negative subscripts to indexed arrays count back from end */
if (entry && ind < 0)
ind = (array_p (entry) ? array_max_index (array_cell (entry)) : 0) + 1 + ind;
ADVANCE_CHAR (rl_line_buffer, slen, p);
break;
case '\'':
- p = skip_to_delim (rl_line_buffer, ++p, "'", SD_NOJMP);
+ p = skip_to_delim (rl_line_buffer, ++p, "'", SD_NOJMP|SD_QUOTEDSTR|SD_COMPLETE);
break;
case '"':
- p = skip_to_delim (rl_line_buffer, ++p, "\"", SD_NOJMP);
+ p = skip_to_delim (rl_line_buffer, ++p, "\"", SD_NOJMP|SD_QUOTEDSTR|SD_COMPLETE);
break;
}
ADVANCE_CHAR (rl_line_buffer, slen, p);
break;
case '\'':
- p = skip_to_delim (rl_line_buffer, ++p, "'", SD_NOJMP);
+ p = skip_to_delim (rl_line_buffer, ++p, "'", SD_NOJMP|SD_QUOTEDSTR|SD_COMPLETE);
break;
case '"':
- p = skip_to_delim (rl_line_buffer, ++p, "\"", SD_NOJMP);
+ p = skip_to_delim (rl_line_buffer, ++p, "\"", SD_NOJMP|SD_QUOTEDSTR|SD_COMPLETE);
break;
}
#include "filecntl.h"
#include "../bashansi.h"
+#include <stdbool.h>
#include <stdio.h>
#include <errno.h>
{
int i, j;
char *line;
- int string_array, texinfo, base_indent, filename_p;
+ int string_array, texinfo, filename_p;
+ int full_indent, base_indent;
if (stream == 0)
return;
string_array = flags & STRING_ARRAY;
filename_p = flags & HELPFILE;
+ texinfo = flags & TEXINFO;
+
+ base_indent = (string_array && single_longdoc_strings && filename_p == 0) ? BASE_INDENT : 0;
+ full_indent = indentation + base_indent;
if (string_array)
{
fprintf (stream, " {\n#if defined (HELP_BUILTIN)\n"); /* } */
- if (single_longdoc_strings)
- {
- if (filename_p == 0)
- {
- if (documentation && documentation[0] && documentation[0][0])
- fprintf (stream, "N_(\"");
- else
- fprintf (stream, "N_(\" "); /* the empty string translates specially. */
- }
- else
- fprintf (stream, "\"");
- }
+ if (filename_p == 0 && single_longdoc_strings)
+ fprintf (stream, "N_(");
}
- base_indent = (string_array && single_longdoc_strings && filename_p == 0) ? BASE_INDENT : 0;
-
- for (i = 0, texinfo = (flags & TEXINFO); documentation && (line = documentation[i]); i++)
+ for (i = 0; documentation && (line = documentation[i]); i++)
{
+ bool first_line = i == 0;
+ bool last_line = documentation[i+1] == 0;
+
/* Allow #ifdef's to be written out verbatim, but don't put them into
separate help files. */
if (*line == '#')
{
- if (string_array && filename_p == 0 && single_longdoc_strings == 0)
+ if (string_array)
fprintf (stream, "%s\n", line);
continue;
}
/* prefix with N_( for gettext */
- if (string_array && single_longdoc_strings == 0)
- {
- if (filename_p == 0)
- {
- if (line[0])
- fprintf (stream, " N_(\"");
- else
- fprintf (stream, " N_(\" "); /* the empty string translates specially. */
- }
- else
- fprintf (stream, " \"");
- }
-
- if (indentation)
- for (j = 0; j < indentation; j++)
- fprintf (stream, " ");
-
- /* Don't indent the first line, because of how the help builtin works. */
- if (i == 0)
- indentation += base_indent;
-
if (string_array)
{
+ if (filename_p == 0 && single_longdoc_strings == 0)
+ fprintf (stream, "N_(");
+ else if (first_line == 0)
+ fputc ('\t', stream);
+ fputc ('"', stream);
+ if (filename_p == 0 && *line == 0 && last_line && first_line && indentation == 0)
+ line = " "; /* the empty string translates specially. */
+ if (indentation && *line)
+ fprintf (stream, "%*.0s", indentation, "");
+
for (j = 0; line[j]; j++)
{
if (line[j] == '\\' || line[j] == '"')
fputc (line[j], stream);
}
- /* closing right paren for gettext */
- if (single_longdoc_strings == 0)
- {
- if (filename_p == 0)
- fprintf (stream, "\"),\n");
- else
- fprintf (stream, "\",\n");
- }
- else if (documentation[i+1])
- /* don't add extra newline after last line */
- fprintf (stream, "\\n\\\n");
+ if (last_line == 0)
+ fprintf (stream, "\\n");
+ fputc ('"', stream);
+ if (filename_p == 0 && single_longdoc_strings == 0)
+ fprintf (stream, "),");
+ if (last_line == 0)
+ fprintf (stream, "\n");
}
else if (texinfo)
- {
+ {
+ if (indentation && *line)
+ fprintf (stream, "%*.0s", indentation, "");
for (j = 0; line[j]; j++)
{
switch (line[j])
fputc ('\n', stream);
}
else
- fprintf (stream, "%s\n", line);
+ fprintf (stream, "%*.0s%s\n", indentation, "", line);
+
+ /* Don't indent the first line, because of how the help builtin works. */
+ indentation = full_indent;
}
/* closing right paren for gettext */
- if (string_array && single_longdoc_strings)
+ if (string_array)
{
- if (filename_p == 0)
- fprintf (stream, "\"),\n");
- else
- fprintf (stream, "\",\n");
+ if (single_longdoc_strings)
+ {
+ if (filename_p == 0)
+ fputc (')', stream);
+ fputc (',', stream);
+ }
+ fprintf (stream, "\n#endif /* HELP_BUILTIN */\n\t(char *)NULL\n};\n");
}
-
- if (string_array)
- fprintf (stream, "#endif /* HELP_BUILTIN */\n (char *)NULL\n};\n");
}
int
char *x;
x = (char *)xrealloc (input_string, size += 128);
- /* Only need to change unwind-protect if input_string changes */
-#ifndef __CHERI_PURE_CAPABILITY__
+#if 0
+ /* This is, in theory, undefined behavior, since input_string may
+ have been freed. */
if (x != input_string)
#endif
{
#define BASH_TIMEFORMAT "\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS"
static const int precs[] = { 0, 100000, 10000, 1000, 100, 10, 1 };
-static const int maxvals[] = { 1, 10, 100, 1000, 10000, 100000, 10000000 };
+static const int maxvals[] = { 1, 10, 100, 1000, 10000, 100000, 1000000 };
/* Expand one `%'-prefixed escape sequence from a time format string. */
/* SEC_FRACTION is in usecs. We normalize and round that based on the
precision. */
-int
+static int
mkfmt (char *buf, int prec, int lng, time_t sec, long sec_fraction)
{
time_t min;
char abuf[INT_STRLEN_BOUND(time_t) + 1];
int ind, aind;
+ /* We want to add a decimal point and PREC places after it if PREC is
+ nonzero. PREC is not greater than 6. SEC_FRACTION is between 0
+ and 999999 (microseconds). */
+ if (prec < 6)
+ {
+ /* We round here because we changed timeval_to_secs to return
+ microseconds and normalized clock_t_to_secs's fractional return
+ value to microseconds, deferring the work to be done to now.
+
+ sec_fraction is in microseconds. Take the value, cut off what we
+ don't want, round up if necessary, then convert back to
+ microseconds. */
+ int frac, rest, maxval;
+
+ maxval = maxvals[6 - prec];
+ frac = sec_fraction / maxval;
+ rest = sec_fraction % maxval;
+
+ if (rest >= maxval/2)
+ frac++;
+
+ if (frac == maxvals[prec])
+ {
+ sec++;
+ sec_fraction = 0;
+ }
+ else
+ sec_fraction = frac * (1000000 / maxvals[prec]);
+ }
+
ind = 0;
abuf[sizeof(abuf) - 1] = '\0';
while (abuf[aind])
buf[ind++] = abuf[aind++];
- /* We want to add a decimal point and PREC places after it if PREC is
- nonzero. PREC is not greater than 6. SEC_FRACTION is between 0
- and 999999 (microseconds). */
- if (prec != 0)
+ if (prec > 0)
{
- /* We round here because we changed timeval_to_secs to return
- microseconds and normalized clock_t_to_secs's fractional return
- value to microseconds, deferring the work to be done to now.
-
- sec_fraction is in microseconds. Take the value, cut off what we
- don't want, round up if necessary, then convert back to
- microseconds. */
- if (prec != 6)
- {
- int frac, rest, maxval;
-
- maxval = maxvals[6 - prec];
- frac = sec_fraction / maxval;
- rest = sec_fraction % maxval;
-
- if (rest >= maxval/2)
- frac++;
-
- sec_fraction = frac * (1000000 / maxvals[prec]);
- }
-
buf[ind++] = locale_decpoint ();
for (aind = 1; aind <= prec; aind++)
{
arithexp = (flags & SD_ARITHEXP);
skipcol = 0;
+ /* If the caller tells us we're skipping a quoted string after the opening
+ quote, and the delimiter is a single- or double-quote, skip all the logic
+ here and just call the same functions as word expansion. */
+ if ((flags & SD_QUOTEDSTR) && (*delims == '\'' || *delims == '"') && delims[1] == '\0')
+ {
+ if (*delims == '\'')
+ i = skip_single_quoted (string, slen, start, 0);
+ else
+ i = skip_double_quoted (string, slen, start, completeflag);
+ CQ_RETURN (i);
+ }
+
i = start;
pass_next = backq = dquote = 0;
while (c = string[i])
#define SD_HISTEXP 0x200 /* skip_to_delim during history expansion */
#define SD_ARITHEXP 0x400 /* skip_to_delim during arithmetic expansion */
#define SD_NOERROR 0x800 /* don't print error messages */
+#define SD_QUOTEDSTR 0x1000 /* skipping a part of a single- or double-quoted string */
extern int skip_to_delim (const char *, int, const char *, int);
-: ${BUILD_DIR:=/usr/local/build/chet/bash/bash-current}
+: ${BUILD_DIR:=$PWD}
THIS_SH=$BUILD_DIR/bash
PATH=$PATH:$BUILD_DIR
-export THIS_SH PATH
+export THIS_SH PATH BUILD_DIR
+
+: ${TMPDIR:=/tmp}
+export TMPDIR
export BASH_TSTOUT=/tmp/xx
rm -f ${BASH_TSTOUT}
+if [ -t 1 ]; then
+ # can't rely on having $'...' or printf understanding \e
+ # bright red background, white foreground text
+ export CSTART=$(printf '\033[01;101;37m') CEND=$(printf '\033[0m')
+else
+ export CSTART= CEND=
+fi
+export TAB=' '
+
${THIS_SH} "$@"
shift - Shift positional parameters.
shift: shift [n]
Shift positional parameters.
-
+
Rename the positional parameters $N+1,$N+2 ... to $1,$2 ... If N is
not given, it is assumed to be 1.
-
+
Exit Status:
Returns success unless N is negative or greater than $#.
builtin: builtin [shell-builtin [arg ...]]
readonly: readonly [-aAf] [name[=value] ...] or readonly -p
:: :
Null command.
-
+
No effect; the command does nothing.
-
+
Exit Status:
Always succeeds.
NAME
DESCRIPTION
Null command.
-
+
No effect; the command does nothing.
-
+
Exit Status:
Always succeeds.
+++ /dev/null
-for f ; do
- echo $f
- sed 's|warning:|${CSTART}warning${CEND}:|' < $f > zzz && mv zzz $f
-done
if (var == 0 || array_p (var) || (assoc_ok && assoc_p (var)))
return var;
+ /* array variables cannot be namerefs */
+ if (var && nameref_p (var) && invisible_p (var))
+ {
+ internal_warning (_("%s: removing nameref attribute"), name);
+ VUNSETATTR (var, att_nameref);
+ }
+
/* Validate any value we inherited from a variable instance at a previous
scope and discard anything that's invalid. */
if (localvar_inherit && assoc_p (var))
if (var == 0 || assoc_p (var) || (array_ok && array_p (var)))
return var;
+ /* assoc variables cannot be namerefs */
+ if (var && nameref_p (var) && invisible_p (var))
+ {
+ internal_warning (_("%s: removing nameref attribute"), name);
+ VUNSETATTR (var, att_nameref);
+ }
+
/* Validate any value we inherited from a variable instance at a previous
scope and discard anything that's invalid. */
if (localvar_inherit && array_p (var))
#endif
{
entry = make_new_variable (newval, table);
- var_setvalue (entry, make_variable_value (entry, value, aflags));
+ VSETATTR (entry, att_assigning);
+ newval = make_variable_value (entry, value, aflags);
+ VUNSETATTR (entry, att_assigning);
+ var_setvalue (entry, newval);
}
}
else if (entry == 0)
{
entry = make_new_variable (name, table);
- var_setvalue (entry, make_variable_value (entry, value, aflags)); /* XXX */
+ VSETATTR (entry, att_assigning);
+ newval = make_variable_value (entry, value, aflags); /* XXX */
+ VUNSETATTR (entry, att_assigning);
+ var_setvalue (entry, newval);
}
else if (entry->assign_func) /* array vars have assign functions now */
{
}
INVALIDATE_EXPORTSTR (entry);
+ VSETATTR (entry, att_assigning);
newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : (char *)value;
+ VUNSETATTR (entry, att_assigning);
if (assoc_p (entry))
entry = (*(entry->assign_func)) (entry, newval, -1, savestring ("0"));
else if (array_p (entry))
return (entry);
}
- /* Variables which are bound are visible. */
- VUNSETATTR (entry, att_invisible);
-
/* If we can optimize the assignment, do so and return. Right now, we
optimize appends to string variables. */
if (can_optimize_assignment (entry, value, aflags))
if (exported_p (entry))
array_needs_making = 1;
+ /* Variables which are bound are visible. */
+ VUNSETATTR (entry, att_invisible);
+
return (entry);
}
+ VSETATTR (entry, att_assigning);
#if defined (ARRAY_VARS)
if (assoc_p (entry) || array_p (entry))
newval = make_array_variable_value (entry, 0, "0", value, aflags);
else
#endif
newval = make_variable_value (entry, value, aflags); /* XXX */
+ VUNSETATTR (entry, att_assigning);
+
+ /* Variables which are bound are visible. */
+ VUNSETATTR (entry, att_invisible);
/* Invalidate any cached export string */
INVALIDATE_EXPORTSTR (entry);
bind_variable_value (SHELL_VAR *var, char *value, int aflags)
{
char *t;
- int invis;
-
- invis = invisible_p (var);
- VUNSETATTR (var, att_invisible);
if (var->assign_func)
{
/* If we're appending, we need the old value, so use
make_variable_value */
+ VSETATTR (var, att_assigning);
t = (aflags & ASS_APPEND) ? make_variable_value (var, value, aflags) : value;
+ VUNSETATTR (var, att_assigning);
+ VUNSETATTR (var, att_invisible);
(*(var->assign_func)) (var, t, -1, 0);
if (t != value && t)
free (t);
}
else
{
+ VSETATTR (var, att_assigning);
t = make_variable_value (var, value, aflags);
+ VUNSETATTR (var, att_assigning);
if ((aflags & (ASS_NAMEREF|ASS_FORCE)) == ASS_NAMEREF && check_selfref (name_cell (var), t, 0))
{
if (variable_context)
{
internal_error (_("%s: nameref variable self references not allowed"), name_cell (var));
free (t);
- if (invis)
- VSETATTR (var, att_invisible); /* XXX */
return ((SHELL_VAR *)NULL);
}
}
if ((aflags & ASS_NAMEREF) && (valid_nameref_value (t, 0) == 0))
{
free (t);
- if (invis)
- VSETATTR (var, att_invisible); /* XXX */
return ((SHELL_VAR *)NULL);
}
FREE (value_cell (var));
var_setvalue (var, t);
+ VUNSETATTR (var, att_invisible);
}
INVALIDATE_EXPORTSTR (var);
return (0);
}
+ /* If we are currently assigning this variable, but the evaluation of the
+ value causes it to be unset, we need to make sure pointers to the
+ variable struct remain valid, and insert a cleared-out version of the
+ variable back into the correct hash table. */
+ if (old_var && assigning_p (old_var))
+ {
+ dispose_variable_value (old_var);
+ var_setvalue (old_var, (char *)NULL);
+
+ old_var->attributes = 0;
+ VSETATTR (old_var, att_assigning);
+ VSETATTR (old_var, att_invisible);
+
+ INVALIDATE_EXPORTSTR (old_var);
+
+ old_var->dynamic_value = NULL;
+ old_var->assign_func = NULL;
+
+ /* leave the context unchanged if we're going to be assigning it */
+ /* old_var->context = 0; */
+
+ new_elt = hash_insert (savestring (old_var->name), v->table, 0);
+ new_elt->data = (PTR_T)old_var;
+ stupidly_hack_special_variables (old_var->name);
+
+ free (elt->key);
+ free (elt);
+ return (0);
+ }
+
/* Have to save a copy of name here, because it might refer to
old_var->name. If so, stupidly_hack_special_variables will
reference freed memory. */