commit bash-20121130 snapshot

This commit is contained in:
Chet Ramey
2012-12-07 11:07:50 -05:00
parent 0500de0b2d
commit 3087e51c0e
25 changed files with 2339 additions and 55 deletions
+75
View File
@@ -3890,3 +3890,78 @@ lib/malloc/{malloc.c,imalloc.h}
lib/readline/bind.c
- sv_histsize: if argument evaluates to a value < 0, unstifle the
history
11/22
-----
redir.c
- do_redirection_internal: if we have REDIR_VARASSIGN set in the
redirection flags and we set up `redirector' using fcntl or dup2,
don't add a redirect to make sure it stays open. Let the
script programmer manage the file handle. Fixes bug reported by
Sam Liddicott <sam@liddicott.com>
11/24
-----
jobs.c
- wait_for_any_job: new function, waits for an unspecified background
job to exit and returns its exit status. Returns -1 on no background
jobs or no children or other errors. Calls wait_for with new
sentinel value ANY_PID
- wait_for: changes to handle argument of ANY_PID: don't look up or
try to modify the child struct, only go through the wait loop once.
Return -1 if waitpid returns no children
jobs.h
- ANY_PID: new define
builtins/wait.def
- new option: -n. Means to wait for the next job and return its exit
status. Returns 127 if there are no background jobs (or no
children). Feature most recently requested by Elliott Forney
<idfah@cs.colostate.edu>
doc/{bash.1,bashref.texi}
- document new `wait -n' option
execute_cmd.c
- execute_command_internal: save make_command_string () result in a
temp variable before calling savestring() on it; avoids evaluating
make_command_string() result twice. Fix from John E. Malmberg
<wb8tyw@qsl.net>
11/28
-----
builtins/declare.def
- declare_internal: if an array variable is declared using `declare -a'
or `declare -A', but not assigned a value, set the `invisible'
attribute so the variable does not show up as set. Fix for bug
about variable initialization reported by Tim Friske <me@timfriske.com>
builtins/{mapfile,read}.def
- after calling find_or_make_array_variable, make sure the invisible
flag is turned off, in case the variable was declared previously
using `declare -a' or `declare -A'. Side effect of above change to
declare_internal
subst.c
- shell_expand_word_list: handle the W_ASSNGLOBAL flag and put -g into
the list of options passed to make_internal_declare as appropriate.
Fix for bug reported by Tim Friske <me@timfriske.com>
11/30
-----
test.c
- unary_op: make sure -v and -n check that the variable is not marked
as invisible before calling var_isset. Fix for bug reported by Tim
Friske <me@timfriske.com>
12/2
----
subst.c
- process_substitute: turn off the `expanding_redir' flag, which
controls whether or not variables.c:find_variable_internal uses the
temporary environment to find variables. We want to use the
temp environment, since we don't have to worry about order of
evaluation in a subshell. Fixes bug reported by Andrey Borzenkov
<arvidjaar@gmail.com>
+128
View File
@@ -3827,3 +3827,131 @@ execute_cmd.c
last element of a pipeline (or not in a pipeline), rather than for
every child. Fixes difference in behavior between /dev/fd and
FIFOs reported by Zev Weiss <zev@bewilderbeest.net>
- execute_null_command: do the same thing in the parent branch after
make_child
11/14
-----
subst.c
- parameter_brace_expand: a variable is null if it's special ($@, $*),
the expansion occurs within double quotes, and the expansion turns
into a quoted null. Fixes debian bug 692447 reported by
Matrosov Dmitriy <sgf.dma@gmail.com>
jobs.c
- run_sigchld_trap: make sure `running_trap' sentinel is set
appropriately
- waitchld: only run the sigchld trap if we're not in a signal
handler, not running a trap, and executing the wait builtin.
Otherwise, queue for later handling. We still run one instance
of the trap handler per exited child. Bulk of fix for bug
reported by Elliott Forney <idfah@cs.colostate.edu>
trap.c
- queue_sigchld_trap: set catch_flag so run_pending_traps notices,
and set trapped_signal_received for completeness. Rest of fix
for bug reported by Elliott Forney <idfah@cs.colostate.edu>
lib/malloc/malloc.c
- block_signals: renamed to _malloc_block_signals, made public
- unblock_signals: renamed to _malloc_unblock_signals, made public
lib/malloc/imalloc.h
- extern declarations for _malloc_{un,}block_signals
lib/malloc/table.c
- mregister_alloc, mregister_free: block signals around table
manipulation
11/15
-----
trap.c
- run_pending_traps: set SIG_INPROGRESS flag around calls to
run_sigchld_handler so other parts of the shell know that the
SIGCHLD trap handler is executing
- run_pending_traps: if we get a situation where we are looking at
running a SIGCHLD trap but the trap string is IMPOSSIBLE_TRAP_HANDLER
and the SIG_INPROGRESS flag is set, just skip it. This is possible
if run_pending_traps is called from a SIGCHLD trap handler run by
run_sigchld_trap
doc/bash.1,lib/readline/doc/{rluser.texi,readline.3}
- corrected description of the effect of `set history-size 0'. Report
from Vesa-Matti J Kari <vmkari@cc.helsinki.fi>
include/stdc.h
- CPP_STRING: new define, replaces __STRING
lib/malloc/{malloc.c,imalloc.h}
- replace __STRING with CPP_STRING
11/16
-----
lib/readline/bind.c
- sv_histsize: if argument evaluates to a value < 0, unstifle the
history
11/22
-----
redir.c
- do_redirection_internal: if we have REDIR_VARASSIGN set in the
redirection flags and we set up `redirector' using fcntl or dup2,
don't add a redirect to make sure it stays open. Let the
script programmer manage the file handle. Fixes bug reported by
Sam Liddicott <sam@liddicott.com>
11/24
-----
jobs.c
- wait_for_any_job: new function, waits for an unspecified background
job to exit and returns its exit status. Returns -1 on no background
jobs or no children or other errors. Calls wait_for with new
sentinel value ANY_PID
- wait_for: changes to handle argument of ANY_PID: don't look up or
try to modify the child struct, only go through the wait loop once.
Return -1 if waitpid returns no children
jobs.h
- ANY_PID: new define
builtins/wait.def
- new option: -n. Means to wait for the next job and return its exit
status. Returns 127 if there are no background jobs (or no
children). Feature most recently requested by Elliott Forney
<idfah@cs.colostate.edu>
doc/{bash.1,bashref.texi}
- document new `wait -n' option
execute_cmd.c
- execute_command_internal: save make_command_string () result in a
temp variable before calling savestring() on it; avoids evaluating
make_command_string() result twice. Fix from John E. Malmberg
<wb8tyw@qsl.net>
11/28
-----
builtins/declare.def
- declare_internal: if an array variable is declared using `declare -a'
or `declare -A', but not assigned a value, set the `invisible'
attribute so the variable does not show up as set. Fix for bug
about variable initialization reported by Tim Friske <me@timfriske.com>
builtins/{mapfile,read}.def
- after calling find_or_make_array_variable, make sure the invisible
flag is turned off, in case the variable was declared previously
using `declare -a' or `declare -A'. Side effect of above change to
declare_internal
subst.c
- shell_expand_word_list: handle the W_ASSNGLOBAL flag and put -g into
the list of options passed to make_internal_declare as appropriate.
Fix for bug reported by Tim Friske <me@timfriske.com>
11/30
-----
test.c
- unary_op: make sure -v and -n check that the variable is not marked
as invisible before calling var_isset. Fix for bug reported by Tim
Friske <me@timfriske.com>
+10 -2
View File
@@ -497,9 +497,17 @@ declare_internal (list, local_var)
{
#if defined (ARRAY_VARS)
if (flags_on & att_assoc)
var = make_new_assoc_variable (name);
{
var = make_new_assoc_variable (name);
if (offset == 0)
VSETATTR (var, att_invisible);
}
else if ((flags_on & att_array) || making_array_special)
var = make_new_array_variable (name);
{
var = make_new_array_variable (name);
if (offset == 0)
VSETATTR (var, att_invisible);
}
else
#endif
+2
View File
@@ -172,6 +172,8 @@ mapfile (fd, line_count_goal, origin, nskip, callback_quantum, callback, array_n
builtin_error (_("%s: not an indexed array"), array_name);
return (EXECUTION_FAILURE);
}
else if (invisible_p (entry))
VUNSETATTR (entry, att_invisible); /* no longer invisible */
if (flags & MAPF_CLEARARRAY)
array_flush (array_cell (entry));
+3
View File
@@ -710,6 +710,8 @@ assign_vars:
xfree (input_string);
return EXECUTION_FAILURE; /* existing associative array */
}
else if (invisible_p (var))
VUNSETATTR (var, att_invisible);
array_flush (array_cell (var));
alist = list_string (input_string, ifs_chars, 0);
@@ -881,6 +883,7 @@ bind_read_variable (name, value)
char *name, *value;
{
SHELL_VAR *v;
#if defined (ARRAY_VARS)
if (valid_array_reference (name) == 0)
v = bind_variable (name, value, 0);
+1068
View File
File diff suppressed because it is too large Load Diff
+31 -4
View File
@@ -22,7 +22,7 @@ $BUILTIN wait
$FUNCTION wait_builtin
$DEPENDS_ON JOB_CONTROL
$PRODUCES wait.c
$SHORT_DOC wait [id ...]
$SHORT_DOC wait [-n] [id ...]
Wait for job completion and return exit status.
Waits for each process identified by an ID, which may be a process ID or a
@@ -31,6 +31,9 @@ given, waits for all currently active child processes, and the return
status is zero. If ID is a a job specification, waits for all processes
in that job's pipeline.
If the -n option is supplied, waits for the next job to terminate and
returns its exit status.
Exit Status:
Returns the status of the last ID; fails if ID is invalid or an invalid
option is given.
@@ -91,13 +94,27 @@ int
wait_builtin (list)
WORD_LIST *list;
{
int status, code;
int status, code, opt, nflag;
volatile int old_interrupt_immediately;
USE_VAR(list);
if (no_options (list))
return (EX_USAGE);
nflag = 0;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "n")) != -1)
{
switch (opt)
{
#if defined (JOB_CONTROL)
case 'n':
nflag = 1;
break;
#endif
default:
builtin_usage ();
return (EXECUTION_FAILURE);
}
}
list = loptend;
old_interrupt_immediately = interrupt_immediately;
@@ -123,6 +140,16 @@ wait_builtin (list)
/* We support jobs or pids.
wait <pid-or-job> [pid-or-job ...] */
#if defined (JOB_CONTROL)
if (nflag)
{
status = wait_for_any_job ();
if (status < 0)
status = 127;
WAIT_RETURN (status);
}
#endif
/* But wait without any arguments means to wait for all of the shell's
currently active background processes. */
if (list == 0)
+7 -4
View File
@@ -5,12 +5,12 @@
.\" Case Western Reserve University
.\" chet@po.cwru.edu
.\"
.\" Last Change: Thu Nov 15 21:03:47 EST 2012
.\" Last Change: Sat Nov 24 15:07:12 EST 2012
.\"
.\" bash_builtins, strip all but Built-Ins section
.if \n(zZ=1 .ig zZ
.if \n(zY=1 .ig zY
.TH BASH 1 "2012 November 15" "GNU Bash 4.2"
.TH BASH 1 "2012 November 24" "GNU Bash 4.2"
.\"
.\" There's some problem with having a `@'
.\" in a tagged paragraph with the BSD man macros.
@@ -10006,7 +10006,7 @@ subsequently reset. The exit status is true unless a
.I name
is readonly.
.TP
\fBwait\fP [\fIn ...\fP]
\fBwait\fP [\fB\--n\fP] [\fIn ...\fP]
Wait for each specified process and return its termination status.
Each
.I n
@@ -10015,7 +10015,10 @@ ID or a job specification; if a job spec is given, all processes
in that job's pipeline are waited for. If
.I n
is not given, all currently active child processes
are waited for, and the return status is zero. If
are waited for, and the return status is zero.
If the \fB\--n\fP option is supplied, \fBwait\fP waits for any job to
terminate and returns its exit status.
If
.I n
specifies a non-existent process or job, the return status is
127. Otherwise, the return status is the exit status of the last
+2
View File
@@ -7505,6 +7505,8 @@ last command waited for.
If a job spec is given, all processes in the job are waited for.
If no arguments are given, all currently active child processes are
waited for, and the return status is zero.
If the @option{-n} option is supplied, @code{wait} waits for any job to
terminate and returns its exit status.
If neither @var{jobspec} nor @var{pid} specifies an active child process
of the shell, the return status is 127.
+3 -3
View File
@@ -2,9 +2,9 @@
Copyright (C) 1988-2012 Free Software Foundation, Inc.
@end ignore
@set LASTCHANGE Sun Oct 7 17:58:01 EDT 2012
@set LASTCHANGE Sat Nov 24 15:06:49 EST 2012
@set EDITION 4.2
@set VERSION 4.2
@set UPDATED 7 October 2012
@set UPDATED-MONTH October 2012
@set UPDATED 24 November 2012
@set UPDATED-MONTH November 2012
+4
View File
@@ -66,6 +66,10 @@ WORD_LIST *list;
return(EX_USAGE);
}
/* Skip over `--' */
if (list->word && ISOPTION (list->word->word, '-'))
list = list->next;
if (*list->word->word == '-' || list->next) {
builtin_usage ();
return (EX_USAGE);
+4 -2
View File
@@ -532,6 +532,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
{
int exec_result, user_subshell, invert, ignore_return, was_error_trap;
REDIRECT *my_undo_list, *exec_undo_list;
char *tcmd;
volatile int last_pid;
volatile int save_line_number;
#if defined (PROCESS_SUBSTITUTION)
@@ -586,8 +587,8 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
/* Fork a subshell, turn off the subshell bit, turn off job
control and call execute_command () on the command again. */
line_number_for_err_trap = line_number;
paren_pid = make_child (savestring (make_command_string (command)),
asynchronous);
tcmd = make_command_string (command);
paren_pid = make_child (savestring (tcmd), asynchronous);
if (user_subshell && signal_is_trapped (ERROR_TRAP) &&
signal_in_progress (DEBUG_TRAP) == 0 && running_trap == 0)
@@ -2199,6 +2200,7 @@ execute_coproc (command, pipe_in, pipe_out, fds_to_close)
close (rpipe[1]);
close (wpipe[0]);
/* XXX - possibly run Coproc->name through word expansion? */
cp = coproc_alloc (command->value.Coproc->name, coproc_pid);
cp->c_rfd = rpipe[0];
cp->c_wfd = wpipe[1];
+93 -9
View File
@@ -933,12 +933,10 @@ realloc_jobs_list ()
}
#if defined (DEBUG)
# if 0
itrace ("realloc_jobs_list: resize jobs list from %d to %d", js.j_jobslots, nsize);
itrace ("realloc_jobs_list: j_lastj changed from %d to %d", js.j_lastj, (j > 0) ? j - 1 : 0);
itrace ("realloc_jobs_list: j_njobs changed from %d to %d", js.j_njobs, j);
itrace ("realloc_jobs_list: js.j_ndead %d js.c_reaped %d", js.j_ndead, js.c_reaped);
# endif
#endif
js.j_firstj = 0;
@@ -966,7 +964,7 @@ realloc_jobs_list ()
reset_current ();
#ifdef DEBUG
/* itrace ("realloc_jobs_list: reset js.j_current (%d) and js.j_previous (%d)", js.j_current, js.j_previous);*/
itrace ("realloc_jobs_list: reset js.j_current (%d) and js.j_previous (%d)", js.j_current, js.j_previous);
#endif
UNBLOCK_CHILD (oset);
@@ -989,7 +987,7 @@ compact_jobs_list (flags)
realloc_jobs_list ();
#ifdef DEBUG
/* itrace("compact_jobs_list: returning %d", (js.j_lastj || jobs[js.j_lastj]) ? js.j_lastj + 1 : 0); */
itrace("compact_jobs_list: returning %d", (js.j_lastj || jobs[js.j_lastj]) ? js.j_lastj + 1 : 0);
#endif
return ((js.j_lastj || jobs[js.j_lastj]) ? js.j_lastj + 1 : 0);
@@ -2396,6 +2394,7 @@ wait_for (pid)
/* In the case that this code is interrupted, and we longjmp () out of it,
we are relying on the code in throw_to_top_level () to restore the
top-level signal mask. */
child = 0;
BLOCK_CHILD (set, oset);
/* Ignore interrupts while waiting for a job run without job control
@@ -2433,7 +2432,8 @@ wait_for (pid)
job = NO_JOB;
do
{
FIND_CHILD (pid, child);
if (pid != ANY_PID)
FIND_CHILD (pid, child);
/* If this child is part of a job, then we are really waiting for the
job to finish. Otherwise, we are waiting for the child to finish.
@@ -2446,7 +2446,7 @@ wait_for (pid)
has already exited before this is called, sigchld_handler will have
called waitchld and the state will be set to JDEAD. */
if (PRUNNING(child) || (job != NO_JOB && RUNNING (job)))
if (pid == ANY_PID || PRUNNING(child) || (job != NO_JOB && RUNNING (job)))
{
#if defined (WAITPID_BROKEN) /* SCOv4 */
sigset_t suspend_set;
@@ -2468,7 +2468,7 @@ wait_for (pid)
# endif
queue_sigchld = 1;
waiting_for_child++;
r = waitchld (pid, 1);
r = waitchld (pid, 1); /* XXX */
waiting_for_child--;
# if defined (MUST_UNBLOCK_CHLD)
sigaction (SIGCHLD, &oact, (struct sigaction *)NULL);
@@ -2478,6 +2478,7 @@ wait_for (pid)
if (r == -1 && errno == ECHILD && this_shell_builtin == wait_builtin)
{
termination_state = -1;
/* XXX - restore sigint handler here? */
goto wait_for_return;
}
@@ -2487,8 +2488,11 @@ wait_for (pid)
if it exists, as JDEAD. */
if (r == -1 && errno == ECHILD)
{
child->running = PS_DONE;
WSTATUS (child->status) = 0; /* XXX -- can't find true status */
if (child)
{
child->running = PS_DONE;
WSTATUS (child->status) = 0; /* XXX -- can't find true status */
}
js.c_living = 0; /* no living child processes */
if (job != NO_JOB)
{
@@ -2496,6 +2500,11 @@ wait_for (pid)
js.c_reaped++;
js.j_ndead++;
}
if (pid == ANY_PID)
{
termination_state = -1;
break;
}
}
#endif /* WAITPID_BROKEN */
}
@@ -2511,6 +2520,11 @@ wait_for (pid)
/* Check for a trapped signal interrupting the wait builtin and jump out */
CHECK_WAIT_INTR;
if (pid == ANY_PID)
/* XXX - could set child but we don't have a handle on what waitchld
reaps. Leave termination_state alone. */
goto wait_for_return;
}
while (PRUNNING (child) || (job != NO_JOB && RUNNING (job)));
@@ -2676,6 +2690,76 @@ wait_for_job (job)
return r;
}
/* Wait for any background job started by this shell to finish. Very
similar to wait_for_background_pids(). Returns the exit status of
the next exiting job, -1 if there are no background jobs. The caller
is responsible for translating -1 into the right return value. */
int
wait_for_any_job ()
{
pid_t pid;
int i, r, waited_for;
sigset_t set, oset;
if (jobs_list_frozen)
return -1;
/* First see if there are any unnotified dead jobs that we can report on */
BLOCK_CHILD (set, oset);
for (i = 0; i < js.j_jobslots; i++)
{
if (jobs[i] && DEADJOB (i) && IS_NOTIFIED (i) == 0)
{
return_job:
r = job_exit_status (i);
notify_of_job_status (); /* XXX */
delete_job (i, 0);
#if defined (COPROCESS_SUPPORT)
coproc_reap ();
#endif
UNBLOCK_CHILD (oset);
return r;
}
}
UNBLOCK_CHILD (oset);
/* At this point, we have no dead jobs in the jobs table. Wait until we
get one, even if it takes multiple pids exiting. */
for (waited_for = 0;;)
{
/* Make sure there is a background job to wait for */
BLOCK_CHILD (set, oset);
for (i = 0; i < js.j_jobslots; i++)
if (jobs[i] && RUNNING (i) && IS_FOREGROUND (i) == 0)
break;
if (i == js.j_jobslots)
{
UNBLOCK_CHILD (oset);
return -1;
}
UNBLOCK_CHILD (oset);
QUIT;
CHECK_TERMSIG;
CHECK_WAIT_INTR;
errno = 0;
r = wait_for (ANY_PID); /* special sentinel value for wait_for */
if (r == -1 && errno == ECHILD)
mark_all_jobs_as_dead ();
/* Now we see if we have any dead jobs and return the first one */
BLOCK_CHILD (set, oset);
for (i = 0; i < js.j_jobslots; i++)
if (jobs[i] && DEADJOB (i))
goto return_job;
UNBLOCK_CHILD (oset);
}
return -1;
}
/* Print info about dead jobs, and then delete them from the list
of known jobs. This does not actually delete jobs when the
shell is not interactive, because the dead jobs are not marked
+3
View File
@@ -156,6 +156,8 @@ struct bgpids {
/* A value which cannot be a process ID. */
#define NO_PID (pid_t)-1
#define ANY_PID (pid_t)-1
/* System calls. */
#if !defined (HAVE_UNISTD_H)
extern pid_t fork (), getpid (), getpgrp ();
@@ -216,6 +218,7 @@ extern int wait_for_single_pid __P((pid_t));
extern void wait_for_background_pids __P((void));
extern int wait_for __P((pid_t));
extern int wait_for_job __P((int));
extern int wait_for_any_job __P((void));
extern void notify_and_cleanup __P((void));
extern void reap_dead_jobs __P((void));
+4 -4
View File
@@ -192,8 +192,8 @@ mregister_alloc (tag, mem, size, file, line)
{
/* oops. table is full. punt. */
fprintf (stderr, _("register_alloc: alloc table is full with FIND_ALLOC?\n"));
if (blocked_sigs)
_malloc_unblock_signals (&set, &oset);
if (blocked_sigs)
_malloc_unblock_signals (&set, &oset);
return;
}
@@ -246,8 +246,8 @@ mregister_free (mem, size, file, line)
#if 0
fprintf (stderr, "register_free: %p not in allocation table?\n", mem);
#endif
if (blocked_sigs)
_malloc_unblock_signals (&set, &oset);
if (blocked_sigs)
_malloc_unblock_signals (&set, &oset);
return;
}
if (tentry->flags & MT_FREE)
+4 -9
View File
@@ -819,15 +819,13 @@ do_redirection_internal (redirect, flags)
REDIRECTION_ERROR (redirector, r, fd);
}
if (flags & RX_UNDOABLE)
if ((flags & RX_UNDOABLE) && (redirect->rflags & REDIR_VARASSIGN) == 0)
{
/* Only setup to undo it if the thing to undo is active. */
if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1))
r = add_undo_redirect (redirector, ri, -1);
else
r = add_undo_close_redirect (redirector);
if (r < 0 && (redirect->rflags & REDIR_VARASSIGN))
close (redirector);
REDIRECTION_ERROR (r, errno, fd);
}
@@ -933,15 +931,13 @@ do_redirection_internal (redirect, flags)
if (flags & RX_ACTIVE)
{
if (flags & RX_UNDOABLE)
if ((flags & RX_UNDOABLE) && (redirect->rflags & REDIR_VARASSIGN) == 0)
{
/* Only setup to undo it if the thing to undo is active. */
if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1))
r = add_undo_redirect (redirector, ri, -1);
else
r = add_undo_close_redirect (redirector);
if (r < 0 && (redirect->rflags & REDIR_VARASSIGN))
close (redirector);
REDIRECTION_ERROR (r, errno, fd);
}
@@ -996,15 +992,13 @@ do_redirection_internal (redirect, flags)
if ((flags & RX_ACTIVE) && (redir_fd != redirector))
{
if (flags & RX_UNDOABLE)
if ((flags & RX_UNDOABLE) && (redirect->rflags & REDIR_VARASSIGN) == 0)
{
/* Only setup to undo it if the thing to undo is active. */
if (fcntl (redirector, F_GETFD, 0) != -1)
r = add_undo_redirect (redirector, ri, redir_fd);
else
r = add_undo_close_redirect (redirector);
if (r < 0 && (redirect->rflags & REDIR_VARASSIGN))
close (redirector);
REDIRECTION_ERROR (r, errno, -1);
}
#if defined (BUFFERED_INPUT)
@@ -1078,6 +1072,7 @@ do_redirection_internal (redirect, flags)
}
r = 0;
/* XXX - only if REDIR_VARASSIGN not set? */
if ((flags & RX_UNDOABLE) && (fcntl (redirector, F_GETFD, 0) != -1))
{
r = add_undo_redirect (redirector, ri, -1);
+11 -3
View File
@@ -5128,6 +5128,10 @@ process_substitute (string, open_for_read_in_child)
dev_fd_list[parent_pipe_fd] = 0;
#endif /* HAVE_DEV_FD */
/* subshells shouldn't have this flag, which controls using the temporary
environment for variable lookups. */
expanding_redir = 0;
result = parse_and_execute (string, "process substitution", (SEVAL_NONINT|SEVAL_NOHIST));
#if !defined (HAVE_DEV_FD)
@@ -7270,7 +7274,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
var_is_null = check_nullness && (var_is_set == 0 || *temp == 0);
/* XXX - this may not need to be restricted to special variables */
if (check_nullness)
var_is_null |= var_is_special && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && QUOTED_NULL (temp);
var_is_null |= var_is_set && var_is_special && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && QUOTED_NULL (temp);
/* Get the rest of the stuff inside the braces. */
if (c && c != RBRACE)
@@ -9231,7 +9235,7 @@ brace_expand_word_list (tlist, eflags)
if (tlist->word->flags & W_NOBRACE)
{
itrace("brace_expand_word_list: %s: W_NOBRACE", tlist->word->word);
/*itrace("brace_expand_word_list: %s: W_NOBRACE", tlist->word->word);*/
PREPEND_LIST (tlist, output_list);
continue;
}
@@ -9341,8 +9345,12 @@ shell_expand_word_list (tlist, eflags)
{
int t;
if (tlist->word->flags & W_ASSIGNASSOC)
if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL)) == (W_ASSIGNASSOC|W_ASSNGLOBAL))
make_internal_declare (tlist->word->word, "-gA");
else if (tlist->word->flags & W_ASSIGNASSOC)
make_internal_declare (tlist->word->word, "-A");
else if (tlist->word->flags & W_ASSNGLOBAL)
make_internal_declare (tlist->word->word, "-g");
t = do_word_assignment (tlist->word, 0);
if (t == 0)
+12 -2
View File
@@ -4888,7 +4888,6 @@ unlink_fifo_list ()
if (nfds == 0)
return;
itrace("unlink_fifo_list");
for (i = 0; nfds && i < totfds; i++)
unlink_fifo (i);
@@ -5129,6 +5128,10 @@ process_substitute (string, open_for_read_in_child)
dev_fd_list[parent_pipe_fd] = 0;
#endif /* HAVE_DEV_FD */
/* subshells shouldn't have this flag, which controls using the temporary
environment for variable lookups. */
expanding_redir = 0;
result = parse_and_execute (string, "process substitution", (SEVAL_NONINT|SEVAL_NOHIST));
#if !defined (HAVE_DEV_FD)
@@ -7269,6 +7272,9 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
var_is_set = temp != (char *)0;
var_is_null = check_nullness && (var_is_set == 0 || *temp == 0);
/* XXX - this may not need to be restricted to special variables */
if (check_nullness)
var_is_null |= var_is_set && var_is_special && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && QUOTED_NULL (temp);
/* Get the rest of the stuff inside the braces. */
if (c && c != RBRACE)
@@ -9339,8 +9345,12 @@ shell_expand_word_list (tlist, eflags)
{
int t;
if (tlist->word->flags & W_ASSIGNASSOC)
if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL)) == (W_ASSIGNASSOC|W_ASSNGLOBAL))
make_internal_declare (tlist->word->word, "-gA");
else if (tlist->word->flags & W_ASSIGNASSOC)
make_internal_declare (tlist->word->word, "-A");
else if (tlist->word->flags & W_ASSNGLOBAL)
make_internal_declare (tlist->word->word, "-g");
t = do_word_assignment (tlist->word, 0);
if (t == 0)
+2 -2
View File
@@ -620,11 +620,11 @@ unary_test (op, arg)
case 'v':
v = find_variable (arg);
return (v && var_isset (v) ? TRUE : FALSE);
return (v && invisible_p (v) == 0 && var_isset (v) ? TRUE : FALSE);
case 'R':
v = find_variable (arg);
return (v && var_isset (v) && nameref_p (v) ? TRUE : FALSE);
return (v && invisible_p (v) == 0 && var_isset (v) && nameref_p (v) ? TRUE : FALSE);
}
/* We can't actually get here, but this shuts up gcc. */
+856
View File
@@ -0,0 +1,856 @@
/* test.c - GNU test program (ksb and mjb) */
/* Modified to run with the GNU shell Apr 25, 1988 by bfox. */
/* Copyright (C) 1987-2010 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
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 <http://www.gnu.org/licenses/>.
*/
/* Define PATTERN_MATCHING to get the csh-like =~ and !~ pattern-matching
binary operators. */
/* #define PATTERN_MATCHING */
#if defined (HAVE_CONFIG_H)
# include <config.h>
#endif
#include <stdio.h>
#include "bashtypes.h"
#if !defined (HAVE_LIMITS_H) && defined (HAVE_SYS_PARAM_H)
# include <sys/param.h>
#endif
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <errno.h>
#if !defined (errno)
extern int errno;
#endif /* !errno */
#if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H)
# include <sys/file.h>
#endif /* !_POSIX_VERSION */
#include "posixstat.h"
#include "filecntl.h"
#include "stat-time.h"
#include "bashintl.h"
#include "shell.h"
#include "pathexp.h"
#include "test.h"
#include "builtins/common.h"
#include <glob/strmatch.h>
#if !defined (STRLEN)
# define STRLEN(s) ((s)[0] ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0)
#endif
#if !defined (STREQ)
# define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0)
#endif /* !STREQ */
#define STRCOLLEQ(a, b) ((a)[0] == (b)[0] && strcoll ((a), (b)) == 0)
#if !defined (R_OK)
#define R_OK 4
#define W_OK 2
#define X_OK 1
#define F_OK 0
#endif /* R_OK */
#define EQ 0
#define NE 1
#define LT 2
#define GT 3
#define LE 4
#define GE 5
#define NT 0
#define OT 1
#define EF 2
/* The following few defines control the truth and false output of each stage.
TRUE and FALSE are what we use to compute the final output value.
SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
Default is TRUE = 1, FALSE = 0, SHELL_BOOLEAN = (!value). */
#define TRUE 1
#define FALSE 0
#define SHELL_BOOLEAN(value) (!(value))
#define TEST_ERREXIT_STATUS 2
static procenv_t test_exit_buf;
static int test_error_return;
#define test_exit(val) \
do { test_error_return = val; longjmp (test_exit_buf, 1); } while (0)
extern int sh_stat __P((const char *, struct stat *));
static int pos; /* The offset of the current argument in ARGV. */
static int argc; /* The number of arguments present in ARGV. */
static char **argv; /* The argument list. */
static int noeval;
static void test_syntax_error __P((char *, char *)) __attribute__((__noreturn__));
static void beyond __P((void)) __attribute__((__noreturn__));
static void integer_expected_error __P((char *)) __attribute__((__noreturn__));
static int unary_operator __P((void));
static int binary_operator __P((void));
static int two_arguments __P((void));
static int three_arguments __P((void));
static int posixtest __P((void));
static int expr __P((void));
static int term __P((void));
static int and __P((void));
static int or __P((void));
static int filecomp __P((char *, char *, int));
static int arithcomp __P((char *, char *, int, int));
static int patcomp __P((char *, char *, int));
static void
test_syntax_error (format, arg)
char *format, *arg;
{
builtin_error (format, arg);
test_exit (TEST_ERREXIT_STATUS);
}
/*
* beyond - call when we're beyond the end of the argument list (an
* error condition)
*/
static void
beyond ()
{
test_syntax_error (_("argument expected"), (char *)NULL);
}
/* Syntax error for when an integer argument was expected, but
something else was found. */
static void
integer_expected_error (pch)
char *pch;
{
test_syntax_error (_("%s: integer expression expected"), pch);
}
/* Increment our position in the argument list. Check that we're not
past the end of the argument list. This check is supressed if the
argument is FALSE. Made a macro for efficiency. */
#define advance(f) do { ++pos; if (f && pos >= argc) beyond (); } while (0)
#define unary_advance() do { advance (1); ++pos; } while (0)
/*
* expr:
* or
*/
static int
expr ()
{
if (pos >= argc)
beyond ();
return (FALSE ^ or ()); /* Same with this. */
}
/*
* or:
* and
* and '-o' or
*/
static int
or ()
{
int value, v2;
value = and ();
if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'o' && !argv[pos][2])
{
advance (0);
v2 = or ();
return (value || v2);
}
return (value);
}
/*
* and:
* term
* term '-a' and
*/
static int
and ()
{
int value, v2;
value = term ();
if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'a' && !argv[pos][2])
{
advance (0);
v2 = and ();
return (value && v2);
}
return (value);
}
/*
* term - parse a term and return 1 or 0 depending on whether the term
* evaluates to true or false, respectively.
*
* term ::=
* '-'('a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'k'|'p'|'r'|'s'|'u'|'w'|'x') filename
* '-'('G'|'L'|'O'|'S'|'N') filename
* '-t' [int]
* '-'('z'|'n') string
* '-o' option
* string
* string ('!='|'='|'==') string
* <int> '-'(eq|ne|le|lt|ge|gt) <int>
* file '-'(nt|ot|ef) file
* '(' <expr> ')'
* int ::=
* positive and negative integers
*/
static int
term ()
{
int value;
if (pos >= argc)
beyond ();
/* Deal with leading `not's. */
if (argv[pos][0] == '!' && argv[pos][1] == '\0')
{
value = 0;
while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0')
{
advance (1);
value = 1 - value;
}
return (value ? !term() : term());
}
/* A paren-bracketed argument. */
if (argv[pos][0] == '(' && argv[pos][1] == '\0') /* ) */
{
advance (1);
value = expr ();
if (argv[pos] == 0) /* ( */
test_syntax_error (_("`)' expected"), (char *)NULL);
else if (argv[pos][0] != ')' || argv[pos][1]) /* ( */
test_syntax_error (_("`)' expected, found %s"), argv[pos]);
advance (0);
return (value);
}
/* are there enough arguments left that this could be dyadic? */
if ((pos + 3 <= argc) && test_binop (argv[pos + 1]))
value = binary_operator ();
/* Might be a switch type argument */
else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
{
if (test_unop (argv[pos]))
value = unary_operator ();
else
test_syntax_error (_("%s: unary operator expected"), argv[pos]);
}
else
{
value = argv[pos][0] != '\0';
advance (0);
}
return (value);
}
static int
stat_mtime (fn, st, ts)
char *fn;
struct stat *st;
struct timespec *ts;
{
int r;
r = sh_stat (fn, st);
if (r < 0)
return r;
*ts = get_stat_mtime (st);
return 0;
}
static int
filecomp (s, t, op)
char *s, *t;
int op;
{
struct stat st1, st2;
struct timespec ts1, ts2;
int r1, r2;
if ((r1 = stat_mtime (s, &st1, &ts1)) < 0)
{
if (op == EF)
return (FALSE);
}
if ((r2 = stat_mtime (t, &st2, &ts2)) < 0)
{
if (op == EF)
return (FALSE);
}
switch (op)
{
case OT: return (r1 < r2 || (r2 == 0 && timespec_cmp (ts1, ts2) < 0));
case NT: return (r1 > r2 || (r1 == 0 && timespec_cmp (ts1, ts2) > 0));
case EF: return (same_file (s, t, &st1, &st2));
}
return (FALSE);
}
static int
arithcomp (s, t, op, flags)
char *s, *t;
int op, flags;
{
intmax_t l, r;
int expok;
if (flags & TEST_ARITHEXP)
{
l = evalexp (s, &expok);
if (expok == 0)
return (FALSE); /* should probably longjmp here */
r = evalexp (t, &expok);
if (expok == 0)
return (FALSE); /* ditto */
}
else
{
if (legal_number (s, &l) == 0)
integer_expected_error (s);
if (legal_number (t, &r) == 0)
integer_expected_error (t);
}
switch (op)
{
case EQ: return (l == r);
case NE: return (l != r);
case LT: return (l < r);
case GT: return (l > r);
case LE: return (l <= r);
case GE: return (l >= r);
}
return (FALSE);
}
static int
patcomp (string, pat, op)
char *string, *pat;
int op;
{
int m;
m = strmatch (pat, string, FNMATCH_EXTFLAG|FNMATCH_IGNCASE);
return ((op == EQ) ? (m == 0) : (m != 0));
}
int
binary_test (op, arg1, arg2, flags)
char *op, *arg1, *arg2;
int flags;
{
int patmatch;
patmatch = (flags & TEST_PATMATCH);
if (op[0] == '=' && (op[1] == '\0' || (op[1] == '=' && op[2] == '\0')))
return (patmatch ? patcomp (arg1, arg2, EQ) : STREQ (arg1, arg2));
else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0')
{
if (shell_compatibility_level > 40 && flags & TEST_LOCALE)
return ((op[0] == '>') ? (strcoll (arg1, arg2) > 0) : (strcoll (arg1, arg2) < 0));
else
return ((op[0] == '>') ? (strcmp (arg1, arg2) > 0) : (strcmp (arg1, arg2) < 0));
}
else if (op[0] == '!' && op[1] == '=' && op[2] == '\0')
return (patmatch ? patcomp (arg1, arg2, NE) : (STREQ (arg1, arg2) == 0));
else if (op[2] == 't')
{
switch (op[1])
{
case 'n': return (filecomp (arg1, arg2, NT)); /* -nt */
case 'o': return (filecomp (arg1, arg2, OT)); /* -ot */
case 'l': return (arithcomp (arg1, arg2, LT, flags)); /* -lt */
case 'g': return (arithcomp (arg1, arg2, GT, flags)); /* -gt */
}
}
else if (op[1] == 'e')
{
switch (op[2])
{
case 'f': return (filecomp (arg1, arg2, EF)); /* -ef */
case 'q': return (arithcomp (arg1, arg2, EQ, flags)); /* -eq */
}
}
else if (op[2] == 'e')
{
switch (op[1])
{
case 'n': return (arithcomp (arg1, arg2, NE, flags)); /* -ne */
case 'g': return (arithcomp (arg1, arg2, GE, flags)); /* -ge */
case 'l': return (arithcomp (arg1, arg2, LE, flags)); /* -le */
}
}
return (FALSE); /* should never get here */
}
static int
binary_operator ()
{
int value;
char *w;
w = argv[pos + 1];
if ((w[0] == '=' && (w[1] == '\0' || (w[1] == '=' && w[2] == '\0'))) || /* =, == */
((w[0] == '>' || w[0] == '<') && w[1] == '\0') || /* <, > */
(w[0] == '!' && w[1] == '=' && w[2] == '\0')) /* != */
{
value = binary_test (w, argv[pos], argv[pos + 2], 0);
pos += 3;
return (value);
}
#if defined (PATTERN_MATCHING)
if ((w[0] == '=' || w[0] == '!') && w[1] == '~' && w[2] == '\0')
{
value = patcomp (argv[pos], argv[pos + 2], w[0] == '=' ? EQ : NE);
pos += 3;
return (value);
}
#endif
if ((w[0] != '-' || w[3] != '\0') || test_binop (w) == 0)
{
test_syntax_error (_("%s: binary operator expected"), w);
/* NOTREACHED */
return (FALSE);
}
value = binary_test (w, argv[pos], argv[pos + 2], 0);
pos += 3;
return value;
}
static int
unary_operator ()
{
char *op;
intmax_t r;
op = argv[pos];
if (test_unop (op) == 0)
return (FALSE);
/* the only tricky case is `-t', which may or may not take an argument. */
if (op[1] == 't')
{
advance (0);
if (pos < argc)
{
if (legal_number (argv[pos], &r))
{
advance (0);
return (unary_test (op, argv[pos - 1]));
}
else
return (FALSE);
}
else
return (unary_test (op, "1"));
}
/* All of the unary operators take an argument, so we first call
unary_advance (), which checks to make sure that there is an
argument, and then advances pos right past it. This means that
pos - 1 is the location of the argument. */
unary_advance ();
return (unary_test (op, argv[pos - 1]));
}
int
unary_test (op, arg)
char *op, *arg;
{
intmax_t r;
struct stat stat_buf;
SHELL_VAR *v;
switch (op[1])
{
case 'a': /* file exists in the file system? */
case 'e':
return (sh_stat (arg, &stat_buf) == 0);
case 'r': /* file is readable? */
return (sh_eaccess (arg, R_OK) == 0);
case 'w': /* File is writeable? */
return (sh_eaccess (arg, W_OK) == 0);
case 'x': /* File is executable? */
return (sh_eaccess (arg, X_OK) == 0);
case 'O': /* File is owned by you? */
return (sh_stat (arg, &stat_buf) == 0 &&
(uid_t) current_user.euid == (uid_t) stat_buf.st_uid);
case 'G': /* File is owned by your group? */
return (sh_stat (arg, &stat_buf) == 0 &&
(gid_t) current_user.egid == (gid_t) stat_buf.st_gid);
case 'N':
return (sh_stat (arg, &stat_buf) == 0 &&
stat_buf.st_atime <= stat_buf.st_mtime);
case 'f': /* File is a file? */
if (sh_stat (arg, &stat_buf) < 0)
return (FALSE);
/* -f is true if the given file exists and is a regular file. */
#if defined (S_IFMT)
return (S_ISREG (stat_buf.st_mode) || (stat_buf.st_mode & S_IFMT) == 0);
#else
return (S_ISREG (stat_buf.st_mode));
#endif /* !S_IFMT */
case 'd': /* File is a directory? */
return (sh_stat (arg, &stat_buf) == 0 && (S_ISDIR (stat_buf.st_mode)));
case 's': /* File has something in it? */
return (sh_stat (arg, &stat_buf) == 0 && stat_buf.st_size > (off_t) 0);
case 'S': /* File is a socket? */
#if !defined (S_ISSOCK)
return (FALSE);
#else
return (sh_stat (arg, &stat_buf) == 0 && S_ISSOCK (stat_buf.st_mode));
#endif /* S_ISSOCK */
case 'c': /* File is character special? */
return (sh_stat (arg, &stat_buf) == 0 && S_ISCHR (stat_buf.st_mode));
case 'b': /* File is block special? */
return (sh_stat (arg, &stat_buf) == 0 && S_ISBLK (stat_buf.st_mode));
case 'p': /* File is a named pipe? */
#ifndef S_ISFIFO
return (FALSE);
#else
return (sh_stat (arg, &stat_buf) == 0 && S_ISFIFO (stat_buf.st_mode));
#endif /* S_ISFIFO */
case 'L': /* Same as -h */
case 'h': /* File is a symbolic link? */
#if !defined (S_ISLNK) || !defined (HAVE_LSTAT)
return (FALSE);
#else
return ((arg[0] != '\0') &&
(lstat (arg, &stat_buf) == 0) && S_ISLNK (stat_buf.st_mode));
#endif /* S_IFLNK && HAVE_LSTAT */
case 'u': /* File is setuid? */
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISUID) != 0);
case 'g': /* File is setgid? */
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISGID) != 0);
case 'k': /* File has sticky bit set? */
#if !defined (S_ISVTX)
/* This is not Posix, and is not defined on some Posix systems. */
return (FALSE);
#else
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISVTX) != 0);
#endif
case 't': /* File fd is a terminal? */
if (legal_number (arg, &r) == 0)
return (FALSE);
return ((r == (int)r) && isatty ((int)r));
case 'n': /* True if arg has some length. */
return (arg[0] != '\0');
case 'z': /* True if arg has no length. */
return (arg[0] == '\0');
case 'o': /* True if option `arg' is set. */
return (minus_o_option_value (arg) == 1);
case 'v':
v = find_variable (arg);
return (v && invisible_p (v) == 0 && var_isset (v) ? TRUE : FALSE);
case 'R':
v = find_variable (arg);
return (v && var_isset (v) && nameref_p (v) ? TRUE : FALSE);
}
/* We can't actually get here, but this shuts up gcc. */
return (FALSE);
}
/* Return TRUE if OP is one of the test command's binary operators. */
int
test_binop (op)
char *op;
{
if (op[0] == '=' && op[1] == '\0')
return (1); /* '=' */
else if ((op[0] == '<' || op[0] == '>') && op[1] == '\0') /* string <, > */
return (1);
else if ((op[0] == '=' || op[0] == '!') && op[1] == '=' && op[2] == '\0')
return (1); /* `==' and `!=' */
#if defined (PATTERN_MATCHING)
else if (op[2] == '\0' && op[1] == '~' && (op[0] == '=' || op[0] == '!'))
return (1);
#endif
else if (op[0] != '-' || op[2] == '\0' || op[3] != '\0')
return (0);
else
{
if (op[2] == 't')
switch (op[1])
{
case 'n': /* -nt */
case 'o': /* -ot */
case 'l': /* -lt */
case 'g': /* -gt */
return (1);
default:
return (0);
}
else if (op[1] == 'e')
switch (op[2])
{
case 'q': /* -eq */
case 'f': /* -ef */
return (1);
default:
return (0);
}
else if (op[2] == 'e')
switch (op[1])
{
case 'n': /* -ne */
case 'g': /* -ge */
case 'l': /* -le */
return (1);
default:
return (0);
}
else
return (0);
}
}
/* Return non-zero if OP is one of the test command's unary operators. */
int
test_unop (op)
char *op;
{
if (op[0] != '-' || op[2] != 0)
return (0);
switch (op[1])
{
case 'a': case 'b': case 'c': case 'd': case 'e':
case 'f': case 'g': case 'h': case 'k': case 'n':
case 'o': case 'p': case 'r': case 's': case 't':
case 'u': case 'v': case 'w': case 'x': case 'z':
case 'G': case 'L': case 'O': case 'S': case 'N':
return (1);
}
return (0);
}
static int
two_arguments ()
{
if (argv[pos][0] == '!' && argv[pos][1] == '\0')
return (argv[pos + 1][0] == '\0');
else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
{
if (test_unop (argv[pos]))
return (unary_operator ());
else
test_syntax_error (_("%s: unary operator expected"), argv[pos]);
}
else
test_syntax_error (_("%s: unary operator expected"), argv[pos]);
return (0);
}
#define ANDOR(s) (s[0] == '-' && !s[2] && (s[1] == 'a' || s[1] == 'o'))
/* This could be augmented to handle `-t' as equivalent to `-t 1', but
POSIX requires that `-t' be given an argument. */
#define ONE_ARG_TEST(s) ((s)[0] != '\0')
static int
three_arguments ()
{
int value;
if (test_binop (argv[pos+1]))
{
value = binary_operator ();
pos = argc;
}
else if (ANDOR (argv[pos+1]))
{
if (argv[pos+1][1] == 'a')
value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]);
else
value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]);
pos = argc;
}
else if (argv[pos][0] == '!' && argv[pos][1] == '\0')
{
advance (1);
value = !two_arguments ();
}
else if (argv[pos][0] == '(' && argv[pos+2][0] == ')')
{
value = ONE_ARG_TEST(argv[pos+1]);
pos = argc;
}
else
test_syntax_error (_("%s: binary operator expected"), argv[pos+1]);
return (value);
}
/* This is an implementation of a Posix.2 proposal by David Korn. */
static int
posixtest ()
{
int value;
switch (argc - 1) /* one extra passed in */
{
case 0:
value = FALSE;
pos = argc;
break;
case 1:
value = ONE_ARG_TEST(argv[1]);
pos = argc;
break;
case 2:
value = two_arguments ();
pos = argc;
break;
case 3:
value = three_arguments ();
break;
case 4:
if (argv[pos][0] == '!' && argv[pos][1] == '\0')
{
advance (1);
value = !three_arguments ();
break;
}
/* FALLTHROUGH */
default:
value = expr ();
}
return (value);
}
/*
* [:
* '[' expr ']'
* test:
* test expr
*/
int
test_command (margc, margv)
int margc;
char **margv;
{
int value;
int code;
USE_VAR(margc);
code = setjmp (test_exit_buf);
if (code)
return (test_error_return);
argv = margv;
if (margv[0] && margv[0][0] == '[' && margv[0][1] == '\0')
{
--margc;
if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1]))
test_syntax_error (_("missing `]'"), (char *)NULL);
if (margc < 2)
test_exit (SHELL_BOOLEAN (FALSE));
}
argc = margc;
pos = 1;
if (pos >= argc)
test_exit (SHELL_BOOLEAN (FALSE));
noeval = 0;
value = posixtest ();
if (pos != argc)
test_syntax_error (_("too many arguments"), (char *)NULL);
test_exit (SHELL_BOOLEAN (value));
}
+4
View File
@@ -1,3 +1,7 @@
recho "A${*:-w}R"
recho "A${*-w}R"
recho "A${*}R"
set -- ""
recho "A${*:-w}R"
+5
View File
@@ -0,0 +1,5 @@
set -- ""
recho "A${*:-w}R"
recho "A${*-w}R"
recho "A${*}R"
+5 -5
View File
@@ -20,7 +20,7 @@ bar ()
exec {v}> $TMPFILE;
echo $v
}
11
10
line 1
line 2
line 3
@@ -35,11 +35,11 @@ EOF
echo $v
}
11
10
foo 1
foo 2
foo 3
11
10
/bin/bash
/bin/csh
/bin/ksh
@@ -64,7 +64,7 @@ iclosev ()
/bin/zsh
./vredir3.sub: line 4: v: ambiguous redirect
after
11 12
10 11
a
a
swizzle is a function
@@ -75,7 +75,7 @@ swizzle ()
exec {stdin}<&$fd0;
exec {stdout}>&$fd1
}
13 11
12 10
a
a
swizzle is a function
-3
View File
@@ -414,10 +414,7 @@ trap_handler (sig)
{
wait_signal_received = sig;
if (interrupt_immediately)
{
itrace("trap_handler: interrupt_immediately = 1: calling longjmp to wait_intr_buf: sig = %d", sig);
longjmp (wait_intr_buf, 1);
}
}
#if defined (READLINE)
+3 -3
View File
@@ -2192,7 +2192,6 @@ make_local_variable (name)
return ((SHELL_VAR *)NULL);
}
itrace("make_local_variable: name = %s old_var = %p variable_context = %d", name, old_var, variable_context);
if (old_var == 0)
new_var = make_new_variable (name, vc->table);
else
@@ -2855,6 +2854,9 @@ assign_in_env (word, flags)
setifs (var);
else
#endif
if (ifsname (name))
itrace("assign_in_env: name = %s value = <%s> flags = %d context = %d", name, value, flags, variable_context);
if (flags)
stupidly_hack_special_variables (name);
@@ -4365,7 +4367,6 @@ push_context (name, is_subshell, tempvars)
int is_subshell;
HASH_TABLE *tempvars;
{
itrace("push_context: %s: subshell = %d variable_context = %d", name, is_subshell, variable_context);
if (is_subshell == 0)
push_dollar_vars ();
variable_context++;
@@ -4377,7 +4378,6 @@ itrace("push_context: %s: subshell = %d variable_context = %d", name, is_subshel
void
pop_context ()
{
itrace("pop_context: variable_context = %d", variable_context);
pop_dollar_vars ();
variable_context--;
pop_var_context ();