mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-06-27 07:43:07 +02:00
bash-20121026 remove leftover and stray files
This commit is contained in:
-3800
File diff suppressed because it is too large
Load Diff
@@ -1,82 +0,0 @@
|
||||
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.
|
||||
@@ -1,544 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/* 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 */
|
||||
-4127
File diff suppressed because it is too large
Load Diff
@@ -1,686 +0,0 @@
|
||||
This file is declare.def, from which is created declare.c.
|
||||
It implements the builtins "declare" and "local" in Bash.
|
||||
|
||||
Copyright (C) 1987-2012 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/>.
|
||||
|
||||
$PRODUCES declare.c
|
||||
|
||||
$BUILTIN declare
|
||||
$FUNCTION declare_builtin
|
||||
$SHORT_DOC declare [-aAfFgilnrtux] [-p] [name[=value] ...]
|
||||
Set variable values and attributes.
|
||||
|
||||
Declare variables and give them attributes. If no NAMEs are given,
|
||||
display the attributes and values of all variables.
|
||||
|
||||
Options:
|
||||
-f restrict action or display to function names and definitions
|
||||
-F restrict display to function names only (plus line number and
|
||||
source file when debugging)
|
||||
-g create global variables when used in a shell function; otherwise
|
||||
ignored
|
||||
-p display the attributes and value of each NAME
|
||||
|
||||
Options which set attributes:
|
||||
-a to make NAMEs indexed arrays (if supported)
|
||||
-A to make NAMEs associative arrays (if supported)
|
||||
-i to make NAMEs have the `integer' attribute
|
||||
-l to convert NAMEs to lower case on assignment
|
||||
-n make NAME a reference to the variable named by its value
|
||||
-r to make NAMEs readonly
|
||||
-t to make NAMEs have the `trace' attribute
|
||||
-u to convert NAMEs to upper case on assignment
|
||||
-x to make NAMEs export
|
||||
|
||||
Using `+' instead of `-' turns off the given attribute.
|
||||
|
||||
Variables with the integer attribute have arithmetic evaluation (see
|
||||
the `let' command) performed when the variable is assigned a value.
|
||||
|
||||
When used in a function, `declare' makes NAMEs local, as with the `local'
|
||||
command. The `-g' option suppresses this behavior.
|
||||
|
||||
Exit Status:
|
||||
Returns success unless an invalid option is supplied or an error occurs.
|
||||
$END
|
||||
|
||||
$BUILTIN typeset
|
||||
$FUNCTION declare_builtin
|
||||
$SHORT_DOC typeset [-aAfFgilrtux] [-p] name[=value] ...
|
||||
Set variable values and attributes.
|
||||
|
||||
Obsolete. See `help declare'.
|
||||
$END
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# ifdef _MINIX
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../bashansi.h"
|
||||
#include "../bashintl.h"
|
||||
|
||||
#include "../shell.h"
|
||||
#include "common.h"
|
||||
#include "builtext.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
extern int array_needs_making;
|
||||
extern int posixly_correct;
|
||||
|
||||
static int declare_internal __P((register WORD_LIST *, int));
|
||||
|
||||
/* Declare or change variable attributes. */
|
||||
int
|
||||
declare_builtin (list)
|
||||
register WORD_LIST *list;
|
||||
{
|
||||
return (declare_internal (list, 0));
|
||||
}
|
||||
|
||||
$BUILTIN local
|
||||
$FUNCTION local_builtin
|
||||
$SHORT_DOC local [option] name[=value] ...
|
||||
Define local variables.
|
||||
|
||||
Create a local variable called NAME, and give it VALUE. OPTION can
|
||||
be any option accepted by `declare'.
|
||||
|
||||
Local variables can only be used within a function; they are visible
|
||||
only to the function where they are defined and its children.
|
||||
|
||||
Exit Status:
|
||||
Returns success unless an invalid option is supplied, an error occurs,
|
||||
or the shell is not executing a function.
|
||||
$END
|
||||
int
|
||||
local_builtin (list)
|
||||
register WORD_LIST *list;
|
||||
{
|
||||
if (variable_context)
|
||||
return (declare_internal (list, 1));
|
||||
else
|
||||
{
|
||||
builtin_error (_("can only be used in a function"));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined (ARRAY_VARS)
|
||||
# define DECLARE_OPTS "+acfgilnprtuxAF"
|
||||
#else
|
||||
# define DECLARE_OPTS "+cfgilnprtuxF"
|
||||
#endif
|
||||
|
||||
/* The workhorse function. */
|
||||
static int
|
||||
declare_internal (list, local_var)
|
||||
register WORD_LIST *list;
|
||||
int local_var;
|
||||
{
|
||||
int flags_on, flags_off, *flags;
|
||||
int any_failed, assign_error, pflag, nodefs, opt, mkglobal, onref, offref;
|
||||
char *t, *subscript_start;
|
||||
SHELL_VAR *var, *refvar;
|
||||
FUNCTION_DEF *shell_fn;
|
||||
|
||||
flags_on = flags_off = any_failed = assign_error = pflag = nodefs = mkglobal = 0;
|
||||
refvar = (SHELL_VAR *)NULL;
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, DECLARE_OPTS)) != EOF)
|
||||
{
|
||||
flags = list_opttype == '+' ? &flags_off : &flags_on;
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
case 'a':
|
||||
#if defined (ARRAY_VARS)
|
||||
*flags |= att_array;
|
||||
break;
|
||||
#else
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
#endif
|
||||
case 'A':
|
||||
#if defined (ARRAY_VARS)
|
||||
*flags |= att_assoc;
|
||||
break;
|
||||
#else
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
#endif
|
||||
case 'p':
|
||||
if (local_var == 0)
|
||||
pflag++;
|
||||
break;
|
||||
case 'F':
|
||||
nodefs++;
|
||||
*flags |= att_function;
|
||||
break;
|
||||
case 'f':
|
||||
*flags |= att_function;
|
||||
break;
|
||||
case 'g':
|
||||
if (flags == &flags_on)
|
||||
mkglobal = 1;
|
||||
break;
|
||||
case 'i':
|
||||
*flags |= att_integer;
|
||||
break;
|
||||
case 'n':
|
||||
*flags |= att_nameref;
|
||||
break;
|
||||
case 'r':
|
||||
*flags |= att_readonly;
|
||||
break;
|
||||
case 't':
|
||||
*flags |= att_trace;
|
||||
break;
|
||||
case 'x':
|
||||
*flags |= att_exported;
|
||||
array_needs_making = 1;
|
||||
break;
|
||||
#if defined (CASEMOD_ATTRS)
|
||||
# if defined (CASEMOD_CAPCASE)
|
||||
case 'c':
|
||||
*flags |= att_capcase;
|
||||
if (flags == &flags_on)
|
||||
flags_off |= att_uppercase|att_lowercase;
|
||||
break;
|
||||
# endif
|
||||
case 'l':
|
||||
*flags |= att_lowercase;
|
||||
if (flags == &flags_on)
|
||||
flags_off |= att_capcase|att_uppercase;
|
||||
break;
|
||||
case 'u':
|
||||
*flags |= att_uppercase;
|
||||
if (flags == &flags_on)
|
||||
flags_off |= att_capcase|att_lowercase;
|
||||
break;
|
||||
#endif /* CASEMOD_ATTRS */
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
list = loptend;
|
||||
|
||||
/* If there are no more arguments left, then we just want to show
|
||||
some variables. */
|
||||
if (list == 0) /* declare -[aAfFirtx] */
|
||||
{
|
||||
/* Show local variables defined at this context level if this is
|
||||
the `local' builtin. */
|
||||
if (local_var)
|
||||
{
|
||||
register SHELL_VAR **vlist;
|
||||
register int i;
|
||||
|
||||
vlist = all_local_variables ();
|
||||
|
||||
if (vlist)
|
||||
{
|
||||
for (i = 0; vlist[i]; i++)
|
||||
print_assignment (vlist[i]);
|
||||
|
||||
free (vlist);
|
||||
}
|
||||
}
|
||||
else if (pflag && (flags_on == 0 || flags_on == att_function))
|
||||
show_all_var_attributes (flags_on == 0, nodefs);
|
||||
else if (flags_on == 0)
|
||||
return (set_builtin ((WORD_LIST *)NULL));
|
||||
else
|
||||
set_or_show_attributes ((WORD_LIST *)NULL, flags_on, nodefs);
|
||||
|
||||
return (sh_chkwrite (EXECUTION_SUCCESS));
|
||||
}
|
||||
|
||||
if (pflag) /* declare -p [-aAfFirtx] name [name...] */
|
||||
{
|
||||
for (any_failed = 0; list; list = list->next)
|
||||
{
|
||||
pflag = show_name_attributes (list->word->word, nodefs);
|
||||
if (pflag)
|
||||
{
|
||||
sh_notfound (list->word->word);
|
||||
any_failed++;
|
||||
}
|
||||
}
|
||||
return (sh_chkwrite (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS));
|
||||
}
|
||||
|
||||
#define NEXT_VARIABLE() free (name); list = list->next; continue
|
||||
|
||||
/* There are arguments left, so we are making variables. */
|
||||
while (list) /* declare [-aAfFirx] name [name ...] */
|
||||
{
|
||||
char *value, *name;
|
||||
int offset, aflags;
|
||||
#if defined (ARRAY_VARS)
|
||||
int making_array_special, compound_array_assign, simple_array_assign;
|
||||
#endif
|
||||
|
||||
name = savestring (list->word->word);
|
||||
offset = assignment (name, 0);
|
||||
aflags = 0;
|
||||
|
||||
if (offset) /* declare [-aAfFirx] name=value */
|
||||
{
|
||||
name[offset] = '\0';
|
||||
value = name + offset + 1;
|
||||
if (name[offset - 1] == '+')
|
||||
{
|
||||
aflags |= ASS_APPEND;
|
||||
name[offset - 1] = '\0';
|
||||
}
|
||||
}
|
||||
else
|
||||
value = "";
|
||||
|
||||
/* Do some lexical error checking on the LHS and RHS of the assignment
|
||||
that is specific to nameref variables. */
|
||||
if (flags_on & att_nameref)
|
||||
{
|
||||
if (valid_array_reference (name))
|
||||
{
|
||||
builtin_error (_("%s: reference variable cannot be an array"), name);
|
||||
assign_error++;
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
else if (STREQ (name, value))
|
||||
{
|
||||
builtin_error (_("%s: nameref variable self references not allowed"), name);
|
||||
assign_error++;
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined (ARRAY_VARS)
|
||||
compound_array_assign = simple_array_assign = 0;
|
||||
subscript_start = (char *)NULL;
|
||||
if (t = strchr (name, '[')) /* ] */
|
||||
{
|
||||
/* If offset != 0 we have already validated any array reference */
|
||||
if (offset == 0 && valid_array_reference (name) == 0)
|
||||
{
|
||||
sh_invalidid (name);
|
||||
assign_error++;
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
subscript_start = t;
|
||||
*t = '\0';
|
||||
making_array_special = 1;
|
||||
}
|
||||
else
|
||||
making_array_special = 0;
|
||||
#endif
|
||||
|
||||
/* If we're in posix mode or not looking for a shell function (since
|
||||
shell function names don't have to be valid identifiers when the
|
||||
shell's not in posix mode), check whether or not the argument is a
|
||||
valid, well-formed shell identifier. */
|
||||
if ((posixly_correct || (flags_on & att_function) == 0) && legal_identifier (name) == 0)
|
||||
{
|
||||
sh_invalidid (name);
|
||||
assign_error++;
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
|
||||
/* If VARIABLE_CONTEXT has a non-zero value, then we are executing
|
||||
inside of a function. This means we should make local variables,
|
||||
not global ones. */
|
||||
|
||||
/* XXX - this has consequences when we're making a local copy of a
|
||||
variable that was in the temporary environment. Watch out
|
||||
for this. */
|
||||
refvar = (SHELL_VAR *)NULL;
|
||||
if (variable_context && mkglobal == 0 && ((flags_on & att_function) == 0))
|
||||
{
|
||||
#if defined (ARRAY_VARS)
|
||||
if (flags_on & att_assoc)
|
||||
var = make_local_assoc_variable (name);
|
||||
else if ((flags_on & att_array) || making_array_special)
|
||||
var = make_local_array_variable (name, making_array_special);
|
||||
else
|
||||
#endif
|
||||
#if 0
|
||||
/* XXX - this doesn't work right yet. */
|
||||
/* See below for rationale for doing this. */
|
||||
if (flags_on & att_nameref)
|
||||
{
|
||||
/* See if we are trying to modify an existing nameref variable */
|
||||
var = find_variable_last_nameref (name);
|
||||
if (var && nameref_p (var) == 0)
|
||||
var = make_local_variable (name);
|
||||
}
|
||||
else if (flags_off & att_nameref)
|
||||
{
|
||||
var = (SHELL_VAR *)NULL;
|
||||
/* See if we are trying to modify an existing nameref variable */
|
||||
refvar = find_variable_last_nameref (name);
|
||||
if (refvar && nameref_p (refvar) == 0)
|
||||
refvar = 0;
|
||||
if (refvar)
|
||||
var = make_local_variable (nameref_cell (refvar));
|
||||
if (var == 0)
|
||||
var = make_local_variable (name);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
var = make_local_variable (name);
|
||||
if (var == 0)
|
||||
{
|
||||
any_failed++;
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
}
|
||||
else
|
||||
var = (SHELL_VAR *)NULL;
|
||||
|
||||
/* If we are declaring a function, then complain about it in some way.
|
||||
We don't let people make functions by saying `typeset -f foo=bar'. */
|
||||
|
||||
/* There should be a way, however, to let people look at a particular
|
||||
function definition by saying `typeset -f foo'. */
|
||||
|
||||
if (flags_on & att_function)
|
||||
{
|
||||
if (offset) /* declare -f [-rix] foo=bar */
|
||||
{
|
||||
builtin_error (_("cannot use `-f' to make functions"));
|
||||
free (name);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else /* declare -f [-rx] name [name...] */
|
||||
{
|
||||
var = find_function (name);
|
||||
|
||||
if (var)
|
||||
{
|
||||
if (readonly_p (var) && (flags_off & att_readonly))
|
||||
{
|
||||
builtin_error (_("%s: readonly function"), name);
|
||||
any_failed++;
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
|
||||
/* declare -[Ff] name [name...] */
|
||||
if (flags_on == att_function && flags_off == 0)
|
||||
{
|
||||
#if defined (DEBUGGER)
|
||||
if (nodefs && debugging_mode)
|
||||
{
|
||||
shell_fn = find_function_def (var->name);
|
||||
if (shell_fn)
|
||||
printf ("%s %d %s\n", var->name, shell_fn->line, shell_fn->source_file);
|
||||
else
|
||||
printf ("%s\n", var->name);
|
||||
}
|
||||
else
|
||||
#endif /* DEBUGGER */
|
||||
{
|
||||
t = nodefs ? var->name
|
||||
: named_function_string (name, function_cell (var), FUNC_MULTILINE|FUNC_EXTERNAL);
|
||||
printf ("%s\n", t);
|
||||
any_failed = sh_chkwrite (any_failed);
|
||||
}
|
||||
}
|
||||
else /* declare -[fF] -[rx] name [name...] */
|
||||
{
|
||||
VSETATTR (var, flags_on);
|
||||
VUNSETATTR (var, flags_off);
|
||||
}
|
||||
}
|
||||
else
|
||||
any_failed++;
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
}
|
||||
else /* declare -[aAirx] name [name...] */
|
||||
{
|
||||
/* Non-null if we just created or fetched a local variable. */
|
||||
/* Here's what ksh93 seems to do. If we are modifying an existing
|
||||
nameref variable, we don't follow the nameref chain past the last
|
||||
nameref, and we set the nameref variable's value so future
|
||||
references to that variable will return the value of the variable
|
||||
we're assigning right now. */
|
||||
if (var == 0 && (flags_on & att_nameref))
|
||||
{
|
||||
/* See if we are trying to modify an existing nameref variable */
|
||||
var = mkglobal ? find_global_variable_last_nameref (name) : find_variable_last_nameref (name);
|
||||
if (var && nameref_p (var) == 0)
|
||||
var = 0;
|
||||
}
|
||||
/* However, if we're turning off the nameref attribute on an existing
|
||||
nameref variable, we first follow the nameref chain to the end,
|
||||
modify the value of the variable this nameref variable references,
|
||||
*CHANGING ITS VALUE AS A SIDE EFFECT* then turn off the nameref
|
||||
flag *LEAVING THE NAMEREF VARIABLE'S VALUE UNCHANGED* */
|
||||
else if (var == 0 && (flags_off & att_nameref))
|
||||
{
|
||||
/* See if we are trying to modify an existing nameref variable */
|
||||
refvar = mkglobal ? find_global_variable_last_nameref (name) : find_variable_last_nameref (name);
|
||||
if (refvar && nameref_p (refvar) == 0)
|
||||
refvar = 0;
|
||||
if (refvar)
|
||||
var = mkglobal ? find_global_variable (nameref_cell (refvar)) : find_variable (nameref_cell (refvar));
|
||||
}
|
||||
#if defined (ARRAY_VARS)
|
||||
else if (var && (array_p (var) || assoc_p (var)) && (flags_on & att_nameref))
|
||||
{
|
||||
builtin_error (_("%s: reference variable cannot be an array"), var->name);
|
||||
assign_error++;
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (var == 0)
|
||||
var = mkglobal ? find_global_variable (name) : find_variable (name);
|
||||
|
||||
if (var == 0)
|
||||
{
|
||||
#if defined (ARRAY_VARS)
|
||||
if (flags_on & att_assoc)
|
||||
var = make_new_assoc_variable (name);
|
||||
else if ((flags_on & att_array) || making_array_special)
|
||||
var = make_new_array_variable (name);
|
||||
else
|
||||
#endif
|
||||
|
||||
if (offset)
|
||||
var = bind_variable (name, "", 0);
|
||||
else
|
||||
{
|
||||
var = bind_variable (name, (char *)NULL, 0);
|
||||
VSETATTR (var, att_invisible);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cannot use declare +r to turn off readonly attribute. */
|
||||
if (readonly_p (var) && (flags_off & att_readonly))
|
||||
{
|
||||
sh_readonly (name);
|
||||
any_failed++;
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
|
||||
/* Cannot use declare to assign value to readonly or noassign
|
||||
variable. */
|
||||
if ((readonly_p (var) || noassign_p (var)) && offset)
|
||||
{
|
||||
if (readonly_p (var))
|
||||
sh_readonly (name);
|
||||
assign_error++;
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
|
||||
#if defined (ARRAY_VARS)
|
||||
if ((making_array_special || (flags_on & (att_array|att_assoc)) || array_p (var) || assoc_p (var)) && offset)
|
||||
{
|
||||
int vlen;
|
||||
vlen = STRLEN (value);
|
||||
|
||||
if (value[0] == '(' && value[vlen-1] == ')')
|
||||
compound_array_assign = 1;
|
||||
else
|
||||
simple_array_assign = 1;
|
||||
}
|
||||
|
||||
/* Cannot use declare +a name or declare +A name to remove an
|
||||
array variable. */
|
||||
if (((flags_off & att_array) && array_p (var)) || ((flags_off & att_assoc) && assoc_p (var)))
|
||||
{
|
||||
builtin_error (_("%s: cannot destroy array variables in this way"), name);
|
||||
any_failed++;
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
|
||||
if ((flags_on & att_array) && assoc_p (var))
|
||||
{
|
||||
builtin_error (_("%s: cannot convert associative to indexed array"), name);
|
||||
any_failed++;
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
if ((flags_on & att_assoc) && array_p (var))
|
||||
{
|
||||
builtin_error (_("%s: cannot convert indexed to associative array"), name);
|
||||
any_failed++;
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
|
||||
/* declare -A name[[n]] makes name an associative array variable. */
|
||||
if (flags_on & att_assoc)
|
||||
{
|
||||
if (assoc_p (var) == 0)
|
||||
var = convert_var_to_assoc (var);
|
||||
}
|
||||
/* declare -a name[[n]] or declare name[n] makes name an indexed
|
||||
array variable. */
|
||||
else if ((making_array_special || (flags_on & att_array)) && array_p (var) == 0 && assoc_p (var) == 0)
|
||||
var = convert_var_to_array (var);
|
||||
#endif /* ARRAY_VARS */
|
||||
|
||||
/* XXX - we note that we are turning on nameref attribute and defer
|
||||
setting it until the assignment has been made so we don't do an
|
||||
inadvertent nameref lookup. Might have to do the same thing for
|
||||
flags_off&att_nameref. */
|
||||
/* XXX - ksh93 makes it an error to set a readonly nameref variable
|
||||
using a single typeset command. */
|
||||
onref = (flags_on & att_nameref);
|
||||
flags_on &= ~att_nameref;
|
||||
if (array_p (var) || assoc_p (var)
|
||||
|| (offset && compound_array_assign)
|
||||
|| simple_array_assign)
|
||||
onref = 0; /* array variables may not be namerefs */
|
||||
|
||||
/* ksh93 seems to do this */
|
||||
offref = (flags_off & att_nameref);
|
||||
flags_off &= ~att_nameref;
|
||||
|
||||
VSETATTR (var, flags_on);
|
||||
VUNSETATTR (var, flags_off);
|
||||
|
||||
#if defined (ARRAY_VARS)
|
||||
if (offset && compound_array_assign)
|
||||
assign_array_var_from_string (var, value, aflags);
|
||||
else if (simple_array_assign && subscript_start)
|
||||
{
|
||||
/* declare [-aA] name[N]=value */
|
||||
*subscript_start = '['; /* ] */
|
||||
var = assign_array_element (name, value, 0); /* XXX - not aflags */
|
||||
*subscript_start = '\0';
|
||||
if (var == 0) /* some kind of assignment error */
|
||||
{
|
||||
assign_error++;
|
||||
flags_on |= onref;
|
||||
flags_off |= offref;
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
}
|
||||
else if (simple_array_assign)
|
||||
{
|
||||
/* let bind_{array,assoc}_variable take care of this. */
|
||||
if (assoc_p (var))
|
||||
bind_assoc_variable (var, name, savestring ("0"), value, aflags);
|
||||
else
|
||||
bind_array_variable (name, 0, value, aflags);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
/* bind_variable_value duplicates the essential internals of
|
||||
bind_variable() */
|
||||
if (offset)
|
||||
bind_variable_value (var, value, aflags);
|
||||
|
||||
/* If we found this variable in the temporary environment, as with
|
||||
`var=value declare -x var', make sure it is treated identically
|
||||
to `var=value export var'. Do the same for `declare -r' and
|
||||
`readonly'. Preserve the attributes, except for att_tempvar. */
|
||||
/* XXX -- should this create a variable in the global scope, or
|
||||
modify the local variable flags? ksh93 has it modify the
|
||||
global scope.
|
||||
Need to handle case like in set_var_attribute where a temporary
|
||||
variable is in the same table as the function local vars. */
|
||||
if ((flags_on & (att_exported|att_readonly)) && tempvar_p (var))
|
||||
{
|
||||
SHELL_VAR *tv;
|
||||
char *tvalue;
|
||||
|
||||
tv = find_tempenv_variable (var->name);
|
||||
if (tv)
|
||||
{
|
||||
tvalue = var_isset (var) ? savestring (value_cell (var)) : savestring ("");
|
||||
tv = bind_variable (var->name, tvalue, 0);
|
||||
tv->attributes |= var->attributes & ~att_tempvar;
|
||||
if (tv->context > 0)
|
||||
VSETATTR (tv, att_propagate);
|
||||
free (tvalue);
|
||||
}
|
||||
VSETATTR (var, att_propagate);
|
||||
}
|
||||
}
|
||||
|
||||
/* Turn on nameref attribute we deferred above. */
|
||||
/* XXX - should we turn on the noassign attribute for consistency with
|
||||
ksh93 when we turn on the nameref attribute? */
|
||||
VSETATTR (var, onref);
|
||||
flags_on |= onref;
|
||||
VUNSETATTR (var, offref);
|
||||
flags_off |= offref;
|
||||
/* Yuck. ksh93 compatibility */
|
||||
if (refvar)
|
||||
VUNSETATTR (refvar, flags_off);
|
||||
|
||||
stupidly_hack_special_variables (name);
|
||||
|
||||
NEXT_VARIABLE ();
|
||||
}
|
||||
|
||||
return (assign_error ? EX_BADASSIGN
|
||||
: ((any_failed == 0) ? EXECUTION_SUCCESS
|
||||
: EXECUTION_FAILURE));
|
||||
}
|
||||
@@ -1,359 +0,0 @@
|
||||
This file is mapfile.def, from which is created mapfile.c.
|
||||
It implements the builtin "mapfile" in Bash.
|
||||
|
||||
Copyright (C) 2005-2006 Rocky Bernstein for Free Software Foundation, Inc.
|
||||
Copyright (C) 2008-2012 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/>.
|
||||
|
||||
$PRODUCES mapfile.c
|
||||
|
||||
$BUILTIN mapfile
|
||||
$FUNCTION mapfile_builtin
|
||||
$SHORT_DOC mapfile [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array]
|
||||
Read lines from the standard input into an indexed array variable.
|
||||
|
||||
Read lines from the standard input into the indexed array variable ARRAY, or
|
||||
from file descriptor FD if the -u option is supplied. The variable MAPFILE
|
||||
is the default ARRAY.
|
||||
|
||||
Options:
|
||||
-n count Copy at most COUNT lines. If COUNT is 0, all lines are copied.
|
||||
-O origin Begin assigning to ARRAY at index ORIGIN. The default index is 0.
|
||||
-s count Discard the first COUNT lines read.
|
||||
-t Remove a trailing newline from each line read.
|
||||
-u fd Read lines from file descriptor FD instead of the standard input.
|
||||
-C callback Evaluate CALLBACK each time QUANTUM lines are read.
|
||||
-c quantum Specify the number of lines read between each call to CALLBACK.
|
||||
|
||||
Arguments:
|
||||
ARRAY Array variable name to use for file data.
|
||||
|
||||
If -C is supplied without -c, the default quantum is 5000. When
|
||||
CALLBACK is evaluated, it is supplied the index of the next array
|
||||
element to be assigned and the line to be assigned to that element
|
||||
as additional arguments.
|
||||
|
||||
If not supplied with an explicit origin, mapfile will clear ARRAY before
|
||||
assigning to it.
|
||||
|
||||
Exit Status:
|
||||
Returns success unless an invalid option is given or ARRAY is readonly or
|
||||
not an indexed array.
|
||||
$END
|
||||
|
||||
$BUILTIN readarray
|
||||
$FUNCTION mapfile_builtin
|
||||
$SHORT_DOC readarray [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array]
|
||||
Read lines from a file into an array variable.
|
||||
|
||||
A synonym for `mapfile'.
|
||||
$END
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "builtins.h"
|
||||
#include "posixstat.h"
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "bashansi.h"
|
||||
#include "bashintl.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "../bashintl.h"
|
||||
#include "../shell.h"
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
#if defined (ARRAY_VARS)
|
||||
|
||||
static int run_callback __P((const char *, unsigned int, const char *));
|
||||
|
||||
#define DEFAULT_ARRAY_NAME "MAPFILE"
|
||||
#define DEFAULT_VARIABLE_NAME "MAPLINE" /* not used right now */
|
||||
|
||||
/* The value specifying how frequently `mapfile' calls the callback. */
|
||||
#define DEFAULT_QUANTUM 5000
|
||||
|
||||
/* Values for FLAGS */
|
||||
#define MAPF_CLEARARRAY 0x01
|
||||
#define MAPF_CHOP 0x02
|
||||
|
||||
static int
|
||||
run_callback (callback, curindex, curline)
|
||||
const char *callback;
|
||||
unsigned int curindex;
|
||||
const char *curline;
|
||||
{
|
||||
unsigned int execlen;
|
||||
char *execstr, *qline;
|
||||
int flags;
|
||||
|
||||
qline = sh_single_quote (curline);
|
||||
execlen = strlen (callback) + strlen (qline) + 10;
|
||||
/* 1 for each space between %s and %d,
|
||||
another 1 for the last nul char for C string. */
|
||||
execlen += 3;
|
||||
execstr = xmalloc (execlen);
|
||||
|
||||
flags = SEVAL_NOHIST;
|
||||
#if 0
|
||||
if (interactive)
|
||||
flags |= SEVAL_INTERACT;
|
||||
#endif
|
||||
snprintf (execstr, execlen, "%s %d %s", callback, curindex, qline);
|
||||
free (qline);
|
||||
return evalstring (execstr, NULL, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
do_chop(line)
|
||||
char * line;
|
||||
{
|
||||
int length;
|
||||
|
||||
length = strlen (line);
|
||||
if (length && line[length-1] == '\n')
|
||||
line[length-1] = '\0';
|
||||
}
|
||||
|
||||
static int
|
||||
mapfile (fd, line_count_goal, origin, nskip, callback_quantum, callback, array_name, flags)
|
||||
int fd;
|
||||
long line_count_goal, origin, nskip, callback_quantum;
|
||||
char *callback, *array_name;
|
||||
int flags;
|
||||
{
|
||||
char *line;
|
||||
size_t line_length;
|
||||
unsigned int array_index, line_count;
|
||||
SHELL_VAR *entry;
|
||||
int unbuffered_read;
|
||||
|
||||
line = NULL;
|
||||
line_length = 0;
|
||||
unbuffered_read = 0;
|
||||
|
||||
/* The following check should be done before reading any lines. Doing it
|
||||
here allows us to call bind_array_element instead of bind_array_variable
|
||||
and skip the variable lookup on every call. */
|
||||
entry = find_or_make_array_variable (array_name, 1);
|
||||
if (entry == 0 || readonly_p (entry) || noassign_p (entry))
|
||||
{
|
||||
if (entry && readonly_p (entry))
|
||||
err_readonly (array_name);
|
||||
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else if (array_p (entry) == 0)
|
||||
{
|
||||
builtin_error (_("%s: not an indexed array"), array_name);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
if (flags & MAPF_CLEARARRAY)
|
||||
array_flush (array_cell (entry));
|
||||
|
||||
#ifndef __CYGWIN__
|
||||
unbuffered_read = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE);
|
||||
#else
|
||||
unbuffered_read = 1;
|
||||
#endif
|
||||
|
||||
zreset ();
|
||||
|
||||
/* Skip any lines at beginning of file? */
|
||||
for (line_count = 0; line_count < nskip; line_count++)
|
||||
if (zgetline (fd, &line, &line_length, unbuffered_read) < 0)
|
||||
break;
|
||||
|
||||
line = 0;
|
||||
line_length = 0;
|
||||
|
||||
/* Reset the buffer for bash own stream */
|
||||
interrupt_immediately++;
|
||||
for (array_index = origin, line_count = 1;
|
||||
zgetline (fd, &line, &line_length, unbuffered_read) != -1;
|
||||
array_index++)
|
||||
{
|
||||
/* Remove trailing newlines? */
|
||||
if (flags & MAPF_CHOP)
|
||||
do_chop (line);
|
||||
|
||||
/* Has a callback been registered and if so is it time to call it? */
|
||||
if (callback && line_count && (line_count % callback_quantum) == 0)
|
||||
{
|
||||
run_callback (callback, array_index, line);
|
||||
|
||||
/* Reset the buffer for bash own stream. */
|
||||
if (unbuffered_read == 0)
|
||||
zsyncfd (fd);
|
||||
}
|
||||
|
||||
bind_array_element (entry, array_index, line, 0);
|
||||
|
||||
/* Have we exceeded # of lines to store? */
|
||||
line_count++;
|
||||
if (line_count_goal != 0 && line_count > line_count_goal)
|
||||
break;
|
||||
}
|
||||
|
||||
xfree (line);
|
||||
|
||||
if (unbuffered_read == 0)
|
||||
zsyncfd (fd);
|
||||
|
||||
interrupt_immediately--;
|
||||
return EXECUTION_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
mapfile_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int opt, code, fd, clear_array, flags;
|
||||
intmax_t intval;
|
||||
long lines, origin, nskip, callback_quantum;
|
||||
char *array_name, *callback;
|
||||
|
||||
clear_array = 1;
|
||||
fd = 0;
|
||||
lines = origin = nskip = 0;
|
||||
flags = MAPF_CLEARARRAY;
|
||||
callback_quantum = DEFAULT_QUANTUM;
|
||||
callback = 0;
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "u:n:O:tC:c:s:")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'u':
|
||||
code = legal_number (list_optarg, &intval);
|
||||
if (code == 0 || intval < 0 || intval != (int)intval)
|
||||
{
|
||||
builtin_error (_("%s: invalid file descriptor specification"), list_optarg);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else
|
||||
fd = intval;
|
||||
|
||||
if (sh_validfd (fd) == 0)
|
||||
{
|
||||
builtin_error (_("%d: invalid file descriptor: %s"), fd, strerror (errno));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
code = legal_number (list_optarg, &intval);
|
||||
if (code == 0 || intval < 0 || intval != (unsigned)intval)
|
||||
{
|
||||
builtin_error (_("%s: invalid line count"), list_optarg);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else
|
||||
lines = intval;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
code = legal_number (list_optarg, &intval);
|
||||
if (code == 0 || intval < 0 || intval != (unsigned)intval)
|
||||
{
|
||||
builtin_error (_("%s: invalid array origin"), list_optarg);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else
|
||||
origin = intval;
|
||||
flags &= ~MAPF_CLEARARRAY;
|
||||
break;
|
||||
case 't':
|
||||
flags |= MAPF_CHOP;
|
||||
break;
|
||||
case 'C':
|
||||
callback = list_optarg;
|
||||
break;
|
||||
case 'c':
|
||||
code = legal_number (list_optarg, &intval);
|
||||
if (code == 0 || intval <= 0 || intval != (unsigned)intval)
|
||||
{
|
||||
builtin_error (_("%s: invalid callback quantum"), list_optarg);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else
|
||||
callback_quantum = intval;
|
||||
break;
|
||||
case 's':
|
||||
code = legal_number (list_optarg, &intval);
|
||||
if (code == 0 || intval < 0 || intval != (unsigned)intval)
|
||||
{
|
||||
builtin_error (_("%s: invalid line count"), list_optarg);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else
|
||||
nskip = intval;
|
||||
break;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
if (list == 0)
|
||||
array_name = DEFAULT_ARRAY_NAME;
|
||||
else if (list->word == 0 || list->word->word == 0)
|
||||
{
|
||||
builtin_error ("internal error: getting variable name");
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else if (list->word->word[0] == '\0')
|
||||
{
|
||||
builtin_error (_("empty array variable name"));
|
||||
return (EX_USAGE);
|
||||
}
|
||||
else
|
||||
array_name = list->word->word;
|
||||
|
||||
if (legal_identifier (array_name) == 0 && valid_array_reference (array_name) == 0)
|
||||
{
|
||||
sh_invalidid (array_name);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
return mapfile (fd, lines, origin, nskip, callback_quantum, callback, array_name, flags);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int
|
||||
mapfile_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
builtin_error (_("array variable support required"));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
#endif /* ARRAY_VARS */
|
||||
-1062
File diff suppressed because it is too large
Load Diff
@@ -1,158 +0,0 @@
|
||||
This file is test.def, from which is created test.c.
|
||||
It implements the builtin "test" in Bash.
|
||||
|
||||
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/>.
|
||||
|
||||
$PRODUCES test.c
|
||||
|
||||
$BUILTIN test
|
||||
$FUNCTION test_builtin
|
||||
$SHORT_DOC test [expr]
|
||||
Evaluate conditional expression.
|
||||
|
||||
Exits with a status of 0 (true) or 1 (false) depending on
|
||||
the evaluation of EXPR. Expressions may be unary or binary. Unary
|
||||
expressions are often used to examine the status of a file. There
|
||||
are string operators and numeric comparison operators as well.
|
||||
|
||||
The behavior of test depends on the number of arguments. Read the
|
||||
bash manual page for the complete specification.
|
||||
|
||||
File operators:
|
||||
|
||||
-a FILE True if file exists.
|
||||
-b FILE True if file is block special.
|
||||
-c FILE True if file is character special.
|
||||
-d FILE True if file is a directory.
|
||||
-e FILE True if file exists.
|
||||
-f FILE True if file exists and is a regular file.
|
||||
-g FILE True if file is set-group-id.
|
||||
-h FILE True if file is a symbolic link.
|
||||
-L FILE True if file is a symbolic link.
|
||||
-k FILE True if file has its `sticky' bit set.
|
||||
-p FILE True if file is a named pipe.
|
||||
-r FILE True if file is readable by you.
|
||||
-s FILE True if file exists and is not empty.
|
||||
-S FILE True if file is a socket.
|
||||
-t FD True if FD is opened on a terminal.
|
||||
-u FILE True if the file is set-user-id.
|
||||
-w FILE True if the file is writable by you.
|
||||
-x FILE True if the file is executable by you.
|
||||
-O FILE True if the file is effectively owned by you.
|
||||
-G FILE True if the file is effectively owned by your group.
|
||||
-N FILE True if the file has been modified since it was last read.
|
||||
|
||||
FILE1 -nt FILE2 True if file1 is newer than file2 (according to
|
||||
modification date).
|
||||
|
||||
FILE1 -ot FILE2 True if file1 is older than file2.
|
||||
|
||||
FILE1 -ef FILE2 True if file1 is a hard link to file2.
|
||||
|
||||
String operators:
|
||||
|
||||
-z STRING True if string is empty.
|
||||
|
||||
-n STRING
|
||||
STRING True if string is not empty.
|
||||
|
||||
STRING1 = STRING2
|
||||
True if the strings are equal.
|
||||
STRING1 != STRING2
|
||||
True if the strings are not equal.
|
||||
STRING1 < STRING2
|
||||
True if STRING1 sorts before STRING2 lexicographically.
|
||||
STRING1 > STRING2
|
||||
True if STRING1 sorts after STRING2 lexicographically.
|
||||
|
||||
Other operators:
|
||||
|
||||
-o OPTION True if the shell option OPTION is enabled.
|
||||
-v VAR True if the shell variable VAR is set
|
||||
! EXPR True if expr is false.
|
||||
EXPR1 -a EXPR2 True if both expr1 AND expr2 are true.
|
||||
EXPR1 -o EXPR2 True if either expr1 OR expr2 is true.
|
||||
|
||||
arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne,
|
||||
-lt, -le, -gt, or -ge.
|
||||
|
||||
Arithmetic binary operators return true if ARG1 is equal, not-equal,
|
||||
less-than, less-than-or-equal, greater-than, or greater-than-or-equal
|
||||
than ARG2.
|
||||
|
||||
Exit Status:
|
||||
Returns success if EXPR evaluates to true; fails if EXPR evaluates to
|
||||
false or an invalid argument is given.
|
||||
$END
|
||||
|
||||
$BUILTIN [
|
||||
$DOCNAME test_bracket
|
||||
$FUNCTION test_builtin
|
||||
$SHORT_DOC [ arg... ]
|
||||
Evaluate conditional expression.
|
||||
|
||||
This is a synonym for the "test" builtin, but the last argument must
|
||||
be a literal `]', to match the opening `['.
|
||||
$END
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# ifdef _MINIX
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "../bashansi.h"
|
||||
#include "../bashintl.h"
|
||||
|
||||
#include "../shell.h"
|
||||
#include "../test.h"
|
||||
#include "common.h"
|
||||
|
||||
extern char *this_command_name;
|
||||
|
||||
/* TEST/[ builtin. */
|
||||
int
|
||||
test_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
char **argv;
|
||||
int argc, result;
|
||||
|
||||
/* We let Matthew Bradburn and Kevin Braunsdorf's code do the
|
||||
actual test command. So turn the list of args into an array
|
||||
of strings, since that is what their code wants. */
|
||||
if (list == 0)
|
||||
{
|
||||
if (this_command_name[0] == '[' && !this_command_name[1])
|
||||
{
|
||||
builtin_error (_("missing `]'"));
|
||||
return (EX_BADUSAGE);
|
||||
}
|
||||
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
argv = make_builtin_argv (list, &argc);
|
||||
result = test_command (argc, argv);
|
||||
free ((char *)argv);
|
||||
|
||||
return (result);
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
This file is wait.def, from which is created wait.c.
|
||||
It implements the builtin "wait" in Bash.
|
||||
|
||||
Copyright (C) 1987-2012 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/>.
|
||||
|
||||
$BUILTIN wait
|
||||
$FUNCTION wait_builtin
|
||||
$DEPENDS_ON JOB_CONTROL
|
||||
$PRODUCES wait.c
|
||||
$SHORT_DOC wait [id ...]
|
||||
Wait for job completion and return exit status.
|
||||
|
||||
Waits for each process identified by an ID, which may be a process ID or a
|
||||
job specification, and reports its termination status. If ID is not
|
||||
given, waits for all currently active child processes, and the return
|
||||
status is zero. If ID is a a job specification, waits for all processes
|
||||
in that job's pipeline.
|
||||
|
||||
Exit Status:
|
||||
Returns the status of the last ID; fails if ID is invalid or an invalid
|
||||
option is given.
|
||||
$END
|
||||
|
||||
$BUILTIN wait
|
||||
$FUNCTION wait_builtin
|
||||
$DEPENDS_ON !JOB_CONTROL
|
||||
$SHORT_DOC wait [pid ...]
|
||||
Wait for process completion and return exit status.
|
||||
|
||||
Waits for each process specified by a PID and reports its termination status.
|
||||
If PID is not given, waits for all currently active child processes,
|
||||
and the return status is zero. PID must be a process ID.
|
||||
|
||||
Exit Status:
|
||||
Returns the status of the last PID; fails if PID is invalid or an invalid
|
||||
option is given.
|
||||
$END
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "../bashtypes.h"
|
||||
#include <signal.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <chartypes.h>
|
||||
|
||||
#include "../bashansi.h"
|
||||
|
||||
#include "../shell.h"
|
||||
#include "../jobs.h"
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
extern int wait_signal_received;
|
||||
|
||||
procenv_t wait_intr_buf;
|
||||
|
||||
/* Wait for the pid in LIST to stop or die. If no arguments are given, then
|
||||
wait for all of the active background processes of the shell and return
|
||||
0. If a list of pids or job specs are given, return the exit status of
|
||||
the last one waited for. */
|
||||
|
||||
#define WAIT_RETURN(s) \
|
||||
do \
|
||||
{ \
|
||||
interrupt_immediately = old_interrupt_immediately;\
|
||||
wait_signal_received = 0; \
|
||||
return (s);\
|
||||
} \
|
||||
while (0)
|
||||
|
||||
int
|
||||
wait_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int status, code;
|
||||
volatile int old_interrupt_immediately;
|
||||
|
||||
USE_VAR(list);
|
||||
|
||||
if (no_options (list))
|
||||
return (EX_USAGE);
|
||||
list = loptend;
|
||||
|
||||
old_interrupt_immediately = interrupt_immediately;
|
||||
#if 0
|
||||
interrupt_immediately++;
|
||||
#endif
|
||||
|
||||
/* POSIX.2 says: When the shell is waiting (by means of the wait utility)
|
||||
for asynchronous commands to complete, the reception of a signal for
|
||||
which a trap has been set shall cause the wait utility to return
|
||||
immediately with an exit status greater than 128, after which the trap
|
||||
associated with the signal shall be taken.
|
||||
|
||||
We handle SIGINT here; it's the only one that needs to be treated
|
||||
specially (I think), since it's handled specially in {no,}jobs.c. */
|
||||
code = setjmp (wait_intr_buf);
|
||||
if (code)
|
||||
{
|
||||
status = 128 + wait_signal_received;
|
||||
WAIT_RETURN (status);
|
||||
}
|
||||
|
||||
/* We support jobs or pids.
|
||||
wait <pid-or-job> [pid-or-job ...] */
|
||||
|
||||
/* But wait without any arguments means to wait for all of the shell's
|
||||
currently active background processes. */
|
||||
if (list == 0)
|
||||
{
|
||||
wait_for_background_pids ();
|
||||
WAIT_RETURN (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
status = EXECUTION_SUCCESS;
|
||||
while (list)
|
||||
{
|
||||
pid_t pid;
|
||||
char *w;
|
||||
intmax_t pid_value;
|
||||
|
||||
w = list->word->word;
|
||||
if (DIGIT (*w))
|
||||
{
|
||||
if (legal_number (w, &pid_value) && pid_value == (pid_t)pid_value)
|
||||
{
|
||||
pid = (pid_t)pid_value;
|
||||
status = wait_for_single_pid (pid);
|
||||
}
|
||||
else
|
||||
{
|
||||
sh_badpid (w);
|
||||
WAIT_RETURN (EXECUTION_FAILURE);
|
||||
}
|
||||
}
|
||||
#if defined (JOB_CONTROL)
|
||||
else if (*w && *w == '%')
|
||||
/* Must be a job spec. Check it out. */
|
||||
{
|
||||
int job;
|
||||
sigset_t set, oset;
|
||||
|
||||
BLOCK_CHILD (set, oset);
|
||||
job = get_job_spec (list);
|
||||
|
||||
if (INVALID_JOB (job))
|
||||
{
|
||||
if (job != DUP_JOB)
|
||||
sh_badjob (list->word->word);
|
||||
UNBLOCK_CHILD (oset);
|
||||
status = 127; /* As per Posix.2, section 4.70.2 */
|
||||
list = list->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Job spec used. Wait for the last pid in the pipeline. */
|
||||
UNBLOCK_CHILD (oset);
|
||||
status = wait_for_job (job);
|
||||
}
|
||||
#endif /* JOB_CONTROL */
|
||||
else
|
||||
{
|
||||
sh_badpid (w);
|
||||
status = EXECUTION_FAILURE;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
WAIT_RETURN (status);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
# 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
-10224
File diff suppressed because it is too large
Load Diff
-8617
File diff suppressed because it is too large
Load Diff
@@ -1,10 +0,0 @@
|
||||
@ignore
|
||||
Copyright (C) 1988-2012 Free Software Foundation, Inc.
|
||||
@end ignore
|
||||
|
||||
@set LASTCHANGE Mon Sep 17 09:26:53 EDT 2012
|
||||
|
||||
@set EDITION 4.2
|
||||
@set VERSION 4.2
|
||||
@set UPDATED 17 September 2012
|
||||
@set UPDATED-MONTH September 2012
|
||||
@@ -1,238 +0,0 @@
|
||||
#
|
||||
# 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
|
||||
@@ -1,549 +0,0 @@
|
||||
#!/bin/bash
|
||||
# ash -- "Adventure shell"
|
||||
# last edit: 86/04/21 D A Gwyn
|
||||
# SCCS ID: @(#)ash.sh 1.4
|
||||
|
||||
OPATH=$PATH
|
||||
|
||||
ask()
|
||||
{
|
||||
echo -n "$@" '[y/n] '
|
||||
read ans
|
||||
|
||||
case "$ans" in
|
||||
y*|Y*)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
CAT=${PAGER:-more}
|
||||
|
||||
ash_inst()
|
||||
{
|
||||
cat <<- EOF
|
||||
|
||||
Instructions for the Adventure shell
|
||||
|
||||
Welcome to the Adventure shell! In this exploration of the UNIX file
|
||||
system, I will act as your eyes and hands. As you move around, I will
|
||||
describe whatever is visible and will carry out your commands. The
|
||||
general form of a command is
|
||||
Verb Object Extra_stuff.
|
||||
Most commands pay no attention to the "Extra_stuff", and many do not
|
||||
need an "Object". A typical command is
|
||||
get all
|
||||
which picks up all files in the current "room" (directory). You can
|
||||
find out what you are carrying by typing the command
|
||||
inventory
|
||||
The command "help" results in a full description of all commands that I
|
||||
understand. To quit the Adventure shell, type
|
||||
quit
|
||||
|
||||
There are UNIX monsters lurking in the background. These are also
|
||||
known as "commands with arguments".
|
||||
|
||||
Good luck!
|
||||
EOF
|
||||
}
|
||||
|
||||
ash_help()
|
||||
{
|
||||
echo "I understand the following commands (synonyms in parentheses):"
|
||||
echo ""
|
||||
|
||||
echo "change OBJECT to NEW_NAME changes the name of the object"
|
||||
echo "clone OBJECT as NEW_NAME duplicates the object"
|
||||
echo "drop OBJECTS leaves the objects in the room"
|
||||
echo "enter (go) PASSAGE takes the labeled passage"
|
||||
echo "examine OBJECTS describes the objects in detail"
|
||||
echo "feed OBJECT to MONSTER stuffs the object into a UNIX monster"
|
||||
echo "get (take) OBJECTS picks up the specified objects"
|
||||
echo "gripe (bug) report a problem with the Adventure shell"
|
||||
echo "help prints this summary"
|
||||
echo "inventory (i) tells what you are carrying"
|
||||
echo "kill (destroy) OBJECTS destroys the objects"
|
||||
echo "look (l) describes the room, including hidden objects"
|
||||
echo "open (read) OBJECT shows the contents of an object"
|
||||
echo "quit (exit) leaves the Adventure shell"
|
||||
echo "resurrect OBJECTS attempts to restore dead objects"
|
||||
echo "steal OBJECT from MONSTER obtains the object from a UNIX monster"
|
||||
echo "throw OBJECT at daemon feeds the object to the printer daemon"
|
||||
echo "up takes the overhead passage"
|
||||
echo "wake MONSTER awakens a UNIX monster"
|
||||
echo "where (w) tells you where you are"
|
||||
echo "xyzzy moves you to your home"
|
||||
}
|
||||
|
||||
MAINT=chet@ins.cwru.edu
|
||||
|
||||
PATH=/usr/ucb:/bin:/usr/bin:/usr/local/bin:.
|
||||
export PATH
|
||||
|
||||
trap 'echo Ouch!' 2 3
|
||||
#trap '' 18 # disable Berkeley job control
|
||||
|
||||
ash_lk(){ echo " $1 " | fgrep " $2 " >&- 2>&-; }
|
||||
ash_pr(){ echo $* | tr ' ' '\012' | pr -5 -t -w75 -l$[ ( $# + 4 ) / 5 ]; }
|
||||
ash_rm(){ echo " $1 " | sed -e "s/ $2 / /" -e 's/^ //' -e 's/ $//'; }
|
||||
|
||||
# enable history, bang history expansion, and emacs editing
|
||||
set -o history
|
||||
set -o histexpand
|
||||
set -o emacs
|
||||
|
||||
cd
|
||||
LIM=.limbo # $HOME/$LIM contains "destroyed" objects
|
||||
mkdir $LIM >&- 2>&-
|
||||
KNAP=.knapsack # $HOME/$KNAP contains objects being "carried"
|
||||
if [ ! -d $KNAP ]
|
||||
then mkdir $KNAP >&- 2>&-
|
||||
if [ $? = 0 ]
|
||||
then echo 'You found a discarded empty knapsack.'
|
||||
else echo 'You have no knapsack to carry things in.'
|
||||
exit 1
|
||||
fi
|
||||
else echo 'One moment while I peek in your old knapsack...'
|
||||
fi
|
||||
|
||||
kn=`echo \`ls -a $KNAP | sed -e '/^\.$/d' -e '/^\.\.$/d'\``
|
||||
|
||||
if ask 'Welcome to the Adventure shell! Do you need instructions?'
|
||||
then
|
||||
ash_inst
|
||||
echo -n 'Type a newline to continue: '
|
||||
read
|
||||
fi
|
||||
|
||||
wiz=false
|
||||
cha=false
|
||||
prev=$LIM
|
||||
while :
|
||||
do room=`pwd`
|
||||
if [ $room != $prev ]
|
||||
then if [ $room = $HOME ]
|
||||
then echo 'You are in your own home.'
|
||||
else echo "You have entered $room."
|
||||
fi
|
||||
exs=
|
||||
obs=
|
||||
hexs=
|
||||
hobs=
|
||||
f=false
|
||||
for i in `ls -a`
|
||||
do case $i in
|
||||
.|..) ;;
|
||||
.*) if [ -f $i ]
|
||||
then hobs="$hobs $i"
|
||||
elif [ -d $i ]
|
||||
then hexs="$hexs $i"
|
||||
else f=true
|
||||
fi
|
||||
;;
|
||||
*) if [ -f $i ]
|
||||
then obs="$obs $i"
|
||||
elif [ -d $i ]
|
||||
then exs="$exs $i"
|
||||
else f=true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [ "$obs" ]
|
||||
then echo 'This room contains:'
|
||||
ash_pr $obs
|
||||
else echo 'The room looks empty.'
|
||||
fi
|
||||
if [ "$exs" ]
|
||||
then echo 'There are exits labeled:'
|
||||
ash_pr $exs
|
||||
echo 'as well as a passage overhead.'
|
||||
else echo 'There is a passage overhead.'
|
||||
fi
|
||||
if sh -c $f
|
||||
then echo 'There are shadowy figures in the corner.'
|
||||
fi
|
||||
prev=$room
|
||||
fi
|
||||
|
||||
read -e -p '-advsh> ' verb obj x # prompt is '-advsh> '
|
||||
if [ $? != 0 ]
|
||||
then verb=quit # EOF
|
||||
fi
|
||||
|
||||
case $verb in
|
||||
change) if [ "$obj" ]
|
||||
then if ash_lk "$obs $hobs" "$obj"
|
||||
then set -- $x
|
||||
case "$1" in
|
||||
to) if [ "$2" ]
|
||||
then if [ -f $2 ]
|
||||
then echo "You must destroy $2 first."
|
||||
set --
|
||||
fi
|
||||
if [ "$2" ]
|
||||
then if mv $obj $2 >&- 2>&-
|
||||
then echo "The $obj shimmers and turns into $2."
|
||||
obs=`ash_rm "$2 $obs" "$obj"`
|
||||
else echo "There is a cloud of smoke but the $obj is unchanged."
|
||||
fi
|
||||
fi
|
||||
else echo 'To what?'
|
||||
fi
|
||||
;;
|
||||
*) echo "Change $obj to what?"
|
||||
;;
|
||||
esac
|
||||
else if ash_lk "$kn" "$obj"
|
||||
then echo 'You must drop it first.'
|
||||
else echo "I see no $obj here."
|
||||
fi
|
||||
fi
|
||||
else echo 'Change what?'
|
||||
fi
|
||||
;;
|
||||
clone) if [ "$obj" ]
|
||||
then if ash_lk "$obs $hobs" "$obj"
|
||||
then if [ ! -r $obj ]
|
||||
then echo "The $obj does not wish to be cloned."
|
||||
else set -- $x
|
||||
case "$1" in
|
||||
as) if [ "$2" ]
|
||||
then if [ -f $2 ]
|
||||
then echo "You must destroy $2 first."
|
||||
else if cp $obj $2 >&- 2>&-
|
||||
then echo "Poof! When the smoke clears, you see the new $2."
|
||||
obs="$obs $2"
|
||||
else echo 'You hear a dull thud but no clone appears.'
|
||||
fi
|
||||
fi
|
||||
else echo 'As what?'
|
||||
fi
|
||||
;;
|
||||
*) echo "Clone $obj as what?"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
else if ash_lk "$kn" "$obj"
|
||||
then echo 'You must drop it first.'
|
||||
else echo "I see no $obj here."
|
||||
fi
|
||||
fi
|
||||
else echo 'Clone what?'
|
||||
fi
|
||||
;;
|
||||
drop) if [ "$obj" ]
|
||||
then for it in $obj $x
|
||||
do if ash_lk "$kn" "$it"
|
||||
then if [ -w $it ]
|
||||
then echo "You must destroy $it first."
|
||||
else if mv $HOME/$KNAP/$it $it >&- 2>&-
|
||||
then echo "$it: dropped."
|
||||
kn=`ash_rm "$kn" "$it"`
|
||||
obs=`echo $it $obs`
|
||||
else echo "The $it is caught in your knapsack."
|
||||
fi
|
||||
fi
|
||||
else echo "You're not carrying the $it!"
|
||||
fi
|
||||
done
|
||||
else echo 'Drop what?'
|
||||
fi
|
||||
;;
|
||||
enter|go) if [ "$obj" ]
|
||||
then if [ $obj != up ]
|
||||
then if ash_lk "$exs $hexs" "$obj"
|
||||
then if [ -x $obj ]
|
||||
then if cd $obj
|
||||
then echo 'You squeeze through the passage.'
|
||||
else echo "You can't go that direction."
|
||||
fi
|
||||
else echo 'An invisible force blocks your way.'
|
||||
fi
|
||||
else echo 'I see no such passage.'
|
||||
fi
|
||||
else if cd ..
|
||||
then echo 'You struggle upwards.'
|
||||
else echo "You can't reach that high."
|
||||
fi
|
||||
fi
|
||||
else echo 'Which passage?'
|
||||
fi
|
||||
;;
|
||||
examine) if [ "$obj" ]
|
||||
then if [ $obj = all ]
|
||||
then $obj=`echo $obs $exs`
|
||||
x=
|
||||
fi
|
||||
for it in $obj $x
|
||||
do if ash_lk "$obs $hobs $exs $hexs" "$it"
|
||||
then echo "Upon close inspection of the $it, you see:"
|
||||
ls -ld $it 2>&-
|
||||
if [ $? != 0 ]
|
||||
then echo "-- when you look directly at the $it, it vanishes."
|
||||
fi
|
||||
else if ash_lk "$kn" "$it"
|
||||
then echo 'You must drop it first.'
|
||||
else echo "I see no $it here."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else echo 'Examine what?'
|
||||
fi
|
||||
;;
|
||||
feed) if [ "$obj" ]
|
||||
then if ash_lk "$obs $hobs" "$obj"
|
||||
then set -- $x
|
||||
case "$1" in
|
||||
to) if [ "$2" ]
|
||||
then shift
|
||||
if PATH=$OPATH $* <$obj 2>&-
|
||||
then echo "The $1 monster devours your $obj."
|
||||
if rm -f $obj >&- 2>&-
|
||||
then obs=`ash_rm "$obs" "$obj"`
|
||||
else echo 'But he spits it back up.'
|
||||
fi
|
||||
else echo "The $1 monster holds his nose in disdain."
|
||||
fi
|
||||
else echo 'To what?'
|
||||
fi
|
||||
;;
|
||||
*) echo "Feed $obj to what?"
|
||||
;;
|
||||
esac
|
||||
else if ash_lk "$kn" "$obj"
|
||||
then echo 'You must drop it first.'
|
||||
else echo "I see no $obj here."
|
||||
fi
|
||||
fi
|
||||
else echo 'Feed what?'
|
||||
fi
|
||||
;;
|
||||
get|take) if [ "$obj" ]
|
||||
then if [ $obj = all ]
|
||||
then obj="$obs"
|
||||
x=
|
||||
fi
|
||||
for it in $obj $x
|
||||
do if ash_lk "$obs $hobs" "$it"
|
||||
then if ash_lk "$kn" "$it"
|
||||
then echo 'You already have one.'
|
||||
else if mv $it $HOME/$KNAP/$it >&- 2>&-
|
||||
then echo "$it: taken."
|
||||
kn="$it $kn"
|
||||
obs=`ash_rm "$obs" "$it"`
|
||||
else echo "The $it is too heavy."
|
||||
fi
|
||||
fi
|
||||
else echo "I see no $it here."
|
||||
fi
|
||||
done
|
||||
else echo 'Get what?'
|
||||
fi
|
||||
;;
|
||||
gripe|bug) echo 'Please describe the problem and your situation at the time it failed.\nEnd the bug report with a line containing just a Ctrl-D.'
|
||||
cat | mail $MAINT -s 'ash bug'
|
||||
echo 'Thank you!'
|
||||
;;
|
||||
help) ash_help
|
||||
;;
|
||||
inventory|i) if [ "$kn" ]
|
||||
then echo 'Your knapsack contains:'
|
||||
ash_pr $kn
|
||||
else echo 'You are poverty-stricken.'
|
||||
fi
|
||||
;;
|
||||
kill|destroy) if [ "$obj" ]
|
||||
then if [ $obj = all ]
|
||||
then x=
|
||||
if ask "Do you really want to attempt to $verb them all?"
|
||||
then obj=`echo $obs`
|
||||
else echo 'Chicken!'
|
||||
obj=
|
||||
fi
|
||||
fi
|
||||
for it in $obj $x
|
||||
do if ash_lk "$obs $hobs" "$it"
|
||||
then if mv $it $HOME/$LIM <&- >&- 2>&-
|
||||
then if [ $verb = kill ]
|
||||
then echo "The $it cannot defend himself; he dies."
|
||||
else echo "You have destroyed the $it; it vanishes."
|
||||
fi
|
||||
obs=`ash_rm "$obs" "$it"`
|
||||
else if [ $verb = kill ]
|
||||
then echo "Your feeble blows are no match for the $it."
|
||||
else echo "The $it is indestructible."
|
||||
fi
|
||||
fi
|
||||
else if ash_lk "$kn" "$it"
|
||||
then echo "You must drop the $it first."
|
||||
found=false
|
||||
else echo "I see no $it here."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else echo 'Kill what?'
|
||||
fi
|
||||
;;
|
||||
look|l) obs=`echo $obs $hobs`
|
||||
hobs=
|
||||
if [ "$obs" ]
|
||||
then echo 'The room contains:'
|
||||
ash_pr $obs
|
||||
else echo 'The room is empty.'
|
||||
fi
|
||||
exs=`echo $exs $hexs`
|
||||
hexs=
|
||||
if [ "$exs" ]
|
||||
then echo 'There are exits plainly labeled:'
|
||||
ash_pr $exs
|
||||
echo 'and a passage directly overhead.'
|
||||
else echo 'The only exit is directly overhead.'
|
||||
fi
|
||||
;;
|
||||
magic) if [ "$obj" = mode ]
|
||||
then if sh -c $cha
|
||||
then echo 'You had your chance and you blew it.'
|
||||
else if ask 'Are you a wizard?'
|
||||
then echo -n 'Prove it! Say the magic word: '
|
||||
read obj
|
||||
if [ "$obj" = armadillo ]
|
||||
then echo 'Yes, master!!'
|
||||
wiz=true
|
||||
else echo "Homie says: I don't think so"
|
||||
cha=true
|
||||
fi
|
||||
else echo "I didn't think so."
|
||||
fi
|
||||
fi
|
||||
else echo 'Nice try.'
|
||||
fi
|
||||
;;
|
||||
open|read) if [ "$obj" ]
|
||||
then if ash_lk "$obs $hobs" "$obj"
|
||||
then if [ -r $obj ]
|
||||
then if [ -s $obj ]
|
||||
then echo "Opening the $obj reveals:"
|
||||
$CAT < $obj
|
||||
if [ $? != 0 ]
|
||||
then echo '-- oops, you lost the contents!'
|
||||
fi
|
||||
else echo "There is nothing inside the $obj."
|
||||
fi
|
||||
else echo "You do not have the proper tools to open the $obj."
|
||||
fi
|
||||
else if ash_lk "$kn" "$obj"
|
||||
then echo 'You must drop it first.'
|
||||
found=false
|
||||
else echo "I see no $obj here."
|
||||
fi
|
||||
fi
|
||||
else echo 'Open what?'
|
||||
fi
|
||||
;;
|
||||
quit|exit) if ask 'Do you really want to quit now?'
|
||||
then if [ "$kn" ]
|
||||
then echo 'The contents of your knapsack will still be there next time.'
|
||||
fi
|
||||
rm -rf $HOME/$LIM
|
||||
echo 'See you later!'
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
resurrect) if [ "$obj" ]
|
||||
then for it in $obj $x
|
||||
do if ash_lk "$obs $hobs" "$it"
|
||||
then echo "The $it is already alive and well."
|
||||
else if mv $HOME/$LIM/$it $it <&- >&- 2>&-
|
||||
then echo "The $it staggers to his feet."
|
||||
obs=`echo $it $obs`
|
||||
else echo "There are sparks but no $it appears."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else echo 'Resurrect what?'
|
||||
fi
|
||||
;;
|
||||
steal) if [ "$obj" ]
|
||||
then if ash_lk "$obs $hobs" "$obj"
|
||||
then echo 'There is already one here.'
|
||||
else set -- $x
|
||||
case "$1" in
|
||||
from) if [ "$2" ]
|
||||
then shift
|
||||
if PATH=$OPATH $* >$obj 2>&-
|
||||
then echo "The $1 monster drops the $obj."
|
||||
obs=`echo $obj $obs`
|
||||
else echo "The $1 monster runs away as you approach."
|
||||
rm -f $obj >&- 2>&-
|
||||
fi
|
||||
else echo 'From what?'
|
||||
fi
|
||||
;;
|
||||
*) echo "Steal $obj from what?"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
else echo 'Steal what?'
|
||||
fi
|
||||
;;
|
||||
throw) if [ "$obj" ]
|
||||
then if ash_lk "$obs $hobs" "$obj"
|
||||
then set -- $x
|
||||
case "$1" in
|
||||
at) case "$2" in
|
||||
daemon) if sh -c "lpr -r $obj"
|
||||
then echo "The daemon catches the $obj, turns it into paper,\nand leaves it in the basket."
|
||||
obs=`ash_rm "$obs" "$obj"`
|
||||
else echo "The daemon is nowhere to be found."
|
||||
fi
|
||||
;;
|
||||
*) echo 'At what?'
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*) echo "Throw $obj at what?"
|
||||
;;
|
||||
esac
|
||||
else if ash_lk "$kn" "$obj"
|
||||
then echo 'It is in your knapsack.'
|
||||
found=false
|
||||
else echo "I see no $obj here."
|
||||
fi
|
||||
fi
|
||||
else echo 'Throw what?'
|
||||
fi
|
||||
;;
|
||||
u|up) if cd ..
|
||||
then echo 'You pull yourself up a level.'
|
||||
else echo "You can't reach that high."
|
||||
fi
|
||||
;;
|
||||
wake) if [ "$obj" ]
|
||||
then echo "You awaken the $obj monster:"
|
||||
PATH=$OPATH $obj $x
|
||||
echo 'The monster slithers back into the darkness.'
|
||||
else echo 'Wake what?'
|
||||
fi
|
||||
;;
|
||||
w|where) echo "You are in $room."
|
||||
;;
|
||||
xyzzy) if cd
|
||||
then echo 'A strange feeling comes over you.'
|
||||
else echo 'Your spell fizzles out.'
|
||||
fi
|
||||
;;
|
||||
*) if [ "$verb" ]
|
||||
then if sh -c $wiz
|
||||
then PATH=$OPATH $verb $obj $x
|
||||
else echo "I don't know how to \"$verb\"."
|
||||
echo 'Type "help" for assistance.'
|
||||
fi
|
||||
else echo 'Say something!'
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
@@ -1,662 +0,0 @@
|
||||
/* input.c -- functions to perform buffered input with synchronization. */
|
||||
|
||||
/* Copyright (C) 1992-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 (_MINIX) && defined (HAVE_SYS_FILE_H)
|
||||
# include <sys/file.h>
|
||||
#endif
|
||||
#include "filecntl.h"
|
||||
#include "posixstat.h"
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "bashansi.h"
|
||||
#include "bashintl.h"
|
||||
|
||||
#include "command.h"
|
||||
#include "general.h"
|
||||
#include "input.h"
|
||||
#include "error.h"
|
||||
#include "externs.h"
|
||||
#include "quit.h"
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif /* !errno */
|
||||
|
||||
#if defined (EAGAIN)
|
||||
# define X_EAGAIN EAGAIN
|
||||
#else
|
||||
# define X_EAGAIN -99
|
||||
#endif
|
||||
|
||||
#if defined (EWOULDBLOCK)
|
||||
# define X_EWOULDBLOCK EWOULDBLOCK
|
||||
#else
|
||||
# define X_EWOULDBLOCK -99
|
||||
#endif
|
||||
|
||||
extern void termsig_handler __P((int));
|
||||
|
||||
/* Functions to handle reading input on systems that don't restart read(2)
|
||||
if a signal is received. */
|
||||
|
||||
static char localbuf[128];
|
||||
static int local_index = 0, local_bufused = 0;
|
||||
|
||||
/* Posix and USG systems do not guarantee to restart read () if it is
|
||||
interrupted by a signal. We do the read ourselves, and restart it
|
||||
if it returns EINTR. */
|
||||
int
|
||||
getc_with_restart (stream)
|
||||
FILE *stream;
|
||||
{
|
||||
unsigned char uc;
|
||||
|
||||
CHECK_TERMSIG;
|
||||
|
||||
/* Try local buffering to reduce the number of read(2) calls. */
|
||||
if (local_index == local_bufused || local_bufused == 0)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
CHECK_TERMSIG;
|
||||
run_pending_traps ();
|
||||
|
||||
local_bufused = read (fileno (stream), localbuf, sizeof(localbuf));
|
||||
if (local_bufused > 0)
|
||||
break;
|
||||
else if (local_bufused == 0)
|
||||
{
|
||||
local_index = 0;
|
||||
return EOF;
|
||||
}
|
||||
else if (errno == X_EAGAIN || errno == X_EWOULDBLOCK)
|
||||
{
|
||||
if (sh_unset_nodelay_mode (fileno (stream)) < 0)
|
||||
{
|
||||
sys_error (_("cannot reset nodelay mode for fd %d"), fileno (stream));
|
||||
return EOF;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (errno != EINTR)
|
||||
{
|
||||
local_index = 0;
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
local_index = 0;
|
||||
}
|
||||
uc = localbuf[local_index++];
|
||||
return uc;
|
||||
}
|
||||
|
||||
int
|
||||
ungetc_with_restart (c, stream)
|
||||
int c;
|
||||
FILE *stream;
|
||||
{
|
||||
if (local_index == 0 || c == EOF)
|
||||
return EOF;
|
||||
localbuf[--local_index] = c;
|
||||
return c;
|
||||
}
|
||||
|
||||
#if defined (BUFFERED_INPUT)
|
||||
|
||||
/* A facility similar to stdio, but input-only. */
|
||||
|
||||
#if defined (USING_BASH_MALLOC)
|
||||
# define MAX_INPUT_BUFFER_SIZE 8176
|
||||
#else
|
||||
# define MAX_INPUT_BUFFER_SIZE 8192
|
||||
#endif
|
||||
|
||||
#if !defined (SEEK_CUR)
|
||||
# define SEEK_CUR 1
|
||||
#endif /* !SEEK_CUR */
|
||||
|
||||
#ifdef max
|
||||
# undef max
|
||||
#endif
|
||||
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#ifdef min
|
||||
# undef min
|
||||
#endif
|
||||
#define min(a, b) ((a) > (b) ? (b) : (a))
|
||||
|
||||
extern int interactive_shell;
|
||||
|
||||
int bash_input_fd_changed;
|
||||
|
||||
/* This provides a way to map from a file descriptor to the buffer
|
||||
associated with that file descriptor, rather than just the other
|
||||
way around. This is needed so that buffers are managed properly
|
||||
in constructs like 3<&4. buffers[x]->b_fd == x -- that is how the
|
||||
correspondence is maintained. */
|
||||
static BUFFERED_STREAM **buffers = (BUFFERED_STREAM **)NULL;
|
||||
static int nbuffers;
|
||||
|
||||
#define ALLOCATE_BUFFERS(n) \
|
||||
do { if ((n) >= nbuffers) allocate_buffers (n); } while (0)
|
||||
|
||||
/* Make sure `buffers' has at least N elements. */
|
||||
static void
|
||||
allocate_buffers (n)
|
||||
int n;
|
||||
{
|
||||
register int i, orig_nbuffers;
|
||||
|
||||
orig_nbuffers = nbuffers;
|
||||
nbuffers = n + 20;
|
||||
buffers = (BUFFERED_STREAM **)xrealloc
|
||||
(buffers, nbuffers * sizeof (BUFFERED_STREAM *));
|
||||
|
||||
/* Zero out the new buffers. */
|
||||
for (i = orig_nbuffers; i < nbuffers; i++)
|
||||
buffers[i] = (BUFFERED_STREAM *)NULL;
|
||||
}
|
||||
|
||||
/* Construct and return a BUFFERED_STREAM corresponding to file descriptor
|
||||
FD, using BUFFER. */
|
||||
static BUFFERED_STREAM *
|
||||
make_buffered_stream (fd, buffer, bufsize)
|
||||
int fd;
|
||||
char *buffer;
|
||||
size_t bufsize;
|
||||
{
|
||||
BUFFERED_STREAM *bp;
|
||||
|
||||
bp = (BUFFERED_STREAM *)xmalloc (sizeof (BUFFERED_STREAM));
|
||||
ALLOCATE_BUFFERS (fd);
|
||||
buffers[fd] = bp;
|
||||
bp->b_fd = fd;
|
||||
bp->b_buffer = buffer;
|
||||
bp->b_size = bufsize;
|
||||
bp->b_used = bp->b_inputp = bp->b_flag = 0;
|
||||
if (bufsize == 1)
|
||||
bp->b_flag |= B_UNBUFF;
|
||||
if (O_TEXT && (fcntl (fd, F_GETFL) & O_TEXT) != 0)
|
||||
bp->b_flag |= O_TEXT;
|
||||
return (bp);
|
||||
}
|
||||
|
||||
/* Allocate a new BUFFERED_STREAM, copy BP to it, and return the new copy. */
|
||||
static BUFFERED_STREAM *
|
||||
copy_buffered_stream (bp)
|
||||
BUFFERED_STREAM *bp;
|
||||
{
|
||||
BUFFERED_STREAM *nbp;
|
||||
|
||||
if (!bp)
|
||||
return ((BUFFERED_STREAM *)NULL);
|
||||
|
||||
nbp = (BUFFERED_STREAM *)xmalloc (sizeof (BUFFERED_STREAM));
|
||||
xbcopy ((char *)bp, (char *)nbp, sizeof (BUFFERED_STREAM));
|
||||
return (nbp);
|
||||
}
|
||||
|
||||
int
|
||||
set_bash_input_fd (fd)
|
||||
int fd;
|
||||
{
|
||||
if (bash_input.type == st_bstream)
|
||||
bash_input.location.buffered_fd = fd;
|
||||
else if (interactive_shell == 0)
|
||||
default_buffered_input = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fd_is_bash_input (fd)
|
||||
int fd;
|
||||
{
|
||||
if (bash_input.type == st_bstream && bash_input.location.buffered_fd == fd)
|
||||
return 1;
|
||||
else if (interactive_shell == 0 && default_buffered_input == fd)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save the buffered stream corresponding to file descriptor FD (which bash
|
||||
is using to read input) to a buffered stream associated with NEW_FD. If
|
||||
NEW_FD is -1, a new file descriptor is allocated with fcntl. The new
|
||||
file descriptor is returned on success, -1 on error. */
|
||||
int
|
||||
save_bash_input (fd, new_fd)
|
||||
int fd, new_fd;
|
||||
{
|
||||
int nfd;
|
||||
|
||||
/* Sync the stream so we can re-read from the new file descriptor. We
|
||||
might be able to avoid this by copying the buffered stream verbatim
|
||||
to the new file descriptor. */
|
||||
if (buffers[fd])
|
||||
sync_buffered_stream (fd);
|
||||
|
||||
/* Now take care of duplicating the file descriptor that bash is
|
||||
using for input, so we can reinitialize it later. */
|
||||
nfd = (new_fd == -1) ? fcntl (fd, F_DUPFD, 10) : new_fd;
|
||||
if (nfd == -1)
|
||||
{
|
||||
if (fcntl (fd, F_GETFD, 0) == 0)
|
||||
sys_error (_("cannot allocate new file descriptor for bash input from fd %d"), fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buffers[nfd])
|
||||
{
|
||||
/* What's this? A stray buffer without an associated open file
|
||||
descriptor? Free up the buffer and report the error. */
|
||||
internal_error (_("save_bash_input: buffer already exists for new fd %d"), nfd);
|
||||
free_buffered_stream (buffers[nfd]);
|
||||
}
|
||||
|
||||
/* Reinitialize bash_input.location. */
|
||||
if (bash_input.type == st_bstream)
|
||||
{
|
||||
bash_input.location.buffered_fd = nfd;
|
||||
fd_to_buffered_stream (nfd);
|
||||
close_buffered_fd (fd); /* XXX */
|
||||
}
|
||||
else
|
||||
/* If the current input type is not a buffered stream, but the shell
|
||||
is not interactive and therefore using a buffered stream to read
|
||||
input (e.g. with an `eval exec 3>output' inside a script), note
|
||||
that the input fd has been changed. pop_stream() looks at this
|
||||
value and adjusts the input fd to the new value of
|
||||
default_buffered_input accordingly. */
|
||||
bash_input_fd_changed++;
|
||||
|
||||
if (default_buffered_input == fd)
|
||||
default_buffered_input = nfd;
|
||||
|
||||
SET_CLOSE_ON_EXEC (nfd);
|
||||
return nfd;
|
||||
}
|
||||
|
||||
/* Check that file descriptor FD is not the one that bash is currently
|
||||
using to read input from a script. FD is about to be duplicated onto,
|
||||
which means that the kernel will close it for us. If FD is the bash
|
||||
input file descriptor, we need to seek backwards in the script (if
|
||||
possible and necessary -- scripts read from stdin are still unbuffered),
|
||||
allocate a new file descriptor to use for bash input, and re-initialize
|
||||
the buffered stream. Make sure the file descriptor used to save bash
|
||||
input is set close-on-exec. Returns 0 on success, -1 on failure. This
|
||||
works only if fd is > 0 -- if fd == 0 and bash is reading input from
|
||||
fd 0, save_bash_input is used instead, to cooperate with input
|
||||
redirection (look at redir.c:add_undo_redirect()). */
|
||||
int
|
||||
check_bash_input (fd)
|
||||
int fd;
|
||||
{
|
||||
if (fd_is_bash_input (fd))
|
||||
{
|
||||
if (fd > 0)
|
||||
return ((save_bash_input (fd, -1) == -1) ? -1 : 0);
|
||||
else if (fd == 0)
|
||||
return ((sync_buffered_stream (fd) == -1) ? -1 : 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is the buffered stream analogue of dup2(fd1, fd2). The
|
||||
BUFFERED_STREAM corresponding to fd2 is deallocated, if one exists.
|
||||
BUFFERS[fd1] is copied to BUFFERS[fd2]. This is called by the
|
||||
redirect code for constructs like 4<&0 and 3</etc/rc.local. */
|
||||
int
|
||||
duplicate_buffered_stream (fd1, fd2)
|
||||
int fd1, fd2;
|
||||
{
|
||||
int is_bash_input, m;
|
||||
|
||||
if (fd1 == fd2)
|
||||
return 0;
|
||||
|
||||
m = max (fd1, fd2);
|
||||
ALLOCATE_BUFFERS (m);
|
||||
|
||||
/* If FD2 is the file descriptor bash is currently using for shell input,
|
||||
we need to do some extra work to make sure that the buffered stream
|
||||
actually exists (it might not if fd1 was not active, and the copy
|
||||
didn't actually do anything). */
|
||||
is_bash_input = (bash_input.type == st_bstream) &&
|
||||
(bash_input.location.buffered_fd == fd2);
|
||||
|
||||
if (buffers[fd2])
|
||||
{
|
||||
/* If the two objects share the same b_buffer, don't free it. */
|
||||
if (buffers[fd1] && buffers[fd1]->b_buffer && buffers[fd1]->b_buffer == buffers[fd2]->b_buffer)
|
||||
buffers[fd2] = (BUFFERED_STREAM *)NULL;
|
||||
else
|
||||
free_buffered_stream (buffers[fd2]);
|
||||
}
|
||||
buffers[fd2] = copy_buffered_stream (buffers[fd1]);
|
||||
if (buffers[fd2])
|
||||
buffers[fd2]->b_fd = fd2;
|
||||
|
||||
if (is_bash_input)
|
||||
{
|
||||
if (!buffers[fd2])
|
||||
fd_to_buffered_stream (fd2);
|
||||
buffers[fd2]->b_flag |= B_WASBASHINPUT;
|
||||
}
|
||||
|
||||
return (fd2);
|
||||
}
|
||||
|
||||
/* Return 1 if a seek on FD will succeed. */
|
||||
#define fd_is_seekable(fd) (lseek ((fd), 0L, SEEK_CUR) >= 0)
|
||||
|
||||
/* Take FD, a file descriptor, and create and return a buffered stream
|
||||
corresponding to it. If something is wrong and the file descriptor
|
||||
is invalid, return a NULL stream. */
|
||||
BUFFERED_STREAM *
|
||||
fd_to_buffered_stream (fd)
|
||||
int fd;
|
||||
{
|
||||
char *buffer;
|
||||
size_t size;
|
||||
struct stat sb;
|
||||
|
||||
if (fstat (fd, &sb) < 0)
|
||||
{
|
||||
close (fd);
|
||||
return ((BUFFERED_STREAM *)NULL);
|
||||
}
|
||||
|
||||
size = (fd_is_seekable (fd)) ? min (sb.st_size, MAX_INPUT_BUFFER_SIZE) : 1;
|
||||
if (size == 0)
|
||||
size = 1;
|
||||
buffer = (char *)xmalloc (size);
|
||||
|
||||
return (make_buffered_stream (fd, buffer, size));
|
||||
}
|
||||
|
||||
/* Return a buffered stream corresponding to FILE, a file name. */
|
||||
BUFFERED_STREAM *
|
||||
open_buffered_stream (file)
|
||||
char *file;
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open (file, O_RDONLY);
|
||||
return ((fd >= 0) ? fd_to_buffered_stream (fd) : (BUFFERED_STREAM *)NULL);
|
||||
}
|
||||
|
||||
/* Deallocate a buffered stream and free up its resources. Make sure we
|
||||
zero out the slot in BUFFERS that points to BP. */
|
||||
void
|
||||
free_buffered_stream (bp)
|
||||
BUFFERED_STREAM *bp;
|
||||
{
|
||||
int n;
|
||||
|
||||
if (!bp)
|
||||
return;
|
||||
|
||||
n = bp->b_fd;
|
||||
if (bp->b_buffer)
|
||||
free (bp->b_buffer);
|
||||
free (bp);
|
||||
buffers[n] = (BUFFERED_STREAM *)NULL;
|
||||
}
|
||||
|
||||
/* Close the file descriptor associated with BP, a buffered stream, and free
|
||||
up the stream. Return the status of closing BP's file descriptor. */
|
||||
int
|
||||
close_buffered_stream (bp)
|
||||
BUFFERED_STREAM *bp;
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (!bp)
|
||||
return (0);
|
||||
fd = bp->b_fd;
|
||||
free_buffered_stream (bp);
|
||||
return (close (fd));
|
||||
}
|
||||
|
||||
/* Deallocate the buffered stream associated with file descriptor FD, and
|
||||
close FD. Return the status of the close on FD. */
|
||||
int
|
||||
close_buffered_fd (fd)
|
||||
int fd;
|
||||
{
|
||||
if (fd < 0)
|
||||
{
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
if (fd >= nbuffers || !buffers || !buffers[fd])
|
||||
return (close (fd));
|
||||
return (close_buffered_stream (buffers[fd]));
|
||||
}
|
||||
|
||||
/* Make the BUFFERED_STREAM associcated with buffers[FD] be BP, and return
|
||||
the old BUFFERED_STREAM. */
|
||||
BUFFERED_STREAM *
|
||||
set_buffered_stream (fd, bp)
|
||||
int fd;
|
||||
BUFFERED_STREAM *bp;
|
||||
{
|
||||
BUFFERED_STREAM *ret;
|
||||
|
||||
ret = buffers[fd];
|
||||
buffers[fd] = bp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read a buffer full of characters from BP, a buffered stream. */
|
||||
static int
|
||||
b_fill_buffer (bp)
|
||||
BUFFERED_STREAM *bp;
|
||||
{
|
||||
ssize_t nr;
|
||||
off_t o;
|
||||
|
||||
CHECK_TERMSIG;
|
||||
/* In an environment where text and binary files are treated differently,
|
||||
compensate for lseek() on text files returning an offset different from
|
||||
the count of characters read() returns. Text-mode streams have to be
|
||||
treated as unbuffered. */
|
||||
if ((bp->b_flag & (B_TEXT | B_UNBUFF)) == B_TEXT)
|
||||
{
|
||||
o = lseek (bp->b_fd, 0, SEEK_CUR);
|
||||
nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
|
||||
if (nr > 0 && nr < lseek (bp->b_fd, 0, SEEK_CUR) - o)
|
||||
{
|
||||
lseek (bp->b_fd, o, SEEK_SET);
|
||||
bp->b_flag |= B_UNBUFF;
|
||||
bp->b_size = 1;
|
||||
nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
|
||||
}
|
||||
}
|
||||
else
|
||||
nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
|
||||
if (nr <= 0)
|
||||
{
|
||||
bp->b_used = 0;
|
||||
bp->b_buffer[0] = 0;
|
||||
if (nr == 0)
|
||||
bp->b_flag |= B_EOF;
|
||||
else
|
||||
bp->b_flag |= B_ERROR;
|
||||
return (EOF);
|
||||
}
|
||||
|
||||
bp->b_used = nr;
|
||||
bp->b_inputp = 0;
|
||||
return (bp->b_buffer[bp->b_inputp++] & 0xFF);
|
||||
}
|
||||
|
||||
/* Get a character from buffered stream BP. */
|
||||
#define bufstream_getc(bp) \
|
||||
(bp->b_inputp == bp->b_used || !bp->b_used) \
|
||||
? b_fill_buffer (bp) \
|
||||
: bp->b_buffer[bp->b_inputp++] & 0xFF
|
||||
|
||||
/* Push C back onto buffered stream BP. */
|
||||
static int
|
||||
bufstream_ungetc(c, bp)
|
||||
int c;
|
||||
BUFFERED_STREAM *bp;
|
||||
{
|
||||
if (c == EOF || bp->b_inputp == 0)
|
||||
return (EOF);
|
||||
|
||||
bp->b_buffer[--bp->b_inputp] = c;
|
||||
return (c);
|
||||
}
|
||||
|
||||
/* Seek backwards on file BFD to synchronize what we've read so far
|
||||
with the underlying file pointer. */
|
||||
int
|
||||
sync_buffered_stream (bfd)
|
||||
int bfd;
|
||||
{
|
||||
BUFFERED_STREAM *bp;
|
||||
off_t chars_left;
|
||||
|
||||
if (buffers == 0 || (bp = buffers[bfd]) == 0)
|
||||
return (-1);
|
||||
|
||||
chars_left = bp->b_used - bp->b_inputp;
|
||||
if (chars_left)
|
||||
lseek (bp->b_fd, -chars_left, SEEK_CUR);
|
||||
bp->b_used = bp->b_inputp = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
buffered_getchar ()
|
||||
{
|
||||
CHECK_TERMSIG;
|
||||
|
||||
#if !defined (DJGPP)
|
||||
return (bufstream_getc (buffers[bash_input.location.buffered_fd]));
|
||||
#else
|
||||
/* On DJGPP, ignore \r. */
|
||||
int ch;
|
||||
while ((ch = bufstream_getc (buffers[bash_input.location.buffered_fd])) == '\r')
|
||||
;
|
||||
return ch;
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
buffered_ungetchar (c)
|
||||
int c;
|
||||
{
|
||||
return (bufstream_ungetc (c, buffers[bash_input.location.buffered_fd]));
|
||||
}
|
||||
|
||||
/* Make input come from file descriptor BFD through a buffered stream. */
|
||||
void
|
||||
with_input_from_buffered_stream (bfd, name)
|
||||
int bfd;
|
||||
char *name;
|
||||
{
|
||||
INPUT_STREAM location;
|
||||
BUFFERED_STREAM *bp;
|
||||
|
||||
location.buffered_fd = bfd;
|
||||
/* Make sure the buffered stream exists. */
|
||||
bp = fd_to_buffered_stream (bfd);
|
||||
init_yy_io (bp == 0 ? return_EOF : buffered_getchar,
|
||||
buffered_ungetchar, st_bstream, name, location);
|
||||
}
|
||||
|
||||
#if defined (TEST)
|
||||
void *
|
||||
xmalloc(s)
|
||||
int s;
|
||||
{
|
||||
return (malloc (s));
|
||||
}
|
||||
|
||||
void *
|
||||
xrealloc(s, size)
|
||||
char *s;
|
||||
int size;
|
||||
{
|
||||
if (!s)
|
||||
return(malloc (size));
|
||||
else
|
||||
return(realloc (s, size));
|
||||
}
|
||||
|
||||
void
|
||||
init_yy_io ()
|
||||
{
|
||||
}
|
||||
|
||||
process(bp)
|
||||
BUFFERED_STREAM *bp;
|
||||
{
|
||||
int c;
|
||||
|
||||
while ((c = bufstream_getc(bp)) != EOF)
|
||||
putchar(c);
|
||||
}
|
||||
|
||||
BASH_INPUT bash_input;
|
||||
|
||||
struct stat dsb; /* can be used from gdb */
|
||||
|
||||
/* imitate /bin/cat */
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char **argv;
|
||||
{
|
||||
register int i;
|
||||
BUFFERED_STREAM *bp;
|
||||
|
||||
if (argc == 1) {
|
||||
bp = fd_to_buffered_stream (0);
|
||||
process(bp);
|
||||
exit(0);
|
||||
}
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (argv[i][0] == '-' && argv[i][1] == '\0') {
|
||||
bp = fd_to_buffered_stream (0);
|
||||
if (!bp)
|
||||
continue;
|
||||
process(bp);
|
||||
free_buffered_stream (bp);
|
||||
} else {
|
||||
bp = open_buffered_stream (argv[i]);
|
||||
if (!bp)
|
||||
continue;
|
||||
process(bp);
|
||||
close_buffered_stream (bp);
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
#endif /* TEST */
|
||||
#endif /* BUFFERED_INPUT */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,76 +0,0 @@
|
||||
# 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
|
||||
@@ -1,625 +0,0 @@
|
||||
/* input.c -- character input functions for readline. */
|
||||
|
||||
/* Copyright (C) 1994-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the GNU Readline Library (Readline), a library
|
||||
for reading lines of text with interactive input and history editing.
|
||||
|
||||
Readline 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.
|
||||
|
||||
Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define READLINE_LIBRARY
|
||||
|
||||
#if defined (__TANDEM)
|
||||
# include <floss.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_CONFIG_H)
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#if defined (HAVE_SYS_FILE_H)
|
||||
# include <sys/file.h>
|
||||
#endif /* HAVE_SYS_FILE_H */
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
|
||||
#if defined (HAVE_STDLIB_H)
|
||||
# include <stdlib.h>
|
||||
#else
|
||||
# include "ansi_stdlib.h"
|
||||
#endif /* HAVE_STDLIB_H */
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "posixselect.h"
|
||||
|
||||
#if defined (FIONREAD_IN_SYS_IOCTL)
|
||||
# include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif /* !errno */
|
||||
|
||||
/* System-specific feature definitions and include files. */
|
||||
#include "rldefs.h"
|
||||
#include "rlmbutil.h"
|
||||
|
||||
/* Some standard library routines. */
|
||||
#include "readline.h"
|
||||
|
||||
#include "rlprivate.h"
|
||||
#include "rlshell.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
/* What kind of non-blocking I/O do we have? */
|
||||
#if !defined (O_NDELAY) && defined (O_NONBLOCK)
|
||||
# define O_NDELAY O_NONBLOCK /* Posix style */
|
||||
#endif
|
||||
|
||||
/* Non-null means it is a pointer to a function to run while waiting for
|
||||
character input. */
|
||||
rl_hook_func_t *rl_event_hook = (rl_hook_func_t *)NULL;
|
||||
|
||||
/* A function to replace _rl_input_available for applications using the
|
||||
callback interface. */
|
||||
rl_hook_func_t *rl_input_available_hook = (rl_hook_func_t *)NULL;
|
||||
|
||||
rl_getc_func_t *rl_getc_function = rl_getc;
|
||||
|
||||
static int _keyboard_input_timeout = 100000; /* 0.1 seconds; it's in usec */
|
||||
|
||||
static int ibuffer_space PARAMS((void));
|
||||
static int rl_get_char PARAMS((int *));
|
||||
static int rl_gather_tyi PARAMS((void));
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Character Input Buffering */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
static int pop_index, push_index;
|
||||
static unsigned char ibuffer[512];
|
||||
static int ibuffer_len = sizeof (ibuffer) - 1;
|
||||
|
||||
#define any_typein (push_index != pop_index)
|
||||
|
||||
int
|
||||
_rl_any_typein ()
|
||||
{
|
||||
return any_typein;
|
||||
}
|
||||
|
||||
/* Return the amount of space available in the buffer for stuffing
|
||||
characters. */
|
||||
static int
|
||||
ibuffer_space ()
|
||||
{
|
||||
if (pop_index > push_index)
|
||||
return (pop_index - push_index - 1);
|
||||
else
|
||||
return (ibuffer_len - (push_index - pop_index));
|
||||
}
|
||||
|
||||
/* Get a key from the buffer of characters to be read.
|
||||
Return the key in KEY.
|
||||
Result is non-zero if there was a key, or 0 if there wasn't. */
|
||||
static int
|
||||
rl_get_char (key)
|
||||
int *key;
|
||||
{
|
||||
if (push_index == pop_index)
|
||||
return (0);
|
||||
|
||||
*key = ibuffer[pop_index++];
|
||||
#if 0
|
||||
if (pop_index >= ibuffer_len)
|
||||
#else
|
||||
if (pop_index > ibuffer_len)
|
||||
#endif
|
||||
pop_index = 0;
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Stuff KEY into the *front* of the input buffer.
|
||||
Returns non-zero if successful, zero if there is
|
||||
no space left in the buffer. */
|
||||
int
|
||||
_rl_unget_char (key)
|
||||
int key;
|
||||
{
|
||||
if (ibuffer_space ())
|
||||
{
|
||||
pop_index--;
|
||||
if (pop_index < 0)
|
||||
pop_index = ibuffer_len;
|
||||
ibuffer[pop_index] = key;
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
_rl_pushed_input_available ()
|
||||
{
|
||||
return (push_index != pop_index);
|
||||
}
|
||||
|
||||
/* If a character is available to be read, then read it and stuff it into
|
||||
IBUFFER. Otherwise, just return. Returns number of characters read
|
||||
(0 if none available) and -1 on error (EIO). */
|
||||
static int
|
||||
rl_gather_tyi ()
|
||||
{
|
||||
int tty;
|
||||
register int tem, result;
|
||||
int chars_avail, k;
|
||||
char input;
|
||||
#if defined(HAVE_SELECT)
|
||||
fd_set readfds, exceptfds;
|
||||
struct timeval timeout;
|
||||
#endif
|
||||
|
||||
chars_avail = 0;
|
||||
tty = fileno (rl_instream);
|
||||
|
||||
#if defined (HAVE_SELECT)
|
||||
FD_ZERO (&readfds);
|
||||
FD_ZERO (&exceptfds);
|
||||
FD_SET (tty, &readfds);
|
||||
FD_SET (tty, &exceptfds);
|
||||
USEC_TO_TIMEVAL (_keyboard_input_timeout, timeout);
|
||||
result = select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout);
|
||||
if (result <= 0)
|
||||
return 0; /* Nothing to read. */
|
||||
#endif
|
||||
|
||||
result = -1;
|
||||
#if defined (FIONREAD)
|
||||
errno = 0;
|
||||
result = ioctl (tty, FIONREAD, &chars_avail);
|
||||
if (result == -1 && errno == EIO)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
#if defined (O_NDELAY)
|
||||
if (result == -1)
|
||||
{
|
||||
tem = fcntl (tty, F_GETFL, 0);
|
||||
|
||||
fcntl (tty, F_SETFL, (tem | O_NDELAY));
|
||||
chars_avail = read (tty, &input, 1);
|
||||
|
||||
fcntl (tty, F_SETFL, tem);
|
||||
if (chars_avail == -1 && errno == EAGAIN)
|
||||
return 0;
|
||||
if (chars_avail == 0) /* EOF */
|
||||
{
|
||||
rl_stuff_char (EOF);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
#endif /* O_NDELAY */
|
||||
|
||||
#if defined (__MINGW32__)
|
||||
/* Use getch/_kbhit to check for available console input, in the same way
|
||||
that we read it normally. */
|
||||
chars_avail = isatty (tty) ? _kbhit () : 0;
|
||||
result = 0;
|
||||
#endif
|
||||
|
||||
/* If there's nothing available, don't waste time trying to read
|
||||
something. */
|
||||
if (chars_avail <= 0)
|
||||
return 0;
|
||||
|
||||
tem = ibuffer_space ();
|
||||
|
||||
if (chars_avail > tem)
|
||||
chars_avail = tem;
|
||||
|
||||
/* One cannot read all of the available input. I can only read a single
|
||||
character at a time, or else programs which require input can be
|
||||
thwarted. If the buffer is larger than one character, I lose.
|
||||
Damn! */
|
||||
if (tem < ibuffer_len)
|
||||
chars_avail = 0;
|
||||
|
||||
if (result != -1)
|
||||
{
|
||||
while (chars_avail--)
|
||||
{
|
||||
RL_CHECK_SIGNALS ();
|
||||
k = (*rl_getc_function) (rl_instream);
|
||||
if (rl_stuff_char (k) == 0)
|
||||
break; /* some problem; no more room */
|
||||
if (k == NEWLINE || k == RETURN)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (chars_avail)
|
||||
rl_stuff_char (input);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
rl_set_keyboard_input_timeout (u)
|
||||
int u;
|
||||
{
|
||||
int o;
|
||||
|
||||
o = _keyboard_input_timeout;
|
||||
if (u >= 0)
|
||||
_keyboard_input_timeout = u;
|
||||
return (o);
|
||||
}
|
||||
|
||||
/* Is there input available to be read on the readline input file
|
||||
descriptor? Only works if the system has select(2) or FIONREAD.
|
||||
Uses the value of _keyboard_input_timeout as the timeout; if another
|
||||
readline function wants to specify a timeout and not leave it up to
|
||||
the user, it should use _rl_input_queued(timeout_value_in_microseconds)
|
||||
instead. */
|
||||
int
|
||||
_rl_input_available ()
|
||||
{
|
||||
#if defined(HAVE_SELECT)
|
||||
fd_set readfds, exceptfds;
|
||||
struct timeval timeout;
|
||||
#endif
|
||||
#if !defined (HAVE_SELECT) && defined(FIONREAD)
|
||||
int chars_avail;
|
||||
#endif
|
||||
int tty;
|
||||
|
||||
if (rl_input_available_hook)
|
||||
return (*rl_input_available_hook) ();
|
||||
|
||||
tty = fileno (rl_instream);
|
||||
|
||||
#if defined (HAVE_SELECT)
|
||||
FD_ZERO (&readfds);
|
||||
FD_ZERO (&exceptfds);
|
||||
FD_SET (tty, &readfds);
|
||||
FD_SET (tty, &exceptfds);
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = _keyboard_input_timeout;
|
||||
return (select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout) > 0);
|
||||
#else
|
||||
|
||||
#if defined (FIONREAD)
|
||||
if (ioctl (tty, FIONREAD, &chars_avail) == 0)
|
||||
return (chars_avail);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined (__MINGW32__)
|
||||
if (isatty (tty))
|
||||
return (_kbhit ());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_rl_input_queued (t)
|
||||
int t;
|
||||
{
|
||||
int old_timeout, r;
|
||||
|
||||
old_timeout = rl_set_keyboard_input_timeout (t);
|
||||
r = _rl_input_available ();
|
||||
rl_set_keyboard_input_timeout (old_timeout);
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
_rl_insert_typein (c)
|
||||
int c;
|
||||
{
|
||||
int key, t, i;
|
||||
char *string;
|
||||
|
||||
i = key = 0;
|
||||
string = (char *)xmalloc (ibuffer_len + 1);
|
||||
string[i++] = (char) c;
|
||||
|
||||
while ((t = rl_get_char (&key)) &&
|
||||
_rl_keymap[key].type == ISFUNC &&
|
||||
_rl_keymap[key].function == rl_insert)
|
||||
string[i++] = key;
|
||||
|
||||
if (t)
|
||||
_rl_unget_char (key);
|
||||
|
||||
string[i] = '\0';
|
||||
rl_insert_text (string);
|
||||
xfree (string);
|
||||
}
|
||||
|
||||
/* Add KEY to the buffer of characters to be read. Returns 1 if the
|
||||
character was stuffed correctly; 0 otherwise. */
|
||||
int
|
||||
rl_stuff_char (key)
|
||||
int key;
|
||||
{
|
||||
if (ibuffer_space () == 0)
|
||||
return 0;
|
||||
|
||||
if (key == EOF)
|
||||
{
|
||||
key = NEWLINE;
|
||||
rl_pending_input = EOF;
|
||||
RL_SETSTATE (RL_STATE_INPUTPENDING);
|
||||
}
|
||||
ibuffer[push_index++] = key;
|
||||
#if 0
|
||||
if (push_index >= ibuffer_len)
|
||||
#else
|
||||
if (push_index > ibuffer_len)
|
||||
#endif
|
||||
push_index = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Make C be the next command to be executed. */
|
||||
int
|
||||
rl_execute_next (c)
|
||||
int c;
|
||||
{
|
||||
rl_pending_input = c;
|
||||
RL_SETSTATE (RL_STATE_INPUTPENDING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Clear any pending input pushed with rl_execute_next() */
|
||||
int
|
||||
rl_clear_pending_input ()
|
||||
{
|
||||
rl_pending_input = 0;
|
||||
RL_UNSETSTATE (RL_STATE_INPUTPENDING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Character Input */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* Read a key, including pending input. */
|
||||
int
|
||||
rl_read_key ()
|
||||
{
|
||||
int c, r;
|
||||
|
||||
if (rl_pending_input)
|
||||
{
|
||||
c = rl_pending_input;
|
||||
rl_clear_pending_input ();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If input is coming from a macro, then use that. */
|
||||
if (c = _rl_next_macro_key ())
|
||||
return (c);
|
||||
|
||||
/* If the user has an event function, then call it periodically. */
|
||||
if (rl_event_hook)
|
||||
{
|
||||
while (rl_event_hook)
|
||||
{
|
||||
if (rl_get_char (&c) != 0)
|
||||
break;
|
||||
|
||||
if ((r = rl_gather_tyi ()) < 0) /* XXX - EIO */
|
||||
{
|
||||
rl_done = 1;
|
||||
return ('\n');
|
||||
}
|
||||
else if (r > 0) /* read something */
|
||||
continue;
|
||||
|
||||
RL_CHECK_SIGNALS ();
|
||||
if (rl_done) /* XXX - experimental */
|
||||
return ('\n');
|
||||
(*rl_event_hook) ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rl_get_char (&c) == 0)
|
||||
c = (*rl_getc_function) (rl_instream);
|
||||
/* fprintf(stderr, "rl_read_key: calling RL_CHECK_SIGNALS: _rl_caught_signal = %d", _rl_caught_signal); */
|
||||
RL_CHECK_SIGNALS ();
|
||||
}
|
||||
}
|
||||
|
||||
return (c);
|
||||
}
|
||||
|
||||
int
|
||||
rl_getc (stream)
|
||||
FILE *stream;
|
||||
{
|
||||
int result;
|
||||
unsigned char c;
|
||||
|
||||
while (1)
|
||||
{
|
||||
RL_CHECK_SIGNALS ();
|
||||
|
||||
/* We know at this point that _rl_caught_signal == 0 */
|
||||
|
||||
#if defined (__MINGW32__)
|
||||
if (isatty (fileno (stream)))
|
||||
return (getch ());
|
||||
#endif
|
||||
result = read (fileno (stream), &c, sizeof (unsigned char));
|
||||
|
||||
if (result == sizeof (unsigned char))
|
||||
return (c);
|
||||
|
||||
/* If zero characters are returned, then the file that we are
|
||||
reading from is empty! Return EOF in that case. */
|
||||
if (result == 0)
|
||||
return (EOF);
|
||||
|
||||
#if defined (__BEOS__)
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
#if defined (EWOULDBLOCK)
|
||||
# define X_EWOULDBLOCK EWOULDBLOCK
|
||||
#else
|
||||
# define X_EWOULDBLOCK -99
|
||||
#endif
|
||||
|
||||
#if defined (EAGAIN)
|
||||
# define X_EAGAIN EAGAIN
|
||||
#else
|
||||
# define X_EAGAIN -99
|
||||
#endif
|
||||
|
||||
if (errno == X_EWOULDBLOCK || errno == X_EAGAIN)
|
||||
{
|
||||
if (sh_unset_nodelay_mode (fileno (stream)) < 0)
|
||||
return (EOF);
|
||||
continue;
|
||||
}
|
||||
|
||||
#undef X_EWOULDBLOCK
|
||||
#undef X_EAGAIN
|
||||
|
||||
fprintf(stderr, "rl_getc: result = %d errno = %d\n", result, errno);
|
||||
|
||||
/* If the error that we received was EINTR, then try again,
|
||||
this is simply an interrupted system call to read (). We allow
|
||||
the read to be interrupted if we caught SIGHUP or SIGTERM (but
|
||||
not SIGINT; let the signal handler deal with that), but if the
|
||||
application sets an event hook, call it for other signals.
|
||||
Otherwise (not EINTR), some error ocurred, also signifying EOF. */
|
||||
if (errno != EINTR)
|
||||
return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF);
|
||||
else if (_rl_caught_signal == SIGHUP || _rl_caught_signal == SIGTERM)
|
||||
return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF);
|
||||
else if (_rl_caught_signal == SIGINT || _rl_caught_signal == SIGQUIT)
|
||||
RL_CHECK_SIGNALS ();
|
||||
|
||||
if (rl_event_hook)
|
||||
{
|
||||
fprintf(stderr, "rl_getc: calling rl_event_hook\n");
|
||||
(*rl_event_hook) ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
/* read multibyte char */
|
||||
int
|
||||
_rl_read_mbchar (mbchar, size)
|
||||
char *mbchar;
|
||||
int size;
|
||||
{
|
||||
int mb_len, c;
|
||||
size_t mbchar_bytes_length;
|
||||
wchar_t wc;
|
||||
mbstate_t ps, ps_back;
|
||||
|
||||
memset(&ps, 0, sizeof (mbstate_t));
|
||||
memset(&ps_back, 0, sizeof (mbstate_t));
|
||||
|
||||
mb_len = 0;
|
||||
while (mb_len < size)
|
||||
{
|
||||
RL_SETSTATE(RL_STATE_MOREINPUT);
|
||||
c = rl_read_key ();
|
||||
RL_UNSETSTATE(RL_STATE_MOREINPUT);
|
||||
|
||||
if (c < 0)
|
||||
break;
|
||||
|
||||
mbchar[mb_len++] = c;
|
||||
|
||||
mbchar_bytes_length = mbrtowc (&wc, mbchar, mb_len, &ps);
|
||||
if (mbchar_bytes_length == (size_t)(-1))
|
||||
break; /* invalid byte sequence for the current locale */
|
||||
else if (mbchar_bytes_length == (size_t)(-2))
|
||||
{
|
||||
/* shorted bytes */
|
||||
ps = ps_back;
|
||||
continue;
|
||||
}
|
||||
else if (mbchar_bytes_length == 0)
|
||||
{
|
||||
mbchar[0] = '\0'; /* null wide character */
|
||||
mb_len = 1;
|
||||
break;
|
||||
}
|
||||
else if (mbchar_bytes_length > (size_t)(0))
|
||||
break;
|
||||
}
|
||||
|
||||
return mb_len;
|
||||
}
|
||||
|
||||
/* Read a multibyte-character string whose first character is FIRST into
|
||||
the buffer MB of length MLEN. Returns the last character read, which
|
||||
may be FIRST. Used by the search functions, among others. Very similar
|
||||
to _rl_read_mbchar. */
|
||||
int
|
||||
_rl_read_mbstring (first, mb, mlen)
|
||||
int first;
|
||||
char *mb;
|
||||
int mlen;
|
||||
{
|
||||
int i, c;
|
||||
mbstate_t ps;
|
||||
|
||||
c = first;
|
||||
memset (mb, 0, mlen);
|
||||
for (i = 0; c >= 0 && i < mlen; i++)
|
||||
{
|
||||
mb[i] = (char)c;
|
||||
memset (&ps, 0, sizeof (mbstate_t));
|
||||
if (_rl_get_char_len (mb, &ps) == -2)
|
||||
{
|
||||
/* Read more for multibyte character */
|
||||
RL_SETSTATE (RL_STATE_MOREINPUT);
|
||||
c = rl_read_key ();
|
||||
RL_UNSETSTATE (RL_STATE_MOREINPUT);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
#endif /* HANDLE_MULTIBYTE */
|
||||
@@ -1,700 +0,0 @@
|
||||
/* signals.c -- signal handling support for readline. */
|
||||
|
||||
/* Copyright (C) 1987-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the GNU Readline Library (Readline), a library
|
||||
for reading lines of text with interactive input and history editing.
|
||||
|
||||
Readline 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.
|
||||
|
||||
Readline 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 Readline. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define READLINE_LIBRARY
|
||||
|
||||
#if defined (HAVE_CONFIG_H)
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h> /* Just for NULL. Yuck. */
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
|
||||
/* System-specific feature definitions and include files. */
|
||||
#include "rldefs.h"
|
||||
|
||||
#if defined (GWINSZ_IN_SYS_IOCTL)
|
||||
# include <sys/ioctl.h>
|
||||
#endif /* GWINSZ_IN_SYS_IOCTL */
|
||||
|
||||
/* Some standard library routines. */
|
||||
#include "readline.h"
|
||||
#include "history.h"
|
||||
|
||||
#include "rlprivate.h"
|
||||
|
||||
#if defined (HANDLE_SIGNALS)
|
||||
|
||||
#if !defined (RETSIGTYPE)
|
||||
# if defined (VOID_SIGHANDLER)
|
||||
# define RETSIGTYPE void
|
||||
# else
|
||||
# define RETSIGTYPE int
|
||||
# endif /* !VOID_SIGHANDLER */
|
||||
#endif /* !RETSIGTYPE */
|
||||
|
||||
#if defined (VOID_SIGHANDLER)
|
||||
# define SIGHANDLER_RETURN return
|
||||
#else
|
||||
# define SIGHANDLER_RETURN return (0)
|
||||
#endif
|
||||
|
||||
/* This typedef is equivalent to the one for Function; it allows us
|
||||
to say SigHandler *foo = signal (SIGKILL, SIG_IGN); */
|
||||
typedef RETSIGTYPE SigHandler ();
|
||||
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
typedef struct sigaction sighandler_cxt;
|
||||
# define rl_sigaction(s, nh, oh) sigaction(s, nh, oh)
|
||||
#else
|
||||
typedef struct { SigHandler *sa_handler; int sa_mask, sa_flags; } sighandler_cxt;
|
||||
# define sigemptyset(m)
|
||||
#endif /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
#ifndef SA_RESTART
|
||||
# define SA_RESTART 0
|
||||
#endif
|
||||
|
||||
static SigHandler *rl_set_sighandler PARAMS((int, SigHandler *, sighandler_cxt *));
|
||||
static void rl_maybe_set_sighandler PARAMS((int, SigHandler *, sighandler_cxt *));
|
||||
static void rl_maybe_restore_sighandler PARAMS((int, sighandler_cxt *));
|
||||
|
||||
static RETSIGTYPE rl_signal_handler PARAMS((int));
|
||||
static RETSIGTYPE _rl_handle_signal PARAMS((int));
|
||||
|
||||
/* Exported variables for use by applications. */
|
||||
|
||||
/* If non-zero, readline will install its own signal handlers for
|
||||
SIGINT, SIGTERM, SIGHUP, SIGQUIT, SIGALRM, SIGTSTP, SIGTTIN, and SIGTTOU. */
|
||||
int rl_catch_signals = 1;
|
||||
|
||||
/* If non-zero, readline will install a signal handler for SIGWINCH. */
|
||||
#ifdef SIGWINCH
|
||||
int rl_catch_sigwinch = 1;
|
||||
#else
|
||||
int rl_catch_sigwinch = 0; /* for the readline state struct in readline.c */
|
||||
#endif
|
||||
|
||||
/* Private variables. */
|
||||
int _rl_interrupt_immediately = 0;
|
||||
int volatile _rl_caught_signal = 0; /* should be sig_atomic_t, but that requires including <signal.h> everywhere */
|
||||
|
||||
/* If non-zero, print characters corresponding to received signals as long as
|
||||
the user has indicated his desire to do so (_rl_echo_control_chars). */
|
||||
int _rl_echoctl = 0;
|
||||
|
||||
int _rl_intr_char = 0;
|
||||
int _rl_quit_char = 0;
|
||||
int _rl_susp_char = 0;
|
||||
|
||||
static int signals_set_flag;
|
||||
static int sigwinch_set_flag;
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Signal Handling */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
static sighandler_cxt old_int, old_term, old_hup, old_alrm, old_quit;
|
||||
#if defined (SIGTSTP)
|
||||
static sighandler_cxt old_tstp, old_ttou, old_ttin;
|
||||
#endif
|
||||
#if defined (SIGWINCH)
|
||||
static sighandler_cxt old_winch;
|
||||
#endif
|
||||
|
||||
_rl_sigcleanup_func_t *_rl_sigcleanup;
|
||||
void *_rl_sigcleanarg;
|
||||
|
||||
/* Readline signal handler functions. */
|
||||
|
||||
/* Called from RL_CHECK_SIGNALS() macro */
|
||||
RETSIGTYPE
|
||||
_rl_signal_handler (sig)
|
||||
int sig;
|
||||
{
|
||||
_rl_caught_signal = 0; /* XXX */
|
||||
|
||||
#if defined (SIGWINCH)
|
||||
if (sig == SIGWINCH)
|
||||
rl_resize_terminal ();
|
||||
else
|
||||
#endif
|
||||
_rl_handle_signal (sig);
|
||||
SIGHANDLER_RETURN;
|
||||
}
|
||||
|
||||
static RETSIGTYPE
|
||||
rl_signal_handler (sig)
|
||||
int sig;
|
||||
{
|
||||
if (_rl_interrupt_immediately)
|
||||
{
|
||||
_rl_interrupt_immediately = 0;
|
||||
_rl_handle_signal (sig);
|
||||
}
|
||||
else
|
||||
_rl_caught_signal = sig;
|
||||
|
||||
SIGHANDLER_RETURN;
|
||||
}
|
||||
|
||||
static RETSIGTYPE
|
||||
_rl_handle_signal (sig)
|
||||
int sig;
|
||||
{
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
sigset_t set;
|
||||
#else /* !HAVE_POSIX_SIGNALS */
|
||||
# if defined (HAVE_BSD_SIGNALS)
|
||||
long omask;
|
||||
# else /* !HAVE_BSD_SIGNALS */
|
||||
sighandler_cxt dummy_cxt; /* needed for rl_set_sighandler call */
|
||||
# endif /* !HAVE_BSD_SIGNALS */
|
||||
#endif /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
RL_SETSTATE(RL_STATE_SIGHANDLER);
|
||||
|
||||
#if !defined (HAVE_BSD_SIGNALS) && !defined (HAVE_POSIX_SIGNALS)
|
||||
/* Since the signal will not be blocked while we are in the signal
|
||||
handler, ignore it until rl_clear_signals resets the catcher. */
|
||||
# if defined (SIGALRM)
|
||||
if (sig == SIGINT || sig == SIGALRM)
|
||||
# else
|
||||
if (sig == SIGINT)
|
||||
# endif
|
||||
rl_set_sighandler (sig, SIG_IGN, &dummy_cxt);
|
||||
#endif /* !HAVE_BSD_SIGNALS && !HAVE_POSIX_SIGNALS */
|
||||
|
||||
/* If there's a sig cleanup function registered, call it and `deregister'
|
||||
the cleanup function to avoid multiple calls */
|
||||
if (_rl_sigcleanup)
|
||||
{
|
||||
(*_rl_sigcleanup) (sig, _rl_sigcleanarg);
|
||||
_rl_sigcleanup = 0;
|
||||
_rl_sigcleanarg = 0;
|
||||
}
|
||||
|
||||
switch (sig)
|
||||
{
|
||||
case SIGINT:
|
||||
_rl_reset_completion_state ();
|
||||
rl_free_line_state ();
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case SIGTERM:
|
||||
case SIGHUP:
|
||||
#if defined (SIGTSTP)
|
||||
case SIGTSTP:
|
||||
case SIGTTOU:
|
||||
case SIGTTIN:
|
||||
#endif /* SIGTSTP */
|
||||
#if defined (SIGALRM)
|
||||
case SIGALRM:
|
||||
#endif
|
||||
#if defined (SIGQUIT)
|
||||
case SIGQUIT:
|
||||
#endif
|
||||
rl_echo_signal_char (sig);
|
||||
rl_cleanup_after_signal ();
|
||||
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
sigemptyset (&set);
|
||||
sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &set);
|
||||
sigdelset (&set, sig);
|
||||
#else /* !HAVE_POSIX_SIGNALS */
|
||||
# if defined (HAVE_BSD_SIGNALS)
|
||||
omask = sigblock (0);
|
||||
# endif /* HAVE_BSD_SIGNALS */
|
||||
#endif /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
#if defined (__EMX__)
|
||||
signal (sig, SIG_ACK);
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_KILL)
|
||||
kill (getpid (), sig);
|
||||
#else
|
||||
raise (sig); /* assume we have raise */
|
||||
#endif
|
||||
|
||||
/* Let the signal that we just sent through. */
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
sigprocmask (SIG_SETMASK, &set, (sigset_t *)NULL);
|
||||
#else /* !HAVE_POSIX_SIGNALS */
|
||||
# if defined (HAVE_BSD_SIGNALS)
|
||||
sigsetmask (omask & ~(sigmask (sig)));
|
||||
# endif /* HAVE_BSD_SIGNALS */
|
||||
#endif /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
rl_reset_after_signal ();
|
||||
}
|
||||
|
||||
RL_UNSETSTATE(RL_STATE_SIGHANDLER);
|
||||
SIGHANDLER_RETURN;
|
||||
}
|
||||
|
||||
#if defined (SIGWINCH)
|
||||
static RETSIGTYPE
|
||||
rl_sigwinch_handler (sig)
|
||||
int sig;
|
||||
{
|
||||
SigHandler *oh;
|
||||
|
||||
#if defined (MUST_REINSTALL_SIGHANDLERS)
|
||||
sighandler_cxt dummy_winch;
|
||||
|
||||
/* We don't want to change old_winch -- it holds the state of SIGWINCH
|
||||
disposition set by the calling application. We need this state
|
||||
because we call the application's SIGWINCH handler after updating
|
||||
our own idea of the screen size. */
|
||||
rl_set_sighandler (SIGWINCH, rl_sigwinch_handler, &dummy_winch);
|
||||
#endif
|
||||
|
||||
RL_SETSTATE(RL_STATE_SIGHANDLER);
|
||||
_rl_caught_signal = sig;
|
||||
|
||||
/* If another sigwinch handler has been installed, call it. */
|
||||
oh = (SigHandler *)old_winch.sa_handler;
|
||||
if (oh && oh != (SigHandler *)SIG_IGN && oh != (SigHandler *)SIG_DFL)
|
||||
(*oh) (sig);
|
||||
|
||||
RL_UNSETSTATE(RL_STATE_SIGHANDLER);
|
||||
SIGHANDLER_RETURN;
|
||||
}
|
||||
#endif /* SIGWINCH */
|
||||
|
||||
/* Functions to manage signal handling. */
|
||||
|
||||
#if !defined (HAVE_POSIX_SIGNALS)
|
||||
static int
|
||||
rl_sigaction (sig, nh, oh)
|
||||
int sig;
|
||||
sighandler_cxt *nh, *oh;
|
||||
{
|
||||
oh->sa_handler = signal (sig, nh->sa_handler);
|
||||
return 0;
|
||||
}
|
||||
#endif /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
/* Set up a readline-specific signal handler, saving the old signal
|
||||
information in OHANDLER. Return the old signal handler, like
|
||||
signal(). */
|
||||
static SigHandler *
|
||||
rl_set_sighandler (sig, handler, ohandler)
|
||||
int sig;
|
||||
SigHandler *handler;
|
||||
sighandler_cxt *ohandler;
|
||||
{
|
||||
sighandler_cxt old_handler;
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
struct sigaction act;
|
||||
|
||||
act.sa_handler = handler;
|
||||
# if defined (SIGWINCH)
|
||||
act.sa_flags = (sig == SIGWINCH) ? SA_RESTART : 0;
|
||||
# else
|
||||
act.sa_flags = 0;
|
||||
# endif /* SIGWINCH */
|
||||
sigemptyset (&act.sa_mask);
|
||||
sigemptyset (&ohandler->sa_mask);
|
||||
sigaction (sig, &act, &old_handler);
|
||||
#else
|
||||
old_handler.sa_handler = (SigHandler *)signal (sig, handler);
|
||||
#endif /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
/* XXX -- assume we have memcpy */
|
||||
/* If rl_set_signals is called twice in a row, don't set the old handler to
|
||||
rl_signal_handler, because that would cause infinite recursion. */
|
||||
if (handler != rl_signal_handler || old_handler.sa_handler != rl_signal_handler)
|
||||
memcpy (ohandler, &old_handler, sizeof (sighandler_cxt));
|
||||
|
||||
return (ohandler->sa_handler);
|
||||
}
|
||||
|
||||
/* Set disposition of SIG to HANDLER, returning old state in OHANDLER. Don't
|
||||
change disposition if OHANDLER indicates the signal was ignored. */
|
||||
static void
|
||||
rl_maybe_set_sighandler (sig, handler, ohandler)
|
||||
int sig;
|
||||
SigHandler *handler;
|
||||
sighandler_cxt *ohandler;
|
||||
{
|
||||
sighandler_cxt dummy;
|
||||
SigHandler *oh;
|
||||
|
||||
sigemptyset (&dummy.sa_mask);
|
||||
dummy.sa_flags = 0;
|
||||
oh = rl_set_sighandler (sig, handler, ohandler);
|
||||
if (oh == (SigHandler *)SIG_IGN)
|
||||
rl_sigaction (sig, ohandler, &dummy);
|
||||
}
|
||||
|
||||
/* Set the disposition of SIG to HANDLER, if HANDLER->sa_handler indicates the
|
||||
signal was not being ignored. MUST only be called for signals whose
|
||||
disposition was changed using rl_maybe_set_sighandler or for which the
|
||||
SIG_IGN check was performed inline (e.g., SIGALRM below). */
|
||||
static void
|
||||
rl_maybe_restore_sighandler (sig, handler)
|
||||
int sig;
|
||||
sighandler_cxt *handler;
|
||||
{
|
||||
sighandler_cxt dummy;
|
||||
|
||||
sigemptyset (&dummy.sa_mask);
|
||||
dummy.sa_flags = 0;
|
||||
if (handler->sa_handler != SIG_IGN)
|
||||
rl_sigaction (sig, handler, &dummy);
|
||||
}
|
||||
|
||||
int
|
||||
rl_set_signals ()
|
||||
{
|
||||
sighandler_cxt dummy;
|
||||
SigHandler *oh;
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
static int sigmask_set = 0;
|
||||
static sigset_t bset, oset;
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
if (rl_catch_signals && sigmask_set == 0)
|
||||
{
|
||||
sigemptyset (&bset);
|
||||
|
||||
sigaddset (&bset, SIGINT);
|
||||
sigaddset (&bset, SIGTERM);
|
||||
sigaddset (&bset, SIGHUP);
|
||||
#if defined (SIGQUIT)
|
||||
sigaddset (&bset, SIGQUIT);
|
||||
#endif
|
||||
#if defined (SIGALRM)
|
||||
sigaddset (&bset, SIGALRM);
|
||||
#endif
|
||||
#if defined (SIGTSTP)
|
||||
sigaddset (&bset, SIGTSTP);
|
||||
#endif
|
||||
#if defined (SIGTTIN)
|
||||
sigaddset (&bset, SIGTTIN);
|
||||
#endif
|
||||
#if defined (SIGTTOU)
|
||||
sigaddset (&bset, SIGTTOU);
|
||||
#endif
|
||||
sigmask_set = 1;
|
||||
}
|
||||
#endif /* HAVE_POSIX_SIGNALS */
|
||||
|
||||
if (rl_catch_signals && signals_set_flag == 0)
|
||||
{
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
sigemptyset (&oset);
|
||||
sigprocmask (SIG_BLOCK, &bset, &oset);
|
||||
#endif
|
||||
|
||||
rl_maybe_set_sighandler (SIGINT, rl_signal_handler, &old_int);
|
||||
rl_maybe_set_sighandler (SIGTERM, rl_signal_handler, &old_term);
|
||||
rl_maybe_set_sighandler (SIGHUP, rl_signal_handler, &old_hup);
|
||||
#if defined (SIGQUIT)
|
||||
rl_maybe_set_sighandler (SIGQUIT, rl_signal_handler, &old_quit);
|
||||
#endif
|
||||
|
||||
#if defined (SIGALRM)
|
||||
oh = rl_set_sighandler (SIGALRM, rl_signal_handler, &old_alrm);
|
||||
if (oh == (SigHandler *)SIG_IGN)
|
||||
rl_sigaction (SIGALRM, &old_alrm, &dummy);
|
||||
#if defined (HAVE_POSIX_SIGNALS) && defined (SA_RESTART)
|
||||
/* If the application using readline has already installed a signal
|
||||
handler with SA_RESTART, SIGALRM will cause reads to be restarted
|
||||
automatically, so readline should just get out of the way. Since
|
||||
we tested for SIG_IGN above, we can just test for SIG_DFL here. */
|
||||
if (oh != (SigHandler *)SIG_DFL && (old_alrm.sa_flags & SA_RESTART))
|
||||
rl_sigaction (SIGALRM, &old_alrm, &dummy);
|
||||
#endif /* HAVE_POSIX_SIGNALS */
|
||||
#endif /* SIGALRM */
|
||||
|
||||
#if defined (SIGTSTP)
|
||||
rl_maybe_set_sighandler (SIGTSTP, rl_signal_handler, &old_tstp);
|
||||
#endif /* SIGTSTP */
|
||||
|
||||
#if defined (SIGTTOU)
|
||||
rl_maybe_set_sighandler (SIGTTOU, rl_signal_handler, &old_ttou);
|
||||
#endif /* SIGTTOU */
|
||||
|
||||
#if defined (SIGTTIN)
|
||||
rl_maybe_set_sighandler (SIGTTIN, rl_signal_handler, &old_ttin);
|
||||
#endif /* SIGTTIN */
|
||||
|
||||
signals_set_flag = 1;
|
||||
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined (SIGWINCH)
|
||||
if (rl_catch_sigwinch && sigwinch_set_flag == 0)
|
||||
{
|
||||
rl_maybe_set_sighandler (SIGWINCH, rl_sigwinch_handler, &old_winch);
|
||||
sigwinch_set_flag = 1;
|
||||
}
|
||||
#endif /* SIGWINCH */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
rl_clear_signals ()
|
||||
{
|
||||
sighandler_cxt dummy;
|
||||
|
||||
if (rl_catch_signals && signals_set_flag == 1)
|
||||
{
|
||||
sigemptyset (&dummy.sa_mask);
|
||||
|
||||
/* Since rl_maybe_set_sighandler doesn't override a SIG_IGN handler,
|
||||
we should in theory not have to restore a handler where
|
||||
old_xxx.sa_handler == SIG_IGN. That's what rl_maybe_restore_sighandler
|
||||
does. Fewer system calls should reduce readline's per-line
|
||||
overhead */
|
||||
rl_maybe_restore_sighandler (SIGINT, &old_int);
|
||||
rl_maybe_restore_sighandler (SIGTERM, &old_term);
|
||||
rl_maybe_restore_sighandler (SIGHUP, &old_hup);
|
||||
#if defined (SIGQUIT)
|
||||
rl_maybe_restore_sighandler (SIGQUIT, &old_quit);
|
||||
#endif
|
||||
#if defined (SIGALRM)
|
||||
rl_maybe_restore_sighandler (SIGALRM, &old_alrm);
|
||||
#endif
|
||||
|
||||
#if defined (SIGTSTP)
|
||||
rl_maybe_restore_sighandler (SIGTSTP, &old_tstp);
|
||||
#endif /* SIGTSTP */
|
||||
|
||||
#if defined (SIGTTOU)
|
||||
rl_maybe_restore_sighandler (SIGTTOU, &old_ttou);
|
||||
#endif /* SIGTTOU */
|
||||
|
||||
#if defined (SIGTTIN)
|
||||
rl_maybe_restore_sighandler (SIGTTIN, &old_ttin);
|
||||
#endif /* SIGTTIN */
|
||||
|
||||
signals_set_flag = 0;
|
||||
}
|
||||
|
||||
#if defined (SIGWINCH)
|
||||
if (rl_catch_sigwinch && sigwinch_set_flag == 1)
|
||||
{
|
||||
sigemptyset (&dummy.sa_mask);
|
||||
rl_sigaction (SIGWINCH, &old_winch, &dummy);
|
||||
sigwinch_set_flag = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Clean up the terminal and readline state after catching a signal, before
|
||||
resending it to the calling application. */
|
||||
void
|
||||
rl_cleanup_after_signal ()
|
||||
{
|
||||
_rl_clean_up_for_exit ();
|
||||
if (rl_deprep_term_function)
|
||||
(*rl_deprep_term_function) ();
|
||||
rl_clear_pending_input ();
|
||||
rl_clear_signals ();
|
||||
}
|
||||
|
||||
/* Reset the terminal and readline state after a signal handler returns. */
|
||||
void
|
||||
rl_reset_after_signal ()
|
||||
{
|
||||
if (rl_prep_term_function)
|
||||
(*rl_prep_term_function) (_rl_meta_flag);
|
||||
rl_set_signals ();
|
||||
}
|
||||
|
||||
/* Free up the readline variable line state for the current line (undo list,
|
||||
any partial history entry, any keyboard macros in progress, and any
|
||||
numeric arguments in process) after catching a signal, before calling
|
||||
rl_cleanup_after_signal(). */
|
||||
void
|
||||
rl_free_line_state ()
|
||||
{
|
||||
register HIST_ENTRY *entry;
|
||||
|
||||
rl_free_undo_list ();
|
||||
|
||||
entry = current_history ();
|
||||
if (entry)
|
||||
entry->data = (char *)NULL;
|
||||
|
||||
_rl_kill_kbd_macro ();
|
||||
rl_clear_message ();
|
||||
_rl_reset_argument ();
|
||||
}
|
||||
|
||||
#endif /* HANDLE_SIGNALS */
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* SIGINT Management */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
static sigset_t sigint_set, sigint_oset;
|
||||
static sigset_t sigwinch_set, sigwinch_oset;
|
||||
#else /* !HAVE_POSIX_SIGNALS */
|
||||
# if defined (HAVE_BSD_SIGNALS)
|
||||
static int sigint_oldmask;
|
||||
static int sigwinch_oldmask;
|
||||
# endif /* HAVE_BSD_SIGNALS */
|
||||
#endif /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
static int sigint_blocked;
|
||||
static int sigwinch_blocked;
|
||||
|
||||
/* Cause SIGINT to not be delivered until the corresponding call to
|
||||
release_sigint(). */
|
||||
void
|
||||
_rl_block_sigint ()
|
||||
{
|
||||
if (sigint_blocked)
|
||||
return;
|
||||
|
||||
sigint_blocked = 1;
|
||||
}
|
||||
|
||||
/* Allow SIGINT to be delivered. */
|
||||
void
|
||||
_rl_release_sigint ()
|
||||
{
|
||||
if (sigint_blocked == 0)
|
||||
return;
|
||||
|
||||
sigint_blocked = 0;
|
||||
RL_CHECK_SIGNALS ();
|
||||
}
|
||||
|
||||
/* Cause SIGWINCH to not be delivered until the corresponding call to
|
||||
release_sigwinch(). */
|
||||
void
|
||||
_rl_block_sigwinch ()
|
||||
{
|
||||
if (sigwinch_blocked)
|
||||
return;
|
||||
|
||||
#if defined (SIGWINCH)
|
||||
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
sigemptyset (&sigwinch_set);
|
||||
sigemptyset (&sigwinch_oset);
|
||||
sigaddset (&sigwinch_set, SIGWINCH);
|
||||
sigprocmask (SIG_BLOCK, &sigwinch_set, &sigwinch_oset);
|
||||
#else /* !HAVE_POSIX_SIGNALS */
|
||||
# if defined (HAVE_BSD_SIGNALS)
|
||||
sigwinch_oldmask = sigblock (sigmask (SIGWINCH));
|
||||
# else /* !HAVE_BSD_SIGNALS */
|
||||
# if defined (HAVE_USG_SIGHOLD)
|
||||
sighold (SIGWINCH);
|
||||
# endif /* HAVE_USG_SIGHOLD */
|
||||
# endif /* !HAVE_BSD_SIGNALS */
|
||||
#endif /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
#endif /* SIGWINCH */
|
||||
|
||||
sigwinch_blocked = 1;
|
||||
}
|
||||
|
||||
/* Allow SIGWINCH to be delivered. */
|
||||
void
|
||||
_rl_release_sigwinch ()
|
||||
{
|
||||
if (sigwinch_blocked == 0)
|
||||
return;
|
||||
|
||||
#if defined (SIGWINCH)
|
||||
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
sigprocmask (SIG_SETMASK, &sigwinch_oset, (sigset_t *)NULL);
|
||||
#else
|
||||
# if defined (HAVE_BSD_SIGNALS)
|
||||
sigsetmask (sigwinch_oldmask);
|
||||
# else /* !HAVE_BSD_SIGNALS */
|
||||
# if defined (HAVE_USG_SIGHOLD)
|
||||
sigrelse (SIGWINCH);
|
||||
# endif /* HAVE_USG_SIGHOLD */
|
||||
# endif /* !HAVE_BSD_SIGNALS */
|
||||
#endif /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
#endif /* SIGWINCH */
|
||||
|
||||
sigwinch_blocked = 0;
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Echoing special control characters */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
void
|
||||
rl_echo_signal_char (sig)
|
||||
int sig;
|
||||
{
|
||||
char cstr[3];
|
||||
int cslen, c;
|
||||
|
||||
if (_rl_echoctl == 0 || _rl_echo_control_chars == 0)
|
||||
return;
|
||||
|
||||
switch (sig)
|
||||
{
|
||||
case SIGINT: c = _rl_intr_char; break;
|
||||
#if defined (SIGQUIT)
|
||||
case SIGQUIT: c = _rl_quit_char; break;
|
||||
#endif
|
||||
#if defined (SIGTSTP)
|
||||
case SIGTSTP: c = _rl_susp_char; break;
|
||||
#endif
|
||||
default: return;
|
||||
}
|
||||
|
||||
if (CTRL_CHAR (c) || c == RUBOUT)
|
||||
{
|
||||
cstr[0] = '^';
|
||||
cstr[1] = CTRL_CHAR (c) ? UNCTRL (c) : '?';
|
||||
cstr[cslen = 2] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
cstr[0] = c;
|
||||
cstr[cslen = 1] = '\0';
|
||||
}
|
||||
|
||||
_rl_output_some_chars (cstr, cslen);
|
||||
}
|
||||
-173
@@ -1,173 +0,0 @@
|
||||
/* zread - read data from file descriptor into buffer with retries */
|
||||
|
||||
/* Copyright (C) 1999-2002 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 <sys/types.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_CUR
|
||||
# define SEEK_CUR 1
|
||||
#endif
|
||||
|
||||
/* Read LEN bytes from FD into BUF. Retry the read on EINTR. Any other
|
||||
error causes the loop to break. */
|
||||
ssize_t
|
||||
zread (fd, buf, len)
|
||||
int fd;
|
||||
char *buf;
|
||||
size_t len;
|
||||
{
|
||||
ssize_t r;
|
||||
|
||||
while ((r = read (fd, buf, len)) < 0 && errno == EINTR)
|
||||
check_signals_and_traps (); /* XXX */
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Read LEN bytes from FD into BUF. Retry the read on EINTR, up to three
|
||||
interrupts. Any other error causes the loop to break. */
|
||||
|
||||
#ifdef NUM_INTR
|
||||
# undef NUM_INTR
|
||||
#endif
|
||||
#define NUM_INTR 3
|
||||
|
||||
ssize_t
|
||||
zreadretry (fd, buf, len)
|
||||
int fd;
|
||||
char *buf;
|
||||
size_t len;
|
||||
{
|
||||
ssize_t r;
|
||||
int nintr;
|
||||
|
||||
for (nintr = 0; ; )
|
||||
{
|
||||
r = read (fd, buf, len);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
if (r == -1 && errno == EINTR)
|
||||
{
|
||||
if (++nintr >= NUM_INTR)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
/* Call read(2) and allow it to be interrupted. Just a stub for now. */
|
||||
ssize_t
|
||||
zreadintr (fd, buf, len)
|
||||
int fd;
|
||||
char *buf;
|
||||
size_t len;
|
||||
{
|
||||
return (read (fd, buf, len));
|
||||
}
|
||||
|
||||
/* Read one character from FD and return it in CP. Return values are as
|
||||
in read(2). This does some local buffering to avoid many one-character
|
||||
calls to read(2), like those the `read' builtin performs. */
|
||||
|
||||
static char lbuf[128];
|
||||
static size_t lind, lused;
|
||||
|
||||
ssize_t
|
||||
zreadc (fd, cp)
|
||||
int fd;
|
||||
char *cp;
|
||||
{
|
||||
ssize_t nr;
|
||||
|
||||
if (lind == lused || lused == 0)
|
||||
{
|
||||
nr = zread (fd, lbuf, sizeof (lbuf));
|
||||
lind = 0;
|
||||
if (nr <= 0)
|
||||
{
|
||||
lused = 0;
|
||||
return nr;
|
||||
}
|
||||
lused = nr;
|
||||
}
|
||||
if (cp)
|
||||
*cp = lbuf[lind++];
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Don't mix calls to zreadc and zreadcintr in the same function, since they
|
||||
use the same local buffer. */
|
||||
ssize_t
|
||||
zreadcintr (fd, cp)
|
||||
int fd;
|
||||
char *cp;
|
||||
{
|
||||
ssize_t nr;
|
||||
|
||||
if (lind == lused || lused == 0)
|
||||
{
|
||||
nr = zreadintr (fd, lbuf, sizeof (lbuf));
|
||||
lind = 0;
|
||||
if (nr <= 0)
|
||||
{
|
||||
lused = 0;
|
||||
return nr;
|
||||
}
|
||||
lused = nr;
|
||||
}
|
||||
if (cp)
|
||||
*cp = lbuf[lind++];
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
zreset ()
|
||||
{
|
||||
lind = lused = 0;
|
||||
}
|
||||
|
||||
/* Sync the seek pointer for FD so that the kernel's idea of the last char
|
||||
read is the last char returned by zreadc. */
|
||||
void
|
||||
zsyncfd (fd)
|
||||
int fd;
|
||||
{
|
||||
off_t off, r;
|
||||
|
||||
off = lused - lind;
|
||||
r = 0;
|
||||
if (off > 0)
|
||||
r = lseek (fd, -off, SEEK_CUR);
|
||||
|
||||
if (r != -1)
|
||||
lused = lind = 0;
|
||||
}
|
||||
@@ -1,965 +0,0 @@
|
||||
/* nojobs.c - functions that make children, remember them, and handle their termination. */
|
||||
|
||||
/* This file works under BSD, System V, minix, and Posix systems. It does
|
||||
not implement job control. */
|
||||
|
||||
/* Copyright (C) 1987-2011 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"
|
||||
#include "filecntl.h"
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if defined (BUFFERED_INPUT)
|
||||
# include "input.h"
|
||||
#endif
|
||||
|
||||
/* Need to include this up here for *_TTY_DRIVER definitions. */
|
||||
#include "shtty.h"
|
||||
|
||||
#include "bashintl.h"
|
||||
|
||||
#include "shell.h"
|
||||
#include "jobs.h"
|
||||
#include "execute_cmd.h"
|
||||
|
||||
#include "builtins/builtext.h" /* for wait_builtin */
|
||||
|
||||
#define DEFAULT_CHILD_MAX 32
|
||||
|
||||
#if defined (_POSIX_VERSION) || !defined (HAVE_KILLPG)
|
||||
# define killpg(pg, sig) kill(-(pg),(sig))
|
||||
#endif /* USG || _POSIX_VERSION */
|
||||
|
||||
#if !defined (HAVE_SIGINTERRUPT) && !defined (HAVE_POSIX_SIGNALS)
|
||||
# define siginterrupt(sig, code)
|
||||
#endif /* !HAVE_SIGINTERRUPT && !HAVE_POSIX_SIGNALS */
|
||||
|
||||
#if defined (HAVE_WAITPID)
|
||||
# define WAITPID(pid, statusp, options) waitpid (pid, statusp, options)
|
||||
#else
|
||||
# define WAITPID(pid, statusp, options) wait (statusp)
|
||||
#endif /* !HAVE_WAITPID */
|
||||
|
||||
/* Return the fd from which we are actually getting input. */
|
||||
#define input_tty() (shell_tty != -1) ? shell_tty : fileno (stderr)
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif /* !errno */
|
||||
|
||||
extern int interactive, interactive_shell, login_shell;
|
||||
extern int subshell_environment;
|
||||
extern int last_command_exit_value, last_command_exit_signal;
|
||||
extern int interrupt_immediately;
|
||||
extern sh_builtin_func_t *this_shell_builtin;
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
extern sigset_t top_level_mask;
|
||||
#endif
|
||||
extern procenv_t wait_intr_buf;
|
||||
extern int wait_signal_received;
|
||||
|
||||
pid_t last_made_pid = NO_PID;
|
||||
pid_t last_asynchronous_pid = NO_PID;
|
||||
|
||||
/* Call this when you start making children. */
|
||||
int already_making_children = 0;
|
||||
|
||||
/* The controlling tty for this shell. */
|
||||
int shell_tty = -1;
|
||||
|
||||
/* If this is non-zero, $LINES and $COLUMNS are reset after every process
|
||||
exits from get_tty_state(). */
|
||||
int check_window_size = CHECKWINSIZE_DEFAULT;
|
||||
|
||||
/* We don't have job control. */
|
||||
int job_control = 0;
|
||||
|
||||
/* STATUS and FLAGS are only valid if pid != NO_PID
|
||||
STATUS is only valid if (flags & PROC_RUNNING) == 0 */
|
||||
struct proc_status {
|
||||
pid_t pid;
|
||||
int status; /* Exit status of PID or 128 + fatal signal number */
|
||||
int flags;
|
||||
};
|
||||
|
||||
/* Values for proc_status.flags */
|
||||
#define PROC_RUNNING 0x01
|
||||
#define PROC_NOTIFIED 0x02
|
||||
#define PROC_ASYNC 0x04
|
||||
#define PROC_SIGNALED 0x10
|
||||
|
||||
/* Return values from find_status_by_pid */
|
||||
#define PROC_BAD -1
|
||||
#define PROC_STILL_ALIVE -2
|
||||
|
||||
static struct proc_status *pid_list = (struct proc_status *)NULL;
|
||||
static int pid_list_size;
|
||||
static int wait_sigint_received;
|
||||
|
||||
static long child_max = -1L;
|
||||
|
||||
static void alloc_pid_list __P((void));
|
||||
static int find_proc_slot __P((void));
|
||||
static int find_index_by_pid __P((pid_t));
|
||||
static int find_status_by_pid __P((pid_t));
|
||||
static int process_exit_status __P((WAIT));
|
||||
static int find_termsig_by_pid __P((pid_t));
|
||||
static int get_termsig __P((WAIT));
|
||||
static void set_pid_status __P((pid_t, WAIT));
|
||||
static void set_pid_flags __P((pid_t, int));
|
||||
static void unset_pid_flags __P((pid_t, int));
|
||||
static int get_pid_flags __P((pid_t));
|
||||
static void add_pid __P((pid_t, int));
|
||||
static void mark_dead_jobs_as_notified __P((int));
|
||||
|
||||
static sighandler wait_sigint_handler __P((int));
|
||||
static char *j_strsignal __P((int));
|
||||
|
||||
#if defined (HAVE_WAITPID)
|
||||
static void reap_zombie_children __P((void));
|
||||
#endif
|
||||
|
||||
#if !defined (HAVE_SIGINTERRUPT) && defined (HAVE_POSIX_SIGNALS)
|
||||
static int siginterrupt __P((int, int));
|
||||
#endif
|
||||
|
||||
static void restore_sigint_handler __P((void));
|
||||
|
||||
/* Allocate new, or grow existing PID_LIST. */
|
||||
static void
|
||||
alloc_pid_list ()
|
||||
{
|
||||
register int i;
|
||||
int old = pid_list_size;
|
||||
|
||||
pid_list_size += 10;
|
||||
pid_list = (struct proc_status *)xrealloc (pid_list, pid_list_size * sizeof (struct proc_status));
|
||||
|
||||
/* None of the newly allocated slots have process id's yet. */
|
||||
for (i = old; i < pid_list_size; i++)
|
||||
pid_list[i].pid = NO_PID;
|
||||
}
|
||||
|
||||
/* Return the offset within the PID_LIST array of an empty slot. This can
|
||||
create new slots if all of the existing slots are taken. */
|
||||
static int
|
||||
find_proc_slot ()
|
||||
{
|
||||
register int i;
|
||||
|
||||
for (i = 0; i < pid_list_size; i++)
|
||||
if (pid_list[i].pid == NO_PID)
|
||||
return (i);
|
||||
|
||||
if (i == pid_list_size)
|
||||
alloc_pid_list ();
|
||||
|
||||
return (i);
|
||||
}
|
||||
|
||||
/* Return the offset within the PID_LIST array of a slot containing PID,
|
||||
or the value NO_PID if the pid wasn't found. */
|
||||
static int
|
||||
find_index_by_pid (pid)
|
||||
pid_t pid;
|
||||
{
|
||||
register int i;
|
||||
|
||||
for (i = 0; i < pid_list_size; i++)
|
||||
if (pid_list[i].pid == pid)
|
||||
return (i);
|
||||
|
||||
return (NO_PID);
|
||||
}
|
||||
|
||||
/* Return the status of PID as looked up in the PID_LIST array. A
|
||||
return value of PROC_BAD indicates that PID wasn't found. */
|
||||
static int
|
||||
find_status_by_pid (pid)
|
||||
pid_t pid;
|
||||
{
|
||||
int i;
|
||||
|
||||
i = find_index_by_pid (pid);
|
||||
if (i == NO_PID)
|
||||
return (PROC_BAD);
|
||||
if (pid_list[i].flags & PROC_RUNNING)
|
||||
return (PROC_STILL_ALIVE);
|
||||
return (pid_list[i].status);
|
||||
}
|
||||
|
||||
static int
|
||||
process_exit_status (status)
|
||||
WAIT status;
|
||||
{
|
||||
if (WIFSIGNALED (status))
|
||||
return (128 + WTERMSIG (status));
|
||||
else
|
||||
return (WEXITSTATUS (status));
|
||||
}
|
||||
|
||||
/* Return the status of PID as looked up in the PID_LIST array. A
|
||||
return value of PROC_BAD indicates that PID wasn't found. */
|
||||
static int
|
||||
find_termsig_by_pid (pid)
|
||||
pid_t pid;
|
||||
{
|
||||
int i;
|
||||
|
||||
i = find_index_by_pid (pid);
|
||||
if (i == NO_PID)
|
||||
return (0);
|
||||
if (pid_list[i].flags & PROC_RUNNING)
|
||||
return (0);
|
||||
return (get_termsig ((WAIT)pid_list[i].status));
|
||||
}
|
||||
|
||||
/* Set LAST_COMMAND_EXIT_SIGNAL depending on STATUS. If STATUS is -1, look
|
||||
up PID in the pid array and set LAST_COMMAND_EXIT_SIGNAL appropriately
|
||||
depending on its flags and exit status. */
|
||||
static int
|
||||
get_termsig (status)
|
||||
WAIT status;
|
||||
{
|
||||
if (WIFSTOPPED (status) == 0 && WIFSIGNALED (status))
|
||||
return (WTERMSIG (status));
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Give PID the status value STATUS in the PID_LIST array. */
|
||||
static void
|
||||
set_pid_status (pid, status)
|
||||
pid_t pid;
|
||||
WAIT status;
|
||||
{
|
||||
int slot;
|
||||
|
||||
#if defined (COPROCESS_SUPPORT)
|
||||
coproc_pidchk (pid, status);
|
||||
#endif
|
||||
|
||||
slot = find_index_by_pid (pid);
|
||||
if (slot == NO_PID)
|
||||
return;
|
||||
|
||||
pid_list[slot].status = process_exit_status (status);
|
||||
pid_list[slot].flags &= ~PROC_RUNNING;
|
||||
if (WIFSIGNALED (status))
|
||||
pid_list[slot].flags |= PROC_SIGNALED;
|
||||
/* If it's not a background process, mark it as notified so it gets
|
||||
cleaned up. */
|
||||
if ((pid_list[slot].flags & PROC_ASYNC) == 0)
|
||||
pid_list[slot].flags |= PROC_NOTIFIED;
|
||||
}
|
||||
|
||||
/* Give PID the flags FLAGS in the PID_LIST array. */
|
||||
static void
|
||||
set_pid_flags (pid, flags)
|
||||
pid_t pid;
|
||||
int flags;
|
||||
{
|
||||
int slot;
|
||||
|
||||
slot = find_index_by_pid (pid);
|
||||
if (slot == NO_PID)
|
||||
return;
|
||||
|
||||
pid_list[slot].flags |= flags;
|
||||
}
|
||||
|
||||
/* Unset FLAGS for PID in the pid list */
|
||||
static void
|
||||
unset_pid_flags (pid, flags)
|
||||
pid_t pid;
|
||||
int flags;
|
||||
{
|
||||
int slot;
|
||||
|
||||
slot = find_index_by_pid (pid);
|
||||
if (slot == NO_PID)
|
||||
return;
|
||||
|
||||
pid_list[slot].flags &= ~flags;
|
||||
}
|
||||
|
||||
/* Return the flags corresponding to PID in the PID_LIST array. */
|
||||
static int
|
||||
get_pid_flags (pid)
|
||||
pid_t pid;
|
||||
{
|
||||
int slot;
|
||||
|
||||
slot = find_index_by_pid (pid);
|
||||
if (slot == NO_PID)
|
||||
return 0;
|
||||
|
||||
return (pid_list[slot].flags);
|
||||
}
|
||||
|
||||
static void
|
||||
add_pid (pid, async)
|
||||
pid_t pid;
|
||||
int async;
|
||||
{
|
||||
int slot;
|
||||
|
||||
slot = find_proc_slot ();
|
||||
|
||||
pid_list[slot].pid = pid;
|
||||
pid_list[slot].status = -1;
|
||||
pid_list[slot].flags = PROC_RUNNING;
|
||||
if (async)
|
||||
pid_list[slot].flags |= PROC_ASYNC;
|
||||
}
|
||||
|
||||
static void
|
||||
mark_dead_jobs_as_notified (force)
|
||||
int force;
|
||||
{
|
||||
register int i, ndead;
|
||||
|
||||
/* first, count the number of non-running async jobs if FORCE == 0 */
|
||||
for (i = ndead = 0; force == 0 && i < pid_list_size; i++)
|
||||
{
|
||||
if (pid_list[i].pid == NO_PID)
|
||||
continue;
|
||||
if (((pid_list[i].flags & PROC_RUNNING) == 0) &&
|
||||
(pid_list[i].flags & PROC_ASYNC))
|
||||
ndead++;
|
||||
}
|
||||
|
||||
if (child_max < 0)
|
||||
child_max = getmaxchild ();
|
||||
if (child_max < 0)
|
||||
child_max = DEFAULT_CHILD_MAX;
|
||||
|
||||
if (force == 0 && ndead <= child_max)
|
||||
return;
|
||||
|
||||
/* If FORCE == 0, we just mark as many non-running async jobs as notified
|
||||
to bring us under the CHILD_MAX limit. */
|
||||
for (i = 0; i < pid_list_size; i++)
|
||||
{
|
||||
if (pid_list[i].pid == NO_PID)
|
||||
continue;
|
||||
if (((pid_list[i].flags & PROC_RUNNING) == 0) &&
|
||||
pid_list[i].pid != last_asynchronous_pid)
|
||||
{
|
||||
pid_list[i].flags |= PROC_NOTIFIED;
|
||||
if (force == 0 && (pid_list[i].flags & PROC_ASYNC) && --ndead <= child_max)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove all dead, notified jobs from the pid_list. */
|
||||
int
|
||||
cleanup_dead_jobs ()
|
||||
{
|
||||
register int i;
|
||||
|
||||
#if defined (HAVE_WAITPID)
|
||||
reap_zombie_children ();
|
||||
#endif
|
||||
|
||||
for (i = 0; i < pid_list_size; i++)
|
||||
{
|
||||
if ((pid_list[i].flags & PROC_RUNNING) == 0 &&
|
||||
(pid_list[i].flags & PROC_NOTIFIED))
|
||||
pid_list[i].pid = NO_PID;
|
||||
}
|
||||
|
||||
#if defined (COPROCESS_SUPPORT)
|
||||
coproc_reap ();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
reap_dead_jobs ()
|
||||
{
|
||||
mark_dead_jobs_as_notified (0);
|
||||
cleanup_dead_jobs ();
|
||||
}
|
||||
|
||||
/* Initialize the job control mechanism, and set up the tty stuff. */
|
||||
initialize_job_control (force)
|
||||
int force;
|
||||
{
|
||||
shell_tty = fileno (stderr);
|
||||
|
||||
if (interactive)
|
||||
get_tty_state ();
|
||||
}
|
||||
|
||||
/* Setup this shell to handle C-C, etc. */
|
||||
void
|
||||
initialize_job_signals ()
|
||||
{
|
||||
set_signal_handler (SIGINT, sigint_sighandler);
|
||||
|
||||
/* If this is a login shell we don't wish to be disturbed by
|
||||
stop signals. */
|
||||
if (login_shell)
|
||||
ignore_tty_job_signals ();
|
||||
}
|
||||
|
||||
#if defined (HAVE_WAITPID)
|
||||
/* Collect the status of all zombie children so that their system
|
||||
resources can be deallocated. */
|
||||
static void
|
||||
reap_zombie_children ()
|
||||
{
|
||||
# if defined (WNOHANG)
|
||||
pid_t pid;
|
||||
WAIT status;
|
||||
|
||||
CHECK_TERMSIG;
|
||||
CHECK_WAIT_INTR;
|
||||
while ((pid = waitpid (-1, (int *)&status, WNOHANG)) > 0)
|
||||
set_pid_status (pid, status);
|
||||
# endif /* WNOHANG */
|
||||
CHECK_TERMSIG;
|
||||
CHECK_WAIT_INTR;
|
||||
}
|
||||
#endif /* WAITPID */
|
||||
|
||||
#if !defined (HAVE_SIGINTERRUPT) && defined (HAVE_POSIX_SIGNALS)
|
||||
|
||||
#if !defined (SA_RESTART)
|
||||
# define SA_RESTART 0
|
||||
#endif
|
||||
|
||||
static int
|
||||
siginterrupt (sig, flag)
|
||||
int sig, flag;
|
||||
{
|
||||
struct sigaction act;
|
||||
|
||||
sigaction (sig, (struct sigaction *)NULL, &act);
|
||||
|
||||
if (flag)
|
||||
act.sa_flags &= ~SA_RESTART;
|
||||
else
|
||||
act.sa_flags |= SA_RESTART;
|
||||
|
||||
return (sigaction (sig, &act, (struct sigaction *)NULL));
|
||||
}
|
||||
#endif /* !HAVE_SIGINTERRUPT && HAVE_POSIX_SIGNALS */
|
||||
|
||||
/* Fork, handling errors. Returns the pid of the newly made child, or 0.
|
||||
COMMAND is just for remembering the name of the command; we don't do
|
||||
anything else with it. ASYNC_P says what to do with the tty. If
|
||||
non-zero, then don't give it away. */
|
||||
pid_t
|
||||
make_child (command, async_p)
|
||||
char *command;
|
||||
int async_p;
|
||||
{
|
||||
pid_t pid;
|
||||
int forksleep;
|
||||
|
||||
/* Discard saved memory. */
|
||||
if (command)
|
||||
free (command);
|
||||
|
||||
start_pipeline ();
|
||||
|
||||
#if defined (BUFFERED_INPUT)
|
||||
/* If default_buffered_input is active, we are reading a script. If
|
||||
the command is asynchronous, we have already duplicated /dev/null
|
||||
as fd 0, but have not changed the buffered stream corresponding to
|
||||
the old fd 0. We don't want to sync the stream in this case. */
|
||||
if (default_buffered_input != -1 && (!async_p || default_buffered_input > 0))
|
||||
sync_buffered_stream (default_buffered_input);
|
||||
#endif /* BUFFERED_INPUT */
|
||||
|
||||
/* Create the child, handle severe errors. Retry on EAGAIN. */
|
||||
forksleep = 1;
|
||||
while ((pid = fork ()) < 0 && errno == EAGAIN && forksleep < FORKSLEEP_MAX)
|
||||
{
|
||||
sys_error ("fork: retry");
|
||||
#if defined (HAVE_WAITPID)
|
||||
/* Posix systems with a non-blocking waitpid () system call available
|
||||
get another chance after zombies are reaped. */
|
||||
reap_zombie_children ();
|
||||
if (forksleep > 1 && sleep (forksleep) != 0)
|
||||
break;
|
||||
#else
|
||||
if (sleep (forksleep) != 0)
|
||||
break;
|
||||
#endif /* HAVE_WAITPID */
|
||||
forksleep <<= 1;
|
||||
}
|
||||
|
||||
if (pid < 0)
|
||||
{
|
||||
sys_error ("fork");
|
||||
last_command_exit_value = EX_NOEXEC;
|
||||
throw_to_top_level ();
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
#if defined (BUFFERED_INPUT)
|
||||
unset_bash_input (0);
|
||||
#endif /* BUFFERED_INPUT */
|
||||
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
/* Restore top-level signal mask. */
|
||||
sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* Ignore INT and QUIT in asynchronous children. */
|
||||
if (async_p)
|
||||
last_asynchronous_pid = getpid ();
|
||||
#endif
|
||||
|
||||
default_tty_job_signals ();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* In the parent. */
|
||||
|
||||
last_made_pid = pid;
|
||||
|
||||
if (async_p)
|
||||
last_asynchronous_pid = pid;
|
||||
|
||||
add_pid (pid, async_p);
|
||||
}
|
||||
return (pid);
|
||||
}
|
||||
|
||||
void
|
||||
ignore_tty_job_signals ()
|
||||
{
|
||||
#if defined (SIGTSTP)
|
||||
set_signal_handler (SIGTSTP, SIG_IGN);
|
||||
set_signal_handler (SIGTTIN, SIG_IGN);
|
||||
set_signal_handler (SIGTTOU, SIG_IGN);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
default_tty_job_signals ()
|
||||
{
|
||||
#if defined (SIGTSTP)
|
||||
set_signal_handler (SIGTSTP, SIG_DFL);
|
||||
set_signal_handler (SIGTTIN, SIG_DFL);
|
||||
set_signal_handler (SIGTTOU, SIG_DFL);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Wait for a single pid (PID) and return its exit status. Called by
|
||||
the wait builtin. */
|
||||
int
|
||||
wait_for_single_pid (pid)
|
||||
pid_t pid;
|
||||
{
|
||||
pid_t got_pid;
|
||||
WAIT status;
|
||||
int pstatus, flags;
|
||||
|
||||
pstatus = find_status_by_pid (pid);
|
||||
|
||||
if (pstatus == PROC_BAD)
|
||||
{
|
||||
internal_error (_("wait: pid %ld is not a child of this shell"), (long)pid);
|
||||
return (127);
|
||||
}
|
||||
|
||||
if (pstatus != PROC_STILL_ALIVE)
|
||||
{
|
||||
if (pstatus > 128)
|
||||
last_command_exit_signal = find_termsig_by_pid (pid);
|
||||
return (pstatus);
|
||||
}
|
||||
|
||||
siginterrupt (SIGINT, 1);
|
||||
while ((got_pid = WAITPID (pid, &status, 0)) != pid)
|
||||
{
|
||||
CHECK_TERMSIG;
|
||||
CHECK_WAIT_INTR;
|
||||
if (got_pid < 0)
|
||||
{
|
||||
if (errno != EINTR && errno != ECHILD)
|
||||
{
|
||||
siginterrupt (SIGINT, 0);
|
||||
sys_error ("wait");
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (got_pid > 0)
|
||||
set_pid_status (got_pid, status);
|
||||
}
|
||||
|
||||
if (got_pid > 0)
|
||||
{
|
||||
set_pid_status (got_pid, status);
|
||||
set_pid_flags (got_pid, PROC_NOTIFIED);
|
||||
}
|
||||
|
||||
siginterrupt (SIGINT, 0);
|
||||
QUIT;
|
||||
|
||||
return (got_pid > 0 ? process_exit_status (status) : -1);
|
||||
}
|
||||
|
||||
/* Wait for all of the shell's children to exit. Called by the `wait'
|
||||
builtin. */
|
||||
void
|
||||
wait_for_background_pids ()
|
||||
{
|
||||
pid_t got_pid;
|
||||
WAIT status;
|
||||
|
||||
/* If we aren't using job control, we let the kernel take care of the
|
||||
bookkeeping for us. wait () will return -1 and set errno to ECHILD
|
||||
when there are no more unwaited-for child processes on both
|
||||
4.2 BSD-based and System V-based systems. */
|
||||
|
||||
siginterrupt (SIGINT, 1);
|
||||
|
||||
/* Wait for ECHILD */
|
||||
while ((got_pid = WAITPID (-1, &status, 0)) != -1)
|
||||
set_pid_status (got_pid, status);
|
||||
|
||||
if (errno != EINTR && errno != ECHILD)
|
||||
{
|
||||
siginterrupt (SIGINT, 0);
|
||||
sys_error("wait");
|
||||
}
|
||||
|
||||
siginterrupt (SIGINT, 0);
|
||||
QUIT;
|
||||
|
||||
mark_dead_jobs_as_notified (1);
|
||||
cleanup_dead_jobs ();
|
||||
}
|
||||
|
||||
/* Make OLD_SIGINT_HANDLER the SIGINT signal handler. */
|
||||
#define INVALID_SIGNAL_HANDLER (SigHandler *)wait_for_background_pids
|
||||
static SigHandler *old_sigint_handler = INVALID_SIGNAL_HANDLER;
|
||||
|
||||
static void
|
||||
restore_sigint_handler ()
|
||||
{
|
||||
if (old_sigint_handler != INVALID_SIGNAL_HANDLER)
|
||||
{
|
||||
set_signal_handler (SIGINT, old_sigint_handler);
|
||||
old_sigint_handler = INVALID_SIGNAL_HANDLER;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle SIGINT while we are waiting for children in a script to exit.
|
||||
All interrupts are effectively ignored by the shell, but allowed to
|
||||
kill a running job. */
|
||||
static sighandler
|
||||
wait_sigint_handler (sig)
|
||||
int sig;
|
||||
{
|
||||
SigHandler *sigint_handler;
|
||||
|
||||
/* If we got a SIGINT while in `wait', and SIGINT is trapped, do
|
||||
what POSIX.2 says (see builtins/wait.def for more info). */
|
||||
if (this_shell_builtin && this_shell_builtin == wait_builtin &&
|
||||
signal_is_trapped (SIGINT) &&
|
||||
((sigint_handler = trap_to_sighandler (SIGINT)) == trap_handler))
|
||||
{
|
||||
last_command_exit_value = EXECUTION_FAILURE;
|
||||
restore_sigint_handler ();
|
||||
interrupt_immediately = 0;
|
||||
trap_handler (SIGINT); /* set pending_traps[SIGINT] */
|
||||
wait_signal_received = SIGINT;
|
||||
SIGRETURN (0);
|
||||
}
|
||||
|
||||
if (interrupt_immediately)
|
||||
{
|
||||
last_command_exit_value = EXECUTION_FAILURE;
|
||||
restore_sigint_handler ();
|
||||
ADDINTERRUPT;
|
||||
QUIT;
|
||||
}
|
||||
|
||||
wait_sigint_received = 1;
|
||||
|
||||
SIGRETURN (0);
|
||||
}
|
||||
|
||||
static char *
|
||||
j_strsignal (s)
|
||||
int s;
|
||||
{
|
||||
static char retcode_name_buffer[64] = { '\0' };
|
||||
char *x;
|
||||
|
||||
x = strsignal (s);
|
||||
if (x == 0)
|
||||
{
|
||||
x = retcode_name_buffer;
|
||||
sprintf (x, "Signal %d", s);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Wait for pid (one of our children) to terminate. This is called only
|
||||
by the execution code in execute_cmd.c. */
|
||||
int
|
||||
wait_for (pid)
|
||||
pid_t pid;
|
||||
{
|
||||
int return_val, pstatus;
|
||||
pid_t got_pid;
|
||||
WAIT status;
|
||||
|
||||
pstatus = find_status_by_pid (pid);
|
||||
|
||||
if (pstatus == PROC_BAD)
|
||||
return (0);
|
||||
|
||||
if (pstatus != PROC_STILL_ALIVE)
|
||||
{
|
||||
if (pstatus > 128)
|
||||
last_command_exit_signal = find_termsig_by_pid (pid);
|
||||
return (pstatus);
|
||||
}
|
||||
|
||||
/* If we are running a script, ignore SIGINT while we're waiting for
|
||||
a child to exit. The loop below does some of this, but not all. */
|
||||
wait_sigint_received = 0;
|
||||
if (interactive_shell == 0)
|
||||
old_sigint_handler = set_signal_handler (SIGINT, wait_sigint_handler);
|
||||
|
||||
while ((got_pid = WAITPID (-1, &status, 0)) != pid) /* XXX was pid now -1 */
|
||||
{
|
||||
CHECK_TERMSIG;
|
||||
CHECK_WAIT_INTR;
|
||||
if (got_pid < 0 && errno == ECHILD)
|
||||
{
|
||||
#if !defined (_POSIX_VERSION)
|
||||
status.w_termsig = status.w_retcode = 0;
|
||||
#else
|
||||
status = 0;
|
||||
#endif /* _POSIX_VERSION */
|
||||
break;
|
||||
}
|
||||
else if (got_pid < 0 && errno != EINTR)
|
||||
programming_error ("wait_for(%ld): %s", (long)pid, strerror(errno));
|
||||
else if (got_pid > 0)
|
||||
set_pid_status (got_pid, status);
|
||||
}
|
||||
|
||||
if (got_pid > 0)
|
||||
set_pid_status (got_pid, status);
|
||||
|
||||
#if defined (HAVE_WAITPID)
|
||||
if (got_pid >= 0)
|
||||
reap_zombie_children ();
|
||||
#endif /* HAVE_WAITPID */
|
||||
|
||||
CHECK_TERMSIG;
|
||||
CHECK_WAIT_INTR;
|
||||
|
||||
if (interactive_shell == 0)
|
||||
{
|
||||
SigHandler *temp_handler;
|
||||
|
||||
temp_handler = old_sigint_handler;
|
||||
restore_sigint_handler ();
|
||||
|
||||
/* If the job exited because of SIGINT, make sure the shell acts as if
|
||||
it had received one also. */
|
||||
if (WIFSIGNALED (status) && (WTERMSIG (status) == SIGINT))
|
||||
{
|
||||
|
||||
if (maybe_call_trap_handler (SIGINT) == 0)
|
||||
{
|
||||
if (temp_handler == SIG_DFL)
|
||||
termsig_handler (SIGINT);
|
||||
else if (temp_handler != INVALID_SIGNAL_HANDLER && temp_handler != SIG_IGN)
|
||||
(*temp_handler) (SIGINT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Default return value. */
|
||||
/* ``a full 8 bits of status is returned'' */
|
||||
return_val = process_exit_status (status);
|
||||
last_command_exit_signal = get_termsig (status);
|
||||
|
||||
#if defined (DONT_REPORT_SIGPIPE) && defined (DONT_REPORT_SIGTERM)
|
||||
# define REPORTSIG(x) ((x) != SIGINT && (x) != SIGPIPE && (x) != SIGTERM)
|
||||
#elif !defined (DONT_REPORT_SIGPIPE) && !defined (DONT_REPORT_SIGTERM)
|
||||
# define REPORTSIG(x) ((x) != SIGINT)
|
||||
#elif defined (DONT_REPORT_SIGPIPE)
|
||||
# define REPORTSIG(x) ((x) != SIGINT && (x) != SIGPIPE)
|
||||
#else
|
||||
# define REPORTSIG(x) ((x) != SIGINT && (x) != SIGTERM)
|
||||
#endif
|
||||
|
||||
if ((WIFSTOPPED (status) == 0) && WIFSIGNALED (status) && REPORTSIG(WTERMSIG (status)))
|
||||
{
|
||||
fprintf (stderr, "%s", j_strsignal (WTERMSIG (status)));
|
||||
if (WIFCORED (status))
|
||||
fprintf (stderr, _(" (core dumped)"));
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
if (interactive_shell && subshell_environment == 0)
|
||||
{
|
||||
if (WIFSIGNALED (status) || WIFSTOPPED (status))
|
||||
set_tty_state ();
|
||||
else
|
||||
get_tty_state ();
|
||||
}
|
||||
else if (interactive_shell == 0 && subshell_environment == 0 && check_window_size)
|
||||
get_new_window_size (0, (int *)0, (int *)0);
|
||||
|
||||
return (return_val);
|
||||
}
|
||||
|
||||
/* Send PID SIGNAL. Returns -1 on failure, 0 on success. If GROUP is non-zero,
|
||||
or PID is less than -1, then kill the process group associated with PID. */
|
||||
int
|
||||
kill_pid (pid, signal, group)
|
||||
pid_t pid;
|
||||
int signal, group;
|
||||
{
|
||||
int result;
|
||||
|
||||
if (pid < -1)
|
||||
{
|
||||
pid = -pid;
|
||||
group = 1;
|
||||
}
|
||||
result = group ? killpg (pid, signal) : kill (pid, signal);
|
||||
return (result);
|
||||
}
|
||||
|
||||
static TTYSTRUCT shell_tty_info;
|
||||
static int got_tty_state;
|
||||
|
||||
/* Fill the contents of shell_tty_info with the current tty info. */
|
||||
get_tty_state ()
|
||||
{
|
||||
int tty;
|
||||
|
||||
tty = input_tty ();
|
||||
if (tty != -1)
|
||||
{
|
||||
ttgetattr (tty, &shell_tty_info);
|
||||
got_tty_state = 1;
|
||||
if (check_window_size)
|
||||
get_new_window_size (0, (int *)0, (int *)0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Make the current tty use the state in shell_tty_info. */
|
||||
int
|
||||
set_tty_state ()
|
||||
{
|
||||
int tty;
|
||||
|
||||
tty = input_tty ();
|
||||
if (tty != -1)
|
||||
{
|
||||
if (got_tty_state == 0)
|
||||
return 0;
|
||||
ttsetattr (tty, &shell_tty_info);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Give the terminal to PGRP. */
|
||||
give_terminal_to (pgrp, force)
|
||||
pid_t pgrp;
|
||||
int force;
|
||||
{
|
||||
}
|
||||
|
||||
/* Stop a pipeline. */
|
||||
int
|
||||
stop_pipeline (async, ignore)
|
||||
int async;
|
||||
COMMAND *ignore;
|
||||
{
|
||||
already_making_children = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
start_pipeline ()
|
||||
{
|
||||
already_making_children = 1;
|
||||
}
|
||||
|
||||
void
|
||||
stop_making_children ()
|
||||
{
|
||||
already_making_children = 0;
|
||||
}
|
||||
|
||||
int
|
||||
get_job_by_pid (pid, block)
|
||||
pid_t pid;
|
||||
int block;
|
||||
{
|
||||
int i;
|
||||
|
||||
i = find_index_by_pid (pid);
|
||||
return ((i == NO_PID) ? PROC_BAD : i);
|
||||
}
|
||||
|
||||
/* Print descriptive information about the job with leader pid PID. */
|
||||
void
|
||||
describe_pid (pid)
|
||||
pid_t pid;
|
||||
{
|
||||
fprintf (stderr, "%ld\n", (long) pid);
|
||||
}
|
||||
|
||||
void
|
||||
freeze_jobs_list ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
unfreeze_jobs_list ()
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
count_all_jobs ()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/* quit.h -- How to handle SIGINT gracefully. */
|
||||
|
||||
/* Copyright (C) 1993-2012 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 (_QUIT_H_)
|
||||
#define _QUIT_H_
|
||||
|
||||
/* Non-zero means SIGINT has already ocurred. */
|
||||
extern volatile int interrupt_state;
|
||||
extern volatile int terminating_signal;
|
||||
|
||||
/* Macro to call a great deal. SIGINT just sets the interrupt_state variable.
|
||||
When it is safe, put QUIT in the code, and the "interrupt" will take
|
||||
place. The same scheme is used for terminating signals (e.g., SIGHUP)
|
||||
and the terminating_signal variable. That calls a function which will
|
||||
end up exiting the shell. */
|
||||
#define QUIT \
|
||||
do { \
|
||||
if (terminating_signal) termsig_handler (terminating_signal); \
|
||||
if (interrupt_state) throw_to_top_level (); \
|
||||
} while (0)
|
||||
|
||||
#define SETINTERRUPT interrupt_state = 1
|
||||
#define CLRINTERRUPT interrupt_state = 0
|
||||
|
||||
#define ADDINTERRUPT interrupt_state++
|
||||
#define DELINTERRUPT interrupt_state--
|
||||
|
||||
/* The same sort of thing, this time just for signals that would ordinarily
|
||||
cause the shell to terminate. */
|
||||
|
||||
#define CHECK_TERMSIG \
|
||||
do { \
|
||||
if (terminating_signal) termsig_handler (terminating_signal); \
|
||||
} while (0)
|
||||
|
||||
#define LASTSIG() \
|
||||
(terminating_signal ? terminating_signal : (interrupt_state ? SIGINT : 0))
|
||||
|
||||
#define CHECK_WAIT_INTR \
|
||||
do { \
|
||||
if (wait_signal_received && this_shell_builtin && (this_shell_builtin == wait_builtin)) \
|
||||
{ \
|
||||
itrace("CHECK_WAIT_INTR: longjmping to wait_intr_buf"); \
|
||||
longjmp (wait_intr_buf, 1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif /* _QUIT_H_ */
|
||||
@@ -1,698 +0,0 @@
|
||||
/* sig.c - interface for shell signal handlers and signal initialization. */
|
||||
|
||||
/* Copyright (C) 1994-2012 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)
|
||||
# ifdef _MINIX
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "bashintl.h"
|
||||
|
||||
#include "shell.h"
|
||||
#if defined (JOB_CONTROL)
|
||||
#include "jobs.h"
|
||||
#endif /* JOB_CONTROL */
|
||||
#include "siglist.h"
|
||||
#include "sig.h"
|
||||
#include "trap.h"
|
||||
|
||||
#include "builtins/common.h"
|
||||
|
||||
#if defined (READLINE)
|
||||
# include "bashline.h"
|
||||
# include <readline/readline.h>
|
||||
#endif
|
||||
|
||||
#if defined (HISTORY)
|
||||
# include "bashhist.h"
|
||||
#endif
|
||||
|
||||
extern int last_command_exit_value;
|
||||
extern int last_command_exit_signal;
|
||||
extern int return_catch_flag;
|
||||
extern int loop_level, continuing, breaking, funcnest;
|
||||
extern int executing_list;
|
||||
extern int comsub_ignore_return;
|
||||
extern int parse_and_execute_level, shell_initialized;
|
||||
#if defined (HISTORY)
|
||||
extern int history_lines_this_session;
|
||||
#endif
|
||||
extern int no_line_editing;
|
||||
|
||||
extern void initialize_siglist ();
|
||||
|
||||
/* Non-zero after SIGINT. */
|
||||
volatile int interrupt_state = 0;
|
||||
|
||||
/* Non-zero after SIGWINCH */
|
||||
volatile int sigwinch_received = 0;
|
||||
|
||||
/* Set to the value of any terminating signal received. */
|
||||
volatile int terminating_signal = 0;
|
||||
|
||||
/* The environment at the top-level R-E loop. We use this in
|
||||
the case of error return. */
|
||||
procenv_t top_level;
|
||||
|
||||
#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
|
||||
/* The signal masks that this shell runs with. */
|
||||
sigset_t top_level_mask;
|
||||
#endif /* JOB_CONTROL */
|
||||
|
||||
/* When non-zero, we throw_to_top_level (). */
|
||||
int interrupt_immediately = 0;
|
||||
|
||||
/* When non-zero, we call the terminating signal handler immediately. */
|
||||
int terminate_immediately = 0;
|
||||
|
||||
#if defined (SIGWINCH)
|
||||
static SigHandler *old_winch = (SigHandler *)SIG_DFL;
|
||||
#endif
|
||||
|
||||
static void initialize_shell_signals __P((void));
|
||||
|
||||
void
|
||||
initialize_signals (reinit)
|
||||
int reinit;
|
||||
{
|
||||
initialize_shell_signals ();
|
||||
initialize_job_signals ();
|
||||
#if !defined (HAVE_SYS_SIGLIST) && !defined (HAVE_UNDER_SYS_SIGLIST) && !defined (HAVE_STRSIGNAL)
|
||||
if (reinit == 0)
|
||||
initialize_siglist ();
|
||||
#endif /* !HAVE_SYS_SIGLIST && !HAVE_UNDER_SYS_SIGLIST && !HAVE_STRSIGNAL */
|
||||
}
|
||||
|
||||
/* A structure describing a signal that terminates the shell if not
|
||||
caught. The orig_handler member is present so children can reset
|
||||
these signals back to their original handlers. */
|
||||
struct termsig {
|
||||
int signum;
|
||||
SigHandler *orig_handler;
|
||||
int orig_flags;
|
||||
};
|
||||
|
||||
#define NULL_HANDLER (SigHandler *)SIG_DFL
|
||||
|
||||
/* The list of signals that would terminate the shell if not caught.
|
||||
We catch them, but just so that we can write the history file,
|
||||
and so forth. */
|
||||
static struct termsig terminating_signals[] = {
|
||||
#ifdef SIGHUP
|
||||
{ SIGHUP, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGINT
|
||||
{ SIGINT, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGILL
|
||||
{ SIGILL, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGTRAP
|
||||
{ SIGTRAP, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGIOT
|
||||
{ SIGIOT, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGDANGER
|
||||
{ SIGDANGER, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGEMT
|
||||
{ SIGEMT, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGFPE
|
||||
{ SIGFPE, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGBUS
|
||||
{ SIGBUS, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGSEGV
|
||||
{ SIGSEGV, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGSYS
|
||||
{ SIGSYS, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGPIPE
|
||||
{ SIGPIPE, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGALRM
|
||||
{ SIGALRM, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGTERM
|
||||
{ SIGTERM, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGXCPU
|
||||
{ SIGXCPU, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGXFSZ
|
||||
{ SIGXFSZ, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGVTALRM
|
||||
{ SIGVTALRM, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#ifdef SIGPROF
|
||||
{ SIGPROF, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef SIGLOST
|
||||
{ SIGLOST, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGUSR1
|
||||
{ SIGUSR1, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
|
||||
#ifdef SIGUSR2
|
||||
{ SIGUSR2, NULL_HANDLER, 0 },
|
||||
#endif
|
||||
};
|
||||
|
||||
#define TERMSIGS_LENGTH (sizeof (terminating_signals) / sizeof (struct termsig))
|
||||
|
||||
#define XSIG(x) (terminating_signals[x].signum)
|
||||
#define XHANDLER(x) (terminating_signals[x].orig_handler)
|
||||
#define XSAFLAGS(x) (terminating_signals[x].orig_flags)
|
||||
|
||||
static int termsigs_initialized = 0;
|
||||
|
||||
/* Initialize signals that will terminate the shell to do some
|
||||
unwind protection. For non-interactive shells, we only call
|
||||
this when a trap is defined for EXIT (0) or when trap is run
|
||||
to display signal dispositions. */
|
||||
void
|
||||
initialize_terminating_signals ()
|
||||
{
|
||||
register int i;
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
struct sigaction act, oact;
|
||||
#endif
|
||||
|
||||
if (termsigs_initialized)
|
||||
return;
|
||||
|
||||
/* The following code is to avoid an expensive call to
|
||||
set_signal_handler () for each terminating_signals. Fortunately,
|
||||
this is possible in Posix. Unfortunately, we have to call signal ()
|
||||
on non-Posix systems for each signal in terminating_signals. */
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
act.sa_handler = termsig_sighandler;
|
||||
act.sa_flags = 0;
|
||||
sigemptyset (&act.sa_mask);
|
||||
sigemptyset (&oact.sa_mask);
|
||||
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
||||
sigaddset (&act.sa_mask, XSIG (i));
|
||||
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
||||
{
|
||||
/* If we've already trapped it, don't do anything. */
|
||||
if (signal_is_trapped (XSIG (i)))
|
||||
continue;
|
||||
|
||||
sigaction (XSIG (i), &act, &oact);
|
||||
XHANDLER(i) = oact.sa_handler;
|
||||
XSAFLAGS(i) = oact.sa_flags;
|
||||
/* Don't do anything with signals that are ignored at shell entry
|
||||
if the shell is not interactive. */
|
||||
/* XXX - should we do this for interactive shells, too? */
|
||||
if (interactive_shell == 0 && XHANDLER (i) == SIG_IGN)
|
||||
{
|
||||
sigaction (XSIG (i), &oact, &act);
|
||||
set_signal_ignored (XSIG (i));
|
||||
}
|
||||
#if defined (SIGPROF) && !defined (_MINIX)
|
||||
if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN)
|
||||
sigaction (XSIG (i), &oact, (struct sigaction *)NULL);
|
||||
#endif /* SIGPROF && !_MINIX */
|
||||
}
|
||||
|
||||
#else /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
||||
{
|
||||
/* If we've already trapped it, don't do anything. */
|
||||
if (signal_is_trapped (XSIG (i)))
|
||||
continue;
|
||||
|
||||
XHANDLER(i) = signal (XSIG (i), termsig_sighandler);
|
||||
XSAFLAGS(i) = 0;
|
||||
/* Don't do anything with signals that are ignored at shell entry
|
||||
if the shell is not interactive. */
|
||||
/* XXX - should we do this for interactive shells, too? */
|
||||
if (interactive_shell == 0 && XHANDLER (i) == SIG_IGN)
|
||||
{
|
||||
signal (XSIG (i), SIG_IGN);
|
||||
set_signal_ignored (XSIG (i));
|
||||
}
|
||||
#ifdef SIGPROF
|
||||
if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN)
|
||||
signal (XSIG (i), XHANDLER (i));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
termsigs_initialized = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
initialize_shell_signals ()
|
||||
{
|
||||
if (interactive)
|
||||
initialize_terminating_signals ();
|
||||
|
||||
#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
|
||||
/* All shells use the signal mask they inherit, and pass it along
|
||||
to child processes. Children will never block SIGCHLD, though. */
|
||||
sigemptyset (&top_level_mask);
|
||||
sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &top_level_mask);
|
||||
# if defined (SIGCHLD)
|
||||
sigdelset (&top_level_mask, SIGCHLD);
|
||||
# endif
|
||||
#endif /* JOB_CONTROL || HAVE_POSIX_SIGNALS */
|
||||
|
||||
/* And, some signals that are specifically ignored by the shell. */
|
||||
set_signal_handler (SIGQUIT, SIG_IGN);
|
||||
|
||||
if (interactive)
|
||||
{
|
||||
set_signal_handler (SIGINT, sigint_sighandler);
|
||||
set_signal_handler (SIGTERM, SIG_IGN);
|
||||
set_sigwinch_handler ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
reset_terminating_signals ()
|
||||
{
|
||||
register int i;
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
struct sigaction act;
|
||||
#endif
|
||||
|
||||
if (termsigs_initialized == 0)
|
||||
return;
|
||||
|
||||
#if defined (HAVE_POSIX_SIGNALS)
|
||||
act.sa_flags = 0;
|
||||
sigemptyset (&act.sa_mask);
|
||||
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
||||
{
|
||||
/* Skip a signal if it's trapped or handled specially, because the
|
||||
trap code will restore the correct value. */
|
||||
if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
|
||||
continue;
|
||||
|
||||
act.sa_handler = XHANDLER (i);
|
||||
act.sa_flags = XSAFLAGS (i);
|
||||
sigaction (XSIG (i), &act, (struct sigaction *) NULL);
|
||||
}
|
||||
#else /* !HAVE_POSIX_SIGNALS */
|
||||
for (i = 0; i < TERMSIGS_LENGTH; i++)
|
||||
{
|
||||
if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
|
||||
continue;
|
||||
|
||||
signal (XSIG (i), XHANDLER (i));
|
||||
}
|
||||
#endif /* !HAVE_POSIX_SIGNALS */
|
||||
|
||||
termsigs_initialized = 0;
|
||||
}
|
||||
#undef XSIG
|
||||
#undef XHANDLER
|
||||
|
||||
/* Run some of the cleanups that should be performed when we run
|
||||
jump_to_top_level from a builtin command context. XXX - might want to
|
||||
also call reset_parser here. */
|
||||
void
|
||||
top_level_cleanup ()
|
||||
{
|
||||
/* Clean up string parser environment. */
|
||||
while (parse_and_execute_level)
|
||||
parse_and_execute_cleanup ();
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
unlink_fifo_list ();
|
||||
#endif /* PROCESS_SUBSTITUTION */
|
||||
|
||||
run_unwind_protects ();
|
||||
loop_level = continuing = breaking = funcnest = 0;
|
||||
executing_list = comsub_ignore_return = return_catch_flag = 0;
|
||||
}
|
||||
|
||||
/* What to do when we've been interrupted, and it is safe to handle it. */
|
||||
void
|
||||
throw_to_top_level ()
|
||||
{
|
||||
int print_newline = 0;
|
||||
|
||||
if (interrupt_state)
|
||||
{
|
||||
print_newline = 1;
|
||||
DELINTERRUPT;
|
||||
}
|
||||
|
||||
if (interrupt_state)
|
||||
return;
|
||||
|
||||
last_command_exit_signal = (last_command_exit_value > 128) ?
|
||||
(last_command_exit_value - 128) : 0;
|
||||
last_command_exit_value |= 128;
|
||||
|
||||
/* Run any traps set on SIGINT. */
|
||||
run_interrupt_trap ();
|
||||
|
||||
/* Clean up string parser environment. */
|
||||
while (parse_and_execute_level)
|
||||
parse_and_execute_cleanup ();
|
||||
|
||||
#if defined (JOB_CONTROL)
|
||||
give_terminal_to (shell_pgrp, 0);
|
||||
#endif /* JOB_CONTROL */
|
||||
|
||||
#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
|
||||
/* This should not be necessary on systems using sigsetjmp/siglongjmp. */
|
||||
sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
|
||||
#endif
|
||||
|
||||
reset_parser ();
|
||||
|
||||
#if defined (READLINE)
|
||||
if (interactive)
|
||||
bashline_reset ();
|
||||
#endif /* READLINE */
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
unlink_fifo_list ();
|
||||
#endif /* PROCESS_SUBSTITUTION */
|
||||
|
||||
run_unwind_protects ();
|
||||
loop_level = continuing = breaking = funcnest = 0;
|
||||
executing_list = comsub_ignore_return = return_catch_flag = 0;
|
||||
|
||||
if (interactive && print_newline)
|
||||
{
|
||||
fflush (stdout);
|
||||
fprintf (stderr, "\n");
|
||||
fflush (stderr);
|
||||
}
|
||||
|
||||
/* An interrupted `wait' command in a script does not exit the script. */
|
||||
if (interactive || (interactive_shell && !shell_initialized) ||
|
||||
(print_newline && signal_is_trapped (SIGINT)))
|
||||
jump_to_top_level (DISCARD);
|
||||
else
|
||||
jump_to_top_level (EXITPROG);
|
||||
}
|
||||
|
||||
/* This is just here to isolate the longjmp calls. */
|
||||
void
|
||||
jump_to_top_level (value)
|
||||
int value;
|
||||
{
|
||||
longjmp (top_level, value);
|
||||
}
|
||||
|
||||
sighandler
|
||||
termsig_sighandler (sig)
|
||||
int sig;
|
||||
{
|
||||
/* If we get called twice with the same signal before handling it,
|
||||
terminate right away. */
|
||||
if (
|
||||
#ifdef SIGHUP
|
||||
sig != SIGHUP &&
|
||||
#endif
|
||||
#ifdef SIGINT
|
||||
sig != SIGINT &&
|
||||
#endif
|
||||
#ifdef SIGDANGER
|
||||
sig != SIGDANGER &&
|
||||
#endif
|
||||
#ifdef SIGPIPE
|
||||
sig != SIGPIPE &&
|
||||
#endif
|
||||
#ifdef SIGALRM
|
||||
sig != SIGALRM &&
|
||||
#endif
|
||||
#ifdef SIGTERM
|
||||
sig != SIGTERM &&
|
||||
#endif
|
||||
#ifdef SIGXCPU
|
||||
sig != SIGXCPU &&
|
||||
#endif
|
||||
#ifdef SIGXFSZ
|
||||
sig != SIGXFSZ &&
|
||||
#endif
|
||||
#ifdef SIGVTALRM
|
||||
sig != SIGVTALRM &&
|
||||
#endif
|
||||
#ifdef SIGLOST
|
||||
sig != SIGLOST &&
|
||||
#endif
|
||||
#ifdef SIGUSR1
|
||||
sig != SIGUSR1 &&
|
||||
#endif
|
||||
#ifdef SIGUSR2
|
||||
sig != SIGUSR2 &&
|
||||
#endif
|
||||
sig == terminating_signal)
|
||||
terminate_immediately = 1;
|
||||
|
||||
terminating_signal = sig;
|
||||
|
||||
/* XXX - should this also trigger when interrupt_immediately is set? */
|
||||
if (terminate_immediately)
|
||||
{
|
||||
#if defined (HISTORY)
|
||||
/* XXX - will inhibit history file being written */
|
||||
# if defined (READLINE)
|
||||
if (interactive_shell == 0 || interactive == 0 || (sig != SIGHUP && sig != SIGTERM) || no_line_editing || (RL_ISSTATE (RL_STATE_READCMD) == 0))
|
||||
# endif
|
||||
history_lines_this_session = 0;
|
||||
#endif
|
||||
terminate_immediately = 0;
|
||||
termsig_handler (sig);
|
||||
}
|
||||
|
||||
#if defined (READLINE)
|
||||
if (interactive_shell && interactive && no_line_editing == 0)
|
||||
bashline_set_event_hook ();
|
||||
#endif
|
||||
|
||||
SIGRETURN (0);
|
||||
}
|
||||
|
||||
void
|
||||
termsig_handler (sig)
|
||||
int sig;
|
||||
{
|
||||
static int handling_termsig = 0;
|
||||
|
||||
/* Simple semaphore to keep this function from being executed multiple
|
||||
times. Since we no longer are running as a signal handler, we don't
|
||||
block multiple occurrences of the terminating signals while running. */
|
||||
if (handling_termsig)
|
||||
return;
|
||||
handling_termsig = 1;
|
||||
terminating_signal = 0; /* keep macro from re-testing true. */
|
||||
|
||||
/* I don't believe this condition ever tests true. */
|
||||
if (sig == SIGINT && signal_is_trapped (SIGINT))
|
||||
run_interrupt_trap ();
|
||||
|
||||
#if 0
|
||||
#if defined (HISTORY)
|
||||
if (interactive_shell && (sig != SIGABRT && sig != SIGINT && sig != SIGHUP && sig != SIGTERM))
|
||||
maybe_save_shell_history ();
|
||||
#endif /* HISTORY */
|
||||
#endif
|
||||
|
||||
#if defined (JOB_CONTROL)
|
||||
if (sig == SIGHUP && (interactive || (subshell_environment & (SUBSHELL_COMSUB|SUBSHELL_PROCSUB))))
|
||||
hangup_all_jobs ();
|
||||
end_job_control ();
|
||||
#endif /* JOB_CONTROL */
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
unlink_fifo_list ();
|
||||
#endif /* PROCESS_SUBSTITUTION */
|
||||
|
||||
/* Reset execution context */
|
||||
loop_level = continuing = breaking = funcnest = 0;
|
||||
executing_list = comsub_ignore_return = return_catch_flag = 0;
|
||||
|
||||
run_exit_trap ();
|
||||
set_signal_handler (sig, SIG_DFL);
|
||||
kill (getpid (), sig);
|
||||
}
|
||||
|
||||
/* What we really do when SIGINT occurs. */
|
||||
sighandler
|
||||
sigint_sighandler (sig)
|
||||
int sig;
|
||||
{
|
||||
#if defined (MUST_REINSTALL_SIGHANDLERS)
|
||||
signal (sig, sigint_sighandler);
|
||||
#endif
|
||||
|
||||
/* interrupt_state needs to be set for the stack of interrupts to work
|
||||
right. Should it be set unconditionally? */
|
||||
if (interrupt_state == 0)
|
||||
ADDINTERRUPT;
|
||||
|
||||
if (interrupt_immediately)
|
||||
{
|
||||
interrupt_immediately = 0;
|
||||
last_command_exit_value = 128 + sig;
|
||||
throw_to_top_level ();
|
||||
}
|
||||
#if defined (READLINE)
|
||||
else if (RL_ISSTATE (RL_STATE_SIGHANDLER))
|
||||
{
|
||||
itrace("sigint_sighandler: installing event hook");
|
||||
bashline_set_event_hook ();
|
||||
}
|
||||
#endif
|
||||
|
||||
SIGRETURN (0);
|
||||
}
|
||||
|
||||
#if defined (SIGWINCH)
|
||||
sighandler
|
||||
sigwinch_sighandler (sig)
|
||||
int sig;
|
||||
{
|
||||
#if defined (MUST_REINSTALL_SIGHANDLERS)
|
||||
set_signal_handler (SIGWINCH, sigwinch_sighandler);
|
||||
#endif /* MUST_REINSTALL_SIGHANDLERS */
|
||||
sigwinch_received = 1;
|
||||
SIGRETURN (0);
|
||||
}
|
||||
#endif /* SIGWINCH */
|
||||
|
||||
void
|
||||
set_sigwinch_handler ()
|
||||
{
|
||||
#if defined (SIGWINCH)
|
||||
old_winch = set_signal_handler (SIGWINCH, sigwinch_sighandler);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
unset_sigwinch_handler ()
|
||||
{
|
||||
#if defined (SIGWINCH)
|
||||
set_signal_handler (SIGWINCH, old_winch);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Signal functions used by the rest of the code. */
|
||||
#if !defined (HAVE_POSIX_SIGNALS)
|
||||
|
||||
/* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */
|
||||
sigprocmask (operation, newset, oldset)
|
||||
int operation, *newset, *oldset;
|
||||
{
|
||||
int old, new;
|
||||
|
||||
if (newset)
|
||||
new = *newset;
|
||||
else
|
||||
new = 0;
|
||||
|
||||
switch (operation)
|
||||
{
|
||||
case SIG_BLOCK:
|
||||
old = sigblock (new);
|
||||
break;
|
||||
|
||||
case SIG_SETMASK:
|
||||
old = sigsetmask (new);
|
||||
break;
|
||||
|
||||
default:
|
||||
internal_error (_("sigprocmask: %d: invalid operation"), operation);
|
||||
}
|
||||
|
||||
if (oldset)
|
||||
*oldset = old;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#if !defined (SA_INTERRUPT)
|
||||
# define SA_INTERRUPT 0
|
||||
#endif
|
||||
|
||||
#if !defined (SA_RESTART)
|
||||
# define SA_RESTART 0
|
||||
#endif
|
||||
|
||||
SigHandler *
|
||||
set_signal_handler (sig, handler)
|
||||
int sig;
|
||||
SigHandler *handler;
|
||||
{
|
||||
struct sigaction act, oact;
|
||||
|
||||
act.sa_handler = handler;
|
||||
act.sa_flags = 0;
|
||||
|
||||
/* XXX - bash-4.2 */
|
||||
/* We don't want a child death to interrupt interruptible system calls, even
|
||||
if we take the time to reap children */
|
||||
#if defined (SIGCHLD)
|
||||
if (sig == SIGCHLD)
|
||||
act.sa_flags |= SA_RESTART; /* XXX */
|
||||
#endif
|
||||
|
||||
sigemptyset (&act.sa_mask);
|
||||
sigemptyset (&oact.sa_mask);
|
||||
sigaction (sig, &act, &oact);
|
||||
return (oact.sa_handler);
|
||||
}
|
||||
#endif /* HAVE_POSIX_SIGNALS */
|
||||
@@ -1,852 +0,0 @@
|
||||
/* test.c - GNU test program (ksb and mjb) */
|
||||
|
||||
/* Modified to run with the GNU shell Apr 25, 1988 by bfox. */
|
||||
|
||||
/* Copyright (C) 1987-2010 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Define PATTERN_MATCHING to get the csh-like =~ and !~ pattern-matching
|
||||
binary operators. */
|
||||
/* #define PATTERN_MATCHING */
|
||||
|
||||
#if defined (HAVE_CONFIG_H)
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "bashtypes.h"
|
||||
|
||||
#if !defined (HAVE_LIMITS_H) && defined (HAVE_SYS_PARAM_H)
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif /* !errno */
|
||||
|
||||
#if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H)
|
||||
# include <sys/file.h>
|
||||
#endif /* !_POSIX_VERSION */
|
||||
#include "posixstat.h"
|
||||
#include "filecntl.h"
|
||||
#include "stat-time.h"
|
||||
|
||||
#include "bashintl.h"
|
||||
|
||||
#include "shell.h"
|
||||
#include "pathexp.h"
|
||||
#include "test.h"
|
||||
#include "builtins/common.h"
|
||||
|
||||
#include <glob/strmatch.h>
|
||||
|
||||
#if !defined (STRLEN)
|
||||
# define STRLEN(s) ((s)[0] ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0)
|
||||
#endif
|
||||
|
||||
#if !defined (STREQ)
|
||||
# define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0)
|
||||
#endif /* !STREQ */
|
||||
#define STRCOLLEQ(a, b) ((a)[0] == (b)[0] && strcoll ((a), (b)) == 0)
|
||||
|
||||
#if !defined (R_OK)
|
||||
#define R_OK 4
|
||||
#define W_OK 2
|
||||
#define X_OK 1
|
||||
#define F_OK 0
|
||||
#endif /* R_OK */
|
||||
|
||||
#define EQ 0
|
||||
#define NE 1
|
||||
#define LT 2
|
||||
#define GT 3
|
||||
#define LE 4
|
||||
#define GE 5
|
||||
|
||||
#define NT 0
|
||||
#define OT 1
|
||||
#define EF 2
|
||||
|
||||
/* The following few defines control the truth and false output of each stage.
|
||||
TRUE and FALSE are what we use to compute the final output value.
|
||||
SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
|
||||
Default is TRUE = 1, FALSE = 0, SHELL_BOOLEAN = (!value). */
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#define SHELL_BOOLEAN(value) (!(value))
|
||||
|
||||
#define TEST_ERREXIT_STATUS 2
|
||||
|
||||
static procenv_t test_exit_buf;
|
||||
static int test_error_return;
|
||||
#define test_exit(val) \
|
||||
do { test_error_return = val; longjmp (test_exit_buf, 1); } while (0)
|
||||
|
||||
extern int sh_stat __P((const char *, struct stat *));
|
||||
|
||||
static int pos; /* The offset of the current argument in ARGV. */
|
||||
static int argc; /* The number of arguments present in ARGV. */
|
||||
static char **argv; /* The argument list. */
|
||||
static int noeval;
|
||||
|
||||
static void test_syntax_error __P((char *, char *)) __attribute__((__noreturn__));
|
||||
static void beyond __P((void)) __attribute__((__noreturn__));
|
||||
static void integer_expected_error __P((char *)) __attribute__((__noreturn__));
|
||||
|
||||
static int unary_operator __P((void));
|
||||
static int binary_operator __P((void));
|
||||
static int two_arguments __P((void));
|
||||
static int three_arguments __P((void));
|
||||
static int posixtest __P((void));
|
||||
|
||||
static int expr __P((void));
|
||||
static int term __P((void));
|
||||
static int and __P((void));
|
||||
static int or __P((void));
|
||||
|
||||
static int filecomp __P((char *, char *, int));
|
||||
static int arithcomp __P((char *, char *, int, int));
|
||||
static int patcomp __P((char *, char *, int));
|
||||
|
||||
static void
|
||||
test_syntax_error (format, arg)
|
||||
char *format, *arg;
|
||||
{
|
||||
builtin_error (format, arg);
|
||||
test_exit (TEST_ERREXIT_STATUS);
|
||||
}
|
||||
|
||||
/*
|
||||
* beyond - call when we're beyond the end of the argument list (an
|
||||
* error condition)
|
||||
*/
|
||||
static void
|
||||
beyond ()
|
||||
{
|
||||
test_syntax_error (_("argument expected"), (char *)NULL);
|
||||
}
|
||||
|
||||
/* Syntax error for when an integer argument was expected, but
|
||||
something else was found. */
|
||||
static void
|
||||
integer_expected_error (pch)
|
||||
char *pch;
|
||||
{
|
||||
test_syntax_error (_("%s: integer expression expected"), pch);
|
||||
}
|
||||
|
||||
/* Increment our position in the argument list. Check that we're not
|
||||
past the end of the argument list. This check is supressed if the
|
||||
argument is FALSE. Made a macro for efficiency. */
|
||||
#define advance(f) do { ++pos; if (f && pos >= argc) beyond (); } while (0)
|
||||
#define unary_advance() do { advance (1); ++pos; } while (0)
|
||||
|
||||
/*
|
||||
* expr:
|
||||
* or
|
||||
*/
|
||||
static int
|
||||
expr ()
|
||||
{
|
||||
if (pos >= argc)
|
||||
beyond ();
|
||||
|
||||
return (FALSE ^ or ()); /* Same with this. */
|
||||
}
|
||||
|
||||
/*
|
||||
* or:
|
||||
* and
|
||||
* and '-o' or
|
||||
*/
|
||||
static int
|
||||
or ()
|
||||
{
|
||||
int value, v2;
|
||||
|
||||
value = and ();
|
||||
if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'o' && !argv[pos][2])
|
||||
{
|
||||
advance (0);
|
||||
v2 = or ();
|
||||
return (value || v2);
|
||||
}
|
||||
|
||||
return (value);
|
||||
}
|
||||
|
||||
/*
|
||||
* and:
|
||||
* term
|
||||
* term '-a' and
|
||||
*/
|
||||
static int
|
||||
and ()
|
||||
{
|
||||
int value, v2;
|
||||
|
||||
value = term ();
|
||||
if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'a' && !argv[pos][2])
|
||||
{
|
||||
advance (0);
|
||||
v2 = and ();
|
||||
return (value && v2);
|
||||
}
|
||||
return (value);
|
||||
}
|
||||
|
||||
/*
|
||||
* term - parse a term and return 1 or 0 depending on whether the term
|
||||
* evaluates to true or false, respectively.
|
||||
*
|
||||
* term ::=
|
||||
* '-'('a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'k'|'p'|'r'|'s'|'u'|'w'|'x') filename
|
||||
* '-'('G'|'L'|'O'|'S'|'N') filename
|
||||
* '-t' [int]
|
||||
* '-'('z'|'n') string
|
||||
* '-o' option
|
||||
* string
|
||||
* string ('!='|'='|'==') string
|
||||
* <int> '-'(eq|ne|le|lt|ge|gt) <int>
|
||||
* file '-'(nt|ot|ef) file
|
||||
* '(' <expr> ')'
|
||||
* int ::=
|
||||
* positive and negative integers
|
||||
*/
|
||||
static int
|
||||
term ()
|
||||
{
|
||||
int value;
|
||||
|
||||
if (pos >= argc)
|
||||
beyond ();
|
||||
|
||||
/* Deal with leading `not's. */
|
||||
if (argv[pos][0] == '!' && argv[pos][1] == '\0')
|
||||
{
|
||||
value = 0;
|
||||
while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0')
|
||||
{
|
||||
advance (1);
|
||||
value = 1 - value;
|
||||
}
|
||||
|
||||
return (value ? !term() : term());
|
||||
}
|
||||
|
||||
/* A paren-bracketed argument. */
|
||||
if (argv[pos][0] == '(' && argv[pos][1] == '\0') /* ) */
|
||||
{
|
||||
advance (1);
|
||||
value = expr ();
|
||||
if (argv[pos] == 0) /* ( */
|
||||
test_syntax_error (_("`)' expected"), (char *)NULL);
|
||||
else if (argv[pos][0] != ')' || argv[pos][1]) /* ( */
|
||||
test_syntax_error (_("`)' expected, found %s"), argv[pos]);
|
||||
advance (0);
|
||||
return (value);
|
||||
}
|
||||
|
||||
/* are there enough arguments left that this could be dyadic? */
|
||||
if ((pos + 3 <= argc) && test_binop (argv[pos + 1]))
|
||||
value = binary_operator ();
|
||||
|
||||
/* Might be a switch type argument */
|
||||
else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
|
||||
{
|
||||
if (test_unop (argv[pos]))
|
||||
value = unary_operator ();
|
||||
else
|
||||
test_syntax_error (_("%s: unary operator expected"), argv[pos]);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = argv[pos][0] != '\0';
|
||||
advance (0);
|
||||
}
|
||||
|
||||
return (value);
|
||||
}
|
||||
|
||||
static int
|
||||
stat_mtime (fn, st, ts)
|
||||
char *fn;
|
||||
struct stat *st;
|
||||
struct timespec *ts;
|
||||
{
|
||||
int r;
|
||||
|
||||
r = sh_stat (fn, st);
|
||||
if (r < 0)
|
||||
return r;
|
||||
*ts = get_stat_mtime (st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
filecomp (s, t, op)
|
||||
char *s, *t;
|
||||
int op;
|
||||
{
|
||||
struct stat st1, st2;
|
||||
struct timespec ts1, ts2;
|
||||
int r1, r2;
|
||||
|
||||
if ((r1 = stat_mtime (s, &st1, &ts1)) < 0)
|
||||
{
|
||||
if (op == EF)
|
||||
return (FALSE);
|
||||
}
|
||||
if ((r2 = stat_mtime (t, &st2, &ts2)) < 0)
|
||||
{
|
||||
if (op == EF)
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case OT: return (r1 < r2 || (r2 == 0 && timespec_cmp (ts1, ts2) < 0));
|
||||
case NT: return (r1 > r2 || (r1 == 0 && timespec_cmp (ts1, ts2) > 0));
|
||||
case EF: return (same_file (s, t, &st1, &st2));
|
||||
}
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
static int
|
||||
arithcomp (s, t, op, flags)
|
||||
char *s, *t;
|
||||
int op, flags;
|
||||
{
|
||||
intmax_t l, r;
|
||||
int expok;
|
||||
|
||||
if (flags & TEST_ARITHEXP)
|
||||
{
|
||||
l = evalexp (s, &expok);
|
||||
if (expok == 0)
|
||||
return (FALSE); /* should probably longjmp here */
|
||||
r = evalexp (t, &expok);
|
||||
if (expok == 0)
|
||||
return (FALSE); /* ditto */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (legal_number (s, &l) == 0)
|
||||
integer_expected_error (s);
|
||||
if (legal_number (t, &r) == 0)
|
||||
integer_expected_error (t);
|
||||
}
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case EQ: return (l == r);
|
||||
case NE: return (l != r);
|
||||
case LT: return (l < r);
|
||||
case GT: return (l > r);
|
||||
case LE: return (l <= r);
|
||||
case GE: return (l >= r);
|
||||
}
|
||||
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
static int
|
||||
patcomp (string, pat, op)
|
||||
char *string, *pat;
|
||||
int op;
|
||||
{
|
||||
int m;
|
||||
|
||||
m = strmatch (pat, string, FNMATCH_EXTFLAG|FNMATCH_IGNCASE);
|
||||
return ((op == EQ) ? (m == 0) : (m != 0));
|
||||
}
|
||||
|
||||
int
|
||||
binary_test (op, arg1, arg2, flags)
|
||||
char *op, *arg1, *arg2;
|
||||
int flags;
|
||||
{
|
||||
int patmatch;
|
||||
|
||||
patmatch = (flags & TEST_PATMATCH);
|
||||
|
||||
if (op[0] == '=' && (op[1] == '\0' || (op[1] == '=' && op[2] == '\0')))
|
||||
return (patmatch ? patcomp (arg1, arg2, EQ) : STREQ (arg1, arg2));
|
||||
else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0')
|
||||
{
|
||||
if (shell_compatibility_level > 40 && flags & TEST_LOCALE)
|
||||
return ((op[0] == '>') ? (strcoll (arg1, arg2) > 0) : (strcoll (arg1, arg2) < 0));
|
||||
else
|
||||
return ((op[0] == '>') ? (strcmp (arg1, arg2) > 0) : (strcmp (arg1, arg2) < 0));
|
||||
}
|
||||
else if (op[0] == '!' && op[1] == '=' && op[2] == '\0')
|
||||
return (patmatch ? patcomp (arg1, arg2, NE) : (STREQ (arg1, arg2) == 0));
|
||||
|
||||
|
||||
else if (op[2] == 't')
|
||||
{
|
||||
switch (op[1])
|
||||
{
|
||||
case 'n': return (filecomp (arg1, arg2, NT)); /* -nt */
|
||||
case 'o': return (filecomp (arg1, arg2, OT)); /* -ot */
|
||||
case 'l': return (arithcomp (arg1, arg2, LT, flags)); /* -lt */
|
||||
case 'g': return (arithcomp (arg1, arg2, GT, flags)); /* -gt */
|
||||
}
|
||||
}
|
||||
else if (op[1] == 'e')
|
||||
{
|
||||
switch (op[2])
|
||||
{
|
||||
case 'f': return (filecomp (arg1, arg2, EF)); /* -ef */
|
||||
case 'q': return (arithcomp (arg1, arg2, EQ, flags)); /* -eq */
|
||||
}
|
||||
}
|
||||
else if (op[2] == 'e')
|
||||
{
|
||||
switch (op[1])
|
||||
{
|
||||
case 'n': return (arithcomp (arg1, arg2, NE, flags)); /* -ne */
|
||||
case 'g': return (arithcomp (arg1, arg2, GE, flags)); /* -ge */
|
||||
case 'l': return (arithcomp (arg1, arg2, LE, flags)); /* -le */
|
||||
}
|
||||
}
|
||||
|
||||
return (FALSE); /* should never get here */
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
binary_operator ()
|
||||
{
|
||||
int value;
|
||||
char *w;
|
||||
|
||||
w = argv[pos + 1];
|
||||
if ((w[0] == '=' && (w[1] == '\0' || (w[1] == '=' && w[2] == '\0'))) || /* =, == */
|
||||
((w[0] == '>' || w[0] == '<') && w[1] == '\0') || /* <, > */
|
||||
(w[0] == '!' && w[1] == '=' && w[2] == '\0')) /* != */
|
||||
{
|
||||
value = binary_test (w, argv[pos], argv[pos + 2], 0);
|
||||
pos += 3;
|
||||
return (value);
|
||||
}
|
||||
|
||||
#if defined (PATTERN_MATCHING)
|
||||
if ((w[0] == '=' || w[0] == '!') && w[1] == '~' && w[2] == '\0')
|
||||
{
|
||||
value = patcomp (argv[pos], argv[pos + 2], w[0] == '=' ? EQ : NE);
|
||||
pos += 3;
|
||||
return (value);
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((w[0] != '-' || w[3] != '\0') || test_binop (w) == 0)
|
||||
{
|
||||
test_syntax_error (_("%s: binary operator expected"), w);
|
||||
/* NOTREACHED */
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
value = binary_test (w, argv[pos], argv[pos + 2], 0);
|
||||
pos += 3;
|
||||
return value;
|
||||
}
|
||||
|
||||
static int
|
||||
unary_operator ()
|
||||
{
|
||||
char *op;
|
||||
intmax_t r;
|
||||
|
||||
op = argv[pos];
|
||||
if (test_unop (op) == 0)
|
||||
return (FALSE);
|
||||
|
||||
/* the only tricky case is `-t', which may or may not take an argument. */
|
||||
if (op[1] == 't')
|
||||
{
|
||||
advance (0);
|
||||
if (pos < argc)
|
||||
{
|
||||
if (legal_number (argv[pos], &r))
|
||||
{
|
||||
advance (0);
|
||||
return (unary_test (op, argv[pos - 1]));
|
||||
}
|
||||
else
|
||||
return (FALSE);
|
||||
}
|
||||
else
|
||||
return (unary_test (op, "1"));
|
||||
}
|
||||
|
||||
/* All of the unary operators take an argument, so we first call
|
||||
unary_advance (), which checks to make sure that there is an
|
||||
argument, and then advances pos right past it. This means that
|
||||
pos - 1 is the location of the argument. */
|
||||
unary_advance ();
|
||||
return (unary_test (op, argv[pos - 1]));
|
||||
}
|
||||
|
||||
int
|
||||
unary_test (op, arg)
|
||||
char *op, *arg;
|
||||
{
|
||||
intmax_t r;
|
||||
struct stat stat_buf;
|
||||
SHELL_VAR *v;
|
||||
|
||||
switch (op[1])
|
||||
{
|
||||
case 'a': /* file exists in the file system? */
|
||||
case 'e':
|
||||
return (sh_stat (arg, &stat_buf) == 0);
|
||||
|
||||
case 'r': /* file is readable? */
|
||||
return (sh_eaccess (arg, R_OK) == 0);
|
||||
|
||||
case 'w': /* File is writeable? */
|
||||
return (sh_eaccess (arg, W_OK) == 0);
|
||||
|
||||
case 'x': /* File is executable? */
|
||||
return (sh_eaccess (arg, X_OK) == 0);
|
||||
|
||||
case 'O': /* File is owned by you? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 &&
|
||||
(uid_t) current_user.euid == (uid_t) stat_buf.st_uid);
|
||||
|
||||
case 'G': /* File is owned by your group? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 &&
|
||||
(gid_t) current_user.egid == (gid_t) stat_buf.st_gid);
|
||||
|
||||
case 'N':
|
||||
return (sh_stat (arg, &stat_buf) == 0 &&
|
||||
stat_buf.st_atime <= stat_buf.st_mtime);
|
||||
|
||||
case 'f': /* File is a file? */
|
||||
if (sh_stat (arg, &stat_buf) < 0)
|
||||
return (FALSE);
|
||||
|
||||
/* -f is true if the given file exists and is a regular file. */
|
||||
#if defined (S_IFMT)
|
||||
return (S_ISREG (stat_buf.st_mode) || (stat_buf.st_mode & S_IFMT) == 0);
|
||||
#else
|
||||
return (S_ISREG (stat_buf.st_mode));
|
||||
#endif /* !S_IFMT */
|
||||
|
||||
case 'd': /* File is a directory? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 && (S_ISDIR (stat_buf.st_mode)));
|
||||
|
||||
case 's': /* File has something in it? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 && stat_buf.st_size > (off_t) 0);
|
||||
|
||||
case 'S': /* File is a socket? */
|
||||
#if !defined (S_ISSOCK)
|
||||
return (FALSE);
|
||||
#else
|
||||
return (sh_stat (arg, &stat_buf) == 0 && S_ISSOCK (stat_buf.st_mode));
|
||||
#endif /* S_ISSOCK */
|
||||
|
||||
case 'c': /* File is character special? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 && S_ISCHR (stat_buf.st_mode));
|
||||
|
||||
case 'b': /* File is block special? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 && S_ISBLK (stat_buf.st_mode));
|
||||
|
||||
case 'p': /* File is a named pipe? */
|
||||
#ifndef S_ISFIFO
|
||||
return (FALSE);
|
||||
#else
|
||||
return (sh_stat (arg, &stat_buf) == 0 && S_ISFIFO (stat_buf.st_mode));
|
||||
#endif /* S_ISFIFO */
|
||||
|
||||
case 'L': /* Same as -h */
|
||||
case 'h': /* File is a symbolic link? */
|
||||
#if !defined (S_ISLNK) || !defined (HAVE_LSTAT)
|
||||
return (FALSE);
|
||||
#else
|
||||
return ((arg[0] != '\0') &&
|
||||
(lstat (arg, &stat_buf) == 0) && S_ISLNK (stat_buf.st_mode));
|
||||
#endif /* S_IFLNK && HAVE_LSTAT */
|
||||
|
||||
case 'u': /* File is setuid? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISUID) != 0);
|
||||
|
||||
case 'g': /* File is setgid? */
|
||||
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISGID) != 0);
|
||||
|
||||
case 'k': /* File has sticky bit set? */
|
||||
#if !defined (S_ISVTX)
|
||||
/* This is not Posix, and is not defined on some Posix systems. */
|
||||
return (FALSE);
|
||||
#else
|
||||
return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISVTX) != 0);
|
||||
#endif
|
||||
|
||||
case 't': /* File fd is a terminal? */
|
||||
if (legal_number (arg, &r) == 0)
|
||||
return (FALSE);
|
||||
return ((r == (int)r) && isatty ((int)r));
|
||||
|
||||
case 'n': /* True if arg has some length. */
|
||||
return (arg[0] != '\0');
|
||||
|
||||
case 'z': /* True if arg has no length. */
|
||||
return (arg[0] == '\0');
|
||||
|
||||
case 'o': /* True if option `arg' is set. */
|
||||
return (minus_o_option_value (arg) == 1);
|
||||
|
||||
case 'v':
|
||||
v = find_variable (arg);
|
||||
return (v && var_isset (v) ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
/* We can't actually get here, but this shuts up gcc. */
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
/* Return TRUE if OP is one of the test command's binary operators. */
|
||||
int
|
||||
test_binop (op)
|
||||
char *op;
|
||||
{
|
||||
if (op[0] == '=' && op[1] == '\0')
|
||||
return (1); /* '=' */
|
||||
else if ((op[0] == '<' || op[0] == '>') && op[1] == '\0') /* string <, > */
|
||||
return (1);
|
||||
else if ((op[0] == '=' || op[0] == '!') && op[1] == '=' && op[2] == '\0')
|
||||
return (1); /* `==' and `!=' */
|
||||
#if defined (PATTERN_MATCHING)
|
||||
else if (op[2] == '\0' && op[1] == '~' && (op[0] == '=' || op[0] == '!'))
|
||||
return (1);
|
||||
#endif
|
||||
else if (op[0] != '-' || op[2] == '\0' || op[3] != '\0')
|
||||
return (0);
|
||||
else
|
||||
{
|
||||
if (op[2] == 't')
|
||||
switch (op[1])
|
||||
{
|
||||
case 'n': /* -nt */
|
||||
case 'o': /* -ot */
|
||||
case 'l': /* -lt */
|
||||
case 'g': /* -gt */
|
||||
return (1);
|
||||
default:
|
||||
return (0);
|
||||
}
|
||||
else if (op[1] == 'e')
|
||||
switch (op[2])
|
||||
{
|
||||
case 'q': /* -eq */
|
||||
case 'f': /* -ef */
|
||||
return (1);
|
||||
default:
|
||||
return (0);
|
||||
}
|
||||
else if (op[2] == 'e')
|
||||
switch (op[1])
|
||||
{
|
||||
case 'n': /* -ne */
|
||||
case 'g': /* -ge */
|
||||
case 'l': /* -le */
|
||||
return (1);
|
||||
default:
|
||||
return (0);
|
||||
}
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return non-zero if OP is one of the test command's unary operators. */
|
||||
int
|
||||
test_unop (op)
|
||||
char *op;
|
||||
{
|
||||
if (op[0] != '-' || op[2] != 0)
|
||||
return (0);
|
||||
|
||||
switch (op[1])
|
||||
{
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e':
|
||||
case 'f': case 'g': case 'h': case 'k': case 'n':
|
||||
case 'o': case 'p': case 'r': case 's': case 't':
|
||||
case 'u': case 'v': case 'w': case 'x': case 'z':
|
||||
case 'G': case 'L': case 'O': case 'S': case 'N':
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
two_arguments ()
|
||||
{
|
||||
if (argv[pos][0] == '!' && argv[pos][1] == '\0')
|
||||
return (argv[pos + 1][0] == '\0');
|
||||
else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
|
||||
{
|
||||
if (test_unop (argv[pos]))
|
||||
return (unary_operator ());
|
||||
else
|
||||
test_syntax_error (_("%s: unary operator expected"), argv[pos]);
|
||||
}
|
||||
else
|
||||
test_syntax_error (_("%s: unary operator expected"), argv[pos]);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
#define ANDOR(s) (s[0] == '-' && !s[2] && (s[1] == 'a' || s[1] == 'o'))
|
||||
|
||||
/* This could be augmented to handle `-t' as equivalent to `-t 1', but
|
||||
POSIX requires that `-t' be given an argument. */
|
||||
#define ONE_ARG_TEST(s) ((s)[0] != '\0')
|
||||
|
||||
static int
|
||||
three_arguments ()
|
||||
{
|
||||
int value;
|
||||
|
||||
if (test_binop (argv[pos+1]))
|
||||
{
|
||||
value = binary_operator ();
|
||||
pos = argc;
|
||||
}
|
||||
else if (ANDOR (argv[pos+1]))
|
||||
{
|
||||
if (argv[pos+1][1] == 'a')
|
||||
value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]);
|
||||
else
|
||||
value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]);
|
||||
pos = argc;
|
||||
}
|
||||
else if (argv[pos][0] == '!' && argv[pos][1] == '\0')
|
||||
{
|
||||
advance (1);
|
||||
value = !two_arguments ();
|
||||
}
|
||||
else if (argv[pos][0] == '(' && argv[pos+2][0] == ')')
|
||||
{
|
||||
value = ONE_ARG_TEST(argv[pos+1]);
|
||||
pos = argc;
|
||||
}
|
||||
else
|
||||
test_syntax_error (_("%s: binary operator expected"), argv[pos+1]);
|
||||
|
||||
return (value);
|
||||
}
|
||||
|
||||
/* This is an implementation of a Posix.2 proposal by David Korn. */
|
||||
static int
|
||||
posixtest ()
|
||||
{
|
||||
int value;
|
||||
|
||||
switch (argc - 1) /* one extra passed in */
|
||||
{
|
||||
case 0:
|
||||
value = FALSE;
|
||||
pos = argc;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
value = ONE_ARG_TEST(argv[1]);
|
||||
pos = argc;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
value = two_arguments ();
|
||||
pos = argc;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
value = three_arguments ();
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (argv[pos][0] == '!' && argv[pos][1] == '\0')
|
||||
{
|
||||
advance (1);
|
||||
value = !three_arguments ();
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
value = expr ();
|
||||
}
|
||||
|
||||
return (value);
|
||||
}
|
||||
|
||||
/*
|
||||
* [:
|
||||
* '[' expr ']'
|
||||
* test:
|
||||
* test expr
|
||||
*/
|
||||
int
|
||||
test_command (margc, margv)
|
||||
int margc;
|
||||
char **margv;
|
||||
{
|
||||
int value;
|
||||
int code;
|
||||
|
||||
USE_VAR(margc);
|
||||
|
||||
code = setjmp (test_exit_buf);
|
||||
|
||||
if (code)
|
||||
return (test_error_return);
|
||||
|
||||
argv = margv;
|
||||
|
||||
if (margv[0] && margv[0][0] == '[' && margv[0][1] == '\0')
|
||||
{
|
||||
--margc;
|
||||
|
||||
if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1]))
|
||||
test_syntax_error (_("missing `]'"), (char *)NULL);
|
||||
|
||||
if (margc < 2)
|
||||
test_exit (SHELL_BOOLEAN (FALSE));
|
||||
}
|
||||
|
||||
argc = margc;
|
||||
pos = 1;
|
||||
|
||||
if (pos >= argc)
|
||||
test_exit (SHELL_BOOLEAN (FALSE));
|
||||
|
||||
noeval = 0;
|
||||
value = posixtest ();
|
||||
|
||||
if (pos != argc)
|
||||
test_syntax_error (_("too many arguments"), (char *)NULL);
|
||||
|
||||
test_exit (SHELL_BOOLEAN (value));
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
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 "$@"
|
||||
@@ -1,371 +0,0 @@
|
||||
# test the ksh-like extended globbing features: [!@*?+](patlist)
|
||||
|
||||
shopt -s extglob
|
||||
|
||||
expect()
|
||||
{
|
||||
echo expect "$@"
|
||||
}
|
||||
|
||||
case "/dev/udp/129.22.8.102/45" in
|
||||
/dev/@(tcp|udp)/*/*) echo ok 1;;
|
||||
*) echo bad 1;;
|
||||
esac
|
||||
|
||||
# valid numbers
|
||||
case 12 in
|
||||
0|[1-9]*([0-9])) echo ok 2;;
|
||||
*) echo bad 2;;
|
||||
esac
|
||||
|
||||
case 12abc in
|
||||
0|[1-9]*([0-9])) echo bad 3;;
|
||||
*) echo ok 3;;
|
||||
esac
|
||||
|
||||
case 1 in
|
||||
0|[1-9]*([0-9])) echo ok 4;;
|
||||
*) echo bad 4;;
|
||||
esac
|
||||
|
||||
# octal numbers
|
||||
case 07 in
|
||||
+([0-7])) echo ok 5;;
|
||||
*) echo bad 5;;
|
||||
esac
|
||||
|
||||
case 0377 in
|
||||
+([0-7])) echo ok 6;;
|
||||
*) echo bad 6;;
|
||||
esac
|
||||
|
||||
case 09 in
|
||||
+([0-7])) echo bad 7;;
|
||||
*) echo ok 7;;
|
||||
esac
|
||||
|
||||
# stuff from korn's book
|
||||
case paragraph in
|
||||
para@(chute|graph)) echo ok 8;;
|
||||
*) echo bad 8;;
|
||||
esac
|
||||
|
||||
case paramour in
|
||||
para@(chute|graph)) echo bad 9;;
|
||||
*) echo ok 9;;
|
||||
esac
|
||||
|
||||
case para991 in
|
||||
para?([345]|99)1) echo ok 10;;
|
||||
*) echo bad 10;;
|
||||
esac
|
||||
|
||||
case para381 in
|
||||
para?([345]|99)1) echo bad 11;;
|
||||
*) echo ok 11;;
|
||||
esac
|
||||
|
||||
case paragraph in
|
||||
para*([0-9])) echo bad 12;;
|
||||
*) echo ok 12;;
|
||||
esac
|
||||
|
||||
case para in
|
||||
para*([0-9])) echo ok 13;;
|
||||
*) echo bad 13;;
|
||||
esac
|
||||
|
||||
case para13829383746592 in
|
||||
para*([0-9])) echo ok 14;;
|
||||
*) echo bad 14;;
|
||||
esac
|
||||
|
||||
case paragraph in
|
||||
para*([0-9])) echo bad 15;;
|
||||
*) echo ok 15;;
|
||||
esac
|
||||
|
||||
case para in
|
||||
para+([0-9])) echo bad 16;;
|
||||
*) echo ok 16;;
|
||||
esac
|
||||
|
||||
case para987346523 in
|
||||
para+([0-9])) echo ok 17;;
|
||||
*) echo bad 17;;
|
||||
esac
|
||||
|
||||
case paragraph in
|
||||
para!(*.[0-9])) echo ok 18;;
|
||||
*) echo bad 18;;
|
||||
esac
|
||||
|
||||
case para.38 in
|
||||
para!(*.[0-9])) echo ok 19;;
|
||||
*) echo bad 19;;
|
||||
esac
|
||||
|
||||
case para.graph in
|
||||
para!(*.[0-9])) echo ok 20;;
|
||||
*) echo bad 20;;
|
||||
esac
|
||||
|
||||
case para39 in
|
||||
para!(*.[0-9])) echo ok 21;;
|
||||
*) echo bad 21;;
|
||||
esac
|
||||
|
||||
# tests derived from those in rosenblatt's korn shell book
|
||||
|
||||
case "" in
|
||||
*(0|1|3|5|7|9)) echo ok 22;;
|
||||
*) echo bad 22;
|
||||
esac
|
||||
|
||||
case 137577991 in
|
||||
*(0|1|3|5|7|9)) echo ok 23;;
|
||||
*) echo bad 23;
|
||||
esac
|
||||
|
||||
case 2468 in
|
||||
*(0|1|3|5|7|9)) echo bad 24;;
|
||||
*) echo ok 24;
|
||||
esac
|
||||
|
||||
case file.c in
|
||||
*.c?(c)) echo ok 25;;
|
||||
*) echo bad 25;;
|
||||
esac
|
||||
|
||||
case file.C in
|
||||
*.c?(c)) echo bad 26;;
|
||||
*) echo ok 26;;
|
||||
esac
|
||||
|
||||
case file.cc in
|
||||
*.c?(c)) echo ok 27;;
|
||||
*) echo bad 27;;
|
||||
esac
|
||||
|
||||
case file.ccc in
|
||||
*.c?(c)) echo bad 28;;
|
||||
*) echo ok 28;;
|
||||
esac
|
||||
|
||||
case parse.y in
|
||||
!(*.c|*.h|Makefile.in|config*|README)) echo ok 29;;
|
||||
*) echo bad 29;;
|
||||
esac
|
||||
|
||||
case shell.c in
|
||||
!(*.c|*.h|Makefile.in|config*|README)) echo bad 30;;
|
||||
*) echo ok 30;;
|
||||
esac
|
||||
|
||||
case Makefile in
|
||||
!(*.c|*.h|Makefile.in|config*|README)) echo ok 31;;
|
||||
*) echo bad 31;;
|
||||
esac
|
||||
|
||||
case "VMS.FILE;1" in
|
||||
*\;[1-9]*([0-9])) echo ok 32;;
|
||||
*) echo bad 32;;
|
||||
esac
|
||||
|
||||
case "VMS.FILE;0" in
|
||||
*\;[1-9]*([0-9])) echo bad 33;;
|
||||
*) echo ok 33;;
|
||||
esac
|
||||
case "VMS.FILE;" in
|
||||
*\;[1-9]*([0-9])) echo bad 34;;
|
||||
*) echo ok 34;;
|
||||
esac
|
||||
case "VMS.FILE;139" in
|
||||
*\;[1-9]*([0-9])) echo ok 35;;
|
||||
*) echo bad 35;;
|
||||
esac
|
||||
case "VMS.FILE;1N" in
|
||||
*\;[1-9]*([0-9])) echo bad 36;;
|
||||
*) echo ok 36;;
|
||||
esac
|
||||
|
||||
# tests derived from the pd-ksh test suite
|
||||
|
||||
MYDIR=$PWD # save where we are
|
||||
|
||||
: ${TMPDIR:=/var/tmp}
|
||||
TESTDIR=$TMPDIR/eglob-test-$$
|
||||
mkdir $TESTDIR
|
||||
builtin cd $TESTDIR || { echo $0: cannot cd to $TESTDIR >&2 ; exit 1; }
|
||||
rm -rf *
|
||||
|
||||
touch abcx abcz bbc
|
||||
expect '!([*)*'
|
||||
echo !([*)*
|
||||
|
||||
expect '+(a|b[)*'
|
||||
echo +(a|b[)*
|
||||
|
||||
expect '[a*(]*z'
|
||||
echo [a*(]*)z
|
||||
|
||||
rm -f abcx abcz bbc
|
||||
|
||||
touch abc
|
||||
|
||||
expect '+()c'
|
||||
echo +()c
|
||||
expect '+()x'
|
||||
echo +()x
|
||||
expect abc
|
||||
echo +(*)c
|
||||
expect '+(*)x'
|
||||
echo +(*)x
|
||||
|
||||
# extended globbing should not be performed on the output of substitutions
|
||||
x='@(*)'
|
||||
expect '@(*)'
|
||||
echo $x
|
||||
|
||||
expect 'no-file+(a|b)stuff'
|
||||
echo no-file+(a|b)stuff
|
||||
expect 'no-file+(a*(c)|b)stuff'
|
||||
echo no-file+(a*(c)|b)stuff
|
||||
|
||||
touch abd acd
|
||||
|
||||
expect 'abd acd'
|
||||
echo a+(b|c)d
|
||||
|
||||
expect 'acd'
|
||||
echo a!(@(b|B))d
|
||||
|
||||
expect 'abd'
|
||||
echo a[b*(foo|bar)]d
|
||||
|
||||
# simple kleene star tests
|
||||
expect no
|
||||
case foo in *(a|b[)) echo yes;; *) echo no;; esac
|
||||
|
||||
expect yes
|
||||
case foo in *(a|b[)|f*) echo yes;; *) echo no;; esac
|
||||
|
||||
# this doesn't work right yet; it is an incorrectly formed pattern
|
||||
expect yes
|
||||
case '*(a|b[)' in *(a|b[)) echo yes;; *) echo no;; esac
|
||||
|
||||
# check extended globbing in pattern removal -- these don't work right yet
|
||||
x=abcdef
|
||||
|
||||
expect '1: bcdef'
|
||||
echo 1: ${x#+(a|abc)}
|
||||
expect '2: def'
|
||||
echo 2: ${x##+(a|abc)}
|
||||
expect '3: abcde'
|
||||
echo 3: ${x%+(def|f)}
|
||||
expect '4: abc'
|
||||
echo 4: ${x%%+(f|def)}
|
||||
|
||||
# these work ok
|
||||
|
||||
expect '5: ef'
|
||||
echo 5: ${x#*(a|b)cd}
|
||||
expect '6: ef'
|
||||
echo 6: "${x#*(a|b)cd}"
|
||||
expect '7: abcdef'
|
||||
echo 7: ${x#"*(a|b)cd"}
|
||||
|
||||
# More tests derived from a bug report concerning extended glob patterns
|
||||
# following a *
|
||||
builtin cd $TESTDIR || { echo $0: cannot cd to $TESTDIR >&2 ; exit 1; }
|
||||
rm -rf *
|
||||
|
||||
touch ab abcdef abef abcfef
|
||||
|
||||
expect 'ab abef'
|
||||
echo ab*(e|f)
|
||||
|
||||
expect 'abcfef abef'
|
||||
echo ab?*(e|f)
|
||||
|
||||
expect abcdef
|
||||
echo ab*d+(e|f)
|
||||
|
||||
expect 'ab abcdef abcfef abef'
|
||||
echo ab**(e|f)
|
||||
|
||||
expect 'abcdef abcfef abef'
|
||||
echo ab*+(e|f)
|
||||
|
||||
case 'abcfefg' in
|
||||
ab**(e|f)) echo ok 37;;
|
||||
*) echo bad 37;;
|
||||
esac
|
||||
|
||||
case 'abcfefg' in
|
||||
ab**(e|f)g) echo ok 38;;
|
||||
*a) echo bad 38;;
|
||||
esac
|
||||
|
||||
case ab in
|
||||
ab*+(e|f)) echo bad 39;;
|
||||
*) echo ok 39;;
|
||||
esac
|
||||
|
||||
case abef in
|
||||
ab***ef) echo ok 40;;
|
||||
*) echo bad 40;;
|
||||
esac
|
||||
|
||||
case abef in
|
||||
ab**) echo ok 41;;
|
||||
*) echo bad 41;;
|
||||
esac
|
||||
|
||||
# bug in all versions up to and including bash-2.05b
|
||||
case "123abc" in
|
||||
*?(a)bc) echo ok 42;;
|
||||
*) echo bad 42;;
|
||||
esac
|
||||
|
||||
# clean up and do the next one
|
||||
|
||||
builtin cd /
|
||||
rm -rf $TESTDIR
|
||||
|
||||
mkdir $TESTDIR
|
||||
builtin cd $TESTDIR
|
||||
|
||||
LC_COLLATE=C # have to set this; it affects the sorting
|
||||
touch a.b a,b a:b a-b a\;b a\ b a_b
|
||||
|
||||
echo a[^[:alnum:]]b
|
||||
echo a[-.,:\;\ _]b
|
||||
|
||||
echo a@([^[:alnum:]])b
|
||||
echo a@([-.,:; _])b
|
||||
echo a@([.])b
|
||||
echo a@([^.])b
|
||||
echo a@([^x])b
|
||||
echo a+([^[:alnum:]])b
|
||||
|
||||
echo a@(.|[^[:alnum:]])b
|
||||
|
||||
builtin cd /
|
||||
rm -rf $TESTDIR
|
||||
|
||||
x=abcdef
|
||||
recho "${x#*(a|b)cd}"
|
||||
|
||||
TEST='a , b'
|
||||
shopt -s globstar
|
||||
echo ${TEST//*([[:space:]]),*([[:space:]])/,}
|
||||
shopt -u globstar
|
||||
|
||||
# this is for the benefit of pure coverage, so it writes the pcv file
|
||||
# in the right place
|
||||
builtin cd "$MYDIR"
|
||||
|
||||
${THIS_SH} ./extglob1.sub
|
||||
|
||||
exit 0
|
||||
@@ -1,50 +0,0 @@
|
||||
:; ./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...
|
||||
:;
|
||||
@@ -1,10 +0,0 @@
|
||||
#! /bin/sh
|
||||
for cmd in sh bash ash ksh zsh
|
||||
do
|
||||
echo
|
||||
echo $cmd:
|
||||
for demo in shx?
|
||||
do
|
||||
$cmd $demo
|
||||
done
|
||||
done
|
||||
@@ -1,115 +0,0 @@
|
||||
# basic nameref tests
|
||||
bar=one
|
||||
flow=two
|
||||
flip=three
|
||||
|
||||
foo=bar
|
||||
typeset -n foo
|
||||
|
||||
typeset -n fee=flow
|
||||
|
||||
echo ${foo}
|
||||
echo ${fee}
|
||||
|
||||
typeset -n fee=flip
|
||||
echo ${fee}
|
||||
|
||||
typeset -n
|
||||
|
||||
echo turning off nameref attribute on foo
|
||||
typeset +n foo=other
|
||||
echo ${foo}
|
||||
echo after +n foo bar = $bar
|
||||
|
||||
unset foo bar fee
|
||||
|
||||
bar=one
|
||||
|
||||
foo=bar
|
||||
typeset -n foo
|
||||
|
||||
foo=two printf "%s\n" $foo
|
||||
foo=two eval 'printf "%s\n" $foo'
|
||||
|
||||
foo=two echo $foo
|
||||
|
||||
unset foo bar
|
||||
# other basic assignment tests
|
||||
bar=one
|
||||
|
||||
echo "expect <one>"
|
||||
recho ${bar}
|
||||
typeset -n foo=bar
|
||||
foo=two
|
||||
|
||||
echo "expect <two>"
|
||||
recho ${bar}
|
||||
|
||||
# this appears to be a ksh93 bug; it doesn't unset foo here and messes up
|
||||
# later
|
||||
unset foo bar
|
||||
|
||||
# initial tests of working inside shell functions
|
||||
echoval()
|
||||
{
|
||||
typeset -n ref=$1
|
||||
printf "%s\n" $ref
|
||||
}
|
||||
|
||||
foo=bar
|
||||
bar=one
|
||||
echo "expect <$foo>"
|
||||
echoval foo
|
||||
echo "expect <$bar>"
|
||||
echoval bar
|
||||
|
||||
unset foo bar
|
||||
changevar()
|
||||
{
|
||||
typeset -n v=$1
|
||||
|
||||
shift
|
||||
v="$@"
|
||||
echo "changevar: expect <$@>"
|
||||
recho "$v"
|
||||
}
|
||||
|
||||
bar=one
|
||||
|
||||
echo "expect <one>"
|
||||
recho ${bar}
|
||||
changevar bar two
|
||||
echo "expect <two>"
|
||||
recho $bar
|
||||
|
||||
changevar bar three four five
|
||||
echo "expect <three four five>"
|
||||
recho "$bar"
|
||||
|
||||
unset foo bar
|
||||
unset -n foo bar
|
||||
readonly foo=one
|
||||
typeset -n bar=foo
|
||||
bar=4
|
||||
foo=4
|
||||
|
||||
echo $foo
|
||||
echo $bar
|
||||
|
||||
assignvar()
|
||||
{
|
||||
typeset -n ref=$1
|
||||
shift
|
||||
ref="$@"
|
||||
}
|
||||
|
||||
readonly foo=one
|
||||
|
||||
assignvar foo two three four
|
||||
echo $foo
|
||||
|
||||
${THIS_SH} ./nameref1.sub
|
||||
${THIS_SH} ./nameref2.sub
|
||||
${THIS_SH} ./nameref3.sub
|
||||
${THIS_SH} ./nameref4.sub
|
||||
${THIS_SH} ./nameref5.sub
|
||||
@@ -1,111 +0,0 @@
|
||||
/* trap.h -- data structures used in the trap mechanism. */
|
||||
|
||||
/* Copyright (C) 1993-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/>.
|
||||
*/
|
||||
|
||||
#if !defined (_TRAP_H_)
|
||||
#define _TRAP_H_
|
||||
|
||||
#include "stdc.h"
|
||||
|
||||
#if !defined (SIG_DFL)
|
||||
#include "bashtypes.h"
|
||||
#include <signal.h>
|
||||
#endif /* SIG_DFL */
|
||||
|
||||
#if !defined (NSIG)
|
||||
#define NSIG 64
|
||||
#endif /* !NSIG */
|
||||
|
||||
#define NO_SIG -1
|
||||
#define DEFAULT_SIG SIG_DFL
|
||||
#define IGNORE_SIG SIG_IGN
|
||||
|
||||
/* Special shell trap names. */
|
||||
#define DEBUG_TRAP NSIG
|
||||
#define ERROR_TRAP NSIG+1
|
||||
#define RETURN_TRAP NSIG+2
|
||||
#define EXIT_TRAP 0
|
||||
|
||||
/* system signals plus special bash traps */
|
||||
#define BASH_NSIG NSIG+3
|
||||
|
||||
/* Flags values for decode_signal() */
|
||||
#define DSIG_SIGPREFIX 0x01 /* don't alllow `SIG' PREFIX */
|
||||
#define DSIG_NOCASE 0x02 /* case-insensitive comparison */
|
||||
|
||||
/* A value which can never be the target of a trap handler. */
|
||||
#define IMPOSSIBLE_TRAP_HANDLER (SigHandler *)initialize_traps
|
||||
|
||||
#define signal_object_p(x,f) (decode_signal (x,f) != NO_SIG)
|
||||
|
||||
#define TRAP_STRING(s) \
|
||||
(signal_is_trapped (s) && signal_is_ignored (s) == 0) ? trap_list[s] \
|
||||
: (char *)NULL
|
||||
|
||||
extern char *trap_list[];
|
||||
|
||||
/* Externally-visible functions declared in trap.c. */
|
||||
extern void initialize_traps __P((void));
|
||||
|
||||
extern void run_pending_traps __P((void));
|
||||
|
||||
extern void queue_sigchld_trap __P((int));
|
||||
extern void maybe_set_sigchld_trap __P((char *));
|
||||
extern void set_impossible_sigchld_trap __P((void));
|
||||
extern void set_sigchld_trap __P((char *));
|
||||
|
||||
extern void set_debug_trap __P((char *));
|
||||
extern void set_error_trap __P((char *));
|
||||
extern void set_return_trap __P((char *));
|
||||
|
||||
extern void set_sigint_trap __P((char *));
|
||||
extern void set_signal __P((int, char *));
|
||||
|
||||
extern void restore_default_signal __P((int));
|
||||
extern void ignore_signal __P((int));
|
||||
extern int run_exit_trap __P((void));
|
||||
extern void run_trap_cleanup __P((int));
|
||||
extern int run_debug_trap __P((void));
|
||||
extern void run_error_trap __P((void));
|
||||
extern void run_return_trap __P((void));
|
||||
|
||||
extern void free_trap_strings __P((void));
|
||||
extern void reset_signal_handlers __P((void));
|
||||
extern void restore_original_signals __P((void));
|
||||
|
||||
extern void get_all_original_signals __P((void));
|
||||
|
||||
extern char *signal_name __P((int));
|
||||
|
||||
extern int decode_signal __P((char *, int));
|
||||
extern void run_interrupt_trap __P((void));
|
||||
extern int maybe_call_trap_handler __P((int));
|
||||
extern int signal_is_special __P((int));
|
||||
extern int signal_is_trapped __P((int));
|
||||
extern int signal_is_pending __P((int));
|
||||
extern int signal_is_ignored __P((int));
|
||||
extern int signal_is_hard_ignored __P((int));
|
||||
extern void set_signal_ignored __P((int));
|
||||
extern int signal_in_progress __P((int));
|
||||
|
||||
extern int first_pending_trap __P((void));
|
||||
extern int any_signals_trapped __P((void));
|
||||
extern int check_signals_and_traps __P((void));
|
||||
|
||||
#endif /* _TRAP_H_ */
|
||||
Reference in New Issue
Block a user