- wait_for: if we're checking for window size changes, allow checks
during trap commands while readline is active or `bind -x' command
execution. Fix from Koichi Murase <myoga.murase@gmail.com>
+
+ 2/22
+ ----
+jobs.c
+ - wait_for: check for window size changes during programmable completion
+
+ 2/23
+ ----
+execute_cmd.c,input.c,jobs.c,nojobs.c,parse.y,redir.c,shell.c
+builtins/{exec.def,read.def}
+input.h
+ - BUFFERED_INPUT: preprocessor #define is gone, this code is now
+ unconditional
+
+ 2/24
+ ----
+builtins/declare.def
+ - declare_internal: set the att_propagate flag on a variable that is
+ marked as att_tempvar only if it's *not* marked att_local. The
+ effect is that local variables with the same name as variables in
+ the temporary environment are not propagated to the previous scope.
+ From a report by Voldemar Lazarev <voldemar.lazarev@alludo.com>
+ that prompted a bug-bash discussion
+
+builtins/setattr.def
+ - set_var_attribute: don't set local variables for which we are setting
+ export or readonly to propagate back to the previous context, but
+ make sure we preserve posix semantics
+
+parse.y
+ - read_token_word: if we're parsing the words of a compound assignment
+ (parser_state & PST_COMPASSIGN), and we have a valid assignment
+ statement as a word ([sub]=value), set W_NOBRACE in the word so we
+ don't try and perform brace expansion on it. This makes ( [sub]=word )
+ closer to name[sub]=word.
+ From a report by Ilkka Virta <itvirta@iki.fi> back in July, 2020:
+ https://lists.gnu.org/archive/html/bug-bash/2020-07/msg00133.html
tests/varenv20.sub f
tests/varenv21.sub f
tests/varenv22.sub f
+tests/varenv23.sub f
tests/version f
tests/version.mini f
tests/vredir.tests f
/* If we found this variable in the temporary environment, as with
`var=value declare -x var', make sure it is treated identically
to `var=value export var'. Do the same for `declare -r' and
- `readonly'. Preserve the attributes, except for att_tempvar. */
+ `readonly'. Preserve the attributes, except for att_tempvar.
+ This doesn't happen in functions, since declare in shell functions
+ always creates local variables (that inherit their value from the
+ tempenv variable). We don't check variable_context; maybe we should. */
/* XXX -- should this create a variable in the global scope, or
modify the local variable flags? ksh93 has it modify the
global scope.
SHELL_VAR *tv;
char *tvalue;
+ /* Temporary environment? Or in the local variable context? */
tv = find_tempenv_variable (name_cell (var));
if (tv)
{
if (tv)
{
tv->attributes |= var->attributes & ~att_tempvar;
- if (tv->context > 0)
+ if (tv->context > 0 && local_p (var) == 0) /* just paranoia here */
VSETATTR (tv, att_propagate);
}
free (tvalue);
}
- VSETATTR (var, att_propagate);
+ /* XXX - don't propagate local variables back to a previous scope,
+ even in posix mode. */
+ if (local_p (var) == 0)
+ VSETATTR (var, att_propagate);
}
/* Turn on nameref attribute we deferred above. */
default_tty_job_signals (); /* undo initialize_job_signals */
#endif /* JOB_CONTROL */
-#if defined (BUFFERED_INPUT)
if (default_buffered_input >= 0)
sync_buffered_stream (default_buffered_input);
-#endif
exit_value = shell_execve (command, args, env);
#include <readline/readline.h>
#endif
-#if defined (BUFFERED_INPUT)
-# include "input.h"
-#endif
+#include "input.h"
#include "shmbutil.h"
#include "timer.h"
begin_unwind_frame ("read_builtin");
-#if defined (BUFFERED_INPUT)
if (interactive == 0 && default_buffered_input >= 0 && fd_is_bash_input (fd))
sync_buffered_stream (default_buffered_input);
-#endif
#if 1
input_is_tty = isatty (fd);
return (1);
}
+/* This is only called by readonly/export, so we can implement posix-mode
+ semantics for special variables. */
void
set_var_attribute (char *name, int attribute, int undo)
{
var = find_variable (name);
else
{
+ /* var=value readonly var */
tv = find_tempenv_variable (name);
/* XXX -- need to handle case where tv is a temp variable in a
function-scope context, since function_env has been merged into
if (var)
VSETATTR (var, att_invisible);
}
- else if (var->context != 0)
+ else if (var->context != 0 && local_p (var) == 0)
VSETATTR (var, att_propagate);
}
}
error messages about multiple directory arguments to `cd'. */
#define CD_COMPLAINS
-/* Define BUFFERED_INPUT if you want the shell to do its own input
- buffering, rather than using stdio. Do not undefine this; it's
- required to preserve semantics required by POSIX. */
-#define BUFFERED_INPUT
-
/* Define ONESHOT if you want sh -c 'command' to avoid forking to execute
`command' whenever possible. This is a big efficiency improvement. */
#define ONESHOT
\fIname\fP=\fB(\fPvalue\fI1\fP ... value\fIn\fP\fB)\fP, where each
\fIvalue\fP may be of the form [\fIsubscript\fP]=\fIstring\fP.
Indexed array assignments do not require anything but \fIstring\fP.
-Each \fIvalue\fP in the list is expanded using all the shell expansions
+Each \fIvalue\fP in the list is expanded using the shell expansions
described below under
.SM
-.BR EXPANSION .
+.BR EXPANSION ,
+but \fIvalue\fPs that are valid variable assignments
+including the brackets and subscript do not undergo
+brace expansion and word splitting, as with individual
+variable assignments.
When assigning to indexed arrays, if the optional brackets and subscript
are supplied, that index is assigned to;
otherwise the index of the element assigned is the last index assigned
otherwise the index of the element assigned is the last index assigned
to by the statement plus one. Indexing starts at zero.
-Each @var{value} in the list undergoes all the shell expansions
-described above (@pxref{Shell Expansions}).
+Each @var{value} in the list undergoes the shell expansions
+described above (@pxref{Shell Expansions}),
+but @var{value}s that are valid variable assignments
+including the brackets and subscript do not undergo
+brace expansion and word splitting, as with individual
+variable assignments.
When assigning to an associative array, the words in a compound assignment
may be either assignment statements, for which the subscript is required,
/* execute_cmd.c -- Execute a COMMAND structure. */
-/* Copyright (C) 1987-2022 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2023 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#include <glob/strmatch.h>
#include <tilde/tilde.h>
-#if defined (BUFFERED_INPUT)
-# include "input.h"
-#endif
+#include "input.h"
#if defined (ALIAS)
# include "alias.h"
if (should_redir_stdin && stdin_redir == 0)
async_redirect_stdin ();
-#if defined (BUFFERED_INPUT)
/* In any case, we are not reading our command input from stdin. */
default_buffered_input = -1;
-#endif
/* We can't optimize away forks if one of the commands executed by the
subshell sets an exit trap, so we set CMD_NO_FORK for simple commands
/* input.c -- functions to perform buffered input with synchronization. */
-/* Copyright (C) 1992-2022 Free Software Foundation, Inc.
+/* Copyright (C) 1992-2023 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
return c;
}
-#if defined (BUFFERED_INPUT)
-
/* A facility similar to stdio, but input-only. */
#if defined (USING_BASH_MALLOC)
exit(0);
}
#endif /* TEST */
-#endif /* BUFFERED_INPUT */
/* input.h -- Structures and unions used for reading input. */
-/* Copyright (C) 1993-2022 Free Software Foundation, Inc.
+/* Copyright (C) 1993-2023 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
enum stream_type {st_none, st_stdin, st_stream, st_string, st_bstream};
-#if defined (BUFFERED_INPUT)
-
/* Possible values for b_flag. */
#undef B_EOF
#undef B_ERROR /* There are some systems with this define */
#define bclearerror(bp) ((bp)->b_flag &= ~(B_ERROR|B_EOF))
-#endif /* BUFFERED_INPUT */
-
typedef union {
FILE *file;
char *string;
-#if defined (BUFFERED_INPUT)
int buffered_fd;
-#endif
} INPUT_STREAM;
typedef struct {
extern int getc_with_restart (FILE *);
extern int ungetc_with_restart (int, FILE *);
-#if defined (BUFFERED_INPUT)
/* Functions from input.c. */
extern int fd_is_bash_input (int);
extern int set_bash_input_fd (int);
extern int buffered_getchar (void);
extern int buffered_ungetchar (int);
extern void with_input_from_buffered_stream (int, char *);
-#endif /* BUFFERED_INPUT */
#endif /* _INPUT_H_ */
#include <sys/param.h>
#endif
-#if defined (BUFFERED_INPUT)
-# include "input.h"
-#endif
+#include "input.h"
/* Need to include this up here for *_TTY_DRIVER definitions. */
#include "shtty.h"
async_p = (flags & FORK_ASYNC);
forksleep = 1;
-#if defined (BUFFERED_INPUT)
/* If default_buffered_input is active, we are reading a script. If
the command is asynchronous, we have already duplicated /dev/null
as fd 0, but have not changed the buffered stream corresponding to
the old fd 0. We don't want to sync the stream in this case. */
- if (default_buffered_input != -1 &&
- (!async_p || default_buffered_input > 0))
+ if (default_buffered_input != -1 && (!async_p || default_buffered_input > 0))
sync_buffered_stream (default_buffered_input);
-#endif /* BUFFERED_INPUT */
/* Create the child, handle severe errors. Retry on EAGAIN. */
while ((pid = fork ()) < 0 && errno == EAGAIN && forksleep < FORKSLEEP_MAX)
child process, go back and change callers who free `command' in
the child process when this returns. */
mypid = getpid ();
-#if defined (BUFFERED_INPUT)
+
/* Close default_buffered_input if it's > 0. We don't close it if it's
0 because that's the file descriptor used when redirecting input,
and it's wrong to close the file in that case. */
unset_bash_input (0);
-#endif /* BUFFERED_INPUT */
CLRINTERRUPT; /* XXX - children have their own interrupt state */
}
else
#if defined (READLINE)
- /* We don't want to do this if we are running a process during
- programmable completion, but we do want to handle window size
- changes for traps while readline is active or a command bound
- to `bind -x'. */
- if (RL_ISSTATE (RL_STATE_COMPLETING) == 0)
- if (RL_ISSTATE(RL_STATE_DISPATCHING|RL_STATE_TERMPREPPED) != 0)
- {
- if (check_window_size)
- get_new_window_size (0, (int *)0, (int *)0);
- }
- else
+ /* We don't want to get the entire tty state if we are running
+ while readline is active or has changed the terminal, but we
+ can handle window size changes during programmable completion,
+ traps while readline is active, or a command bound using
+ `bind -x'. */
+ if (RL_ISSTATE(RL_STATE_COMPLETING|RL_STATE_DISPATCHING|RL_STATE_TERMPREPPED) != 0)
+ {
+ if (check_window_size)
+ get_new_window_size (0, (int *)0, (int *)0);
+ }
+ else
#endif
get_tty_state ();
/* This file works under BSD, System V, minix, and Posix systems. It does
not implement job control. */
-/* Copyright (C) 1987-2022 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2023 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#include <signal.h>
#include <errno.h>
-#if defined (BUFFERED_INPUT)
-# include "input.h"
-#endif
+#include "input.h"
/* Need to include this up here for *_TTY_DRIVER definitions. */
#include "shtty.h"
async_p = (flags & FORK_ASYNC);
start_pipeline ();
-#if defined (BUFFERED_INPUT)
/* If default_buffered_input is active, we are reading a script. If
the command is asynchronous, we have already duplicated /dev/null
as fd 0, but have not changed the buffered stream corresponding to
the old fd 0. We don't want to sync the stream in this case. */
if (default_buffered_input != -1 && (!async_p || default_buffered_input > 0))
sync_buffered_stream (default_buffered_input);
-#endif /* BUFFERED_INPUT */
/* Block SIGTERM here and unblock in child after fork resets the
set of pending signals */
if (pid == 0)
{
-#if defined (BUFFERED_INPUT)
unset_bash_input (0);
-#endif /* BUFFERED_INPUT */
CLRINTERRUPT; /* XXX - children have their own interrupt state */
return (*(bash_input.ungetter)) (c);
}
-#if defined (BUFFERED_INPUT)
#ifdef INCLUDE_UNUSED
int
input_file_descriptor (void)
}
}
#endif
-#endif /* BUFFERED_INPUT */
/* **************************************************************** */
/* */
struct stream_saver *next;
BASH_INPUT bash_input;
int line;
-#if defined (BUFFERED_INPUT)
BUFFERED_STREAM *bstream;
-#endif /* BUFFERED_INPUT */
} STREAM_SAVER;
/* The globally known line number. */
xbcopy ((char *)&bash_input, (char *)&(saver->bash_input), sizeof (BASH_INPUT));
-#if defined (BUFFERED_INPUT)
saver->bstream = (BUFFERED_STREAM *)NULL;
/* If we have a buffered stream, clear out buffers[fd]. */
if (bash_input.type == st_bstream && bash_input.location.buffered_fd >= 0)
saver->bstream = set_buffered_stream (bash_input.location.buffered_fd,
(BUFFERED_STREAM *)NULL);
-#endif /* BUFFERED_INPUT */
saver->line = line_number;
bash_input.name = (char *)NULL;
saver->bash_input.name,
saver->bash_input.location);
-#if defined (BUFFERED_INPUT)
/* If we have a buffered stream, restore buffers[fd]. */
/* If the input file descriptor was changed while this was on the
save stack, update the buffered fd to the new file descriptor and
/* XXX could free buffered stream returned as result here. */
set_buffered_stream (bash_input.location.buffered_fd, saver->bstream);
}
-#endif /* BUFFERED_INPUT */
line_number = saver->line;
QUIT;
last_was_backslash = 0;
- if (sigwinch_received)
- {
- sigwinch_received = 0;
- get_new_window_size (0, (int *)0, (int *)0);
- }
+ CHECK_WINCH;
if (eol_ungetc_lookahead)
{
if (i == 0)
shell_input_line_terminator = EOF;
-#if defined (BUFFERED_INPUT)
if (i == 0 && bash_input.type == st_bstream)
{
BUFFERED_STREAM *bp;
shell_input_line_terminator = READERR;
}
else
-#endif
- if (i == 0 && interactive_shell == 0 && bash_input.type == st_stream && ferror (stdin))
- shell_input_line_terminator = READERR;
+ if (i == 0 && interactive_shell == 0 && bash_input.type == st_stream && ferror (stdin))
+ shell_input_line_terminator = READERR;
/* If we want to make read errors cancel execution of any partial
line, take out the checks for i == 0 above and set i = 0 if
{
the_word->flags |= W_NOSPLIT;
if (parser_state & PST_COMPASSIGN)
- the_word->flags |= W_NOGLOB; /* XXX - W_NOBRACE? */
+ the_word->flags |= W_NOGLOB|W_NOBRACE;
}
}
do { \
if (sigterm_received) termsig_handler (SIGTERM); \
} while (0)
+
+#define CHECK_WINCH \
+do { \
+ if (sigwinch_received) \
+ { \
+ sigwinch_received = 0; \
+ get_new_window_size (0, (int *)0, (int *)0); \
+ } \
+} while (0)
+
#endif /* _QUIT_H_ */
/* redir.c -- Functions to perform input and output redirection. */
-/* Copyright (C) 1997-2022 Free Software Foundation, Inc.
+/* Copyright (C) 1997-2023 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#include "redir.h"
#include "trap.h"
-#if defined (BUFFERED_INPUT)
-# include "input.h"
-#endif
+#include "input.h"
#include "builtins/pipesize.h"
REDIRECTION_ERROR (r, errno, fd);
}
-#if defined (BUFFERED_INPUT)
/* inhibit call to sync_buffered_stream() for async processes */
if (redirector != 0 || (subshell_environment & SUBSHELL_ASYNC) == 0)
check_bash_input (redirector);
-#endif
/* Make sure there is no pending output before we change the state
of the underlying file descriptor, since the builtins use stdio
return (errno);
}
-#if defined (BUFFERED_INPUT)
/* Do not change the buffered stream for an implicit redirection
of /dev/null to fd 0 for asynchronous commands without job
control (r_inputa_direction). */
if (ri == r_input_direction || ri == r_input_output)
duplicate_buffered_stream (fd, redirector);
-#endif /* BUFFERED_INPUT */
/*
* If we're remembering, then this is the result of a while, for
if (fd != redirector)
{
-#if defined (BUFFERED_INPUT)
if (INPUT_REDIRECT (ri))
close_buffered_fd (fd);
else
-#endif /* !BUFFERED_INPUT */
close (fd); /* Don't close what we just opened! */
}
REDIRECTION_ERROR (r, errno, fd);
}
-#if defined (BUFFERED_INPUT)
check_bash_input (redirector);
-#endif
+
if (redirect->rflags & REDIR_VARASSIGN)
{
if ((r = redir_varassign (redirect, redirector)) < 0)
return (r);
}
-#if defined (BUFFERED_INPUT)
duplicate_buffered_stream (fd, redirector);
-#endif
if ((flags & RX_CLEXEC) && (redirector > 2))
SET_CLOSE_ON_EXEC (redirector);
}
if (fd != redirector)
-#if defined (BUFFERED_INPUT)
close_buffered_fd (fd);
-#else
- close (fd);
-#endif
}
break;
REDIRECTION_ERROR (r, errno, -1);
}
}
-#if defined (BUFFERED_INPUT)
+
/* inhibit call to sync_buffered_stream() for async processes */
if (redirector != 0 || (subshell_environment & SUBSHELL_ASYNC) == 0)
check_bash_input (redirector);
-#endif
+
if (redirect->rflags & REDIR_VARASSIGN)
{
if ((r = redir_varassign (redirect, redirector)) < 0)
else if (dup2 (redir_fd, redirector) < 0)
return (errno);
-#if defined (BUFFERED_INPUT)
if (ri == r_duplicating_input || ri == r_move_input)
duplicate_buffered_stream (redir_fd, redirector);
-#endif /* BUFFERED_INPUT */
/* First duplicate the close-on-exec state of redirectee. dup2
leaves the flag unset on the new descriptor, which means it
#endif
xtrace_fdchk (redirector);
-#if defined (BUFFERED_INPUT)
/* inhibit call to sync_buffered_stream() for async processes */
if (redirector != 0 || (subshell_environment & SUBSHELL_ASYNC) == 0)
check_bash_input (redirector);
r = close_buffered_fd (redirector);
-#else /* !BUFFERED_INPUT */
- r = close (redirector);
-#endif /* !BUFFERED_INPUT */
if (r < 0 && (flags & RX_INTERNAL) && (errno == EIO || errno == ENOSPC))
REDIRECTION_ERROR (r, errno, -1);
/* shell.c -- GNU's idea of the POSIX shell specification. */
-/* Copyright (C) 1987-2022 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2023 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
char *exec_argv0;
-#if defined (BUFFERED_INPUT)
/* The file descriptor from which the shell is reading input. */
int default_buffered_input = -1;
-#endif
/* The following two variables are not static so they can show up in $-. */
int read_from_stdin; /* -s flag supplied */
command_execution_string = shell_script_filename = NULL;
want_pending_command = locally_skip_execution = read_from_stdin = 0;
default_input = stdin;
-#if defined (BUFFERED_INPUT)
default_buffered_input = -1;
-#endif
/* Fix for the `infinite process creation' bug when running shell scripts
from startup files on System V. */
{
/* In this mode, bash is reading a script from stdin, which is a
pipe or redirected file. */
-#if defined (BUFFERED_INPUT)
default_buffered_input = fileno (stdin); /* == 0 */
-#else
- setbuf (default_input, NULL);
-#endif /* !BUFFERED_INPUT */
read_from_stdin = 1;
}
else if (top_level_arg_index == argc) /* arg index before startup files */
not match with ours. */
fd = move_to_high_fd (fd, 1, -1);
-#if defined (BUFFERED_INPUT)
default_buffered_input = fd;
SET_CLOSE_ON_EXEC (default_buffered_input);
-#else /* !BUFFERED_INPUT */
- default_input = fdopen (fd, "r");
-
- if (default_input == 0)
- {
- file_error (filename);
- exit (EX_NOTFOUND);
- }
-
- SET_CLOSE_ON_EXEC (fd);
- if (fileno (default_input) != fd)
- SET_CLOSE_ON_EXEC (fileno (default_input));
-#endif /* !BUFFERED_INPUT */
/* Just about the only way for this code to be executed is if something
like `bash -i /dev/stdin' is executed. */
dup2 (fd, 0);
close (fd);
fd = 0;
-#if defined (BUFFERED_INPUT)
default_buffered_input = 0;
-#else
- fclose (default_input);
- default_input = stdin;
-#endif
}
else if (forced_interactive && fd_is_tty == 0)
/* But if a script is called with something like `bash -i scriptname',
{
/* Make sure the fd from which we are reading input is not in
no-delay mode. */
-#if defined (BUFFERED_INPUT)
if (interactive == 0)
sh_unset_nodelay_mode (default_buffered_input);
else
-#endif /* !BUFFERED_INPUT */
sh_unset_nodelay_mode (fileno (stdin));
/* with_input_from_stdin really means `with_input_from_readline' */
if (interactive && no_line_editing == 0)
with_input_from_stdin ();
-#if defined (BUFFERED_INPUT)
else if (interactive == 0)
with_input_from_buffered_stream (default_buffered_input, dollar_vars[0]);
-#endif /* BUFFERED_INPUT */
else
with_input_from_stream (default_input, dollar_vars[0]);
}
void
unset_bash_input (int check_zero)
{
-#if defined (BUFFERED_INPUT)
if ((check_zero && default_buffered_input >= 0) ||
(check_zero == 0 && default_buffered_input > 0))
{
default_buffered_input = bash_input.location.buffered_fd = -1;
bash_input.type = st_none; /* XXX */
}
-#else /* !BUFFERED_INPUT */
- if (default_input)
- {
- fclose (default_input);
- default_input = (FILE *)NULL;
- }
-#endif /* !BUFFERED_INPUT */
}
trap:f
trap -- 'echo trap:$FUNCNAME' EXIT
trap:f
+declare -rx x="4"
+declare -rx y="5"
+f:3
+f1:3
+global1:bcde
+f:3
+f1:3
+global2:bcde
+f:3
+f1:3
+global:bcde
+f:3
+f1:3
+global:bcde
+f:3
+bcde
+f:3
+bcde
+f1:3
+global: bcde
+f1:3
+global: 3
+f: 3
+global: declare -rx c="3"
+f1: 4
+global: declare -- b="8"
a=z
a=b
a=z
${THIS_SH} ./varenv20.sub
${THIS_SH} ./varenv21.sub
${THIS_SH} ./varenv22.sub
+${THIS_SH} ./varenv23.sub
# make sure variable scoping is done right
tt() { typeset a=b;echo a=$a; };a=z;echo a=$a;tt;echo a=$a
--- /dev/null
+# This program 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.
+#
+# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# posix rules for assignments preceding export/readonly in functions, using
+# local and global vars; default rules for assignments preceding declare/local
+# in functions, always using local vars.
+#
+# changes to local variables should never propagate upward from the function
+# to its caller, even in posix mode
+
+x=1
+x=4 declare -r x
+declare -p x
+y=2
+y=5 readonly y
+declare -p y
+# can't use x and y from here on
+
+f() { local -r a=3; echo f:$a; }
+f1() { declare -r b=3; echo f1:$b; }
+
+a=4 f
+b=4 f1
+echo global1:$a $b
+
+set -o posix
+a=4 f
+b=4 f1
+echo global2:$a $b
+set +o posix
+
+unset -f f f1
+
+f() { local a=3; readonly a; echo f:$a; }
+f1() { local b=3; declare -r b; echo f1:$b; }
+
+a=4 f
+b=4 f1
+echo global:$a $b
+
+set -o posix
+a=4 f
+b=4 f1
+echo global:$a $b
+set +o posix
+
+unset -f f f1
+
+f() { local a; a=3 readonly a; echo f:$a; }
+
+a=4 f
+echo $a
+set -o posix
+a=4 f
+echo $a
+set +o posix
+
+f1() { a=3 readonly a; echo f1:$a; }
+
+a=7 f1
+echo global: $a
+
+set -o posix
+a=7 f1
+echo global: $a
+set +o posix
+
+unset -f f f1
+# can't use a from here on
+
+c=7 b=8
+f() { c=3 readonly c; echo f: $c; }
+f1() { b=4 declare -r b; echo f1: $b; }
+
+f
+echo global: $(declare -p c)
+
+f1
+echo global: $(declare -p b)
+
+unset -f f f1
+
+# can't use c from here on
+
+
SHELL_VAR *var;
var = (SHELL_VAR *)data;
- if (tempvar_p (var) && (var->attributes & att_propagate))
+ if (tempvar_p (var) && propagate_p (var))
push_temp_var (data);
else
{
propagated, bind it in the previous scope before disposing it. */
/* XXX - This isn't exactly right, because all tempenv variables have the
export attribute set. */
- if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate))
+ if (tempvar_p (var) && exported_p (var) && propagate_p (var))
{
var->attributes &= ~att_tempvar; /* XXX */
v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);