mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-06-29 00:19:51 +02:00
commit bash-20140926 snapshot
This commit is contained in:
+55
-1
@@ -6795,4 +6795,58 @@ variables.c
|
||||
builtins/evalstring.c
|
||||
- parse_and_execute: if SEVAL_ONECMD flag set, return immediately after
|
||||
calling execute_command_internal. Final piece for fix for bug
|
||||
reported by Stephane Chazelas <stephane.chazelas@gmail.com>
|
||||
reported by Stephane Chazelas <stephane.chazelas@gmail.com>. Part of
|
||||
CVE-2014-6271
|
||||
|
||||
9/24
|
||||
----
|
||||
parse.y
|
||||
- reset_parser: reset eol_ungetc_lookahead to 0 here, since we don't want
|
||||
shell_getc returning it on the next call. Fixes problem reported by
|
||||
Tavis Ormandy <taviso@cmpxchg8b.com> and Michal Zalewski
|
||||
<lcamtuf@coredump.cx>. Potentially part of CVE-2014-6271; fix for
|
||||
CVE-2014-7169
|
||||
|
||||
9/25
|
||||
----
|
||||
parse.y
|
||||
- push_heredoc: new function, pushes a here-doc redirection onto
|
||||
redir_stack handling overflow of redir_stack. Exits on overflow.
|
||||
Original fix from Florian Weimer <fweimer@redhat.com>
|
||||
- change straight assignments to redir_stack to call push_redir
|
||||
- add one to size of word_lineno stack to avoid off-by-one error
|
||||
below in read_token_word. Overflow just results in line numbers
|
||||
being wrong
|
||||
|
||||
9/27
|
||||
----
|
||||
{execute_cmd,trap}.c
|
||||
- changes to make minimal-config builds work again, mostly missing
|
||||
#ifdefs for optional features
|
||||
|
||||
builtins/common.c
|
||||
- builtin_help: dummy version to be included if HELP_BUILTIN not
|
||||
defined, for minimal-config builds
|
||||
|
||||
variables.c
|
||||
- initialize_shell_variables: incorporated patches from Florian
|
||||
Weimer <fweimer@redhat.com> to change the strings bash looks
|
||||
for when importing shell functions from the environment. It
|
||||
adds a prefix (BASH_FUNC_) and a suffix (%%) to the name to
|
||||
mark it as having been created by bash as an exported function.
|
||||
Fix for remote attacks part of CVE-2014-6271 and CVE-2014-7169
|
||||
- mk_env_string: takes new argument, indicating whether we are
|
||||
constructing a function
|
||||
- mk_env_string: encodes function names as described above, so
|
||||
initialize_shell_variables can find them
|
||||
|
||||
9/28
|
||||
----
|
||||
copy_cmd.c
|
||||
- copy_redirects: before calling savestring on here_doc_eof, make
|
||||
sure it's not NULL (it could have been the result of a here
|
||||
document delimited by EOF or EOS). Fixes bug reported by
|
||||
Michal Zalewski <lcamtuf@coredump.cx>.
|
||||
|
||||
make_cmd.c
|
||||
- make_redirection: initialize here_doc_eof member to NULL
|
||||
|
||||
+6849
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
||||
Starting bash with the `--posix' command-line option or executing
|
||||
`set -o posix' while bash is running will cause bash to conform more
|
||||
closely to the Posix.2 standard by changing the behavior to match that
|
||||
specified by Posix.2 in areas where the bash default differs.
|
||||
|
||||
The following list is what's changed when `posix mode' is in effect:
|
||||
|
||||
1. When a command in the hash table no longer exists, bash will re-search
|
||||
$PATH to find the new location. This is also available with
|
||||
`shopt -s checkhash'.
|
||||
|
||||
2. The >& redirection does not redirect stdout and stderr.
|
||||
|
||||
3. The message printed by the job control code and builtins when a job
|
||||
exits with a non-zero status is `Done(status)'.
|
||||
|
||||
4. Reserved words may not be aliased.
|
||||
|
||||
5. The Posix.2 PS1 and PS2 expansions of `!' -> history number and
|
||||
`!!' -> `!' are enabled, and parameter expansion is performed on
|
||||
the value regardless of the setting of the `promptvars' option.
|
||||
|
||||
6. Interactive comments are enabled by default. (Note that bash has
|
||||
them on by default anyway.)
|
||||
|
||||
7. The Posix.2 startup files are executed ($ENV) rather than the normal
|
||||
bash files.
|
||||
|
||||
8. Tilde expansion is only performed on assignments preceding a command
|
||||
name, rather than on all assignment statements on the line.
|
||||
|
||||
9. The default history file is ~/.sh_history (default value of $HISTFILE).
|
||||
|
||||
10. The output of `kill -l' prints all the signal names on a single line,
|
||||
separated by spaces.
|
||||
|
||||
11. Non-interactive shells exit if `file' in `. file' is not found.
|
||||
|
||||
12. Redirection operators do not perform pathname expansion on the word
|
||||
in the redirection unless the shell is interactive
|
||||
|
||||
13. Function names must be valid shell identifiers. That is, they may not
|
||||
contain characters other than letters, digits, and underscores, and
|
||||
may not start with a digit. Declaring a function with an illegal name
|
||||
causes a fatal syntax error in non-interactive shells.
|
||||
|
||||
14. Posix.2 `special' builtins are found before shell functions during command
|
||||
lookup.
|
||||
|
||||
15. If a Posix.2 special builtin returns an error status, a non-interactive
|
||||
shell exits. The fatal errors are those listed in the POSIX.2 standard,
|
||||
and include things like passing incorrect options, redirection errors,
|
||||
variable assignment errors for assignments preceding the command name,
|
||||
and so on.
|
||||
|
||||
16. The environment passed to executed commands is not sorted. Neither is
|
||||
the output of `set'. This is not strictly Posix.2 behavior, but sh
|
||||
does it this way. Ksh does not. It's not necessary to sort the
|
||||
environment; no program should rely on it being sorted.
|
||||
|
||||
17. If the `cd' builtin finds a directory to change to using $CDPATH, the
|
||||
value it assigns to $PWD does not contain any symbolic links, as if
|
||||
`cd -P' had been executed.
|
||||
|
||||
18. A non-interactive shell exits with an error status if a variable
|
||||
assignment error occurs when no command name follows the assignment
|
||||
statements. A variable assignment error occurs, for example, when
|
||||
trying to assign a value to a read-only variable.
|
||||
|
||||
19. A non-interactive shell exits with an error status if the iteration
|
||||
variable in a for statement or the selection variable in a select
|
||||
statement is a read-only variable.
|
||||
|
||||
20. Process substitution is not available.
|
||||
|
||||
21. Assignment statements preceding POSIX.2 `special' builtins persist in
|
||||
the shell environment after the builtin completes.
|
||||
|
||||
There is other Posix.2 behavior that bash does not implement. Specifically:
|
||||
|
||||
1. Assignment statements affect the execution environment of all builtins,
|
||||
not just special ones.
|
||||
@@ -0,0 +1,544 @@
|
||||
This file is set.def, from which is created set.c.
|
||||
It implements the "set" and "unset" builtins in Bash.
|
||||
|
||||
Copyright (C) 1987, 1989, 1991 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 1, 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; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
$PRODUCES set.c
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../shell.h"
|
||||
#include "../flags.h"
|
||||
|
||||
#include "bashgetopt.h"
|
||||
|
||||
extern int interactive;
|
||||
extern int noclobber, posixly_correct;
|
||||
#if defined (READLINE)
|
||||
extern int rl_editing_mode, no_line_editing;
|
||||
#endif /* READLINE */
|
||||
|
||||
$BUILTIN set
|
||||
$FUNCTION set_builtin
|
||||
$SHORT_DOC set [--abefhkmnptuvxldBCHP] [-o option] [arg ...]
|
||||
-a Mark variables which are modified or created for export.
|
||||
-b Notify of job termination immediately.
|
||||
-e Exit immediately if a command exits with a non-zero status.
|
||||
-f Disable file name generation (globbing).
|
||||
-h Locate and remember function commands as functions are
|
||||
defined. Function commands are normally looked up when
|
||||
the function is executed.
|
||||
-i Force the shell to be an "interactive" one. Interactive shells
|
||||
always read `~/.bashrc' on startup.
|
||||
-k All keyword arguments are placed in the environment for a
|
||||
command, not just those that precede the command name.
|
||||
-m Job control is enabled.
|
||||
-n Read commands but do not execute them.
|
||||
-o option-name
|
||||
Set the variable corresponding to option-name:
|
||||
allexport same as -a
|
||||
braceexpand same as -B
|
||||
#if defined (READLINE)
|
||||
emacs use an emacs-style line editing interface
|
||||
#endif /* READLINE */
|
||||
errexit same as -e
|
||||
histexpand same as -H
|
||||
ignoreeof the shell will not exit upon reading EOF
|
||||
interactive-comments
|
||||
allow comments to appear in interactive commands
|
||||
monitor same as -m
|
||||
noclobber disallow redirection to existing files
|
||||
noexec same as -n
|
||||
noglob same as -f
|
||||
nohash same as -d
|
||||
notify save as -b
|
||||
nounset same as -u
|
||||
physical same as -P
|
||||
posix change the behavior of bash where the default
|
||||
operation differs from the 1003.2 standard to
|
||||
match the standard
|
||||
privileged same as -p
|
||||
verbose same as -v
|
||||
#if defined (READLINE)
|
||||
vi use a vi-style line editing interface
|
||||
#endif /* READLINE */
|
||||
xtrace same as -x
|
||||
-p Turned on whenever the real and effective user ids do not match.
|
||||
Disables processing of the $ENV file and importing of shell
|
||||
functions. Turning this option off causes the effective uid and
|
||||
gid to be set to the real uid and gid.
|
||||
-t Exit after reading and executing one command.
|
||||
-u Treat unset variables as an error when substituting.
|
||||
-v Print shell input lines as they are read.
|
||||
-x Print commands and their arguments as they are executed.
|
||||
-l Save and restore the binding of the NAME in a FOR command.
|
||||
-d Disable the hashing of commands that are looked up for execution.
|
||||
Normally, commands are remembered in a hash table, and once
|
||||
found, do not have to be looked up again.
|
||||
#if defined (BRACE_EXPANSION)
|
||||
-B the shell will perform brace expansion
|
||||
#endif /* BRACE_EXPANSION */
|
||||
#if defined (BANG_HISTORY)
|
||||
-H Enable ! style history substitution. This flag is on
|
||||
by default.
|
||||
#endif /* BANG_HISTORY */
|
||||
-C If set, disallow existing regular files to be overwritten
|
||||
by redirection of output.
|
||||
-P If set, do not follow symbolic links when executing commands
|
||||
such as cd which change the current directory.
|
||||
|
||||
Using + rather than - causes these flags to be turned off. The
|
||||
flags can also be used upon invocation of the shell. The current
|
||||
set of flags may be found in $-. The remaining n ARGs are positional
|
||||
parameters and are assigned, in order, to $1, $2, .. $n. If no
|
||||
ARGs are given, all shell variables are printed.
|
||||
$END
|
||||
|
||||
/* An a-list used to match long options for set -o to the corresponding
|
||||
option letter. */
|
||||
struct {
|
||||
char *name;
|
||||
int letter;
|
||||
} o_options[] = {
|
||||
{ "allexport", 'a' },
|
||||
#if defined (BRACE_EXPANSION)
|
||||
{ "braceexpand",'B' },
|
||||
#endif
|
||||
{ "errexit", 'e' },
|
||||
{ "histexpand", 'H' },
|
||||
{ "monitor", 'm' },
|
||||
{ "noexec", 'n' },
|
||||
{ "noglob", 'f' },
|
||||
{ "nohash", 'd' },
|
||||
#if defined (JOB_CONTROL)
|
||||
{ "notify", 'b' },
|
||||
#endif /* JOB_CONTROL */
|
||||
{"nounset", 'u' },
|
||||
{"physical", 'P' },
|
||||
{"privileged", 'p' },
|
||||
{"verbose", 'v' },
|
||||
{"xtrace", 'x' },
|
||||
{(char *)NULL, 0},
|
||||
};
|
||||
|
||||
#define MINUS_O_FORMAT "%-15s\t%s\n"
|
||||
|
||||
void
|
||||
list_minus_o_opts ()
|
||||
{
|
||||
register int i;
|
||||
char *on = "on", *off = "off";
|
||||
|
||||
printf (MINUS_O_FORMAT, "noclobber", (noclobber == 1) ? on : off);
|
||||
|
||||
if (find_variable ("ignoreeof") || find_variable ("IGNOREEOF"))
|
||||
printf (MINUS_O_FORMAT, "ignoreeof", on);
|
||||
else
|
||||
printf (MINUS_O_FORMAT, "ignoreeof", off);
|
||||
|
||||
printf (MINUS_O_FORMAT, "interactive-comments",
|
||||
interactive_comments ? on : off);
|
||||
|
||||
printf (MINUS_O_FORMAT, "posix", posixly_correct ? on : off);
|
||||
|
||||
#if defined (READLINE)
|
||||
if (no_line_editing)
|
||||
{
|
||||
printf (MINUS_O_FORMAT, "emacs", off);
|
||||
printf (MINUS_O_FORMAT, "vi", off);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Magic. This code `knows' how readline handles rl_editing_mode. */
|
||||
printf (MINUS_O_FORMAT, "emacs", (rl_editing_mode == 1) ? on : off);
|
||||
printf (MINUS_O_FORMAT, "vi", (rl_editing_mode == 0) ? on : off);
|
||||
}
|
||||
#endif /* READLINE */
|
||||
|
||||
for (i = 0; o_options[i].name; i++)
|
||||
{
|
||||
int *on_or_off, zero = 0;
|
||||
|
||||
on_or_off = find_flag (o_options[i].letter);
|
||||
if (on_or_off == FLAG_UNKNOWN)
|
||||
on_or_off = &zero;
|
||||
printf (MINUS_O_FORMAT, o_options[i].name, (*on_or_off == 1) ? on : off);
|
||||
}
|
||||
}
|
||||
|
||||
set_minus_o_option (on_or_off, option_name)
|
||||
int on_or_off;
|
||||
char *option_name;
|
||||
{
|
||||
int option_char = -1;
|
||||
|
||||
if (STREQ (option_name, "noclobber"))
|
||||
{
|
||||
if (on_or_off == FLAG_ON)
|
||||
bind_variable ("noclobber", "");
|
||||
else
|
||||
unbind_variable ("noclobber");
|
||||
stupidly_hack_special_variables ("noclobber");
|
||||
}
|
||||
else if (STREQ (option_name, "ignoreeof"))
|
||||
{
|
||||
unbind_variable ("ignoreeof");
|
||||
unbind_variable ("IGNOREEOF");
|
||||
if (on_or_off == FLAG_ON)
|
||||
bind_variable ("IGNOREEOF", "10");
|
||||
stupidly_hack_special_variables ("IGNOREEOF");
|
||||
}
|
||||
|
||||
#if defined (READLINE)
|
||||
else if ((STREQ (option_name, "emacs")) || (STREQ (option_name, "vi")))
|
||||
{
|
||||
if (on_or_off == FLAG_ON)
|
||||
{
|
||||
rl_variable_bind ("editing-mode", option_name);
|
||||
|
||||
if (interactive)
|
||||
with_input_from_stdin ();
|
||||
no_line_editing = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int isemacs = (rl_editing_mode == 1);
|
||||
if ((isemacs && STREQ (option_name, "emacs")) ||
|
||||
(!isemacs && STREQ (option_name, "vi")))
|
||||
{
|
||||
if (interactive)
|
||||
with_input_from_stream (stdin, "stdin");
|
||||
no_line_editing = 1;
|
||||
}
|
||||
else
|
||||
builtin_error ("not in %s editing mode", option_name);
|
||||
}
|
||||
}
|
||||
#endif /* READLINE */
|
||||
else if (STREQ (option_name, "interactive-comments"))
|
||||
interactive_comments = (on_or_off == FLAG_ON);
|
||||
else if (STREQ (option_name, "posix"))
|
||||
{
|
||||
posixly_correct = (on_or_off == FLAG_ON);
|
||||
unbind_variable ("POSIXLY_CORRECT");
|
||||
unbind_variable ("POSIX_PEDANTIC");
|
||||
if (on_or_off == FLAG_ON)
|
||||
{
|
||||
bind_variable ("POSIXLY_CORRECT", "");
|
||||
stupidly_hack_special_variables ("POSIXLY_CORRECT");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
register int i;
|
||||
for (i = 0; o_options[i].name; i++)
|
||||
{
|
||||
if (STREQ (option_name, o_options[i].name))
|
||||
{
|
||||
option_char = o_options[i].letter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (option_char == -1)
|
||||
{
|
||||
builtin_error ("%s: unknown option name", option_name);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
if (change_flag (option_char, on_or_off) == FLAG_ERROR)
|
||||
{
|
||||
bad_option (option_name);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
/* Set some flags from the word values in the input list. If LIST is empty,
|
||||
then print out the values of the variables instead. If LIST contains
|
||||
non-flags, then set $1 - $9 to the successive words of LIST. */
|
||||
set_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int on_or_off, flag_name, force_assignment = 0;
|
||||
|
||||
if (!list)
|
||||
{
|
||||
SHELL_VAR **vars;
|
||||
|
||||
vars = all_shell_variables ();
|
||||
if (vars)
|
||||
{
|
||||
print_var_list (vars);
|
||||
free (vars);
|
||||
}
|
||||
|
||||
vars = all_shell_functions ();
|
||||
if (vars)
|
||||
{
|
||||
print_var_list (vars);
|
||||
free (vars);
|
||||
}
|
||||
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
/* Check validity of flag arguments. */
|
||||
if (*list->word->word == '-' || *list->word->word == '+')
|
||||
{
|
||||
register char *arg;
|
||||
WORD_LIST *save_list = list;
|
||||
|
||||
while (list && (arg = list->word->word))
|
||||
{
|
||||
char c;
|
||||
|
||||
if (arg[0] != '-' && arg[0] != '+')
|
||||
break;
|
||||
|
||||
/* `-' or `--' signifies end of flag arguments. */
|
||||
if (arg[0] == '-' &&
|
||||
(!arg[1] || (arg[1] == '-' && !arg[2])))
|
||||
break;
|
||||
|
||||
while (c = *++arg)
|
||||
{
|
||||
if (find_flag (c) == FLAG_UNKNOWN && c != 'o')
|
||||
{
|
||||
char s[2];
|
||||
s[0] = c; s[1] = '\0';
|
||||
bad_option (s);
|
||||
if (c == '?')
|
||||
builtin_usage ();
|
||||
return (c == '?' ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
list = save_list;
|
||||
}
|
||||
|
||||
/* Do the set command. While the list consists of words starting with
|
||||
'-' or '+' treat them as flags, otherwise, start assigning them to
|
||||
$1 ... $n. */
|
||||
while (list)
|
||||
{
|
||||
char *string = list->word->word;
|
||||
|
||||
/* If the argument is `--' or `-' then signal the end of the list
|
||||
and remember the remaining arguments. */
|
||||
if (string[0] == '-' && (!string[1] || (string[1] == '-' && !string[2])))
|
||||
{
|
||||
list = list->next;
|
||||
|
||||
/* `set --' unsets the positional parameters. */
|
||||
if (string[1] == '-')
|
||||
force_assignment = 1;
|
||||
|
||||
/* Until told differently, the old shell behaviour of
|
||||
`set - [arg ...]' being equivalent to `set +xv [arg ...]'
|
||||
stands. Posix.2 says the behaviour is marked as obsolescent. */
|
||||
else
|
||||
{
|
||||
change_flag ('x', '+');
|
||||
change_flag ('v', '+');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ((on_or_off = *string) &&
|
||||
(on_or_off == '-' || on_or_off == '+'))
|
||||
{
|
||||
int i = 1;
|
||||
while (flag_name = string[i++])
|
||||
{
|
||||
if (flag_name == '?')
|
||||
{
|
||||
builtin_usage ();
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
else if (flag_name == 'o') /* -+o option-name */
|
||||
{
|
||||
char *option_name;
|
||||
WORD_LIST *opt;
|
||||
|
||||
opt = list->next;
|
||||
|
||||
if (!opt)
|
||||
{
|
||||
list_minus_o_opts ();
|
||||
continue;
|
||||
}
|
||||
|
||||
option_name = opt->word->word;
|
||||
|
||||
if (!option_name || !*option_name || (*option_name == '-'))
|
||||
{
|
||||
list_minus_o_opts ();
|
||||
continue;
|
||||
}
|
||||
list = list->next; /* Skip over option name. */
|
||||
|
||||
if (set_minus_o_option (on_or_off, option_name) != EXECUTION_SUCCESS)
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (change_flag (flag_name, on_or_off) == FLAG_ERROR)
|
||||
{
|
||||
char opt[3];
|
||||
opt[0] = on_or_off;
|
||||
opt[1] = flag_name;
|
||||
opt[2] = '\0';
|
||||
bad_option (opt);
|
||||
builtin_usage ();
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
/* Assigning $1 ... $n */
|
||||
if (list || force_assignment)
|
||||
remember_args (list, 1);
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
$BUILTIN unset
|
||||
$FUNCTION unset_builtin
|
||||
$SHORT_DOC unset [-f] [-v] [name ...]
|
||||
For each NAME, remove the corresponding variable or function. Given
|
||||
the `-v', unset will only act on variables. Given the `-f' flag,
|
||||
unset will only act on functions. With neither flag, unset first
|
||||
tries to unset a variable, and if that fails, then tries to unset a
|
||||
function. Some variables (such as PATH and IFS) cannot be unset; also
|
||||
see readonly.
|
||||
$END
|
||||
|
||||
#define NEXT_VARIABLE() any_failed++; list = list->next; continue;
|
||||
|
||||
unset_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int unset_function, unset_variable, unset_array, opt, any_failed;
|
||||
char *name;
|
||||
|
||||
unset_function = unset_variable = unset_array = any_failed = 0;
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "fv")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'f':
|
||||
unset_function = 1;
|
||||
break;
|
||||
case 'v':
|
||||
unset_variable = 1;
|
||||
break;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
list = loptend;
|
||||
|
||||
if (unset_function && unset_variable)
|
||||
{
|
||||
builtin_error ("cannot simultaneously unset a function and a variable");
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
while (list)
|
||||
{
|
||||
SHELL_VAR *var;
|
||||
int tem;
|
||||
#if defined (ARRAY_VARS)
|
||||
char *t;
|
||||
#endif
|
||||
|
||||
name = list->word->word;
|
||||
|
||||
#if defined (ARRAY_VARS)
|
||||
if (!unset_function && valid_array_reference (name))
|
||||
{
|
||||
t = strchr (name, '[');
|
||||
*t++ = '\0';
|
||||
unset_array++;
|
||||
}
|
||||
#endif
|
||||
|
||||
var = unset_function ? find_function (name) : find_variable (name);
|
||||
|
||||
if (var && !unset_function && non_unsettable_p (var))
|
||||
{
|
||||
builtin_error ("%s: cannot unset", name);
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
|
||||
/* Posix.2 says that unsetting readonly variables is an error. */
|
||||
if (var && readonly_p (var))
|
||||
{
|
||||
builtin_error ("%s: cannot unset: readonly %s",
|
||||
name, unset_function ? "function" : "variable");
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
|
||||
/* Unless the -f option is supplied, the name refers to a variable. */
|
||||
#if defined (ARRAY_VARS)
|
||||
if (var && unset_array)
|
||||
{
|
||||
if (array_p (var) == 0)
|
||||
{
|
||||
builtin_error ("%s: not an array variable", name);
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
else
|
||||
tem = unbind_array_element (var, t);
|
||||
}
|
||||
else
|
||||
#endif /* ARRAY_VARS */
|
||||
tem = makunbound (name, unset_function ? shell_functions : shell_variables);
|
||||
|
||||
/* This is what Posix.2 draft 11+ says. ``If neither -f nor -v
|
||||
is specified, the name refers to a variable; if a variable by
|
||||
that name does not exist, a function by that name, if any,
|
||||
shall be unset.'' */
|
||||
if ((tem == -1) && !unset_function && !unset_variable)
|
||||
tem = makunbound (name, shell_functions);
|
||||
|
||||
if (tem == -1)
|
||||
any_failed++;
|
||||
else if (!unset_function)
|
||||
stupidly_hack_special_variables (name);
|
||||
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
if (any_failed)
|
||||
return (EXECUTION_FAILURE);
|
||||
else
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/* unwind_prot.h - Macros and functions for hacking unwind protection. */
|
||||
|
||||
/* Copyright (C) 1993 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 2, 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; see the file COPYING. If not, write to the Free Software
|
||||
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
#if !defined (_UNWIND_PROT_H)
|
||||
#define _UNWIND_PROT_H
|
||||
|
||||
/* Run a function without interrupts. */
|
||||
extern void begin_unwind_frame ();
|
||||
extern void discard_unwind_frame ();
|
||||
extern void run_unwind_frame ();
|
||||
extern void add_unwind_protect ();
|
||||
extern void remove_unwind_protect ();
|
||||
extern void run_unwind_protects ();
|
||||
extern void unwind_protect_var ();
|
||||
|
||||
/* Define for people who like their code to look a certain way. */
|
||||
#define end_unwind_frame()
|
||||
|
||||
/* How to protect an integer. */
|
||||
#define unwind_protect_int(X) unwind_protect_var (&(X), (char *)(X), sizeof (int))
|
||||
|
||||
/* How to protect a pointer to a string. */
|
||||
#define unwind_protect_string(X) \
|
||||
unwind_protect_var ((int *)&(X), (X), sizeof (char *))
|
||||
|
||||
/* How to protect any old pointer. */
|
||||
#define unwind_protect_pointer(X) unwind_protect_string (X)
|
||||
|
||||
/* How to protect the contents of a jmp_buf. */
|
||||
#define unwind_protect_jmp_buf(X) \
|
||||
unwind_protect_var ((int *)(X), (char *)(X), sizeof (procenv_t))
|
||||
|
||||
#endif /* _UNWIND_PROT_H */
|
||||
@@ -906,3 +906,11 @@ initialize_shell_builtins ()
|
||||
qsort (shell_builtins, num_shell_builtins, sizeof (struct builtin),
|
||||
(QSFUNC *)shell_builtin_compare);
|
||||
}
|
||||
|
||||
#if !defined (HELP_BUILTIN)
|
||||
void
|
||||
builtin_help ()
|
||||
{
|
||||
printf ("%s: %s\n", this_command_name, _("help not available in this version"));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,908 @@
|
||||
/* common.c - utility functions for all builtins */
|
||||
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# ifdef _MINIX
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <chartypes.h>
|
||||
#include "../bashtypes.h"
|
||||
#include "posixstat.h"
|
||||
#include <signal.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#if defined (PREFER_STDARG)
|
||||
# include <stdarg.h>
|
||||
#else
|
||||
# include <varargs.h>
|
||||
#endif
|
||||
|
||||
#include "../bashansi.h"
|
||||
#include "../bashintl.h"
|
||||
|
||||
#define NEED_FPURGE_DECL
|
||||
|
||||
#include "../shell.h"
|
||||
#include "maxpath.h"
|
||||
#include "../flags.h"
|
||||
#include "../jobs.h"
|
||||
#include "../builtins.h"
|
||||
#include "../input.h"
|
||||
#include "../execute_cmd.h"
|
||||
#include "../trap.h"
|
||||
#include "bashgetopt.h"
|
||||
#include "common.h"
|
||||
#include "builtext.h"
|
||||
#include <tilde/tilde.h>
|
||||
|
||||
#if defined (HISTORY)
|
||||
# include "../bashhist.h"
|
||||
#endif
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif /* !errno */
|
||||
|
||||
extern int indirection_level, subshell_environment;
|
||||
extern int line_number;
|
||||
extern int last_command_exit_value;
|
||||
extern int trap_saved_exit_value;
|
||||
extern int running_trap;
|
||||
extern int posixly_correct;
|
||||
extern char *this_command_name, *shell_name;
|
||||
extern const char * const bash_getcwd_errstr;
|
||||
|
||||
/* Used by some builtins and the mainline code. */
|
||||
sh_builtin_func_t *last_shell_builtin = (sh_builtin_func_t *)NULL;
|
||||
sh_builtin_func_t *this_shell_builtin = (sh_builtin_func_t *)NULL;
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Error reporting, usage, and option processing */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* This is a lot like report_error (), but it is for shell builtins
|
||||
instead of shell control structures, and it won't ever exit the
|
||||
shell. */
|
||||
|
||||
static void
|
||||
builtin_error_prolog ()
|
||||
{
|
||||
char *name;
|
||||
|
||||
name = get_name_for_error ();
|
||||
fprintf (stderr, "%s: ", name);
|
||||
|
||||
if (interactive_shell == 0)
|
||||
fprintf (stderr, _("line %d: "), executing_line_number ());
|
||||
|
||||
if (this_command_name && *this_command_name)
|
||||
fprintf (stderr, "%s: ", this_command_name);
|
||||
}
|
||||
|
||||
void
|
||||
#if defined (PREFER_STDARG)
|
||||
builtin_error (const char *format, ...)
|
||||
#else
|
||||
builtin_error (format, va_alist)
|
||||
const char *format;
|
||||
va_dcl
|
||||
#endif
|
||||
{
|
||||
va_list args;
|
||||
|
||||
builtin_error_prolog ();
|
||||
|
||||
SH_VA_START (args, format);
|
||||
|
||||
vfprintf (stderr, format, args);
|
||||
va_end (args);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
#if defined (PREFER_STDARG)
|
||||
builtin_warning (const char *format, ...)
|
||||
#else
|
||||
builtin_warning (format, va_alist)
|
||||
const char *format;
|
||||
va_dcl
|
||||
#endif
|
||||
{
|
||||
va_list args;
|
||||
|
||||
builtin_error_prolog ();
|
||||
fprintf (stderr, _("warning: "));
|
||||
|
||||
SH_VA_START (args, format);
|
||||
|
||||
vfprintf (stderr, format, args);
|
||||
va_end (args);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
/* Print a usage summary for the currently-executing builtin command. */
|
||||
void
|
||||
builtin_usage ()
|
||||
{
|
||||
if (this_command_name && *this_command_name)
|
||||
fprintf (stderr, _("%s: usage: "), this_command_name);
|
||||
fprintf (stderr, "%s\n", _(current_builtin->short_doc));
|
||||
fflush (stderr);
|
||||
}
|
||||
|
||||
/* Return if LIST is NULL else barf and jump to top_level. Used by some
|
||||
builtins that do not accept arguments. */
|
||||
void
|
||||
no_args (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
if (list)
|
||||
{
|
||||
builtin_error (_("too many arguments"));
|
||||
top_level_cleanup ();
|
||||
jump_to_top_level (DISCARD);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that no options were given to the currently-executing builtin,
|
||||
and return 0 if there were options. */
|
||||
int
|
||||
no_options (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int opt;
|
||||
|
||||
reset_internal_getopt ();
|
||||
if ((opt = internal_getopt (list, "")) != -1)
|
||||
{
|
||||
if (opt == GETOPT_HELP)
|
||||
{
|
||||
builtin_help ();
|
||||
return (2);
|
||||
}
|
||||
builtin_usage ();
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
sh_needarg (s)
|
||||
char *s;
|
||||
{
|
||||
builtin_error (_("%s: option requires an argument"), s);
|
||||
}
|
||||
|
||||
void
|
||||
sh_neednumarg (s)
|
||||
char *s;
|
||||
{
|
||||
builtin_error (_("%s: numeric argument required"), s);
|
||||
}
|
||||
|
||||
void
|
||||
sh_notfound (s)
|
||||
char *s;
|
||||
{
|
||||
builtin_error (_("%s: not found"), s);
|
||||
}
|
||||
|
||||
/* Function called when one of the builtin commands detects an invalid
|
||||
option. */
|
||||
void
|
||||
sh_invalidopt (s)
|
||||
char *s;
|
||||
{
|
||||
builtin_error (_("%s: invalid option"), s);
|
||||
}
|
||||
|
||||
void
|
||||
sh_invalidoptname (s)
|
||||
char *s;
|
||||
{
|
||||
builtin_error (_("%s: invalid option name"), s);
|
||||
}
|
||||
|
||||
void
|
||||
sh_invalidid (s)
|
||||
char *s;
|
||||
{
|
||||
builtin_error (_("`%s': not a valid identifier"), s);
|
||||
}
|
||||
|
||||
void
|
||||
sh_invalidnum (s)
|
||||
char *s;
|
||||
{
|
||||
char *msg;
|
||||
|
||||
if (*s == '0' && isdigit (s[1]))
|
||||
msg = _("invalid octal number");
|
||||
else if (*s == '0' && s[1] == 'x')
|
||||
msg = _("invalid hex number");
|
||||
else
|
||||
msg = _("invalid number");
|
||||
builtin_error ("%s: %s", s, msg);
|
||||
}
|
||||
|
||||
void
|
||||
sh_invalidsig (s)
|
||||
char *s;
|
||||
{
|
||||
builtin_error (_("%s: invalid signal specification"), s);
|
||||
}
|
||||
|
||||
void
|
||||
sh_badpid (s)
|
||||
char *s;
|
||||
{
|
||||
builtin_error (_("`%s': not a pid or valid job spec"), s);
|
||||
}
|
||||
|
||||
void
|
||||
sh_readonly (s)
|
||||
const char *s;
|
||||
{
|
||||
builtin_error (_("%s: readonly variable"), s);
|
||||
}
|
||||
|
||||
void
|
||||
sh_erange (s, desc)
|
||||
char *s, *desc;
|
||||
{
|
||||
if (s)
|
||||
builtin_error (_("%s: %s out of range"), s, desc ? desc : _("argument"));
|
||||
else
|
||||
builtin_error (_("%s out of range"), desc ? desc : _("argument"));
|
||||
}
|
||||
|
||||
#if defined (JOB_CONTROL)
|
||||
void
|
||||
sh_badjob (s)
|
||||
char *s;
|
||||
{
|
||||
builtin_error (_("%s: no such job"), s);
|
||||
}
|
||||
|
||||
void
|
||||
sh_nojobs (s)
|
||||
char *s;
|
||||
{
|
||||
if (s)
|
||||
builtin_error (_("%s: no job control"), s);
|
||||
else
|
||||
builtin_error (_("no job control"));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (RESTRICTED_SHELL)
|
||||
void
|
||||
sh_restricted (s)
|
||||
char *s;
|
||||
{
|
||||
if (s)
|
||||
builtin_error (_("%s: restricted"), s);
|
||||
else
|
||||
builtin_error (_("restricted"));
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
sh_notbuiltin (s)
|
||||
char *s;
|
||||
{
|
||||
builtin_error (_("%s: not a shell builtin"), s);
|
||||
}
|
||||
|
||||
void
|
||||
sh_wrerror ()
|
||||
{
|
||||
#if defined (DONT_REPORT_BROKEN_PIPE_WRITE_ERRORS) && defined (EPIPE)
|
||||
if (errno != EPIPE)
|
||||
#endif /* DONT_REPORT_BROKEN_PIPE_WRITE_ERRORS && EPIPE */
|
||||
builtin_error (_("write error: %s"), strerror (errno));
|
||||
}
|
||||
|
||||
void
|
||||
sh_ttyerror (set)
|
||||
int set;
|
||||
{
|
||||
if (set)
|
||||
builtin_error (_("error setting terminal attributes: %s"), strerror (errno));
|
||||
else
|
||||
builtin_error (_("error getting terminal attributes: %s"), strerror (errno));
|
||||
}
|
||||
|
||||
int
|
||||
sh_chkwrite (s)
|
||||
int s;
|
||||
{
|
||||
fflush (stdout);
|
||||
if (ferror (stdout))
|
||||
{
|
||||
sh_wrerror ();
|
||||
fpurge (stdout);
|
||||
clearerr (stdout);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
return (s);
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Shell positional parameter manipulation */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* Convert a WORD_LIST into a C-style argv. Return the number of elements
|
||||
in the list in *IP, if IP is non-null. A convenience function for
|
||||
loadable builtins; also used by `test'. */
|
||||
char **
|
||||
make_builtin_argv (list, ip)
|
||||
WORD_LIST *list;
|
||||
int *ip;
|
||||
{
|
||||
char **argv;
|
||||
|
||||
argv = strvec_from_word_list (list, 0, 1, ip);
|
||||
argv[0] = this_command_name;
|
||||
return argv;
|
||||
}
|
||||
|
||||
/* Remember LIST in $1 ... $9, and REST_OF_ARGS. If DESTRUCTIVE is
|
||||
non-zero, then discard whatever the existing arguments are, else
|
||||
only discard the ones that are to be replaced. */
|
||||
void
|
||||
remember_args (list, destructive)
|
||||
WORD_LIST *list;
|
||||
int destructive;
|
||||
{
|
||||
register int i;
|
||||
|
||||
for (i = 1; i < 10; i++)
|
||||
{
|
||||
if ((destructive || list) && dollar_vars[i])
|
||||
{
|
||||
free (dollar_vars[i]);
|
||||
dollar_vars[i] = (char *)NULL;
|
||||
}
|
||||
|
||||
if (list)
|
||||
{
|
||||
dollar_vars[i] = savestring (list->word->word);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* If arguments remain, assign them to REST_OF_ARGS.
|
||||
Note that copy_word_list (NULL) returns NULL, and
|
||||
that dispose_words (NULL) does nothing. */
|
||||
if (destructive || list)
|
||||
{
|
||||
dispose_words (rest_of_args);
|
||||
rest_of_args = copy_word_list (list);
|
||||
}
|
||||
|
||||
if (destructive)
|
||||
set_dollar_vars_changed ();
|
||||
}
|
||||
|
||||
static int changed_dollar_vars;
|
||||
|
||||
/* Have the dollar variables been reset to new values since we last
|
||||
checked? */
|
||||
int
|
||||
dollar_vars_changed ()
|
||||
{
|
||||
return (changed_dollar_vars);
|
||||
}
|
||||
|
||||
void
|
||||
set_dollar_vars_unchanged ()
|
||||
{
|
||||
changed_dollar_vars = 0;
|
||||
}
|
||||
|
||||
void
|
||||
set_dollar_vars_changed ()
|
||||
{
|
||||
if (variable_context)
|
||||
changed_dollar_vars |= ARGS_FUNC;
|
||||
else if (this_shell_builtin == set_builtin)
|
||||
changed_dollar_vars |= ARGS_SETBLTIN;
|
||||
else
|
||||
changed_dollar_vars |= ARGS_INVOC;
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Validating numeric input and arguments */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* Read a numeric arg for this_command_name, the name of the shell builtin
|
||||
that wants it. LIST is the word list that the arg is to come from.
|
||||
Accept only the numeric argument; report an error if other arguments
|
||||
follow. If FATAL is 1, call throw_to_top_level, which exits the
|
||||
shell; if it's 2, call jump_to_top_level (DISCARD), which aborts the
|
||||
current command; if FATAL is 0, return an indication of an invalid
|
||||
number by setting *NUMOK == 0 and return -1. */
|
||||
int
|
||||
get_numeric_arg (list, fatal, count)
|
||||
WORD_LIST *list;
|
||||
int fatal;
|
||||
intmax_t *count;
|
||||
{
|
||||
char *arg;
|
||||
|
||||
if (count)
|
||||
*count = 1;
|
||||
|
||||
if (list && list->word && ISOPTION (list->word->word, '-'))
|
||||
list = list->next;
|
||||
|
||||
if (list)
|
||||
{
|
||||
arg = list->word->word;
|
||||
if (arg == 0 || (legal_number (arg, count) == 0))
|
||||
{
|
||||
sh_neednumarg (list->word->word ? list->word->word : "`'");
|
||||
if (fatal == 0)
|
||||
return 0;
|
||||
else if (fatal == 1) /* fatal == 1; abort */
|
||||
throw_to_top_level ();
|
||||
else /* fatal == 2; discard current command */
|
||||
{
|
||||
top_level_cleanup ();
|
||||
jump_to_top_level (DISCARD);
|
||||
}
|
||||
}
|
||||
no_args (list->next);
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Get an eight-bit status value from LIST */
|
||||
int
|
||||
get_exitstat (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int status;
|
||||
intmax_t sval;
|
||||
char *arg;
|
||||
|
||||
if (list && list->word && ISOPTION (list->word->word, '-'))
|
||||
list = list->next;
|
||||
|
||||
if (list == 0)
|
||||
{
|
||||
/* If we're not running the DEBUG trap, the return builtin, when not
|
||||
given any arguments, uses the value of $? before the trap ran. If
|
||||
given an argument, return uses it. This means that the trap can't
|
||||
change $?. The DEBUG trap gets to change $?, though, since that is
|
||||
part of its reason for existing, and because the extended debug mode
|
||||
does things with the return value. */
|
||||
if (this_shell_builtin == return_builtin && running_trap > 0 && running_trap != DEBUG_TRAP+1)
|
||||
return (trap_saved_exit_value);
|
||||
return (last_command_exit_value);
|
||||
}
|
||||
|
||||
arg = list->word->word;
|
||||
if (arg == 0 || legal_number (arg, &sval) == 0)
|
||||
{
|
||||
sh_neednumarg (list->word->word ? list->word->word : "`'");
|
||||
return EX_BADUSAGE;
|
||||
}
|
||||
no_args (list->next);
|
||||
|
||||
status = sval & 255;
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Return the octal number parsed from STRING, or -1 to indicate
|
||||
that the string contained a bad number. */
|
||||
int
|
||||
read_octal (string)
|
||||
char *string;
|
||||
{
|
||||
int result, digits;
|
||||
|
||||
result = digits = 0;
|
||||
while (*string && ISOCTAL (*string))
|
||||
{
|
||||
digits++;
|
||||
result = (result * 8) + (*string++ - '0');
|
||||
if (result > 0777)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (digits == 0 || *string)
|
||||
result = -1;
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Manipulating the current working directory */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* Return a consed string which is the current working directory.
|
||||
FOR_WHOM is the name of the caller for error printing. */
|
||||
char *the_current_working_directory = (char *)NULL;
|
||||
|
||||
char *
|
||||
get_working_directory (for_whom)
|
||||
char *for_whom;
|
||||
{
|
||||
if (no_symbolic_links)
|
||||
{
|
||||
FREE (the_current_working_directory);
|
||||
the_current_working_directory = (char *)NULL;
|
||||
}
|
||||
|
||||
if (the_current_working_directory == 0)
|
||||
{
|
||||
#if defined (GETCWD_BROKEN)
|
||||
the_current_working_directory = getcwd (0, PATH_MAX);
|
||||
#else
|
||||
the_current_working_directory = getcwd (0, 0);
|
||||
#endif
|
||||
if (the_current_working_directory == 0)
|
||||
{
|
||||
fprintf (stderr, _("%s: error retrieving current directory: %s: %s\n"),
|
||||
(for_whom && *for_whom) ? for_whom : get_name_for_error (),
|
||||
_(bash_getcwd_errstr), strerror (errno));
|
||||
return (char *)NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return (savestring (the_current_working_directory));
|
||||
}
|
||||
|
||||
/* Make NAME our internal idea of the current working directory. */
|
||||
void
|
||||
set_working_directory (name)
|
||||
char *name;
|
||||
{
|
||||
FREE (the_current_working_directory);
|
||||
the_current_working_directory = savestring (name);
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Job control support functions */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
#if defined (JOB_CONTROL)
|
||||
int
|
||||
get_job_by_name (name, flags)
|
||||
const char *name;
|
||||
int flags;
|
||||
{
|
||||
register int i, wl, cl, match, job;
|
||||
register PROCESS *p;
|
||||
register JOB *j;
|
||||
|
||||
job = NO_JOB;
|
||||
wl = strlen (name);
|
||||
for (i = js.j_jobslots - 1; i >= 0; i--)
|
||||
{
|
||||
j = get_job_by_jid (i);
|
||||
if (j == 0 || ((flags & JM_STOPPED) && J_JOBSTATE(j) != JSTOPPED))
|
||||
continue;
|
||||
|
||||
p = j->pipe;
|
||||
do
|
||||
{
|
||||
if (flags & JM_EXACT)
|
||||
{
|
||||
cl = strlen (p->command);
|
||||
match = STREQN (p->command, name, cl);
|
||||
}
|
||||
else if (flags & JM_SUBSTRING)
|
||||
match = strcasestr (p->command, name) != (char *)0;
|
||||
else
|
||||
match = STREQN (p->command, name, wl);
|
||||
|
||||
if (match == 0)
|
||||
{
|
||||
p = p->next;
|
||||
continue;
|
||||
}
|
||||
else if (flags & JM_FIRSTMATCH)
|
||||
return i; /* return first match */
|
||||
else if (job != NO_JOB)
|
||||
{
|
||||
if (this_shell_builtin)
|
||||
builtin_error (_("%s: ambiguous job spec"), name);
|
||||
else
|
||||
internal_error (_("%s: ambiguous job spec"), name);
|
||||
return (DUP_JOB);
|
||||
}
|
||||
else
|
||||
job = i;
|
||||
}
|
||||
while (p != j->pipe);
|
||||
}
|
||||
|
||||
return (job);
|
||||
}
|
||||
|
||||
/* Return the job spec found in LIST. */
|
||||
int
|
||||
get_job_spec (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
register char *word;
|
||||
int job, jflags;
|
||||
|
||||
if (list == 0)
|
||||
return (js.j_current);
|
||||
|
||||
word = list->word->word;
|
||||
|
||||
if (*word == '\0')
|
||||
return (NO_JOB);
|
||||
|
||||
if (*word == '%')
|
||||
word++;
|
||||
|
||||
if (DIGIT (*word) && all_digits (word))
|
||||
{
|
||||
job = atoi (word);
|
||||
return (job > js.j_jobslots ? NO_JOB : job - 1);
|
||||
}
|
||||
|
||||
jflags = 0;
|
||||
switch (*word)
|
||||
{
|
||||
case 0:
|
||||
case '%':
|
||||
case '+':
|
||||
return (js.j_current);
|
||||
|
||||
case '-':
|
||||
return (js.j_previous);
|
||||
|
||||
case '?': /* Substring search requested. */
|
||||
jflags |= JM_SUBSTRING;
|
||||
word++;
|
||||
/* FALLTHROUGH */
|
||||
|
||||
default:
|
||||
return get_job_by_name (word, jflags);
|
||||
}
|
||||
}
|
||||
#endif /* JOB_CONTROL */
|
||||
|
||||
/*
|
||||
* NOTE: `kill' calls this function with forcecols == 0
|
||||
*/
|
||||
int
|
||||
display_signal_list (list, forcecols)
|
||||
WORD_LIST *list;
|
||||
int forcecols;
|
||||
{
|
||||
register int i, column;
|
||||
char *name;
|
||||
int result, signum, dflags;
|
||||
intmax_t lsignum;
|
||||
|
||||
result = EXECUTION_SUCCESS;
|
||||
if (!list)
|
||||
{
|
||||
for (i = 1, column = 0; i < NSIG; i++)
|
||||
{
|
||||
name = signal_name (i);
|
||||
if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7))
|
||||
continue;
|
||||
|
||||
if (posixly_correct && !forcecols)
|
||||
{
|
||||
/* This is for the kill builtin. POSIX.2 says the signal names
|
||||
are displayed without the `SIG' prefix. */
|
||||
if (STREQN (name, "SIG", 3))
|
||||
name += 3;
|
||||
printf ("%s%s", name, (i == NSIG - 1) ? "" : " ");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("%2d) %s", i, name);
|
||||
|
||||
if (++column < 5)
|
||||
printf ("\t");
|
||||
else
|
||||
{
|
||||
printf ("\n");
|
||||
column = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((posixly_correct && !forcecols) || column != 0)
|
||||
printf ("\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
/* List individual signal names or numbers. */
|
||||
while (list)
|
||||
{
|
||||
if (legal_number (list->word->word, &lsignum))
|
||||
{
|
||||
/* This is specified by Posix.2 so that exit statuses can be
|
||||
mapped into signal numbers. */
|
||||
if (lsignum > 128)
|
||||
lsignum -= 128;
|
||||
if (lsignum < 0 || lsignum >= NSIG)
|
||||
{
|
||||
sh_invalidsig (list->word->word);
|
||||
result = EXECUTION_FAILURE;
|
||||
list = list->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
signum = lsignum;
|
||||
name = signal_name (signum);
|
||||
if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7))
|
||||
{
|
||||
list = list->next;
|
||||
continue;
|
||||
}
|
||||
#if defined (JOB_CONTROL)
|
||||
/* POSIX.2 says that `kill -l signum' prints the signal name without
|
||||
the `SIG' prefix. */
|
||||
printf ("%s\n", (this_shell_builtin == kill_builtin) ? name + 3 : name);
|
||||
#else
|
||||
printf ("%s\n", name);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
dflags = DSIG_NOCASE;
|
||||
if (posixly_correct == 0 || this_shell_builtin != kill_builtin)
|
||||
dflags |= DSIG_SIGPREFIX;
|
||||
signum = decode_signal (list->word->word, dflags);
|
||||
if (signum == NO_SIG)
|
||||
{
|
||||
sh_invalidsig (list->word->word);
|
||||
result = EXECUTION_FAILURE;
|
||||
list = list->next;
|
||||
continue;
|
||||
}
|
||||
printf ("%d\n", signum);
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Finding builtin commands and their functions */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* Perform a binary search and return the address of the builtin function
|
||||
whose name is NAME. If the function couldn't be found, or the builtin
|
||||
is disabled or has no function associated with it, return NULL.
|
||||
Return the address of the builtin.
|
||||
DISABLED_OKAY means find it even if the builtin is disabled. */
|
||||
struct builtin *
|
||||
builtin_address_internal (name, disabled_okay)
|
||||
char *name;
|
||||
int disabled_okay;
|
||||
{
|
||||
int hi, lo, mid, j;
|
||||
|
||||
hi = num_shell_builtins - 1;
|
||||
lo = 0;
|
||||
|
||||
while (lo <= hi)
|
||||
{
|
||||
mid = (lo + hi) / 2;
|
||||
|
||||
j = shell_builtins[mid].name[0] - name[0];
|
||||
|
||||
if (j == 0)
|
||||
j = strcmp (shell_builtins[mid].name, name);
|
||||
|
||||
if (j == 0)
|
||||
{
|
||||
/* It must have a function pointer. It must be enabled, or we
|
||||
must have explicitly allowed disabled functions to be found,
|
||||
and it must not have been deleted. */
|
||||
if (shell_builtins[mid].function &&
|
||||
((shell_builtins[mid].flags & BUILTIN_DELETED) == 0) &&
|
||||
((shell_builtins[mid].flags & BUILTIN_ENABLED) || disabled_okay))
|
||||
return (&shell_builtins[mid]);
|
||||
else
|
||||
return ((struct builtin *)NULL);
|
||||
}
|
||||
if (j > 0)
|
||||
hi = mid - 1;
|
||||
else
|
||||
lo = mid + 1;
|
||||
}
|
||||
return ((struct builtin *)NULL);
|
||||
}
|
||||
|
||||
/* Return the pointer to the function implementing builtin command NAME. */
|
||||
sh_builtin_func_t *
|
||||
find_shell_builtin (name)
|
||||
char *name;
|
||||
{
|
||||
current_builtin = builtin_address_internal (name, 0);
|
||||
return (current_builtin ? current_builtin->function : (sh_builtin_func_t *)NULL);
|
||||
}
|
||||
|
||||
/* Return the address of builtin with NAME, whether it is enabled or not. */
|
||||
sh_builtin_func_t *
|
||||
builtin_address (name)
|
||||
char *name;
|
||||
{
|
||||
current_builtin = builtin_address_internal (name, 1);
|
||||
return (current_builtin ? current_builtin->function : (sh_builtin_func_t *)NULL);
|
||||
}
|
||||
|
||||
/* Return the function implementing the builtin NAME, but only if it is a
|
||||
POSIX.2 special builtin. */
|
||||
sh_builtin_func_t *
|
||||
find_special_builtin (name)
|
||||
char *name;
|
||||
{
|
||||
current_builtin = builtin_address_internal (name, 0);
|
||||
return ((current_builtin && (current_builtin->flags & SPECIAL_BUILTIN)) ?
|
||||
current_builtin->function :
|
||||
(sh_builtin_func_t *)NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
shell_builtin_compare (sbp1, sbp2)
|
||||
struct builtin *sbp1, *sbp2;
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result = sbp1->name[0] - sbp2->name[0]) == 0)
|
||||
result = strcmp (sbp1->name, sbp2->name);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/* Sort the table of shell builtins so that the binary search will work
|
||||
in find_shell_builtin. */
|
||||
void
|
||||
initialize_shell_builtins ()
|
||||
{
|
||||
qsort (shell_builtins, num_shell_builtins, sizeof (struct builtin),
|
||||
(QSFUNC *)shell_builtin_compare);
|
||||
}
|
||||
+1
-1
@@ -126,7 +126,7 @@ copy_redirect (redirect)
|
||||
{
|
||||
case r_reading_until:
|
||||
case r_deblank_reading_until:
|
||||
new_redirect->here_doc_eof = savestring (redirect->here_doc_eof);
|
||||
new_redirect->here_doc_eof = redirect->here_doc_eof ? savestring (redirect->here_doc_eof) : 0;
|
||||
/*FALLTHROUGH*/
|
||||
case r_reading_string:
|
||||
case r_appending_to:
|
||||
|
||||
+453
@@ -0,0 +1,453 @@
|
||||
/* copy_command.c -- copy a COMMAND structure. This is needed
|
||||
primarily for making function definitions, but I'm not sure
|
||||
that anyone else will need it. */
|
||||
|
||||
/* Copyright (C) 1987-2009 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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "bashtypes.h"
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "shell.h"
|
||||
|
||||
static PATTERN_LIST *copy_case_clause __P((PATTERN_LIST *));
|
||||
static PATTERN_LIST *copy_case_clauses __P((PATTERN_LIST *));
|
||||
static FOR_COM *copy_for_command __P((FOR_COM *));
|
||||
#if defined (ARITH_FOR_COMMAND)
|
||||
static ARITH_FOR_COM *copy_arith_for_command __P((ARITH_FOR_COM *));
|
||||
#endif
|
||||
static GROUP_COM *copy_group_command __P((GROUP_COM *));
|
||||
static SUBSHELL_COM *copy_subshell_command __P((SUBSHELL_COM *));
|
||||
static COPROC_COM *copy_coproc_command __P((COPROC_COM *));
|
||||
static CASE_COM *copy_case_command __P((CASE_COM *));
|
||||
static WHILE_COM *copy_while_command __P((WHILE_COM *));
|
||||
static IF_COM *copy_if_command __P((IF_COM *));
|
||||
#if defined (DPAREN_ARITHMETIC)
|
||||
static ARITH_COM *copy_arith_command __P((ARITH_COM *));
|
||||
#endif
|
||||
#if defined (COND_COMMAND)
|
||||
static COND_COM *copy_cond_command __P((COND_COM *));
|
||||
#endif
|
||||
static SIMPLE_COM *copy_simple_command __P((SIMPLE_COM *));
|
||||
|
||||
WORD_DESC *
|
||||
copy_word (w)
|
||||
WORD_DESC *w;
|
||||
{
|
||||
WORD_DESC *new_word;
|
||||
|
||||
new_word = make_bare_word (w->word);
|
||||
new_word->flags = w->flags;
|
||||
return (new_word);
|
||||
}
|
||||
|
||||
/* Copy the chain of words in LIST. Return a pointer to
|
||||
the new chain. */
|
||||
WORD_LIST *
|
||||
copy_word_list (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
WORD_LIST *new_list;
|
||||
|
||||
for (new_list = (WORD_LIST *)NULL; list; list = list->next)
|
||||
new_list = make_word_list (copy_word (list->word), new_list);
|
||||
|
||||
return (REVERSE_LIST (new_list, WORD_LIST *));
|
||||
}
|
||||
|
||||
static PATTERN_LIST *
|
||||
copy_case_clause (clause)
|
||||
PATTERN_LIST *clause;
|
||||
{
|
||||
PATTERN_LIST *new_clause;
|
||||
|
||||
new_clause = (PATTERN_LIST *)xmalloc (sizeof (PATTERN_LIST));
|
||||
new_clause->patterns = copy_word_list (clause->patterns);
|
||||
new_clause->action = copy_command (clause->action);
|
||||
new_clause->flags = clause->flags;
|
||||
return (new_clause);
|
||||
}
|
||||
|
||||
static PATTERN_LIST *
|
||||
copy_case_clauses (clauses)
|
||||
PATTERN_LIST *clauses;
|
||||
{
|
||||
PATTERN_LIST *new_list, *new_clause;
|
||||
|
||||
for (new_list = (PATTERN_LIST *)NULL; clauses; clauses = clauses->next)
|
||||
{
|
||||
new_clause = copy_case_clause (clauses);
|
||||
new_clause->next = new_list;
|
||||
new_list = new_clause;
|
||||
}
|
||||
return (REVERSE_LIST (new_list, PATTERN_LIST *));
|
||||
}
|
||||
|
||||
/* Copy a single redirect. */
|
||||
REDIRECT *
|
||||
copy_redirect (redirect)
|
||||
REDIRECT *redirect;
|
||||
{
|
||||
REDIRECT *new_redirect;
|
||||
|
||||
new_redirect = (REDIRECT *)xmalloc (sizeof (REDIRECT));
|
||||
#if 0
|
||||
FASTCOPY ((char *)redirect, (char *)new_redirect, (sizeof (REDIRECT)));
|
||||
#else
|
||||
*new_redirect = *redirect; /* let the compiler do the fast structure copy */
|
||||
#endif
|
||||
|
||||
if (redirect->rflags & REDIR_VARASSIGN)
|
||||
new_redirect->redirector.filename = copy_word (redirect->redirector.filename);
|
||||
|
||||
switch (redirect->instruction)
|
||||
{
|
||||
case r_reading_until:
|
||||
case r_deblank_reading_until:
|
||||
if (redirect->here_doc_eof)
|
||||
new_redirect->here_doc_eof = savestring (redirect->here_doc_eof);
|
||||
else
|
||||
new_redirect->here_doc_eof = 0;
|
||||
/*FALLTHROUGH*/
|
||||
case r_reading_string:
|
||||
case r_appending_to:
|
||||
case r_output_direction:
|
||||
case r_input_direction:
|
||||
case r_inputa_direction:
|
||||
case r_err_and_out:
|
||||
case r_append_err_and_out:
|
||||
case r_input_output:
|
||||
case r_output_force:
|
||||
case r_duplicating_input_word:
|
||||
case r_duplicating_output_word:
|
||||
case r_move_input_word:
|
||||
case r_move_output_word:
|
||||
new_redirect->redirectee.filename = copy_word (redirect->redirectee.filename);
|
||||
break;
|
||||
case r_duplicating_input:
|
||||
case r_duplicating_output:
|
||||
case r_move_input:
|
||||
case r_move_output:
|
||||
case r_close_this:
|
||||
break;
|
||||
}
|
||||
return (new_redirect);
|
||||
}
|
||||
|
||||
REDIRECT *
|
||||
copy_redirects (list)
|
||||
REDIRECT *list;
|
||||
{
|
||||
REDIRECT *new_list, *temp;
|
||||
|
||||
for (new_list = (REDIRECT *)NULL; list; list = list->next)
|
||||
{
|
||||
temp = copy_redirect (list);
|
||||
temp->next = new_list;
|
||||
new_list = temp;
|
||||
}
|
||||
return (REVERSE_LIST (new_list, REDIRECT *));
|
||||
}
|
||||
|
||||
static FOR_COM *
|
||||
copy_for_command (com)
|
||||
FOR_COM *com;
|
||||
{
|
||||
FOR_COM *new_for;
|
||||
|
||||
new_for = (FOR_COM *)xmalloc (sizeof (FOR_COM));
|
||||
new_for->flags = com->flags;
|
||||
new_for->line = com->line;
|
||||
new_for->name = copy_word (com->name);
|
||||
new_for->map_list = copy_word_list (com->map_list);
|
||||
new_for->action = copy_command (com->action);
|
||||
return (new_for);
|
||||
}
|
||||
|
||||
#if defined (ARITH_FOR_COMMAND)
|
||||
static ARITH_FOR_COM *
|
||||
copy_arith_for_command (com)
|
||||
ARITH_FOR_COM *com;
|
||||
{
|
||||
ARITH_FOR_COM *new_arith_for;
|
||||
|
||||
new_arith_for = (ARITH_FOR_COM *)xmalloc (sizeof (ARITH_FOR_COM));
|
||||
new_arith_for->flags = com->flags;
|
||||
new_arith_for->line = com->line;
|
||||
new_arith_for->init = copy_word_list (com->init);
|
||||
new_arith_for->test = copy_word_list (com->test);
|
||||
new_arith_for->step = copy_word_list (com->step);
|
||||
new_arith_for->action = copy_command (com->action);
|
||||
return (new_arith_for);
|
||||
}
|
||||
#endif /* ARITH_FOR_COMMAND */
|
||||
|
||||
static GROUP_COM *
|
||||
copy_group_command (com)
|
||||
GROUP_COM *com;
|
||||
{
|
||||
GROUP_COM *new_group;
|
||||
|
||||
new_group = (GROUP_COM *)xmalloc (sizeof (GROUP_COM));
|
||||
new_group->command = copy_command (com->command);
|
||||
return (new_group);
|
||||
}
|
||||
|
||||
static SUBSHELL_COM *
|
||||
copy_subshell_command (com)
|
||||
SUBSHELL_COM *com;
|
||||
{
|
||||
SUBSHELL_COM *new_subshell;
|
||||
|
||||
new_subshell = (SUBSHELL_COM *)xmalloc (sizeof (SUBSHELL_COM));
|
||||
new_subshell->command = copy_command (com->command);
|
||||
new_subshell->flags = com->flags;
|
||||
return (new_subshell);
|
||||
}
|
||||
|
||||
static COPROC_COM *
|
||||
copy_coproc_command (com)
|
||||
COPROC_COM *com;
|
||||
{
|
||||
COPROC_COM *new_coproc;
|
||||
|
||||
new_coproc = (COPROC_COM *)xmalloc (sizeof (COPROC_COM));
|
||||
new_coproc->name = savestring (com->name);
|
||||
new_coproc->command = copy_command (com->command);
|
||||
new_coproc->flags = com->flags;
|
||||
return (new_coproc);
|
||||
}
|
||||
|
||||
static CASE_COM *
|
||||
copy_case_command (com)
|
||||
CASE_COM *com;
|
||||
{
|
||||
CASE_COM *new_case;
|
||||
|
||||
new_case = (CASE_COM *)xmalloc (sizeof (CASE_COM));
|
||||
new_case->flags = com->flags;
|
||||
new_case->line = com->line;
|
||||
new_case->word = copy_word (com->word);
|
||||
new_case->clauses = copy_case_clauses (com->clauses);
|
||||
return (new_case);
|
||||
}
|
||||
|
||||
static WHILE_COM *
|
||||
copy_while_command (com)
|
||||
WHILE_COM *com;
|
||||
{
|
||||
WHILE_COM *new_while;
|
||||
|
||||
new_while = (WHILE_COM *)xmalloc (sizeof (WHILE_COM));
|
||||
new_while->flags = com->flags;
|
||||
new_while->test = copy_command (com->test);
|
||||
new_while->action = copy_command (com->action);
|
||||
return (new_while);
|
||||
}
|
||||
|
||||
static IF_COM *
|
||||
copy_if_command (com)
|
||||
IF_COM *com;
|
||||
{
|
||||
IF_COM *new_if;
|
||||
|
||||
new_if = (IF_COM *)xmalloc (sizeof (IF_COM));
|
||||
new_if->flags = com->flags;
|
||||
new_if->test = copy_command (com->test);
|
||||
new_if->true_case = copy_command (com->true_case);
|
||||
new_if->false_case = com->false_case ? copy_command (com->false_case) : com->false_case;
|
||||
return (new_if);
|
||||
}
|
||||
|
||||
#if defined (DPAREN_ARITHMETIC)
|
||||
static ARITH_COM *
|
||||
copy_arith_command (com)
|
||||
ARITH_COM *com;
|
||||
{
|
||||
ARITH_COM *new_arith;
|
||||
|
||||
new_arith = (ARITH_COM *)xmalloc (sizeof (ARITH_COM));
|
||||
new_arith->flags = com->flags;
|
||||
new_arith->exp = copy_word_list (com->exp);
|
||||
new_arith->line = com->line;
|
||||
|
||||
return (new_arith);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (COND_COMMAND)
|
||||
static COND_COM *
|
||||
copy_cond_command (com)
|
||||
COND_COM *com;
|
||||
{
|
||||
COND_COM *new_cond;
|
||||
|
||||
new_cond = (COND_COM *)xmalloc (sizeof (COND_COM));
|
||||
new_cond->flags = com->flags;
|
||||
new_cond->line = com->line;
|
||||
new_cond->type = com->type;
|
||||
new_cond->op = com->op ? copy_word (com->op) : com->op;
|
||||
new_cond->left = com->left ? copy_cond_command (com->left) : (COND_COM *)NULL;
|
||||
new_cond->right = com->right ? copy_cond_command (com->right) : (COND_COM *)NULL;
|
||||
|
||||
return (new_cond);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_COM *
|
||||
copy_simple_command (com)
|
||||
SIMPLE_COM *com;
|
||||
{
|
||||
SIMPLE_COM *new_simple;
|
||||
|
||||
new_simple = (SIMPLE_COM *)xmalloc (sizeof (SIMPLE_COM));
|
||||
new_simple->flags = com->flags;
|
||||
new_simple->words = copy_word_list (com->words);
|
||||
new_simple->redirects = com->redirects ? copy_redirects (com->redirects) : (REDIRECT *)NULL;
|
||||
new_simple->line = com->line;
|
||||
return (new_simple);
|
||||
}
|
||||
|
||||
FUNCTION_DEF *
|
||||
copy_function_def_contents (old, new_def)
|
||||
FUNCTION_DEF *old, *new_def;
|
||||
{
|
||||
new_def->name = copy_word (old->name);
|
||||
new_def->command = old->command ? copy_command (old->command) : old->command;
|
||||
new_def->flags = old->flags;
|
||||
new_def->line = old->line;
|
||||
new_def->source_file = old->source_file ? savestring (old->source_file) : old->source_file;
|
||||
return (new_def);
|
||||
}
|
||||
|
||||
FUNCTION_DEF *
|
||||
copy_function_def (com)
|
||||
FUNCTION_DEF *com;
|
||||
{
|
||||
FUNCTION_DEF *new_def;
|
||||
|
||||
new_def = (FUNCTION_DEF *)xmalloc (sizeof (FUNCTION_DEF));
|
||||
new_def = copy_function_def_contents (com, new_def);
|
||||
return (new_def);
|
||||
}
|
||||
|
||||
/* Copy the command structure in COMMAND. Return a pointer to the
|
||||
copy. Don't you forget to dispose_command () on this pointer
|
||||
later! */
|
||||
COMMAND *
|
||||
copy_command (command)
|
||||
COMMAND *command;
|
||||
{
|
||||
COMMAND *new_command;
|
||||
|
||||
if (command == NULL)
|
||||
return (command);
|
||||
|
||||
new_command = (COMMAND *)xmalloc (sizeof (COMMAND));
|
||||
FASTCOPY ((char *)command, (char *)new_command, sizeof (COMMAND));
|
||||
new_command->flags = command->flags;
|
||||
new_command->line = command->line;
|
||||
|
||||
if (command->redirects)
|
||||
new_command->redirects = copy_redirects (command->redirects);
|
||||
|
||||
switch (command->type)
|
||||
{
|
||||
case cm_for:
|
||||
new_command->value.For = copy_for_command (command->value.For);
|
||||
break;
|
||||
|
||||
#if defined (ARITH_FOR_COMMAND)
|
||||
case cm_arith_for:
|
||||
new_command->value.ArithFor = copy_arith_for_command (command->value.ArithFor);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined (SELECT_COMMAND)
|
||||
case cm_select:
|
||||
new_command->value.Select =
|
||||
(SELECT_COM *)copy_for_command ((FOR_COM *)command->value.Select);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case cm_group:
|
||||
new_command->value.Group = copy_group_command (command->value.Group);
|
||||
break;
|
||||
|
||||
case cm_subshell:
|
||||
new_command->value.Subshell = copy_subshell_command (command->value.Subshell);
|
||||
break;
|
||||
|
||||
case cm_coproc:
|
||||
new_command->value.Coproc = copy_coproc_command (command->value.Coproc);
|
||||
break;
|
||||
|
||||
case cm_case:
|
||||
new_command->value.Case = copy_case_command (command->value.Case);
|
||||
break;
|
||||
|
||||
case cm_until:
|
||||
case cm_while:
|
||||
new_command->value.While = copy_while_command (command->value.While);
|
||||
break;
|
||||
|
||||
case cm_if:
|
||||
new_command->value.If = copy_if_command (command->value.If);
|
||||
break;
|
||||
|
||||
#if defined (DPAREN_ARITHMETIC)
|
||||
case cm_arith:
|
||||
new_command->value.Arith = copy_arith_command (command->value.Arith);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined (COND_COMMAND)
|
||||
case cm_cond:
|
||||
new_command->value.Cond = copy_cond_command (command->value.Cond);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case cm_simple:
|
||||
new_command->value.Simple = copy_simple_command (command->value.Simple);
|
||||
break;
|
||||
|
||||
case cm_connection:
|
||||
{
|
||||
CONNECTION *new_connection;
|
||||
|
||||
new_connection = (CONNECTION *)xmalloc (sizeof (CONNECTION));
|
||||
new_connection->connector = command->value.Connection->connector;
|
||||
new_connection->first = copy_command (command->value.Connection->first);
|
||||
new_connection->second = copy_command (command->value.Connection->second);
|
||||
new_command->value.Connection = new_connection;
|
||||
break;
|
||||
}
|
||||
|
||||
case cm_function_def:
|
||||
new_command->value.Function_def = copy_function_def (command->value.Function_def);
|
||||
break;
|
||||
}
|
||||
return (new_command);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
# This file is a shell script that caches the results of configure
|
||||
# tests for CYGWIN32 so they don't need to be done when cross-compiling.
|
||||
|
||||
# AC_FUNC_GETPGRP should also define GETPGRP_VOID
|
||||
ac_cv_func_getpgrp_void=${ac_cv_func_getpgrp_void='yes'}
|
||||
# AC_FUNC_SETVBUF_REVERSED should not define anything else
|
||||
ac_cv_func_setvbuf_reversed=${ac_cv_func_setvbuf_reversed='no'}
|
||||
# on CYGWIN32, system calls do not restart
|
||||
ac_cv_sys_restartable_syscalls=${ac_cv_sys_restartable_syscalls='no'}
|
||||
bash_cv_sys_restartable_syscalls=${bash_cv_sys_restartable_syscalls='no'}
|
||||
|
||||
# these may be necessary, but they are currently commented out
|
||||
#ac_cv_c_bigendian=${ac_cv_c_bigendian='no'}
|
||||
ac_cv_sizeof_char_p=${ac_cv_sizeof_char_p='4'}
|
||||
ac_cv_sizeof_int=${ac_cv_sizeof_int='4'}
|
||||
ac_cv_sizeof_long=${ac_cv_sizeof_long='4'}
|
||||
ac_cv_sizeof_double=${ac_cv_sizeof_double='8'}
|
||||
|
||||
bash_cv_dup2_broken=${bash_cv_dup2_broken='no'}
|
||||
bash_cv_pgrp_pipe=${bash_cv_pgrp_pipe='no'}
|
||||
bash_cv_type_rlimit=${bash_cv_type_rlimit='long'}
|
||||
bash_cv_decl_under_sys_siglist=${bash_cv_decl_under_sys_siglist='no'}
|
||||
bash_cv_under_sys_siglist=${bash_cv_under_sys_siglist='no'}
|
||||
bash_cv_sys_siglist=${bash_cv_sys_siglist='no'}
|
||||
bash_cv_opendir_not_robust=${bash_cv_opendir_not_robust='no'}
|
||||
bash_cv_getenv_redef=${bash_cv_getenv_redef='yes'}
|
||||
bash_cv_printf_declared=${bash_cv_printf_declared='yes'}
|
||||
bash_cv_ulimit_maxfds=${bash_cv_ulimit_maxfds='no'}
|
||||
bash_cv_getcwd_calls_popen=${bash_cv_getcwd_calls_popen='no'}
|
||||
bash_cv_must_reinstall_sighandlers=${bash_cv_must_reinstall_sighandlers='no'}
|
||||
bash_cv_job_control_missing=${bash_cv_job_control_missing='present'}
|
||||
bash_cv_sys_named_pipes=${bash_cv_sys_named_pipes='missing'}
|
||||
bash_cv_func_sigsetjmp=${bash_cv_func_sigsetjmp='missing'}
|
||||
bash_cv_mail_dir=${bash_cv_mail_dir='unknown'}
|
||||
bash_cv_func_strcoll_broken=${bash_cv_func_strcoll_broken='no'}
|
||||
|
||||
bash_cv_type_int32_t=${bash_cv_type_int32_t='int'}
|
||||
bash_cv_type_u_int32_t=${bash_cv_type_u_int32_t='int'}
|
||||
|
||||
ac_cv_type_bits64_t=${ac_cv_type_bits64_t='no'}
|
||||
|
||||
# end of cross-build/cygwin32.cache
|
||||
+1745
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+2101
-1089
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,238 @@
|
||||
#
|
||||
# Simple makefile for the sample loadable builtins
|
||||
#
|
||||
# Copyright (C) 1996 Free Software Foundation, Inc.
|
||||
|
||||
# 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 2, 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, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
||||
|
||||
# Include some boilerplate Gnu makefile definitions.
|
||||
prefix = @prefix@
|
||||
|
||||
exec_prefix = @exec_prefix@
|
||||
bindir = @bindir@
|
||||
libdir = @libdir@
|
||||
infodir = @infodir@
|
||||
includedir = @includedir@
|
||||
|
||||
topdir = @top_srcdir@
|
||||
BUILD_DIR = @BUILD_DIR@
|
||||
srcdir = @srcdir@
|
||||
VPATH = .:@srcdir@
|
||||
|
||||
@SET_MAKE@
|
||||
CC = @CC@
|
||||
RM = rm -f
|
||||
|
||||
SHELL = @MAKE_SHELL@
|
||||
|
||||
host_os = @host_os@
|
||||
host_cpu = @host_cpu@
|
||||
host_vendor = @host_vendor@
|
||||
|
||||
CFLAGS = @CFLAGS@
|
||||
LOCAL_CFLAGS = @LOCAL_CFLAGS@
|
||||
DEFS = @DEFS@
|
||||
LOCAL_DEFS = @LOCAL_DEFS@
|
||||
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
|
||||
BASHINCDIR = ${topdir}/include
|
||||
|
||||
LIBBUILD = ${BUILD_DIR}/lib
|
||||
|
||||
INTL_LIBSRC = ${topdir}/lib/intl
|
||||
INTL_BUILDDIR = ${LIBBUILD}/intl
|
||||
INTL_INC = @INTL_INC@
|
||||
LIBINTL_H = @LIBINTL_H@
|
||||
|
||||
CCFLAGS = $(DEFS) $(LOCAL_DEFS) $(LOCAL_CFLAGS) $(CFLAGS)
|
||||
|
||||
#
|
||||
# These values are generated for configure by ${topdir}/support/shobj-conf.
|
||||
# If your system is not supported by that script, but includes facilities for
|
||||
# dynamic loading of shared objects, please update the script and send the
|
||||
# changes to bash-maintainers@gnu.org.
|
||||
#
|
||||
SHOBJ_CC = @SHOBJ_CC@
|
||||
SHOBJ_CFLAGS = @SHOBJ_CFLAGS@
|
||||
SHOBJ_LD = @SHOBJ_LD@
|
||||
SHOBJ_LDFLAGS = @SHOBJ_LDFLAGS@
|
||||
SHOBJ_XLDFLAGS = @SHOBJ_XLDFLAGS@
|
||||
SHOBJ_LIBS = @SHOBJ_LIBS@
|
||||
SHOBJ_STATUS = @SHOBJ_STATUS@
|
||||
|
||||
INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins \
|
||||
-I$(BASHINCDIR) -I$(BUILD_DIR) -I$(LIBBUILD) \
|
||||
-I$(BUILD_DIR)/builtins $(INTL_INC)
|
||||
|
||||
.c.o:
|
||||
$(SHOBJ_CC) $(SHOBJ_CFLAGS) $(CCFLAGS) $(INC) -c -o $@ $<
|
||||
|
||||
|
||||
ALLPROG = print truefalse sleep pushd finfo logname basename dirname \
|
||||
tty pathchk tee head mkdir rmdir printenv id whoami \
|
||||
uname sync push ln unlink cut realpath getconf strftime
|
||||
OTHERPROG = necho hello cat
|
||||
|
||||
all: $(SHOBJ_STATUS)
|
||||
|
||||
supported: $(ALLPROG)
|
||||
others: $(OTHERPROG)
|
||||
|
||||
unsupported:
|
||||
@echo "Your system (${host_os}) is not supported by the"
|
||||
@echo "${topdir}/support/shobj-conf script."
|
||||
@echo "If your operating system provides facilities for dynamic"
|
||||
@echo "loading of shared objects using the dlopen(3) interface,"
|
||||
@echo "please update the script and re-run configure.
|
||||
@echo "Please send the changes you made to bash-maintainers@gnu.org"
|
||||
@echo "for inclusion in future bash releases."
|
||||
|
||||
everything: supported others
|
||||
|
||||
print: print.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ print.o $(SHOBJ_LIBS)
|
||||
|
||||
necho: necho.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ necho.o $(SHOBJ_LIBS)
|
||||
|
||||
getconf: getconf.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ getconf.o $(SHOBJ_LIBS)
|
||||
|
||||
hello: hello.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ hello.o $(SHOBJ_LIBS)
|
||||
|
||||
truefalse: truefalse.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ truefalse.o $(SHOBJ_LIBS)
|
||||
|
||||
sleep: sleep.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ sleep.o $(SHOBJ_LIBS)
|
||||
|
||||
finfo: finfo.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ finfo.o $(SHOBJ_LIBS)
|
||||
|
||||
cat: cat.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ cat.o $(SHOBJ_LIBS)
|
||||
|
||||
logname: logname.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ logname.o $(SHOBJ_LIBS)
|
||||
|
||||
basename: basename.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ basename.o $(SHOBJ_LIBS)
|
||||
|
||||
dirname: dirname.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ dirname.o $(SHOBJ_LIBS)
|
||||
|
||||
tty: tty.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ tty.o $(SHOBJ_LIBS)
|
||||
|
||||
pathchk: pathchk.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ pathchk.o $(SHOBJ_LIBS)
|
||||
|
||||
tee: tee.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ tee.o $(SHOBJ_LIBS)
|
||||
|
||||
mkdir: mkdir.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mkdir.o $(SHOBJ_LIBS)
|
||||
|
||||
rmdir: rmdir.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ rmdir.o $(SHOBJ_LIBS)
|
||||
|
||||
head: head.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ head.o $(SHOBJ_LIBS)
|
||||
|
||||
printenv: printenv.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ printenv.o $(SHOBJ_LIBS)
|
||||
|
||||
id: id.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ id.o $(SHOBJ_LIBS)
|
||||
|
||||
whoami: whoami.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ whoami.o $(SHOBJ_LIBS)
|
||||
|
||||
uname: uname.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ uname.o $(SHOBJ_LIBS)
|
||||
|
||||
sync: sync.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ sync.o $(SHOBJ_LIBS)
|
||||
|
||||
push: push.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ push.o $(SHOBJ_LIBS)
|
||||
|
||||
ln: ln.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ ln.o $(SHOBJ_LIBS)
|
||||
|
||||
unlink: unlink.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ unlink.o $(SHOBJ_LIBS)
|
||||
|
||||
cut: cut.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ cut.o $(SHOBJ_LIBS)
|
||||
|
||||
realpath: realpath.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ realpath.o $(SHOBJ_LIBS)
|
||||
|
||||
strftime: strftime.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ strftime.o $(SHOBJ_LIBS)
|
||||
|
||||
# pushd is a special case. We use the same source that the builtin version
|
||||
# uses, with special compilation options.
|
||||
#
|
||||
pushd.c: ${topdir}/builtins/pushd.def
|
||||
$(RM) $@
|
||||
${BUILD_DIR}/builtins/mkbuiltins -D ${topdir}/builtins ${topdir}/builtins/pushd.def
|
||||
|
||||
pushd.o: pushd.c
|
||||
$(RM) $@
|
||||
$(SHOBJ_CC) -DHAVE_CONFIG_H -DPUSHD_AND_POPD -DLOADABLE_BUILTIN $(SHOBJ_CFLAGS) $(CFLAGS) $(CPPFLAGS) $(INC) -c -o $@ $<
|
||||
|
||||
pushd: pushd.o
|
||||
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ pushd.o $(SHOBJ_LIBS)
|
||||
|
||||
clean:
|
||||
$(RM) $(ALLPROG) $(OTHERPROG) *.o
|
||||
-( cd perl && ${MAKE} ${MFLAGS} $@ )
|
||||
|
||||
mostlyclean: clean
|
||||
-( cd perl && ${MAKE} ${MFLAGS} $@ )
|
||||
|
||||
distclean maintainer-clean: clean
|
||||
$(RM) Makefile pushd.c
|
||||
-( cd perl && ${MAKE} ${MFLAGS} $@ )
|
||||
|
||||
print.o: print.c
|
||||
truefalse.o: truefalse.c
|
||||
sleep.o: sleep.c
|
||||
finfo.o: finfo.c
|
||||
logname.o: logname.c
|
||||
basename.o: basename.c
|
||||
dirname.o: dirname.c
|
||||
tty.o: tty.c
|
||||
pathchk.o: pathchk.c
|
||||
tee.o: tee.c
|
||||
head.o: head.c
|
||||
rmdir.o: rmdir.c
|
||||
necho.o: necho.c
|
||||
getconf.o: getconf.c
|
||||
hello.o: hello.c
|
||||
cat.o: cat.c
|
||||
printenv.o: printenv.c
|
||||
id.o: id.c
|
||||
whoami.o: whoami.c
|
||||
uname.o: uname.c
|
||||
sync.o: sync.c
|
||||
push.o: push.c
|
||||
mkdir.o: mkdir.c
|
||||
realpath.o: realpath.c
|
||||
strftime.o: strftime.c
|
||||
@@ -5416,7 +5416,9 @@ shell_execve (command, args, env)
|
||||
|
||||
unbind_args (); /* remove the positional parameters */
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
clear_fifo_list (); /* pipe fds are what they are now */
|
||||
#endif
|
||||
|
||||
longjmp (subshell_top_level, 1);
|
||||
/*NOTREACHED*/
|
||||
|
||||
+5538
File diff suppressed because it is too large
Load Diff
@@ -53,6 +53,7 @@ extern int interactive_comments;
|
||||
extern int check_hashed_filenames;
|
||||
extern int source_uses_path;
|
||||
extern int source_searches_cwd;
|
||||
extern int posixly_correct;
|
||||
|
||||
static char *bash_special_tilde_expansions __P((char *));
|
||||
static int unquoted_tilde_word __P((const char *));
|
||||
@@ -244,6 +245,15 @@ check_identifier (word, check_word)
|
||||
return (1);
|
||||
}
|
||||
|
||||
int
|
||||
legal_function_name (string)
|
||||
char *string;
|
||||
{
|
||||
if (absolute_program (string)) /* don't allow slash */
|
||||
return 0;
|
||||
return (posixly_correct ? legal_identifier (string) : 1);
|
||||
}
|
||||
|
||||
/* Return 1 if STRING comprises a valid alias name. The shell accepts
|
||||
essentially all characters except those which must be quoted to the
|
||||
parser (which disqualifies them from alias expansion anyway) and `/'. */
|
||||
|
||||
+1207
File diff suppressed because it is too large
Load Diff
@@ -283,6 +283,7 @@ extern void print_rlimtype __P((RLIMTYPE, int));
|
||||
extern int all_digits __P((char *));
|
||||
extern int legal_number __P((const char *, intmax_t *));
|
||||
extern int legal_identifier __P((char *));
|
||||
extern int legal_function_name __P((char *));
|
||||
extern int check_identifier __P((WORD_DESC *, int));
|
||||
extern int legal_alias_name __P((char *, int));
|
||||
extern int assignment __P((const char *, int));
|
||||
|
||||
+328
@@ -0,0 +1,328 @@
|
||||
/* general.h -- defines that everybody likes to use. */
|
||||
|
||||
/* Copyright (C) 1993-2009 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/>.
|
||||
*/
|
||||
|
||||
#if !defined (_GENERAL_H_)
|
||||
#define _GENERAL_H_
|
||||
|
||||
#include "stdc.h"
|
||||
|
||||
#include "bashtypes.h"
|
||||
#include "chartypes.h"
|
||||
|
||||
#if defined (HAVE_SYS_RESOURCE_H) && defined (RLIMTYPE)
|
||||
# if defined (HAVE_SYS_TIME_H)
|
||||
# include <sys/time.h>
|
||||
# endif
|
||||
# include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_STRING_H)
|
||||
# include <string.h>
|
||||
#else
|
||||
# include <strings.h>
|
||||
#endif /* !HAVE_STRING_H */
|
||||
|
||||
#if defined (HAVE_LIMITS_H)
|
||||
# include <limits.h>
|
||||
#endif
|
||||
|
||||
#include "xmalloc.h"
|
||||
|
||||
/* NULL pointer type. */
|
||||
#if !defined (NULL)
|
||||
# if defined (__STDC__)
|
||||
# define NULL ((void *) 0)
|
||||
# else
|
||||
# define NULL 0x0
|
||||
# endif /* !__STDC__ */
|
||||
#endif /* !NULL */
|
||||
|
||||
/* Hardly used anymore */
|
||||
#define pointer_to_int(x) (int)((char *)x - (char *)0)
|
||||
|
||||
#if defined (alpha) && defined (__GNUC__) && !defined (strchr) && !defined (__STDC__)
|
||||
extern char *strchr (), *strrchr ();
|
||||
#endif
|
||||
|
||||
#if !defined (strcpy) && (defined (HAVE_DECL_STRCPY) && !HAVE_DECL_STRCPY)
|
||||
extern char *strcpy __P((char *, const char *));
|
||||
#endif
|
||||
|
||||
#if !defined (savestring)
|
||||
# define savestring(x) (char *)strcpy (xmalloc (1 + strlen (x)), (x))
|
||||
#endif
|
||||
|
||||
#ifndef member
|
||||
# define member(c, s) ((c) ? ((char *)mbschr ((s), (c)) != (char *)NULL) : 0)
|
||||
#endif
|
||||
|
||||
#ifndef whitespace
|
||||
#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
|
||||
#endif
|
||||
|
||||
#ifndef CHAR_MAX
|
||||
# ifdef __CHAR_UNSIGNED__
|
||||
# define CHAR_MAX 0xff
|
||||
# else
|
||||
# define CHAR_MAX 0x7f
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef CHAR_BIT
|
||||
# define CHAR_BIT 8
|
||||
#endif
|
||||
|
||||
/* Nonzero if the integer type T is signed. */
|
||||
#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
|
||||
|
||||
/* Bound on length of the string representing an integer value of type T.
|
||||
Subtract one for the sign bit if T is signed;
|
||||
302 / 1000 is log10 (2) rounded up;
|
||||
add one for integer division truncation;
|
||||
add one more for a minus sign if t is signed. */
|
||||
#define INT_STRLEN_BOUND(t) \
|
||||
((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 \
|
||||
+ 1 + TYPE_SIGNED (t))
|
||||
|
||||
|
||||
/* Define exactly what a legal shell identifier consists of. */
|
||||
#define legal_variable_starter(c) (ISALPHA(c) || (c == '_'))
|
||||
#define legal_variable_char(c) (ISALNUM(c) || c == '_')
|
||||
|
||||
/* Definitions used in subst.c and by the `read' builtin for field
|
||||
splitting. */
|
||||
#define spctabnl(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
|
||||
|
||||
/* All structs which contain a `next' field should have that field
|
||||
as the first field in the struct. This means that functions
|
||||
can be written to handle the general case for linked lists. */
|
||||
typedef struct g_list {
|
||||
struct g_list *next;
|
||||
} GENERIC_LIST;
|
||||
|
||||
/* Here is a generic structure for associating character strings
|
||||
with integers. It is used in the parser for shell tokenization. */
|
||||
typedef struct {
|
||||
char *word;
|
||||
int token;
|
||||
} STRING_INT_ALIST;
|
||||
|
||||
/* A macro to avoid making an unneccessary function call. */
|
||||
#define REVERSE_LIST(list, type) \
|
||||
((list && list->next) ? (type)list_reverse ((GENERIC_LIST *)list) \
|
||||
: (type)(list))
|
||||
|
||||
#if __GNUC__ > 1
|
||||
# define FASTCOPY(s, d, n) __builtin_memcpy ((d), (s), (n))
|
||||
#else /* !__GNUC__ */
|
||||
# if !defined (HAVE_BCOPY)
|
||||
# if !defined (HAVE_MEMMOVE)
|
||||
# define FASTCOPY(s, d, n) memcpy ((d), (s), (n))
|
||||
# else
|
||||
# define FASTCOPY(s, d, n) memmove ((d), (s), (n))
|
||||
# endif /* !HAVE_MEMMOVE */
|
||||
# else /* HAVE_BCOPY */
|
||||
# define FASTCOPY(s, d, n) bcopy ((s), (d), (n))
|
||||
# endif /* HAVE_BCOPY */
|
||||
#endif /* !__GNUC__ */
|
||||
|
||||
/* String comparisons that possibly save a function call each. */
|
||||
#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0)
|
||||
#define STREQN(a, b, n) ((n == 0) ? (1) \
|
||||
: ((a)[0] == (b)[0] && strncmp(a, b, n) == 0))
|
||||
|
||||
/* More convenience definitions that possibly save system or libc calls. */
|
||||
#define STRLEN(s) (((s) && (s)[0]) ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0)
|
||||
#define FREE(s) do { if (s) free (s); } while (0)
|
||||
#define MEMBER(c, s) (((c) && c == (s)[0] && !(s)[1]) || (member(c, s)))
|
||||
|
||||
/* A fairly hairy macro to check whether an allocated string has more room,
|
||||
and to resize it using xrealloc if it does not.
|
||||
STR is the string (char *)
|
||||
CIND is the current index into the string (int)
|
||||
ROOM is the amount of additional room we need in the string (int)
|
||||
CSIZE is the currently-allocated size of STR (int)
|
||||
SINCR is how much to increment CSIZE before calling xrealloc (int) */
|
||||
|
||||
#define RESIZE_MALLOCED_BUFFER(str, cind, room, csize, sincr) \
|
||||
do { \
|
||||
if ((cind) + (room) >= csize) \
|
||||
{ \
|
||||
while ((cind) + (room) >= csize) \
|
||||
csize += (sincr); \
|
||||
str = xrealloc (str, csize); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Function pointers can be declared as (Function *)foo. */
|
||||
#if !defined (_FUNCTION_DEF)
|
||||
# define _FUNCTION_DEF
|
||||
typedef int Function ();
|
||||
typedef void VFunction ();
|
||||
typedef char *CPFunction (); /* no longer used */
|
||||
typedef char **CPPFunction (); /* no longer used */
|
||||
#endif /* _FUNCTION_DEF */
|
||||
|
||||
#ifndef SH_FUNCTION_TYPEDEF
|
||||
# define SH_FUNCTION_TYPEDEF
|
||||
|
||||
/* Shell function typedefs with prototypes */
|
||||
/* `Generic' function pointer typedefs */
|
||||
|
||||
typedef int sh_intfunc_t __P((int));
|
||||
typedef int sh_ivoidfunc_t __P((void));
|
||||
typedef int sh_icpfunc_t __P((char *));
|
||||
typedef int sh_icppfunc_t __P((char **));
|
||||
typedef int sh_iptrfunc_t __P((PTR_T));
|
||||
|
||||
typedef void sh_voidfunc_t __P((void));
|
||||
typedef void sh_vintfunc_t __P((int));
|
||||
typedef void sh_vcpfunc_t __P((char *));
|
||||
typedef void sh_vcppfunc_t __P((char **));
|
||||
typedef void sh_vptrfunc_t __P((PTR_T));
|
||||
|
||||
typedef int sh_wdesc_func_t __P((WORD_DESC *));
|
||||
typedef int sh_wlist_func_t __P((WORD_LIST *));
|
||||
|
||||
typedef int sh_glist_func_t __P((GENERIC_LIST *));
|
||||
|
||||
typedef char *sh_string_func_t __P((char *)); /* like savestring, et al. */
|
||||
|
||||
typedef int sh_msg_func_t __P((const char *, ...)); /* printf(3)-like */
|
||||
typedef void sh_vmsg_func_t __P((const char *, ...)); /* printf(3)-like */
|
||||
|
||||
/* Specific function pointer typedefs. Most of these could be done
|
||||
with #defines. */
|
||||
typedef void sh_sv_func_t __P((char *)); /* sh_vcpfunc_t */
|
||||
typedef void sh_free_func_t __P((PTR_T)); /* sh_vptrfunc_t */
|
||||
typedef void sh_resetsig_func_t __P((int)); /* sh_vintfunc_t */
|
||||
|
||||
typedef int sh_ignore_func_t __P((const char *)); /* sh_icpfunc_t */
|
||||
|
||||
typedef int sh_assign_func_t __P((const char *));
|
||||
typedef int sh_wassign_func_t __P((WORD_DESC *, int));
|
||||
|
||||
typedef int sh_builtin_func_t __P((WORD_LIST *)); /* sh_wlist_func_t */
|
||||
|
||||
#endif /* SH_FUNCTION_TYPEDEF */
|
||||
|
||||
#define NOW ((time_t) time ((time_t *) 0))
|
||||
|
||||
/* Some defines for calling file status functions. */
|
||||
#define FS_EXISTS 0x1
|
||||
#define FS_EXECABLE 0x2
|
||||
#define FS_EXEC_PREFERRED 0x4
|
||||
#define FS_EXEC_ONLY 0x8
|
||||
#define FS_DIRECTORY 0x10
|
||||
#define FS_NODIRS 0x20
|
||||
#define FS_READABLE 0x40
|
||||
|
||||
/* Default maximum for move_to_high_fd */
|
||||
#define HIGH_FD_MAX 256
|
||||
|
||||
/* The type of function passed as the fourth argument to qsort(3). */
|
||||
#ifdef __STDC__
|
||||
typedef int QSFUNC (const void *, const void *);
|
||||
#else
|
||||
typedef int QSFUNC ();
|
||||
#endif
|
||||
|
||||
/* Some useful definitions for Unix pathnames. Argument convention:
|
||||
x == string, c == character */
|
||||
|
||||
#if !defined (__CYGWIN__)
|
||||
# define ABSPATH(x) ((x)[0] == '/')
|
||||
# define RELPATH(x) ((x)[0] != '/')
|
||||
#else /* __CYGWIN__ */
|
||||
# define ABSPATH(x) (((x)[0] && ISALPHA((unsigned char)(x)[0]) && (x)[1] == ':') || ISDIRSEP((x)[0]))
|
||||
# define RELPATH(x) (ABSPATH(x) == 0)
|
||||
#endif /* __CYGWIN__ */
|
||||
|
||||
#define ROOTEDPATH(x) (ABSPATH(x))
|
||||
|
||||
#define DIRSEP '/'
|
||||
#if !defined (__CYGWIN__)
|
||||
# define ISDIRSEP(c) ((c) == '/')
|
||||
#else
|
||||
# define ISDIRSEP(c) ((c) == '/' || (c) == '\\')
|
||||
#endif /* __CYGWIN__ */
|
||||
#define PATHSEP(c) (ISDIRSEP(c) || (c) == 0)
|
||||
|
||||
#if 0
|
||||
/* Declarations for functions defined in xmalloc.c */
|
||||
extern PTR_T xmalloc __P((size_t));
|
||||
extern PTR_T xrealloc __P((void *, size_t));
|
||||
extern void xfree __P((void *));
|
||||
#endif
|
||||
|
||||
/* Declarations for functions defined in general.c */
|
||||
extern void posix_initialize __P((int));
|
||||
|
||||
#if defined (RLIMTYPE)
|
||||
extern RLIMTYPE string_to_rlimtype __P((char *));
|
||||
extern void print_rlimtype __P((RLIMTYPE, int));
|
||||
#endif
|
||||
|
||||
extern int all_digits __P((char *));
|
||||
extern int legal_number __P((const char *, intmax_t *));
|
||||
extern int legal_identifier __P((char *));
|
||||
extern int check_identifier __P((WORD_DESC *, int));
|
||||
extern int legal_alias_name __P((char *, int));
|
||||
extern int assignment __P((const char *, int));
|
||||
|
||||
extern int sh_unset_nodelay_mode __P((int));
|
||||
extern int sh_validfd __P((int));
|
||||
extern int fd_ispipe __P((int));
|
||||
extern void check_dev_tty __P((void));
|
||||
extern int move_to_high_fd __P((int, int, int));
|
||||
extern int check_binary_file __P((char *, int));
|
||||
|
||||
#ifdef _POSIXSTAT_H_
|
||||
extern int same_file __P((char *, char *, struct stat *, struct stat *));
|
||||
#endif
|
||||
|
||||
extern int sh_openpipe __P((int *));
|
||||
extern int sh_closepipe __P((int *));
|
||||
|
||||
extern int file_exists __P((char *));
|
||||
extern int file_isdir __P((char *));
|
||||
extern int file_iswdir __P((char *));
|
||||
extern int path_dot_or_dotdot __P((const char *));
|
||||
extern int absolute_pathname __P((const char *));
|
||||
extern int absolute_program __P((const char *));
|
||||
|
||||
extern char *make_absolute __P((char *, char *));
|
||||
extern char *base_pathname __P((char *));
|
||||
extern char *full_pathname __P((char *));
|
||||
extern char *polite_directory_format __P((char *));
|
||||
extern char *trim_pathname __P((char *, int));
|
||||
extern char *printable_filename __P((char *, int));
|
||||
|
||||
extern char *extract_colon_unit __P((char *, int *));
|
||||
|
||||
extern void tilde_initialize __P((void));
|
||||
extern char *bash_tilde_find_word __P((const char *, int, int *));
|
||||
extern char *bash_tilde_expand __P((const char *, int));
|
||||
|
||||
extern int group_member __P((gid_t));
|
||||
extern char **get_group_list __P((int *));
|
||||
extern int *get_group_array __P((int *));
|
||||
|
||||
#endif /* _GENERAL_H_ */
|
||||
@@ -42,7 +42,9 @@
|
||||
#define WLPAREN L'('
|
||||
#define WRPAREN L')'
|
||||
|
||||
/* Make sure these names continue to agree with what's in smatch.c */
|
||||
extern char *glob_patscan __P((char *, char *, int));
|
||||
extern wchar_t *glob_patscan_wc __P((wchar_t *, wchar_t *, int));
|
||||
|
||||
/* Return 1 of the first character of WSTRING could match the first
|
||||
character of pattern WPAT. Wide character version. */
|
||||
@@ -378,6 +380,7 @@ bad_bracket:
|
||||
return matlen;
|
||||
}
|
||||
|
||||
#if defined (EXTENDED_GLOB)
|
||||
/* Skip characters in PAT and return the final occurrence of DIRSEP. This
|
||||
is only called when extended_glob is set, so we have to skip over extglob
|
||||
patterns x(...) */
|
||||
@@ -408,3 +411,4 @@ glob_dirscan (pat, dirsep)
|
||||
}
|
||||
return d;
|
||||
}
|
||||
#endif /* EXTENDED_GLOB */
|
||||
|
||||
@@ -0,0 +1,412 @@
|
||||
/* gmisc.c -- miscellaneous pattern matching utility functions for Bash.
|
||||
|
||||
Copyright (C) 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/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "bashtypes.h"
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "bashansi.h"
|
||||
#include "shmbutil.h"
|
||||
|
||||
#include "stdc.h"
|
||||
|
||||
#ifndef LPAREN
|
||||
# define LPAREN '('
|
||||
#endif
|
||||
#ifndef RPAREN
|
||||
# define RPAREN ')'
|
||||
#endif
|
||||
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
#define WLPAREN L'('
|
||||
#define WRPAREN L')'
|
||||
|
||||
/* Make sure these names continue to agree with what's in smatch.c */
|
||||
extern char *glob_patscan __P((char *, char *, int));
|
||||
extern wchar_t *glob_patscan_wc __P((wchar_t *, wchar_t *, int));
|
||||
|
||||
/* Return 1 of the first character of WSTRING could match the first
|
||||
character of pattern WPAT. Wide character version. */
|
||||
int
|
||||
match_pattern_wchar (wpat, wstring)
|
||||
wchar_t *wpat, *wstring;
|
||||
{
|
||||
wchar_t wc;
|
||||
|
||||
if (*wstring == 0)
|
||||
return (0);
|
||||
|
||||
switch (wc = *wpat++)
|
||||
{
|
||||
default:
|
||||
return (*wstring == wc);
|
||||
case L'\\':
|
||||
return (*wstring == *wpat);
|
||||
case L'?':
|
||||
return (*wpat == WLPAREN ? 1 : (*wstring != L'\0'));
|
||||
case L'*':
|
||||
return (1);
|
||||
case L'+':
|
||||
case L'!':
|
||||
case L'@':
|
||||
return (*wpat == WLPAREN ? 1 : (*wstring == wc));
|
||||
case L'[':
|
||||
return (*wstring != L'\0');
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
wmatchlen (wpat, wmax)
|
||||
wchar_t *wpat;
|
||||
size_t wmax;
|
||||
{
|
||||
wchar_t wc;
|
||||
int matlen, bracklen, t, in_cclass, in_collsym, in_equiv;
|
||||
|
||||
if (*wpat == 0)
|
||||
return (0);
|
||||
|
||||
matlen = in_cclass = in_collsym = in_equiv = 0;
|
||||
while (wc = *wpat++)
|
||||
{
|
||||
switch (wc)
|
||||
{
|
||||
default:
|
||||
matlen++;
|
||||
break;
|
||||
case L'\\':
|
||||
if (*wpat == 0)
|
||||
return ++matlen;
|
||||
else
|
||||
{
|
||||
matlen++;
|
||||
wpat++;
|
||||
}
|
||||
break;
|
||||
case L'?':
|
||||
if (*wpat == WLPAREN)
|
||||
return (matlen = -1); /* XXX for now */
|
||||
else
|
||||
matlen++;
|
||||
break;
|
||||
case L'*':
|
||||
return (matlen = -1);
|
||||
case L'+':
|
||||
case L'!':
|
||||
case L'@':
|
||||
if (*wpat == WLPAREN)
|
||||
return (matlen = -1); /* XXX for now */
|
||||
else
|
||||
matlen++;
|
||||
break;
|
||||
case L'[':
|
||||
/* scan for ending `]', skipping over embedded [:...:] */
|
||||
bracklen = 1;
|
||||
wc = *wpat++;
|
||||
do
|
||||
{
|
||||
if (wc == 0)
|
||||
{
|
||||
wpat--; /* back up to NUL */
|
||||
matlen += bracklen;
|
||||
goto bad_bracket;
|
||||
}
|
||||
else if (wc == L'\\')
|
||||
{
|
||||
/* *wpat == backslash-escaped character */
|
||||
bracklen++;
|
||||
/* If the backslash or backslash-escape ends the string,
|
||||
bail. The ++wpat skips over the backslash escape */
|
||||
if (*wpat == 0 || *++wpat == 0)
|
||||
{
|
||||
matlen += bracklen;
|
||||
goto bad_bracket;
|
||||
}
|
||||
}
|
||||
else if (wc == L'[' && *wpat == L':') /* character class */
|
||||
{
|
||||
wpat++;
|
||||
bracklen++;
|
||||
in_cclass = 1;
|
||||
}
|
||||
else if (in_cclass && wc == L':' && *wpat == L']')
|
||||
{
|
||||
wpat++;
|
||||
bracklen++;
|
||||
in_cclass = 0;
|
||||
}
|
||||
else if (wc == L'[' && *wpat == L'.') /* collating symbol */
|
||||
{
|
||||
wpat++;
|
||||
bracklen++;
|
||||
if (*wpat == L']') /* right bracket can appear as collating symbol */
|
||||
{
|
||||
wpat++;
|
||||
bracklen++;
|
||||
}
|
||||
in_collsym = 1;
|
||||
}
|
||||
else if (in_collsym && wc == L'.' && *wpat == L']')
|
||||
{
|
||||
wpat++;
|
||||
bracklen++;
|
||||
in_collsym = 0;
|
||||
}
|
||||
else if (wc == L'[' && *wpat == L'=') /* equivalence class */
|
||||
{
|
||||
wpat++;
|
||||
bracklen++;
|
||||
if (*wpat == L']') /* right bracket can appear as equivalence class */
|
||||
{
|
||||
wpat++;
|
||||
bracklen++;
|
||||
}
|
||||
in_equiv = 1;
|
||||
}
|
||||
else if (in_equiv && wc == L'=' && *wpat == L']')
|
||||
{
|
||||
wpat++;
|
||||
bracklen++;
|
||||
in_equiv = 0;
|
||||
}
|
||||
else
|
||||
bracklen++;
|
||||
}
|
||||
while ((wc = *wpat++) != L']');
|
||||
matlen++; /* bracket expression can only match one char */
|
||||
bad_bracket:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return matlen;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
extglob_pattern_p (pat)
|
||||
char *pat;
|
||||
{
|
||||
switch (pat[0])
|
||||
{
|
||||
case '*':
|
||||
case '+':
|
||||
case '!':
|
||||
case '@':
|
||||
case '?':
|
||||
return (pat[1] == LPAREN);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return 1 of the first character of STRING could match the first
|
||||
character of pattern PAT. Used to avoid n2 calls to strmatch(). */
|
||||
int
|
||||
match_pattern_char (pat, string)
|
||||
char *pat, *string;
|
||||
{
|
||||
char c;
|
||||
|
||||
if (*string == 0)
|
||||
return (0);
|
||||
|
||||
switch (c = *pat++)
|
||||
{
|
||||
default:
|
||||
return (*string == c);
|
||||
case '\\':
|
||||
return (*string == *pat);
|
||||
case '?':
|
||||
return (*pat == LPAREN ? 1 : (*string != '\0'));
|
||||
case '*':
|
||||
return (1);
|
||||
case '+':
|
||||
case '!':
|
||||
case '@':
|
||||
return (*pat == LPAREN ? 1 : (*string == c));
|
||||
case '[':
|
||||
return (*string != '\0');
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
umatchlen (pat, max)
|
||||
char *pat;
|
||||
size_t max;
|
||||
{
|
||||
char c;
|
||||
int matlen, bracklen, t, in_cclass, in_collsym, in_equiv;
|
||||
|
||||
if (*pat == 0)
|
||||
return (0);
|
||||
|
||||
matlen = in_cclass = in_collsym = in_equiv = 0;
|
||||
while (c = *pat++)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
default:
|
||||
matlen++;
|
||||
break;
|
||||
case '\\':
|
||||
if (*pat == 0)
|
||||
return ++matlen;
|
||||
else
|
||||
{
|
||||
matlen++;
|
||||
pat++;
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
if (*pat == LPAREN)
|
||||
return (matlen = -1); /* XXX for now */
|
||||
else
|
||||
matlen++;
|
||||
break;
|
||||
case '*':
|
||||
return (matlen = -1);
|
||||
case '+':
|
||||
case '!':
|
||||
case '@':
|
||||
if (*pat == LPAREN)
|
||||
return (matlen = -1); /* XXX for now */
|
||||
else
|
||||
matlen++;
|
||||
break;
|
||||
case '[':
|
||||
/* scan for ending `]', skipping over embedded [:...:] */
|
||||
bracklen = 1;
|
||||
c = *pat++;
|
||||
do
|
||||
{
|
||||
if (c == 0)
|
||||
{
|
||||
pat--; /* back up to NUL */
|
||||
matlen += bracklen;
|
||||
goto bad_bracket;
|
||||
}
|
||||
else if (c == '\\')
|
||||
{
|
||||
/* *pat == backslash-escaped character */
|
||||
bracklen++;
|
||||
/* If the backslash or backslash-escape ends the string,
|
||||
bail. The ++pat skips over the backslash escape */
|
||||
if (*pat == 0 || *++pat == 0)
|
||||
{
|
||||
matlen += bracklen;
|
||||
goto bad_bracket;
|
||||
}
|
||||
}
|
||||
else if (c == '[' && *pat == ':') /* character class */
|
||||
{
|
||||
pat++;
|
||||
bracklen++;
|
||||
in_cclass = 1;
|
||||
}
|
||||
else if (in_cclass && c == ':' && *pat == ']')
|
||||
{
|
||||
pat++;
|
||||
bracklen++;
|
||||
in_cclass = 0;
|
||||
}
|
||||
else if (c == '[' && *pat == '.') /* collating symbol */
|
||||
{
|
||||
pat++;
|
||||
bracklen++;
|
||||
if (*pat == ']') /* right bracket can appear as collating symbol */
|
||||
{
|
||||
pat++;
|
||||
bracklen++;
|
||||
}
|
||||
in_collsym = 1;
|
||||
}
|
||||
else if (in_collsym && c == '.' && *pat == ']')
|
||||
{
|
||||
pat++;
|
||||
bracklen++;
|
||||
in_collsym = 0;
|
||||
}
|
||||
else if (c == '[' && *pat == '=') /* equivalence class */
|
||||
{
|
||||
pat++;
|
||||
bracklen++;
|
||||
if (*pat == ']') /* right bracket can appear as equivalence class */
|
||||
{
|
||||
pat++;
|
||||
bracklen++;
|
||||
}
|
||||
in_equiv = 1;
|
||||
}
|
||||
else if (in_equiv && c == '=' && *pat == ']')
|
||||
{
|
||||
pat++;
|
||||
bracklen++;
|
||||
in_equiv = 0;
|
||||
}
|
||||
else
|
||||
bracklen++;
|
||||
}
|
||||
while ((c = *pat++) != ']');
|
||||
matlen++; /* bracket expression can only match one char */
|
||||
bad_bracket:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return matlen;
|
||||
}
|
||||
|
||||
/* Skip characters in PAT and return the final occurrence of DIRSEP. This
|
||||
is only called when extended_glob is set, so we have to skip over extglob
|
||||
patterns x(...) */
|
||||
char *
|
||||
glob_dirscan (pat, dirsep)
|
||||
char *pat;
|
||||
int dirsep;
|
||||
{
|
||||
char *p, *d, *pe, *se;
|
||||
|
||||
d = pe = se = 0;
|
||||
for (p = pat; p && *p; p++)
|
||||
{
|
||||
if (extglob_pattern_p (p))
|
||||
{
|
||||
if (se == 0)
|
||||
se = p + strlen (p) - 1;
|
||||
pe = glob_patscan (p + 2, se, 0);
|
||||
if (pe == 0)
|
||||
continue;
|
||||
else if (*pe == 0)
|
||||
break;
|
||||
p = pe - 1; /* will do increment above */
|
||||
continue;
|
||||
}
|
||||
if (*p == dirsep)
|
||||
d = p;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
# This makefile for Readline library documentation is in -*- text -*- mode.
|
||||
# Emacs likes it that way.
|
||||
RM = rm -f
|
||||
|
||||
MAKEINFO = makeinfo
|
||||
TEXI2DVI = texi2dvi
|
||||
TEXI2HTML = texi2html
|
||||
QUIETPS = #set this to -q to shut up dvips
|
||||
DVIPS = dvips -D 300 $(QUIETPS) -o $@ # tricky
|
||||
|
||||
INSTALL_DATA = cp
|
||||
infodir = /usr/local/info
|
||||
|
||||
RLSRC = rlman.texinfo rluser.texinfo rltech.texinfo
|
||||
HISTSRC = hist.texinfo hsuser.texinfo hstech.texinfo
|
||||
|
||||
DVIOBJ = readline.dvi history.dvi
|
||||
INFOOBJ = readline.info history.info
|
||||
PSOBJ = readline.ps history.ps
|
||||
HTMLOBJ = readline.html history.html
|
||||
|
||||
all: info dvi html ps
|
||||
nodvi: info html
|
||||
|
||||
readline.dvi: $(RLSRC)
|
||||
$(TEXI2DVI) rlman.texinfo
|
||||
mv rlman.dvi readline.dvi
|
||||
|
||||
readline.info: $(RLSRC)
|
||||
$(MAKEINFO) --no-split -o $@ rlman.texinfo
|
||||
|
||||
history.dvi: ${HISTSRC}
|
||||
$(TEXI2DVI) hist.texinfo
|
||||
mv hist.dvi history.dvi
|
||||
|
||||
history.info: ${HISTSRC}
|
||||
$(MAKEINFO) --no-split -o $@ hist.texinfo
|
||||
|
||||
readline.ps: readline.dvi
|
||||
$(RM) $@
|
||||
$(DVIPS) readline.dvi
|
||||
|
||||
history.ps: history.dvi
|
||||
$(RM) $@
|
||||
$(DVIPS) history.dvi
|
||||
|
||||
readline.html: ${RLSRC}
|
||||
$(TEXI2HTML) rlman.texinfo
|
||||
sed -e 's:rlman.html:readline.html:' -e 's:rlman_toc.html:readline_toc.html:' rlman.html > readline.html
|
||||
sed -e 's:rlman.html:readline.html:' -e 's:rlman_toc.html:readline_toc.html:' rlman_toc.html > readline_toc.html
|
||||
$(RM) rlman.html rlman_toc.html
|
||||
|
||||
history.html: ${HISTSRC}
|
||||
$(TEXI2HTML) hist.texinfo
|
||||
sed -e 's:hist.html:history.html:' -e 's:hist_toc.html:history_toc.html:' hist.html > history.html
|
||||
sed -e 's:hist.html:history.html:' -e 's:hist_toc.html:history_toc.html:' hist_toc.html > history_toc.html
|
||||
$(RM) hist.html hist_toc.html
|
||||
|
||||
info: $(INFOOBJ)
|
||||
dvi: $(DVIOBJ)
|
||||
ps: $(PSOBJ)
|
||||
html: $(HTMLOBJ)
|
||||
|
||||
clean:
|
||||
$(RM) *.aux *.cp *.fn *.ky *.log *.pg *.toc *.tp *.vr *.cps *.pgs \
|
||||
*.fns *.kys *.tps *.vrs *.o core
|
||||
|
||||
distclean: clean
|
||||
mostlyclean: clean
|
||||
|
||||
maintainer-clean: clean
|
||||
$(RM) *.dvi *.info *.info-* *.ps *.html
|
||||
|
||||
install: info
|
||||
${INSTALL_DATA} readline.info $(infodir)/readline.info
|
||||
${INSTALL_DATA} history.info $(infodir)/history.info
|
||||
@@ -695,6 +695,7 @@ make_redirection (source, instruction, dest_and_filename, flags)
|
||||
/* First do the common cases. */
|
||||
temp->redirector = source;
|
||||
temp->redirectee = dest_and_filename;
|
||||
temp->here_doc_eof = 0;
|
||||
temp->instruction = instruction;
|
||||
temp->flags = 0;
|
||||
temp->rflags = flags;
|
||||
|
||||
+894
@@ -0,0 +1,894 @@
|
||||
/* make_cmd.c -- Functions for making instances of the various
|
||||
parser constructs. */
|
||||
|
||||
/* Copyright (C) 1989-2009 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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "bashtypes.h"
|
||||
#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
|
||||
# include <sys/file.h>
|
||||
#endif
|
||||
#include "filecntl.h"
|
||||
#include "bashansi.h"
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "bashintl.h"
|
||||
|
||||
#include "parser.h"
|
||||
#include "syntax.h"
|
||||
#include "command.h"
|
||||
#include "general.h"
|
||||
#include "error.h"
|
||||
#include "flags.h"
|
||||
#include "make_cmd.h"
|
||||
#include "dispose_cmd.h"
|
||||
#include "variables.h"
|
||||
#include "subst.h"
|
||||
#include "input.h"
|
||||
#include "ocache.h"
|
||||
#include "externs.h"
|
||||
|
||||
#if defined (JOB_CONTROL)
|
||||
#include "jobs.h"
|
||||
#endif
|
||||
|
||||
#include "shmbutil.h"
|
||||
|
||||
extern int line_number, current_command_line_count, parser_state;
|
||||
extern int last_command_exit_value;
|
||||
|
||||
/* Object caching */
|
||||
sh_obj_cache_t wdcache = {0, 0, 0};
|
||||
sh_obj_cache_t wlcache = {0, 0, 0};
|
||||
|
||||
#define WDCACHESIZE 60
|
||||
#define WLCACHESIZE 60
|
||||
|
||||
static COMMAND *make_for_or_select __P((enum command_type, WORD_DESC *, WORD_LIST *, COMMAND *, int));
|
||||
#if defined (ARITH_FOR_COMMAND)
|
||||
static WORD_LIST *make_arith_for_expr __P((char *));
|
||||
#endif
|
||||
static COMMAND *make_until_or_while __P((enum command_type, COMMAND *, COMMAND *));
|
||||
|
||||
void
|
||||
cmd_init ()
|
||||
{
|
||||
ocache_create (wdcache, WORD_DESC, WDCACHESIZE);
|
||||
ocache_create (wlcache, WORD_LIST, WLCACHESIZE);
|
||||
}
|
||||
|
||||
WORD_DESC *
|
||||
alloc_word_desc ()
|
||||
{
|
||||
WORD_DESC *temp;
|
||||
|
||||
ocache_alloc (wdcache, WORD_DESC, temp);
|
||||
temp->flags = 0;
|
||||
temp->word = 0;
|
||||
return temp;
|
||||
}
|
||||
|
||||
WORD_DESC *
|
||||
make_bare_word (string)
|
||||
const char *string;
|
||||
{
|
||||
WORD_DESC *temp;
|
||||
|
||||
temp = alloc_word_desc ();
|
||||
|
||||
if (*string)
|
||||
temp->word = savestring (string);
|
||||
else
|
||||
{
|
||||
temp->word = (char *)xmalloc (1);
|
||||
temp->word[0] = '\0';
|
||||
}
|
||||
|
||||
return (temp);
|
||||
}
|
||||
|
||||
WORD_DESC *
|
||||
make_word_flags (w, string)
|
||||
WORD_DESC *w;
|
||||
const char *string;
|
||||
{
|
||||
register int i;
|
||||
size_t slen;
|
||||
DECLARE_MBSTATE;
|
||||
|
||||
i = 0;
|
||||
slen = strlen (string);
|
||||
while (i < slen)
|
||||
{
|
||||
switch (string[i])
|
||||
{
|
||||
case '$':
|
||||
w->flags |= W_HASDOLLAR;
|
||||
break;
|
||||
case '\\':
|
||||
break; /* continue the loop */
|
||||
case '\'':
|
||||
case '`':
|
||||
case '"':
|
||||
w->flags |= W_QUOTED;
|
||||
break;
|
||||
}
|
||||
|
||||
ADVANCE_CHAR (string, slen, i);
|
||||
}
|
||||
|
||||
return (w);
|
||||
}
|
||||
|
||||
WORD_DESC *
|
||||
make_word (string)
|
||||
const char *string;
|
||||
{
|
||||
WORD_DESC *temp;
|
||||
|
||||
temp = make_bare_word (string);
|
||||
return (make_word_flags (temp, string));
|
||||
}
|
||||
|
||||
WORD_DESC *
|
||||
make_word_from_token (token)
|
||||
int token;
|
||||
{
|
||||
char tokenizer[2];
|
||||
|
||||
tokenizer[0] = token;
|
||||
tokenizer[1] = '\0';
|
||||
|
||||
return (make_word (tokenizer));
|
||||
}
|
||||
|
||||
WORD_LIST *
|
||||
make_word_list (word, wlink)
|
||||
WORD_DESC *word;
|
||||
WORD_LIST *wlink;
|
||||
{
|
||||
WORD_LIST *temp;
|
||||
|
||||
ocache_alloc (wlcache, WORD_LIST, temp);
|
||||
|
||||
temp->word = word;
|
||||
temp->next = wlink;
|
||||
return (temp);
|
||||
}
|
||||
|
||||
COMMAND *
|
||||
make_command (type, pointer)
|
||||
enum command_type type;
|
||||
SIMPLE_COM *pointer;
|
||||
{
|
||||
COMMAND *temp;
|
||||
|
||||
temp = (COMMAND *)xmalloc (sizeof (COMMAND));
|
||||
temp->type = type;
|
||||
temp->value.Simple = pointer;
|
||||
temp->value.Simple->flags = temp->flags = 0;
|
||||
temp->redirects = (REDIRECT *)NULL;
|
||||
return (temp);
|
||||
}
|
||||
|
||||
COMMAND *
|
||||
command_connect (com1, com2, connector)
|
||||
COMMAND *com1, *com2;
|
||||
int connector;
|
||||
{
|
||||
CONNECTION *temp;
|
||||
|
||||
temp = (CONNECTION *)xmalloc (sizeof (CONNECTION));
|
||||
temp->connector = connector;
|
||||
temp->first = com1;
|
||||
temp->second = com2;
|
||||
return (make_command (cm_connection, (SIMPLE_COM *)temp));
|
||||
}
|
||||
|
||||
static COMMAND *
|
||||
make_for_or_select (type, name, map_list, action, lineno)
|
||||
enum command_type type;
|
||||
WORD_DESC *name;
|
||||
WORD_LIST *map_list;
|
||||
COMMAND *action;
|
||||
int lineno;
|
||||
{
|
||||
FOR_COM *temp;
|
||||
|
||||
temp = (FOR_COM *)xmalloc (sizeof (FOR_COM));
|
||||
temp->flags = 0;
|
||||
temp->name = name;
|
||||
temp->line = lineno;
|
||||
temp->map_list = map_list;
|
||||
temp->action = action;
|
||||
return (make_command (type, (SIMPLE_COM *)temp));
|
||||
}
|
||||
|
||||
COMMAND *
|
||||
make_for_command (name, map_list, action, lineno)
|
||||
WORD_DESC *name;
|
||||
WORD_LIST *map_list;
|
||||
COMMAND *action;
|
||||
int lineno;
|
||||
{
|
||||
return (make_for_or_select (cm_for, name, map_list, action, lineno));
|
||||
}
|
||||
|
||||
COMMAND *
|
||||
make_select_command (name, map_list, action, lineno)
|
||||
WORD_DESC *name;
|
||||
WORD_LIST *map_list;
|
||||
COMMAND *action;
|
||||
int lineno;
|
||||
{
|
||||
#if defined (SELECT_COMMAND)
|
||||
return (make_for_or_select (cm_select, name, map_list, action, lineno));
|
||||
#else
|
||||
last_command_exit_value = 2;
|
||||
return ((COMMAND *)NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined (ARITH_FOR_COMMAND)
|
||||
static WORD_LIST *
|
||||
make_arith_for_expr (s)
|
||||
char *s;
|
||||
{
|
||||
WORD_LIST *result;
|
||||
WORD_DESC *wd;
|
||||
|
||||
if (s == 0 || *s == '\0')
|
||||
return ((WORD_LIST *)NULL);
|
||||
wd = make_word (s);
|
||||
wd->flags |= W_NOGLOB|W_NOSPLIT|W_QUOTED|W_DQUOTE; /* no word splitting or globbing */
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
wd->flags |= W_NOPROCSUB; /* no process substitution */
|
||||
#endif
|
||||
result = make_word_list (wd, (WORD_LIST *)NULL);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Note that this function calls dispose_words on EXPRS, since it doesn't
|
||||
use the word list directly. We free it here rather than at the caller
|
||||
because no other function in this file requires that the caller free
|
||||
any arguments. */
|
||||
COMMAND *
|
||||
make_arith_for_command (exprs, action, lineno)
|
||||
WORD_LIST *exprs;
|
||||
COMMAND *action;
|
||||
int lineno;
|
||||
{
|
||||
#if defined (ARITH_FOR_COMMAND)
|
||||
ARITH_FOR_COM *temp;
|
||||
WORD_LIST *init, *test, *step;
|
||||
char *s, *t, *start;
|
||||
int nsemi, i;
|
||||
|
||||
init = test = step = (WORD_LIST *)NULL;
|
||||
/* Parse the string into the three component sub-expressions. */
|
||||
start = t = s = exprs->word->word;
|
||||
for (nsemi = 0; ;)
|
||||
{
|
||||
/* skip whitespace at the start of each sub-expression. */
|
||||
while (whitespace (*s))
|
||||
s++;
|
||||
start = s;
|
||||
/* skip to the semicolon or EOS */
|
||||
i = skip_to_delim (start, 0, ";", SD_NOJMP|SD_NOPROCSUB);
|
||||
s = start + i;
|
||||
|
||||
t = (i > 0) ? substring (start, 0, i) : (char *)NULL;
|
||||
|
||||
nsemi++;
|
||||
switch (nsemi)
|
||||
{
|
||||
case 1:
|
||||
init = make_arith_for_expr (t);
|
||||
break;
|
||||
case 2:
|
||||
test = make_arith_for_expr (t);
|
||||
break;
|
||||
case 3:
|
||||
step = make_arith_for_expr (t);
|
||||
break;
|
||||
}
|
||||
|
||||
FREE (t);
|
||||
if (*s == '\0')
|
||||
break;
|
||||
s++; /* skip over semicolon */
|
||||
}
|
||||
|
||||
if (nsemi != 3)
|
||||
{
|
||||
if (nsemi < 3)
|
||||
parser_error (lineno, _("syntax error: arithmetic expression required"));
|
||||
else
|
||||
parser_error (lineno, _("syntax error: `;' unexpected"));
|
||||
parser_error (lineno, _("syntax error: `((%s))'"), exprs->word->word);
|
||||
free (init);
|
||||
free (test);
|
||||
free (step);
|
||||
last_command_exit_value = 2;
|
||||
return ((COMMAND *)NULL);
|
||||
}
|
||||
|
||||
temp = (ARITH_FOR_COM *)xmalloc (sizeof (ARITH_FOR_COM));
|
||||
temp->flags = 0;
|
||||
temp->line = lineno;
|
||||
temp->init = init ? init : make_arith_for_expr ("1");
|
||||
temp->test = test ? test : make_arith_for_expr ("1");
|
||||
temp->step = step ? step : make_arith_for_expr ("1");
|
||||
temp->action = action;
|
||||
|
||||
dispose_words (exprs);
|
||||
return (make_command (cm_arith_for, (SIMPLE_COM *)temp));
|
||||
#else
|
||||
dispose_words (exprs);
|
||||
last_command_exit_value = 2;
|
||||
return ((COMMAND *)NULL);
|
||||
#endif /* ARITH_FOR_COMMAND */
|
||||
}
|
||||
|
||||
COMMAND *
|
||||
make_group_command (command)
|
||||
COMMAND *command;
|
||||
{
|
||||
GROUP_COM *temp;
|
||||
|
||||
temp = (GROUP_COM *)xmalloc (sizeof (GROUP_COM));
|
||||
temp->command = command;
|
||||
return (make_command (cm_group, (SIMPLE_COM *)temp));
|
||||
}
|
||||
|
||||
COMMAND *
|
||||
make_case_command (word, clauses, lineno)
|
||||
WORD_DESC *word;
|
||||
PATTERN_LIST *clauses;
|
||||
int lineno;
|
||||
{
|
||||
CASE_COM *temp;
|
||||
|
||||
temp = (CASE_COM *)xmalloc (sizeof (CASE_COM));
|
||||
temp->flags = 0;
|
||||
temp->line = lineno;
|
||||
temp->word = word;
|
||||
temp->clauses = REVERSE_LIST (clauses, PATTERN_LIST *);
|
||||
return (make_command (cm_case, (SIMPLE_COM *)temp));
|
||||
}
|
||||
|
||||
PATTERN_LIST *
|
||||
make_pattern_list (patterns, action)
|
||||
WORD_LIST *patterns;
|
||||
COMMAND *action;
|
||||
{
|
||||
PATTERN_LIST *temp;
|
||||
|
||||
temp = (PATTERN_LIST *)xmalloc (sizeof (PATTERN_LIST));
|
||||
temp->patterns = REVERSE_LIST (patterns, WORD_LIST *);
|
||||
temp->action = action;
|
||||
temp->next = NULL;
|
||||
temp->flags = 0;
|
||||
return (temp);
|
||||
}
|
||||
|
||||
COMMAND *
|
||||
make_if_command (test, true_case, false_case)
|
||||
COMMAND *test, *true_case, *false_case;
|
||||
{
|
||||
IF_COM *temp;
|
||||
|
||||
temp = (IF_COM *)xmalloc (sizeof (IF_COM));
|
||||
temp->flags = 0;
|
||||
temp->test = test;
|
||||
temp->true_case = true_case;
|
||||
temp->false_case = false_case;
|
||||
return (make_command (cm_if, (SIMPLE_COM *)temp));
|
||||
}
|
||||
|
||||
static COMMAND *
|
||||
make_until_or_while (which, test, action)
|
||||
enum command_type which;
|
||||
COMMAND *test, *action;
|
||||
{
|
||||
WHILE_COM *temp;
|
||||
|
||||
temp = (WHILE_COM *)xmalloc (sizeof (WHILE_COM));
|
||||
temp->flags = 0;
|
||||
temp->test = test;
|
||||
temp->action = action;
|
||||
return (make_command (which, (SIMPLE_COM *)temp));
|
||||
}
|
||||
|
||||
COMMAND *
|
||||
make_while_command (test, action)
|
||||
COMMAND *test, *action;
|
||||
{
|
||||
return (make_until_or_while (cm_while, test, action));
|
||||
}
|
||||
|
||||
COMMAND *
|
||||
make_until_command (test, action)
|
||||
COMMAND *test, *action;
|
||||
{
|
||||
return (make_until_or_while (cm_until, test, action));
|
||||
}
|
||||
|
||||
COMMAND *
|
||||
make_arith_command (exp)
|
||||
WORD_LIST *exp;
|
||||
{
|
||||
#if defined (DPAREN_ARITHMETIC)
|
||||
COMMAND *command;
|
||||
ARITH_COM *temp;
|
||||
|
||||
command = (COMMAND *)xmalloc (sizeof (COMMAND));
|
||||
command->value.Arith = temp = (ARITH_COM *)xmalloc (sizeof (ARITH_COM));
|
||||
|
||||
temp->flags = 0;
|
||||
temp->line = line_number;
|
||||
temp->exp = exp;
|
||||
|
||||
command->type = cm_arith;
|
||||
command->redirects = (REDIRECT *)NULL;
|
||||
command->flags = 0;
|
||||
|
||||
return (command);
|
||||
#else
|
||||
last_command_exit_value = 2;
|
||||
return ((COMMAND *)NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined (COND_COMMAND)
|
||||
struct cond_com *
|
||||
make_cond_node (type, op, left, right)
|
||||
int type;
|
||||
WORD_DESC *op;
|
||||
struct cond_com *left, *right;
|
||||
{
|
||||
COND_COM *temp;
|
||||
|
||||
temp = (COND_COM *)xmalloc (sizeof (COND_COM));
|
||||
temp->flags = 0;
|
||||
temp->line = line_number;
|
||||
temp->type = type;
|
||||
temp->op = op;
|
||||
temp->left = left;
|
||||
temp->right = right;
|
||||
|
||||
return (temp);
|
||||
}
|
||||
#endif
|
||||
|
||||
COMMAND *
|
||||
make_cond_command (cond_node)
|
||||
COND_COM *cond_node;
|
||||
{
|
||||
#if defined (COND_COMMAND)
|
||||
COMMAND *command;
|
||||
|
||||
command = (COMMAND *)xmalloc (sizeof (COMMAND));
|
||||
command->value.Cond = cond_node;
|
||||
|
||||
command->type = cm_cond;
|
||||
command->redirects = (REDIRECT *)NULL;
|
||||
command->flags = 0;
|
||||
command->line = cond_node ? cond_node->line : 0;
|
||||
|
||||
return (command);
|
||||
#else
|
||||
last_command_exit_value = 2;
|
||||
return ((COMMAND *)NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
COMMAND *
|
||||
make_bare_simple_command ()
|
||||
{
|
||||
COMMAND *command;
|
||||
SIMPLE_COM *temp;
|
||||
|
||||
command = (COMMAND *)xmalloc (sizeof (COMMAND));
|
||||
command->value.Simple = temp = (SIMPLE_COM *)xmalloc (sizeof (SIMPLE_COM));
|
||||
|
||||
temp->flags = 0;
|
||||
temp->line = line_number;
|
||||
temp->words = (WORD_LIST *)NULL;
|
||||
temp->redirects = (REDIRECT *)NULL;
|
||||
|
||||
command->type = cm_simple;
|
||||
command->redirects = (REDIRECT *)NULL;
|
||||
command->flags = 0;
|
||||
|
||||
return (command);
|
||||
}
|
||||
|
||||
/* Return a command which is the connection of the word or redirection
|
||||
in ELEMENT, and the command * or NULL in COMMAND. */
|
||||
COMMAND *
|
||||
make_simple_command (element, command)
|
||||
ELEMENT element;
|
||||
COMMAND *command;
|
||||
{
|
||||
/* If we are starting from scratch, then make the initial command
|
||||
structure. Also note that we have to fill in all the slots, since
|
||||
malloc doesn't return zeroed space. */
|
||||
if (command == 0)
|
||||
{
|
||||
command = make_bare_simple_command ();
|
||||
parser_state |= PST_REDIRLIST;
|
||||
}
|
||||
|
||||
if (element.word)
|
||||
{
|
||||
command->value.Simple->words = make_word_list (element.word, command->value.Simple->words);
|
||||
parser_state &= ~PST_REDIRLIST;
|
||||
}
|
||||
else if (element.redirect)
|
||||
{
|
||||
REDIRECT *r = element.redirect;
|
||||
/* Due to the way <> is implemented, there may be more than a single
|
||||
redirection in element.redirect. We just follow the chain as far
|
||||
as it goes, and hook onto the end. */
|
||||
while (r->next)
|
||||
r = r->next;
|
||||
r->next = command->value.Simple->redirects;
|
||||
command->value.Simple->redirects = element.redirect;
|
||||
}
|
||||
|
||||
return (command);
|
||||
}
|
||||
|
||||
/* Because we are Bourne compatible, we read the input for this
|
||||
<< or <<- redirection now, from wherever input is coming from.
|
||||
We store the input read into a WORD_DESC. Replace the text of
|
||||
the redirectee.word with the new input text. If <<- is on,
|
||||
then remove leading TABS from each line. */
|
||||
void
|
||||
make_here_document (temp, lineno)
|
||||
REDIRECT *temp;
|
||||
int lineno;
|
||||
{
|
||||
int kill_leading, redir_len;
|
||||
char *redir_word, *document, *full_line;
|
||||
int document_index, document_size, delim_unquoted;
|
||||
|
||||
if (temp->instruction != r_deblank_reading_until &&
|
||||
temp->instruction != r_reading_until)
|
||||
{
|
||||
internal_error (_("make_here_document: bad instruction type %d"), temp->instruction);
|
||||
return;
|
||||
}
|
||||
|
||||
kill_leading = temp->instruction == r_deblank_reading_until;
|
||||
|
||||
document = (char *)NULL;
|
||||
document_index = document_size = 0;
|
||||
|
||||
/* Quote removal is the only expansion performed on the delimiter
|
||||
for here documents, making it an extremely special case. */
|
||||
redir_word = string_quote_removal (temp->redirectee.filename->word, 0);
|
||||
|
||||
/* redirection_expand will return NULL if the expansion results in
|
||||
multiple words or no words. Check for that here, and just abort
|
||||
this here document if it does. */
|
||||
if (redir_word)
|
||||
redir_len = strlen (redir_word);
|
||||
else
|
||||
{
|
||||
temp->here_doc_eof = (char *)xmalloc (1);
|
||||
temp->here_doc_eof[0] = '\0';
|
||||
goto document_done;
|
||||
}
|
||||
|
||||
free (temp->redirectee.filename->word);
|
||||
temp->here_doc_eof = redir_word;
|
||||
|
||||
/* Read lines from wherever lines are coming from.
|
||||
For each line read, if kill_leading, then kill the
|
||||
leading tab characters.
|
||||
If the line matches redir_word exactly, then we have
|
||||
manufactured the document. Otherwise, add the line to the
|
||||
list of lines in the document. */
|
||||
|
||||
/* If the here-document delimiter was quoted, the lines should
|
||||
be read verbatim from the input. If it was not quoted, we
|
||||
need to perform backslash-quoted newline removal. */
|
||||
delim_unquoted = (temp->redirectee.filename->flags & W_QUOTED) == 0;
|
||||
while (full_line = read_secondary_line (delim_unquoted))
|
||||
{
|
||||
register char *line;
|
||||
int len;
|
||||
|
||||
line = full_line;
|
||||
line_number++;
|
||||
|
||||
/* If set -v is in effect, echo the line read. read_secondary_line/
|
||||
read_a_line leaves the newline at the end, so don't print another. */
|
||||
if (echo_input_at_read)
|
||||
fprintf (stderr, "%s", line);
|
||||
|
||||
if (kill_leading && *line)
|
||||
{
|
||||
/* Hack: To be compatible with some Bourne shells, we
|
||||
check the word before stripping the whitespace. This
|
||||
is a hack, though. */
|
||||
if (STREQN (line, redir_word, redir_len) && line[redir_len] == '\n')
|
||||
goto document_done;
|
||||
|
||||
while (*line == '\t')
|
||||
line++;
|
||||
}
|
||||
|
||||
if (*line == 0)
|
||||
continue;
|
||||
|
||||
if (STREQN (line, redir_word, redir_len) && line[redir_len] == '\n')
|
||||
goto document_done;
|
||||
|
||||
len = strlen (line);
|
||||
if (len + document_index >= document_size)
|
||||
{
|
||||
document_size = document_size ? 2 * (document_size + len) : len + 2;
|
||||
document = (char *)xrealloc (document, document_size);
|
||||
}
|
||||
|
||||
/* len is guaranteed to be > 0 because of the check for line
|
||||
being an empty string before the call to strlen. */
|
||||
FASTCOPY (line, document + document_index, len);
|
||||
document_index += len;
|
||||
}
|
||||
|
||||
if (full_line == 0)
|
||||
internal_warning (_("here-document at line %d delimited by end-of-file (wanted `%s')"), lineno, redir_word);
|
||||
|
||||
document_done:
|
||||
if (document)
|
||||
document[document_index] = '\0';
|
||||
else
|
||||
{
|
||||
document = (char *)xmalloc (1);
|
||||
document[0] = '\0';
|
||||
}
|
||||
temp->redirectee.filename->word = document;
|
||||
}
|
||||
|
||||
/* Generate a REDIRECT from SOURCE, DEST, and INSTRUCTION.
|
||||
INSTRUCTION is the instruction type, SOURCE is a file descriptor,
|
||||
and DEST is a file descriptor or a WORD_DESC *. */
|
||||
REDIRECT *
|
||||
make_redirection (source, instruction, dest_and_filename, flags)
|
||||
REDIRECTEE source;
|
||||
enum r_instruction instruction;
|
||||
REDIRECTEE dest_and_filename;
|
||||
int flags;
|
||||
{
|
||||
REDIRECT *temp;
|
||||
WORD_DESC *w;
|
||||
int wlen;
|
||||
intmax_t lfd;
|
||||
|
||||
temp = (REDIRECT *)xmalloc (sizeof (REDIRECT));
|
||||
|
||||
/* First do the common cases. */
|
||||
temp->redirector = source;
|
||||
temp->redirectee = dest_and_filename;
|
||||
temp->instruction = instruction;
|
||||
temp->flags = 0;
|
||||
temp->rflags = flags;
|
||||
temp->next = (REDIRECT *)NULL;
|
||||
|
||||
switch (instruction)
|
||||
{
|
||||
|
||||
case r_output_direction: /* >foo */
|
||||
case r_output_force: /* >| foo */
|
||||
case r_err_and_out: /* &>filename */
|
||||
temp->flags = O_TRUNC | O_WRONLY | O_CREAT;
|
||||
break;
|
||||
|
||||
case r_appending_to: /* >>foo */
|
||||
case r_append_err_and_out: /* &>> filename */
|
||||
temp->flags = O_APPEND | O_WRONLY | O_CREAT;
|
||||
break;
|
||||
|
||||
case r_input_direction: /* <foo */
|
||||
case r_inputa_direction: /* foo & makes this. */
|
||||
temp->flags = O_RDONLY;
|
||||
break;
|
||||
|
||||
case r_input_output: /* <>foo */
|
||||
temp->flags = O_RDWR | O_CREAT;
|
||||
break;
|
||||
|
||||
case r_deblank_reading_until: /* <<-foo */
|
||||
case r_reading_until: /* << foo */
|
||||
case r_reading_string: /* <<< foo */
|
||||
case r_close_this: /* <&- */
|
||||
case r_duplicating_input: /* 1<&2 */
|
||||
case r_duplicating_output: /* 1>&2 */
|
||||
break;
|
||||
|
||||
/* the parser doesn't pass these. */
|
||||
case r_move_input: /* 1<&2- */
|
||||
case r_move_output: /* 1>&2- */
|
||||
case r_move_input_word: /* 1<&$foo- */
|
||||
case r_move_output_word: /* 1>&$foo- */
|
||||
break;
|
||||
|
||||
/* The way the lexer works we have to do this here. */
|
||||
case r_duplicating_input_word: /* 1<&$foo */
|
||||
case r_duplicating_output_word: /* 1>&$foo */
|
||||
w = dest_and_filename.filename;
|
||||
wlen = strlen (w->word) - 1;
|
||||
if (w->word[wlen] == '-') /* Yuck */
|
||||
{
|
||||
w->word[wlen] = '\0';
|
||||
if (all_digits (w->word) && legal_number (w->word, &lfd) && lfd == (int)lfd)
|
||||
{
|
||||
dispose_word (w);
|
||||
temp->instruction = (instruction == r_duplicating_input_word) ? r_move_input : r_move_output;
|
||||
temp->redirectee.dest = lfd;
|
||||
}
|
||||
else
|
||||
temp->instruction = (instruction == r_duplicating_input_word) ? r_move_input_word : r_move_output_word;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
programming_error (_("make_redirection: redirection instruction `%d' out of range"), instruction);
|
||||
abort ();
|
||||
break;
|
||||
}
|
||||
return (temp);
|
||||
}
|
||||
|
||||
COMMAND *
|
||||
make_function_def (name, command, lineno, lstart)
|
||||
WORD_DESC *name;
|
||||
COMMAND *command;
|
||||
int lineno, lstart;
|
||||
{
|
||||
FUNCTION_DEF *temp;
|
||||
#if defined (ARRAY_VARS)
|
||||
SHELL_VAR *bash_source_v;
|
||||
ARRAY *bash_source_a;
|
||||
#endif
|
||||
|
||||
temp = (FUNCTION_DEF *)xmalloc (sizeof (FUNCTION_DEF));
|
||||
temp->command = command;
|
||||
temp->name = name;
|
||||
temp->line = lineno;
|
||||
temp->flags = 0;
|
||||
command->line = lstart;
|
||||
|
||||
/* Information used primarily for debugging. */
|
||||
temp->source_file = 0;
|
||||
#if defined (ARRAY_VARS)
|
||||
GET_ARRAY_FROM_VAR ("BASH_SOURCE", bash_source_v, bash_source_a);
|
||||
if (bash_source_a && array_num_elements (bash_source_a) > 0)
|
||||
temp->source_file = array_reference (bash_source_a, 0);
|
||||
#endif
|
||||
#if defined (DEBUGGER)
|
||||
bind_function_def (name->word, temp);
|
||||
#endif
|
||||
|
||||
temp->source_file = temp->source_file ? savestring (temp->source_file) : 0;
|
||||
return (make_command (cm_function_def, (SIMPLE_COM *)temp));
|
||||
}
|
||||
|
||||
COMMAND *
|
||||
make_subshell_command (command)
|
||||
COMMAND *command;
|
||||
{
|
||||
SUBSHELL_COM *temp;
|
||||
|
||||
temp = (SUBSHELL_COM *)xmalloc (sizeof (SUBSHELL_COM));
|
||||
temp->command = command;
|
||||
temp->flags = CMD_WANT_SUBSHELL;
|
||||
return (make_command (cm_subshell, (SIMPLE_COM *)temp));
|
||||
}
|
||||
|
||||
COMMAND *
|
||||
make_coproc_command (name, command)
|
||||
char *name;
|
||||
COMMAND *command;
|
||||
{
|
||||
COPROC_COM *temp;
|
||||
|
||||
temp = (COPROC_COM *)xmalloc (sizeof (COPROC_COM));
|
||||
temp->name = savestring (name);
|
||||
temp->command = command;
|
||||
temp->flags = CMD_WANT_SUBSHELL|CMD_COPROC_SUBSHELL;
|
||||
return (make_command (cm_coproc, (SIMPLE_COM *)temp));
|
||||
}
|
||||
|
||||
/* Reverse the word list and redirection list in the simple command
|
||||
has just been parsed. It seems simpler to do this here the one
|
||||
time then by any other method that I can think of. */
|
||||
COMMAND *
|
||||
clean_simple_command (command)
|
||||
COMMAND *command;
|
||||
{
|
||||
if (command->type != cm_simple)
|
||||
command_error ("clean_simple_command", CMDERR_BADTYPE, command->type, 0);
|
||||
else
|
||||
{
|
||||
command->value.Simple->words =
|
||||
REVERSE_LIST (command->value.Simple->words, WORD_LIST *);
|
||||
command->value.Simple->redirects =
|
||||
REVERSE_LIST (command->value.Simple->redirects, REDIRECT *);
|
||||
}
|
||||
|
||||
parser_state &= ~PST_REDIRLIST;
|
||||
return (command);
|
||||
}
|
||||
|
||||
/* The Yacc grammar productions have a problem, in that they take a
|
||||
list followed by an ampersand (`&') and do a simple command connection,
|
||||
making the entire list effectively asynchronous, instead of just
|
||||
the last command. This means that when the list is executed, all
|
||||
the commands have stdin set to /dev/null when job control is not
|
||||
active, instead of just the last. This is wrong, and needs fixing
|
||||
up. This function takes the `&' and applies it to the last command
|
||||
in the list. This is done only for lists connected by `;'; it makes
|
||||
`;' bind `tighter' than `&'. */
|
||||
COMMAND *
|
||||
connect_async_list (command, command2, connector)
|
||||
COMMAND *command, *command2;
|
||||
int connector;
|
||||
{
|
||||
COMMAND *t, *t1, *t2;
|
||||
|
||||
t1 = command;
|
||||
t = command->value.Connection->second;
|
||||
|
||||
if (!t || (command->flags & CMD_WANT_SUBSHELL) ||
|
||||
command->value.Connection->connector != ';')
|
||||
{
|
||||
t = command_connect (command, command2, connector);
|
||||
return t;
|
||||
}
|
||||
|
||||
/* This is just defensive programming. The Yacc precedence rules
|
||||
will generally hand this function a command where t points directly
|
||||
to the command we want (e.g. given a ; b ; c ; d &, t1 will point
|
||||
to the `a ; b ; c' list and t will be the `d'). We only want to do
|
||||
this if the list is not being executed as a unit in the background
|
||||
with `( ... )', so we have to check for CMD_WANT_SUBSHELL. That's
|
||||
the only way to tell. */
|
||||
while (((t->flags & CMD_WANT_SUBSHELL) == 0) && t->type == cm_connection &&
|
||||
t->value.Connection->connector == ';')
|
||||
{
|
||||
t1 = t;
|
||||
t = t->value.Connection->second;
|
||||
}
|
||||
/* Now we have t pointing to the last command in the list, and
|
||||
t1->value.Connection->second == t. */
|
||||
t2 = command_connect (t, command2, connector);
|
||||
t1->value.Connection->second = t2;
|
||||
return command;
|
||||
}
|
||||
@@ -959,7 +959,7 @@ describe_pid (pid)
|
||||
fprintf (stderr, "%ld\n", (long) pid);
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
freeze_jobs_list ()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -168,6 +168,9 @@ static char *read_a_line __P((int));
|
||||
|
||||
static int reserved_word_acceptable __P((int));
|
||||
static int yylex __P((void));
|
||||
|
||||
static void push_heredoc __P((REDIRECT *));
|
||||
static char *mk_alexpansion __P((char *));
|
||||
static int alias_expand_token __P((char *));
|
||||
static int time_command_acceptable __P((void));
|
||||
static int special_case_tokens __P((char *));
|
||||
@@ -265,7 +268,9 @@ int parser_state;
|
||||
|
||||
/* Variables to manage the task of reading here documents, because we need to
|
||||
defer the reading until after a complete command has been collected. */
|
||||
static REDIRECT *redir_stack[10];
|
||||
#define HEREDOC_MAX 16
|
||||
|
||||
static REDIRECT *redir_stack[HEREDOC_MAX];
|
||||
int need_here_doc;
|
||||
|
||||
/* Where shell input comes from. History expansion is performed on each
|
||||
@@ -307,7 +312,7 @@ static int global_extglob;
|
||||
or `for WORD' begins. This is a nested command maximum, since the array
|
||||
index is decremented after a case, select, or for command is parsed. */
|
||||
#define MAX_CASE_NEST 128
|
||||
static int word_lineno[MAX_CASE_NEST];
|
||||
static int word_lineno[MAX_CASE_NEST+1];
|
||||
static int word_top = -1;
|
||||
|
||||
/* If non-zero, it is the token that we want read_token to return
|
||||
@@ -520,42 +525,42 @@ redirection: '>' WORD
|
||||
source.dest = 0;
|
||||
redir.filename = $2;
|
||||
$$ = make_redirection (source, r_reading_until, redir, 0);
|
||||
redir_stack[need_here_doc++] = $$;
|
||||
push_heredoc ($$);
|
||||
}
|
||||
| NUMBER LESS_LESS WORD
|
||||
{
|
||||
source.dest = $1;
|
||||
redir.filename = $3;
|
||||
$$ = make_redirection (source, r_reading_until, redir, 0);
|
||||
redir_stack[need_here_doc++] = $$;
|
||||
push_heredoc ($$);
|
||||
}
|
||||
| REDIR_WORD LESS_LESS WORD
|
||||
{
|
||||
source.filename = $1;
|
||||
redir.filename = $3;
|
||||
$$ = make_redirection (source, r_reading_until, redir, REDIR_VARASSIGN);
|
||||
redir_stack[need_here_doc++] = $$;
|
||||
push_heredoc ($$);
|
||||
}
|
||||
| LESS_LESS_MINUS WORD
|
||||
{
|
||||
source.dest = 0;
|
||||
redir.filename = $2;
|
||||
$$ = make_redirection (source, r_deblank_reading_until, redir, 0);
|
||||
redir_stack[need_here_doc++] = $$;
|
||||
push_heredoc ($$);
|
||||
}
|
||||
| NUMBER LESS_LESS_MINUS WORD
|
||||
{
|
||||
source.dest = $1;
|
||||
redir.filename = $3;
|
||||
$$ = make_redirection (source, r_deblank_reading_until, redir, 0);
|
||||
redir_stack[need_here_doc++] = $$;
|
||||
push_heredoc ($$);
|
||||
}
|
||||
| REDIR_WORD LESS_LESS_MINUS WORD
|
||||
{
|
||||
source.filename = $1;
|
||||
redir.filename = $3;
|
||||
$$ = make_redirection (source, r_deblank_reading_until, redir, REDIR_VARASSIGN);
|
||||
redir_stack[need_here_doc++] = $$;
|
||||
push_heredoc ($$);
|
||||
}
|
||||
| LESS_LESS_LESS WORD
|
||||
{
|
||||
@@ -2636,6 +2641,21 @@ yylex ()
|
||||
which allow ESAC to be the next one read. */
|
||||
static int esacs_needed_count;
|
||||
|
||||
static void
|
||||
push_heredoc (r)
|
||||
REDIRECT *r;
|
||||
{
|
||||
if (need_here_doc >= HEREDOC_MAX)
|
||||
{
|
||||
last_command_exit_value = EX_BADUSAGE;
|
||||
need_here_doc = 0;
|
||||
report_syntax_error (_("maximum here-document count exceeded"));
|
||||
reset_parser ();
|
||||
exit_shell (last_command_exit_value);
|
||||
}
|
||||
redir_stack[need_here_doc++] = r;
|
||||
}
|
||||
|
||||
void
|
||||
gather_here_documents ()
|
||||
{
|
||||
@@ -2958,6 +2978,8 @@ reset_parser ()
|
||||
FREE (word_desc_to_read);
|
||||
word_desc_to_read = (WORD_DESC *)NULL;
|
||||
|
||||
eol_ungetc_lookahead = 0;
|
||||
|
||||
current_token = '\n'; /* XXX */
|
||||
last_read_token = '\n';
|
||||
token_to_read = '\n';
|
||||
|
||||
+1
-1
@@ -25,6 +25,6 @@
|
||||
regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh
|
||||
looks for to find the patch level (for the sccs version string). */
|
||||
|
||||
#define PATCHLEVEL 24
|
||||
#define PATCHLEVEL 25
|
||||
|
||||
#endif /* _PATCHLEVEL_H_ */
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
BUILD_DIR=/usr/local/build/chet/bash/bash-current
|
||||
BUILD_DIR=/usr/local/build/bash/bash-current
|
||||
THIS_SH=$BUILD_DIR/bash
|
||||
PATH=$PATH:$BUILD_DIR
|
||||
|
||||
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
BUILD_DIR=/usr/local/build/chet/bash/bash-current
|
||||
THIS_SH=$BUILD_DIR/bash
|
||||
PATH=$PATH:$BUILD_DIR
|
||||
|
||||
export THIS_SH PATH
|
||||
|
||||
rm -f /tmp/xx
|
||||
|
||||
/bin/sh "$@"
|
||||
@@ -0,0 +1,50 @@
|
||||
:; ./shx
|
||||
|
||||
sh:
|
||||
<&$fd ok
|
||||
nlbq Mon Aug 3 02:45:00 EDT 1992
|
||||
bang geoff
|
||||
quote 712824302
|
||||
setbq defmsgid=<1992Aug3.024502.6176@host>
|
||||
bgwait sleep done... wait 6187
|
||||
|
||||
|
||||
bash:
|
||||
<&$fd ok
|
||||
nlbq Mon Aug 3 02:45:09 EDT 1992
|
||||
bang geoff
|
||||
quote 712824311
|
||||
setbq defmsgid=<1992Aug3.024512.6212@host>
|
||||
bgwait sleep done... wait 6223
|
||||
|
||||
|
||||
ash:
|
||||
<&$fd shx1: 4: Syntax error: Bad fd number
|
||||
nlbq Mon Aug 3 02:45:19 EDT 1992
|
||||
bang geoff
|
||||
quote getdate: `"now"' not a valid date
|
||||
|
||||
setbq defmsgid=<1992Aug3.` echo 024521
|
||||
bgwait sleep done... wait 6241
|
||||
|
||||
|
||||
ksh:
|
||||
<&$fd ok
|
||||
nlbq ./shx: 6248 Memory fault - core dumped
|
||||
bang geoff
|
||||
quote getdate: `"now"' not a valid date
|
||||
|
||||
setbq defmsgid=<1992Aug3.024530.6257@host>
|
||||
bgwait no such job: 6265
|
||||
wait 6265
|
||||
sleep done...
|
||||
|
||||
zsh:
|
||||
<&$fd ok
|
||||
nlbq Mon Aug 3 02:45:36 EDT 1992
|
||||
bang shx3: event not found: /s/ [4]
|
||||
quote 712824337
|
||||
setbq defmsgid=<..6290@host>
|
||||
bgwait shx7: unmatched " [9]
|
||||
sleep done...
|
||||
:;
|
||||
@@ -0,0 +1,10 @@
|
||||
#! /bin/sh
|
||||
for cmd in sh bash ash ksh zsh
|
||||
do
|
||||
echo
|
||||
echo $cmd:
|
||||
for demo in shx?
|
||||
do
|
||||
$cmd $demo
|
||||
done
|
||||
done
|
||||
@@ -857,7 +857,9 @@ run_exit_trap ()
|
||||
retval = trap_saved_exit_value;
|
||||
|
||||
running_trap = 0;
|
||||
#if defined (ARRAY_VARS)
|
||||
array_dispose (ps);
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
+63
-23
@@ -1,6 +1,6 @@
|
||||
/* variables.c -- Functions for hacking shell variables. */
|
||||
|
||||
/* Copyright (C) 1987-2013 Free Software Foundation, Inc.
|
||||
/* Copyright (C) 1987-2014 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
@@ -83,6 +83,11 @@
|
||||
|
||||
#define ifsname(s) ((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0')
|
||||
|
||||
#define BASHFUNC_PREFIX "BASH_FUNC_"
|
||||
#define BASHFUNC_PREFLEN 10 /* == strlen(BASHFUNC_PREFIX */
|
||||
#define BASHFUNC_SUFFIX "%%"
|
||||
#define BASHFUNC_SUFFLEN 2 /* == strlen(BASHFUNC_SUFFIX) */
|
||||
|
||||
/* flags for find_variable_internal */
|
||||
|
||||
#define FV_FORCETEMPENV 0x01
|
||||
@@ -284,7 +289,7 @@ static void push_temp_var __P((PTR_T));
|
||||
static void propagate_temp_var __P((PTR_T));
|
||||
static void dispose_temporary_env __P((sh_free_func_t *));
|
||||
|
||||
static inline char *mk_env_string __P((const char *, const char *));
|
||||
static inline char *mk_env_string __P((const char *, const char *, int));
|
||||
static char **make_env_array_from_var_list __P((SHELL_VAR **));
|
||||
static char **make_var_export_array __P((VAR_CONTEXT *));
|
||||
static char **make_func_export_array __P((void));
|
||||
@@ -354,22 +359,33 @@ initialize_shell_variables (env, privmode)
|
||||
|
||||
/* If exported function, define it now. Don't import functions from
|
||||
the environment in privileged mode. */
|
||||
if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4))
|
||||
if (privmode == 0 && read_but_dont_execute == 0 &&
|
||||
STREQN (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN) &&
|
||||
STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN) &&
|
||||
STREQN ("() {", string, 4))
|
||||
{
|
||||
string_length = strlen (string);
|
||||
temp_string = (char *)xmalloc (3 + string_length + char_index);
|
||||
size_t namelen;
|
||||
char *tname; /* desired imported function name */
|
||||
|
||||
strcpy (temp_string, name);
|
||||
temp_string[char_index] = ' ';
|
||||
strcpy (temp_string + char_index + 1, string);
|
||||
namelen = char_index - BASHFUNC_PREFLEN - BASHFUNC_SUFFLEN;
|
||||
|
||||
tname = name + BASHFUNC_PREFLEN; /* start of func name */
|
||||
tname[namelen] = '\0'; /* now tname == func name */
|
||||
|
||||
string_length = strlen (string);
|
||||
temp_string = (char *)xmalloc (namelen + string_length + 2);
|
||||
|
||||
memcpy (temp_string, tname, namelen);
|
||||
temp_string[namelen] = ' ';
|
||||
memcpy (temp_string + namelen + 1, string, string_length + 1);
|
||||
|
||||
/* Don't import function names that are invalid identifiers from the
|
||||
environment, though we still allow them to be defined as shell
|
||||
variables. */
|
||||
if (legal_identifier (name))
|
||||
parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
|
||||
if (absolute_program (tname) == 0 && (posixly_correct == 0 || legal_identifier (tname)))
|
||||
parse_and_execute (temp_string, tname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
|
||||
|
||||
if (temp_var = find_function (name))
|
||||
if (temp_var = find_function (tname))
|
||||
{
|
||||
VSETATTR (temp_var, (att_exported|att_imported));
|
||||
array_needs_making = 1;
|
||||
@@ -382,8 +398,11 @@ initialize_shell_variables (env, privmode)
|
||||
array_needs_making = 1;
|
||||
}
|
||||
last_command_exit_value = 1;
|
||||
report_error (_("error importing function definition for `%s'"), name);
|
||||
report_error (_("error importing function definition for `%s'"), tname);
|
||||
}
|
||||
|
||||
/* Restore original suffix */
|
||||
tname[namelen] = BASHFUNC_SUFFIX[0];
|
||||
}
|
||||
#if defined (ARRAY_VARS)
|
||||
# if ARRAY_EXPORT
|
||||
@@ -3021,7 +3040,7 @@ assign_in_env (word, flags)
|
||||
var->context = variable_context; /* XXX */
|
||||
|
||||
INVALIDATE_EXPORTSTR (var);
|
||||
var->exportstr = mk_env_string (name, value);
|
||||
var->exportstr = mk_env_string (name, value, 0);
|
||||
|
||||
array_needs_making = 1;
|
||||
|
||||
@@ -3919,21 +3938,42 @@ merge_temporary_env ()
|
||||
/* **************************************************************** */
|
||||
|
||||
static inline char *
|
||||
mk_env_string (name, value)
|
||||
mk_env_string (name, value, isfunc)
|
||||
const char *name, *value;
|
||||
int isfunc;
|
||||
{
|
||||
int name_len, value_len;
|
||||
char *p;
|
||||
size_t name_len, value_len;
|
||||
char *p, *q;
|
||||
|
||||
name_len = strlen (name);
|
||||
value_len = STRLEN (value);
|
||||
p = (char *)xmalloc (2 + name_len + value_len);
|
||||
strcpy (p, name);
|
||||
p[name_len] = '=';
|
||||
if (value && *value)
|
||||
strcpy (p + name_len + 1, value);
|
||||
|
||||
/* If we are exporting a shell function, construct the encoded function
|
||||
name. */
|
||||
if (isfunc && value)
|
||||
{
|
||||
p = (char *)xmalloc (BASHFUNC_PREFLEN + name_len + BASHFUNC_SUFFLEN + value_len + 2);
|
||||
q = p;
|
||||
memcpy (q, BASHFUNC_PREFIX, BASHFUNC_PREFLEN);
|
||||
q += BASHFUNC_PREFLEN;
|
||||
memcpy (q, name, name_len);
|
||||
q += name_len;
|
||||
memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN);
|
||||
q += BASHFUNC_SUFFLEN;
|
||||
}
|
||||
else
|
||||
p[name_len + 1] = '\0';
|
||||
{
|
||||
p = (char *)xmalloc (2 + name_len + value_len);
|
||||
memcpy (p, name, name_len);
|
||||
q = p + name_len;
|
||||
}
|
||||
|
||||
q[0] = '=';
|
||||
if (value && *value)
|
||||
memcpy (q + 1, value, value_len + 1);
|
||||
else
|
||||
q[1] = '\0';
|
||||
|
||||
return (p);
|
||||
}
|
||||
|
||||
@@ -4019,7 +4059,7 @@ make_env_array_from_var_list (vars)
|
||||
/* Gee, I'd like to get away with not using savestring() if we're
|
||||
using the cached exportstr... */
|
||||
list[list_index] = USE_EXPORTSTR ? savestring (value)
|
||||
: mk_env_string (var->name, value);
|
||||
: mk_env_string (var->name, value, function_p (var));
|
||||
|
||||
if (USE_EXPORTSTR == 0)
|
||||
SAVE_EXPORTSTR (var, list[list_index]);
|
||||
|
||||
+5432
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user