mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-06-27 15:43:18 +02:00
commit bash-20121130 snapshot
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+31
-4
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
recho "A${*:-w}R"
|
||||
recho "A${*-w}R"
|
||||
recho "A${*}R"
|
||||
|
||||
set -- ""
|
||||
|
||||
recho "A${*:-w}R"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
set -- ""
|
||||
|
||||
recho "A${*:-w}R"
|
||||
recho "A${*-w}R"
|
||||
recho "A${*}R"
|
||||
+5
-5
@@ -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
|
||||
|
||||
@@ -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
@@ -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 ();
|
||||
|
||||
Reference in New Issue
Block a user