builtins/getopts.def
- dogetopts: call sh_getopt_reset when binding the name variable fails
for some reason
+
+ 7/18
+ ----
+lib/readline/text.c
+ - _rl_char_search: make sure that character search arguments are
+ added to any macro currently being defined
+ Report from A4-Tacks <wdsjxhno1001@163.com>
+
+lib/readline/histfile.c
+ - read_history_slow: new function that just reads the history file in
+ 4096-byte chunks until EOF, used when the history file isn't a
+ regular file.
+ - read_history_range: use read_history_slow when the history file isn't
+ a regular file
+ Suggested by several, most recently by <macbeth.112358@gmail.com> in 2/25
+
+ 7/19
+ ----
+parse.y,input.h,eval.c,array.h
+ - revert changes from 7/6 based on report from
+ Sam James <sam@gentoo.org>
+
+parse.y
+ - exec_restore_parser_state: version of restore_parser_state that
+ doesn't restore the pushed string list, since some calls to
+ parse_and_execute may modify it out from underneath us
+ - parse_comsub: use exec_restore_parser_state instead of inline code
+ (the version in parse_compound_assignment is a little more complex)
+ - parser_unset_string_list: extern function to set pushed_string_list
+ to NULL from outside parse.y
+ - execute_variable_command: set pushed_string_list to NULL after
+ saving the parser state, since we don't want parse_and_execute to
+ pop a string off the list that was there before it was called
+ Fixes bug reported by Carl Johnson <carl.johnson.new.hampshire@gmail.com>
+
+shell.h
+ - exec_restore_parser_state: extern declaration
+ - parser_unset_string_list: extern declaration
+
+trap.c
+ - _run_trap_internal: use parser_unset_string_list after saving
+ parser_state to fix bug with DEBUG trap if it's invoked during
+ an alias ending with a newline (similar to issue from 7/6)
+ - run_pending_traps: call parser_unset_string_list as above
+
+ 7/21
+ ----
+bashline.c
+ - bash_execute_unix_command,edit_and_execute_command: use
+ parser_unset_string_list after saving parser_state; executing a
+ command while readline is active shouldn't modify any existing
+ parser state
+
+ 7/25
+ ----
+command.h
+ - revert changes to COMMAND and SIMPLE_COM from 7/16
+ From a report by Jessica Clarke <jrtc27@jrtc27.com>
+
+expr.c
+ - expassign,expshift: avoid C23 undefined behavior when performing
+ left and right arithmetic shifts
+ From https://savannah.gnu.org/patch/?10532
+ bkallus <benjamin.p.kallus.gr@dartmouth.edu>
#define ALL_ELEMENT_SUB(c) ((c) == '@' || (c) == '*')
/* In eval.c, but uses ARRAY * */
-extern int execute_array_command (ARRAY *, void *, int);
+extern int execute_array_command (ARRAY *, void *);
#endif /* _ARRAY_H_ */
(*rl_deprep_term_function) ();
rl_clear_signals ();
save_parser_state (&ps);
+ parser_unset_string_list ();
r = parse_and_execute (command, (editing_mode == VI_EDITING_MODE) ? "v" : "C-xC-e", SEVAL_NOHIST);
restore_parser_state (&ps);
begin_unwind_frame ("execute-unix-command");
save_parser_state (&ps);
+ parser_unset_string_list ();
rl_clear_signals ();
add_unwind_protect (uw_unbind_readline_variables, 0);
add_unwind_protect (uw_restore_parser_state, &ps);
/* What a command looks like. */
typedef struct command {
+ enum command_type type; /* FOR CASE WHILE IF CONNECTION or SIMPLE. */
int flags; /* Flags controlling execution environment. */
int line; /* line number the command starts on */
REDIRECT *redirects; /* Special redirects for FOR CASE, etc. */
struct subshell_com *Subshell;
struct coproc_com *Coproc;
} value;
- enum command_type type; /* FOR CASE WHILE IF CONNECTION SIMPLE, etc. */
} COMMAND;
/* Structure used to represent the CONNECTION type. */
typedef struct simple_com {
int flags; /* See description of CMD flags. */
int line; /* line number the command starts on */
- REDIRECT *redirects; /* Redirections to perform. */
WORD_LIST *words; /* The program name, the arguments,
variable assignments, etc. */
+ REDIRECT *redirects; /* Redirections to perform. */
} SIMPLE_COM;
/* The "function definition" command. */
# defined sleep(n) _sleep ((n) * 1000)
#endif
#include <fcntl.h>
- GL_MDA_DEFINES
+
+
#ifndef O_NOATIME
#define O_NOATIME 0
#endif
#if defined (ARRAY_VARS)
/* Caller ensures that A has a non-zero number of elements */
int
-execute_array_command (ARRAY *a, void *v, int flags)
+execute_array_command (ARRAY *a, void *v)
{
char *tag;
char **argv;
for (i = 0; i < argc; i++)
{
if (argv[i] && argv[i][0])
- execute_variable_command (argv[i], tag, flags);
+ execute_variable_command (argv[i], tag);
}
strvec_dispose (argv);
return 0;
if (array_p (pcv))
{
if ((pcmds = array_cell (pcv)) && array_num_elements (pcmds) > 0)
- execute_array_command (pcmds, "PROMPT_COMMAND", 0);
+ execute_array_command (pcmds, "PROMPT_COMMAND");
return;
}
else if (assoc_p (pcv))
command_to_execute = value_cell (pcv);
if (command_to_execute && *command_to_execute)
- execute_variable_command (command_to_execute, "PROMPT_COMMAND", 0);
+ execute_variable_command (command_to_execute, "PROMPT_COMMAND");
}
/* Call the YACC-generated parser and return the status of the parse.
{
char *s;
struct tm t, *tm;
- time_t now, secs;
+ time_t now;
+ intmax_t secs;
char *datestr, *format;
int i, opt;
if (STREQ (datestr, date_time_modifiers[i].shorthand))
{
secs = now + date_time_modifiers[i].incr;
- printf ("%ld\n", secs);
+ printf ("%jd\n", secs);
return (EXECUTION_SUCCESS);
}
}
if (s && *s)
builtin_warning("%s: not completely converted (%s)", datestr, s);
- printf ("%ld\n", secs);
+ printf ("%jd\n", secs);
return (EXECUTION_SUCCESS);
}
lowest precedence. */
#define EXP_LOWEST expcomma
-#ifndef MAX_INT_LEN
-# define MAX_INT_LEN 32
-#endif
-
struct lvalue
{
char *tokstr; /* possibly-rewritten lvalue if not NULL */
lvalue -= value;
break;
case LSH:
- lvalue <<= value;
+ lvalue = (uintmax_t)lvalue << (value & (TYPE_WIDTH(uintmax_t) - 1));
break;
case RSH:
- lvalue >>= value;
+ lvalue >>= (value & (TYPE_WIDTH(uintmax_t) - 1));
break;
case BAND:
lvalue &= value;
static intmax_t
expshift (void)
{
- register intmax_t val1, val2;
+ intmax_t val1, val2;
val1 = expaddsub ();
val2 = expaddsub ();
if (op == LSH)
- val1 = val1 << val2;
+ val1 = (uintmax_t)val1 << (val2 & (TYPE_WIDTH(uintmax_t) - 1));
else
- val1 = val1 >> val2;
+ val1 = val1 >> (val2 & (TYPE_WIDTH(uintmax_t) - 1));
lasttok = NUM;
}
extern char *read_secondary_line (int);
extern int find_reserved_word (const char *);
extern void gather_here_documents (void);
-extern void execute_variable_command (const char *, const char *, int);
+extern void execute_variable_command (const char *, const char *);
extern int *save_token_state (void);
extern void restore_token_state (int *);
# include <unistd.h>
#endif
+#include <string.h>
#include <ctype.h>
#if defined (__EMX__)
static int histfile_restore (const char *, const char *);
static int history_rename (const char *, const char *);
+static int history_write_slow (int, HIST_ENTRY **, int, int);
+static ssize_t history_read_slow (int, char **);
+
/* Return the string that should be used in the place of this
filename. This only matters when you don't specify the
filename to read_history (), or write_history (). */
return (read_history_range (filename, 0, -1));
}
+#define RBUFSIZE 4096
+
+/* Read from a non-regular file until EOF, assuming we can't trust the file
+ size as reported by fstat. */
+static ssize_t
+history_read_slow (int fd, char **bufp)
+{
+ char *ret, *r;
+ size_t retsize, retlen;
+ char rbuf[RBUFSIZE];
+ ssize_t nr, nw;
+
+ if (bufp == 0)
+ return -1;
+
+ retsize = RBUFSIZE;
+ ret = malloc(retsize);
+ if (ret == 0)
+ return -1;
+ retlen = 0;
+
+ while (nr = read (fd, rbuf, sizeof (rbuf)))
+ {
+ if (nr < 0)
+ {
+ free (ret);
+ *bufp = NULL;
+ return -1;
+ }
+
+ if (retlen >= retsize - nr - 1)
+ {
+ retsize *= 2;
+ r = realloc (ret, retsize);
+ if (r == 0)
+ {
+ free(ret);
+ *bufp = NULL;
+ return -1;
+ }
+ ret = r;
+ }
+ memcpy (ret + retlen, rbuf, nr);
+ retlen += nr;
+ }
+ if (retlen + 1 >= retsize)
+ {
+ retsize += 1;
+ r = realloc (ret, retsize);
+ if (r == 0)
+ {
+ free (ret);
+ *bufp = NULL;
+ return -1;
+ }
+ ret = r;
+ }
+ ret[retlen] = '\0';
+
+ *bufp = ret;
+ return (ssize_t)retlen;
+}
+
/* Read a range of lines from FILENAME, adding them to the history list.
Start reading at the FROM'th line and end at the TO'th. If FROM
is zero, start at the beginning. If TO is less than FROM, read
if (S_ISREG (finfo.st_mode) == 0)
{
-#ifdef EFTYPE
- errno = EFTYPE;
-#else
- errno = EINVAL;
-#endif
- goto error_and_exit;
+ chars_read = history_read_slow (file, &buffer);
+ if (chars_read == 0)
+ {
+ free (buffer);
+ free (input);
+ close (file);
+ return 0;
+ }
+ goto after_file_read;
}
else
{
chars_read = read (file, buffer, file_size);
#endif
+
+after_file_read:
if (chars_read < 0)
{
error_and_exit:
else
{
key = _rl_bracketed_read_key ();
+ /* XXX - add to macro def? */
rl_restore_prompt ();
rl_clear_message ();
RL_UNSETSTATE(RL_STATE_NUMERICARG);
_rl_char_search (int count, int fdir, int bdir)
{
char mbchar[MB_LEN_MAX];
- int mb_len;
+ int mb_len, i;
mb_len = _rl_read_mbchar (mbchar, MB_LEN_MAX);
if (mb_len <= 0)
return 1;
+ if (RL_ISSTATE (RL_STATE_MACRODEF))
+ for (i = 0; i < mb_len; i++)
+ _rl_add_macro_char (mbchar[i]);
+
if (count < 0)
return (_rl_char_search_internal (-count, bdir, mbchar, mb_len));
else
int c;
c = _rl_bracketed_read_key ();
+
if (c < 0)
return 1;
+ if (RL_ISSTATE (RL_STATE_MACRODEF))
+ _rl_add_macro_char (c);
+
if (count < 0)
return (_rl_char_search_internal (-count, bdir, c));
else
# defined sleep(n) _sleep ((n) * 1000)
#endif
#include <fcntl.h>
- ]GL_MDA_DEFINES[
+ ]
+ [
#ifndef O_NOATIME
#define O_NOATIME 0
#endif
| pipeline BAR_AND newline_list pipeline
{
/* Make cmd1 |& cmd2 equivalent to cmd1 2>&1 | cmd2 */
- COMMAND *tc;
REDIRECTEE rd, sd;
- REDIRECT *r;
+ REDIRECT *r, **rp;
- tc = $1->type == cm_simple ? (COMMAND *)$1->value.Simple : $1;
+ rp = $1->type == cm_simple ? &$1->value.Simple->redirects : &$1->redirects;
sd.dest = 2;
rd.dest = 1;
r = make_redirection (sd, r_duplicating_output, rd, 0);
- if (tc->redirects)
+ if (*rp)
{
register REDIRECT *t;
- for (t = tc->redirects; t->next; t = t->next)
+ for (t = *rp; t->next; t = t->next)
;
t->next = r;
}
else
- tc->redirects = r;
+ *rp = r;
$$ = command_connect ($1, $4, '|');
}
#endif
}
+void
+parser_unset_string_list (void)
+{
+ pushed_string_list = (STRING_SAVER *)NULL;
+}
+
#if defined (ALIAS)
/* Before freeing AP, make sure that there aren't any cases of pointer
aliasing that could cause us to reference freed memory later on. */
}
void
-execute_variable_command (const char *command, const char *vname, int flags)
+execute_variable_command (const char *command, const char *vname)
{
char *last_lastarg;
sh_parser_state_t ps;
- if (flags)
- save_parser_state (&ps);
+ save_parser_state (&ps);
+ pushed_string_list = (STRING_SAVER *)NULL;
last_lastarg = save_lastarg ();
parse_and_execute (savestring (command), vname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_NOOPTIMIZE|SEVAL_NOTIFY);
- if (flags)
- restore_parser_state (&ps);
+ restore_parser_state (&ps);
bind_lastarg (last_lastarg);
FREE (last_lastarg);
char *ret, *tcmd;
size_t retlen;
sh_parser_state_t ps;
- STRING_SAVER *saved_strings;
COMMAND *saved_global, *parsed_command;
/* Posix interp 217 says arithmetic expressions have precedence, so
/* We don't want to restore the old pushed string list, since we might have
used it to consume additional input from an alias while parsing this
command substitution. */
- saved_strings = pushed_string_list;
- restore_parser_state (&ps);
- pushed_string_list = saved_strings;
+ exec_restore_parser_state (&ps);
simplecmd_lineno = save_lineno;
restore_parser_state (ps);
}
+/* Special version of restore parser state for cases where we called
+ parse_and_execute(), which may have modified the pushed string list
+ out from underneath us. We may need to use this in other places,
+ like running traps while processing aliases. */
+void
+exec_restore_parser_state (sh_parser_state_t *ps)
+{
+ STRING_SAVER *ss;
+
+ ss = pushed_string_list;
+ restore_parser_state (ps);
+ pushed_string_list = ss;
+}
+
/* Free the parts of a parser state struct that have allocated memory. */
void
flush_parser_state (sh_parser_state_t *ps)
extern void restore_parser_state (sh_parser_state_t *);
extern void flush_parser_state (sh_parser_state_t *);
extern void uw_restore_parser_state (void *);
+extern void exec_restore_parser_state (sh_parser_state_t *);
+extern void parser_unset_string_list (void);
extern sh_input_line_state_t *save_input_line_state (sh_input_line_state_t *);
extern void restore_input_line_state (sh_input_line_state_t *);
# test out the new $(< filename) code
# it should be exactly equivalent to $(cat filename)
-FILENAME=$TMPDIR/bashtmp.x$$
+FILENAME=$TMPDIR/bashtmp.x$$ ; rm -f $TMPDIR/bashtmp.x*
trap 'rm -f $FILENAME' 0
echo foo 2>&1 | cat
}
foo
+foo
+foo
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
1
7
type bix
bix
+
+echo foo |& tee $TMPDIR/bar
+cat $TMPDIR/bar
+rm -f $TMPDIR/bar
trap_command = savestring (old_trap);
save_parser_state (&pstate);
+ parser_unset_string_list ();
save_subst_varlist = subst_assign_varlist;
subst_assign_varlist = 0;
save_tempenv = temporary_env;
#endif
save_parser_state (&pstate);
+ parser_unset_string_list ();
+
save_subst_varlist = subst_assign_varlist;
subst_assign_varlist = 0;
save_tempenv = temporary_env;