diff --git a/CHANGES b/CHANGES index 109906ad..400bd384 100644 --- a/CHANGES +++ b/CHANGES @@ -1403,7 +1403,7 @@ y. The {x} + +input.c + - make_buffered_stream: use B_TEXT in buffered stream flags instead + of (typo) O_TEXT. Report and fix from Eric Blake + + 9/27 + ---- +execute_cmd.c + - shell_execve: call reset_parser before calling initialize_subshell, + which calls delete_all_aliases. reset_parser wants to free the + pushed string list, which has pointers back into the alias table + (use after free) + - execute_simple_command: if we fork for an async command, make sure + the child process increments shell_level before performing any + word expansions, so $BASH_SUBSHELL is incremented. Fixes issue + reported by ziyunfei <446240525@qq.com> diff --git a/builtins/common.h b/builtins/common.h index 83607455..7ff0ca33 100644 --- a/builtins/common.h +++ b/builtins/common.h @@ -202,7 +202,7 @@ extern int evalstring __P((char *, const char *, int)); extern void parse_and_execute_cleanup __P((void)); extern int parse_string __P((char *, const char *, int, char **)); extern int should_suppress_fork __P((COMMAND *)); -extern int optimize_fork __P((COMMAND *)); +extern void optimize_fork __P((COMMAND *)); /* Functions from evalfile.c */ extern int maybe_execute_file __P((const char *, int)); diff --git a/builtins/evalstring.c b/builtins/evalstring.c index 11abc459..b800da9e 100644 --- a/builtins/evalstring.c +++ b/builtins/evalstring.c @@ -110,7 +110,7 @@ should_suppress_fork (command) ((command->flags & CMD_INVERT_RETURN) == 0)); } -int +void optimize_fork (command) COMMAND *command; { diff --git a/builtins/set.def b/builtins/set.def index 3ec6bb95..f582a41b 100644 --- a/builtins/set.def +++ b/builtins/set.def @@ -932,6 +932,8 @@ unset_builtin (list) if (tem == -1 && unset_function == 0 && unset_variable == 0) tem = unbind_func (name); + name = list->word->word; /* reset above for namerefs */ + /* SUSv3, POSIX.1-2001 say: ``Unsetting a variable or function that was not previously set shall not be considered an error.'' */ diff --git a/examples/loadables/ln.c b/examples/loadables/ln.c index 3a675acb..a853bc99 100644 --- a/examples/loadables/ln.c +++ b/examples/loadables/ln.c @@ -46,6 +46,7 @@ typedef int unix_link_syscall_t __P((const char *, const char *)); #define LN_SYMLINK 0x01 #define LN_UNLINK 0x02 +#define LN_NOFOLLOW 0x04 static unix_link_syscall_t *linkfn; static int dolink (); @@ -71,6 +72,10 @@ ln_builtin (list) case 's': flags |= LN_SYMLINK; break; + case 'h': + case 'n': + flags |= LN_NOFOLLOW; + break; default: builtin_usage (); return (EX_USAGE); @@ -139,8 +144,10 @@ mkdirpath (dir, file) #if defined (HAVE_LSTAT) # define LSTAT lstat +# define LSTAT_OR_STAT_IF(c, f, b) ((c) ? lstat((f), (b)) : stat((f), (b))) #else # define LSTAT stat +# define LSTAT_OR_STAT_IF(c, f, b) (stat((f), (b))) #endif static int @@ -172,7 +179,7 @@ dolink (src, dst, flags) /* If the destination is a directory, create the final filename by appending the basename of the source to the destination. */ dst_path = 0; - if ((stat (dst, &dsb) == 0) && S_ISDIR (dsb.st_mode)) + if ((LSTAT_OR_STAT_IF((flags & LN_NOFOLLOW), dst, &dsb) == 0) && S_ISDIR (dsb.st_mode)) { if ((p = strrchr (src, '/')) == 0) p = src; @@ -211,7 +218,8 @@ char *ln_doc[] = { "Create a new directory entry with the same modes as the original", "file. The -f option means to unlink any existing file, permitting", "the link to occur. The -s option means to create a symbolic link.", - "By default, ln makes hard links.", + "By default, ln makes hard links. Specifying -n or its synonym -h", + "causes ln to not resolve symlinks in the target file or directory.", (char *)NULL }; @@ -222,6 +230,6 @@ struct builtin ln_struct = { ln_builtin, /* function implementing the builtin */ BUILTIN_ENABLED, /* initial flags for builtin */ ln_doc, /* array of long documentation strings. */ - "ln [-fs] file1 [file2] OR ln [-fs] file ... directory", /* usage synopsis; becomes short_doc */ + "ln [-fhns] file1 [file2] OR ln [-fhns] file ... directory", /* usage synopsis; becomes short_doc */ 0 /* reserved for internal use */ }; diff --git a/execute_cmd.c b/execute_cmd.c index 2b36a8b0..daa08c23 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -4046,6 +4046,9 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) last_asynchronous_pid = old_last_async_pid; CHECK_SIGTERM; + + if (async) + subshell_level++; /* not for pipes yet */ } else { @@ -4067,6 +4070,8 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) } } + QUIT; /* XXX */ + /* If we are re-running this as the result of executing the `command' builtin, do not expand the command words a second time. */ if ((simple_command->flags & CMD_INHIBIT_EXPANSION) == 0) @@ -4237,7 +4242,8 @@ run_builtin: setup_async_signals (); } - subshell_level++; + if (async == 0) + subshell_level++; execute_subshell_builtin_or_function (words, simple_command->redirects, builtin, func, pipe_in, pipe_out, async, fds_to_close, @@ -5464,6 +5470,7 @@ shell_execve (command, args, env) /* We have committed to attempting to execute the contents of this file as shell commands. */ + reset_parser (); initialize_subshell (); set_sigint_handler (); @@ -5508,8 +5515,6 @@ shell_execve (command, args, env) clear_fifo_list (); /* pipe fds are what they are now */ #endif - reset_parser (); - sh_longjmp (subshell_top_level, 1); /*NOTREACHED*/ } diff --git a/input.c b/input.c index 2a9ca265..308b87e4 100644 --- a/input.c +++ b/input.c @@ -205,7 +205,7 @@ make_buffered_stream (fd, buffer, bufsize) if (bufsize == 1) bp->b_flag |= B_UNBUFF; if (O_TEXT && (fcntl (fd, F_GETFL) & O_TEXT) != 0) - bp->b_flag |= O_TEXT; + bp->b_flag |= B_TEXT; return (bp); } diff --git a/jobs.c b/jobs.c index 56f257bd..b04886e5 100644 --- a/jobs.c +++ b/jobs.c @@ -3393,6 +3393,13 @@ itrace("waitchld: waitpid returns %d block = %d children_exited = %d", pid, bloc if (pid <= 0) continue; /* jumps right to the test */ + /* Linux kernels appear to signal the parent but not interrupt the + waitpid() (or restart it even without SA_RESTART) on SIGINT, so if + we saw a SIGINT and the process exited or died due to some other + signal, assume the child caught the SIGINT. */ + if (wait_sigint_received && (WIFSIGNALED (status) == 0 || WTERMSIG (status) != SIGINT)) + child_caught_sigint = 1; + /* If the child process did die due to SIGINT, forget our assumption that it caught or otherwise handled it. */ if (WIFSIGNALED (status) && WTERMSIG (status) == SIGINT) @@ -3624,13 +3631,17 @@ set_job_status_and_cleanup (job) seen it, and wait_sigint_received is non-zero, because keyboard signals are sent to process groups) or via kill(2) to the foreground process by another process (or itself). If the shell did receive the - SIGINT, it needs to perform normal SIGINT processing. */ + SIGINT, it needs to perform normal SIGINT processing. XXX - should + this change its behavior depending on whether the last command in an + pipeline exited due to SIGINT, or any process in the pipeline? Right + now it does this if any process in the pipeline exits due to SIGINT. */ else if (wait_sigint_received && child_caught_sigint == 0 && IS_FOREGROUND (job) && IS_JOBCONTROL (job) == 0) { int old_frozen; +itrace("waitchld: special handling for SIGINT"); wait_sigint_received = 0; /* If SIGINT is trapped, set the exit status so that the trap diff --git a/lib/readline/history.c b/lib/readline/history.c index a0b12e1d..216b2a56 100644 --- a/lib/readline/history.c +++ b/lib/readline/history.c @@ -325,7 +325,7 @@ add_history (string) } } - temp = alloc_history_entry (string, hist_inittime ()); + temp = alloc_history_entry ((char *)string, hist_inittime ()); the_history[history_length] = (HIST_ENTRY *)NULL; the_history[history_length - 1] = temp; diff --git a/lib/readline/rlconf.h b/lib/readline/rlconf.h index fa528355..82fd0de1 100644 --- a/lib/readline/rlconf.h +++ b/lib/readline/rlconf.h @@ -64,7 +64,7 @@ /* Define this if you want to enable code that talks to the Linux kernel tty auditing system. */ -#define ENABLE_TTY_AUDIT_SUPPORT +/* #define ENABLE_TTY_AUDIT_SUPPORT */ /* Defaults for the various editing mode indicators, inserted at the beginning of the last (maybe only) line of the prompt if show-mode-in-prompt is on */ diff --git a/print_cmd.c b/print_cmd.c index b0d276fe..617501e4 100644 --- a/print_cmd.c +++ b/print_cmd.c @@ -41,6 +41,7 @@ #include "shell.h" #include "flags.h" #include /* use <...> so we pick it up from the build directory */ +#include "input.h" #include "shmbutil.h" diff --git a/subst.c b/subst.c index a9938b46..d8dae547 100644 --- a/subst.c +++ b/subst.c @@ -5873,8 +5873,8 @@ command_substitute (string, quoted) { builtin_ignoring_errexit = 0; change_flag ('e', FLAG_OFF); - set_shellopts (); } + set_shellopts (); /* If we are expanding a redirection, we can dispose of any temporary environment we received, since redirections are not supposed to have diff --git a/tests/RUN-ONE-TEST b/tests/RUN-ONE-TEST index d29259a9..a5ab273d 100755 --- a/tests/RUN-ONE-TEST +++ b/tests/RUN-ONE-TEST @@ -1,4 +1,4 @@ -BUILD_DIR=/usr/local/build/chet/bash/bash-current +BUILD_DIR=/usr/local/build/bash/bash-current THIS_SH=$BUILD_DIR/bash PATH=$PATH:$BUILD_DIR diff --git a/tests/exp.right b/tests/exp.right index 165f41b9..5e6d6cbb 100644 --- a/tests/exp.right +++ b/tests/exp.right @@ -228,5 +228,9 @@ declare -- var="x\001y\177z"$ argv[1] = <$'x\001y\177z'> argv[1] = var=$'x\001y\177z' +./exp8.sub: line 16: xyz: syntax error: invalid arithmetic operator (error token is "z") +declare -a array=() +declare -a array=([0]=$'x\001y\177z') argv[1] = -declare -a array=([0]="x\001y\177z")$ +declare -a array=([0]=$'x\001y\177z') +declare -A array=([$'x\001y\177z']=$'a\242b\002c' ) diff --git a/tests/exp8.sub b/tests/exp8.sub index f4997778..99340838 100644 --- a/tests/exp8.sub +++ b/tests/exp8.sub @@ -12,6 +12,20 @@ recho ${var@Q} recho ${var@P} echo ${var@A} +unset array +array=( [$'x\001y\177z']=foo ) # should be error +echo ${array[@]@A} + +unset array +declare -a array=([0]=$'x\001y\177z') +declare -p array + +unset array array=( "$var" ) recho ${array[@]} -echo ${array[@]@A} | sed -n l +echo ${array[@]@A} + +unset array +declare -A array +array=( [$'x\001y\177z']=$'a\242b\002c' ) +echo ${array[@]@A} diff --git a/tests/herestr.right b/tests/herestr.right index 096dda11..03f38ed6 100644 --- a/tests/herestr.right +++ b/tests/herestr.right @@ -24,8 +24,7 @@ f3 () echo $(echo hi) echo ho echo off to work we go -declare -a uu=([0]="" [1]="kghfjk" [2]="jkfzuk" [3]="i -") +declare -a uu=([0]="" [1]="kghfjk" [2]="jkfzuk" [3]=$'i\n') foo bar foo bar qux:::::bax diff --git a/tests/nquote.right b/tests/nquote.right index 79bd587d..e3ae5558 100644 --- a/tests/nquote.right +++ b/tests/nquote.right @@ -57,8 +57,8 @@ $'\'abcd\'' ' 1 argv[1] = <^?> -0000000 del nl +0000000 del nl 0000002 -0000000 esc fs gs rs us del nl +0000000 esc fs gs rs us del nl 0000007 \q diff --git a/tests/nquote3.sub b/tests/nquote3.sub index 6c84a641..5d417670 100644 --- a/tests/nquote3.sub +++ b/tests/nquote3.sub @@ -1,6 +1,8 @@ +. ./test-glue-functions + recho $'\c?' -echo $'\c?' |od -a -echo $'\c[\c\\\c]\c^\c_\c?' |od -a +echo $'\c?' | od -a | _intl_normalize_spaces +echo $'\c[\c\\\c]\c^\c_\c?' | od -a | _intl_normalize_spaces echo $'\q' diff --git a/trap.c b/trap.c index bf911acd..17ed9ee6 100644 --- a/trap.c +++ b/trap.c @@ -320,7 +320,6 @@ run_pending_traps () while (pending_traps[sig]--) instead of the if statement. */ if (pending_traps[sig]) { -itrace("run_pending_traps: %d: %d", sig, pending_traps[sig]); if (running_trap == sig+1) /*continue*/; @@ -404,7 +403,6 @@ itrace("run_pending_traps: %d: %d", sig, pending_traps[sig]); /* XXX - set pending_traps[sig] = 0 here? */ pending_traps[sig] = 0; evalstring (savestring (trap_list[sig]), "trap", SEVAL_NONINT|SEVAL_NOHIST|SEVAL_RESETLINE); -itrace("run_pending_traps: evalstring returns"); #if defined (JOB_CONTROL) restore_pipeline (1); #endif