mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-07-02 01:40:49 +02:00
bash-20140625 remove leftover and stray files
This commit is contained in:
-6488
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 */
|
||||
@@ -1,368 +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 delim;
|
||||
|
||||
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, delim, flags)
|
||||
int fd;
|
||||
long line_count_goal, origin, nskip, callback_quantum;
|
||||
char *callback, *array_name;
|
||||
int delim;
|
||||
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);
|
||||
}
|
||||
else if (invisible_p (entry))
|
||||
VUNSETATTR (entry, att_invisible); /* no longer invisible */
|
||||
|
||||
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, delim, unbuffered_read) < 0)
|
||||
break;
|
||||
|
||||
line = 0;
|
||||
line_length = 0;
|
||||
|
||||
/* Reset the buffer for bash own stream */
|
||||
for (array_index = origin, line_count = 1;
|
||||
zgetline (fd, &line, &line_length, delim, 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);
|
||||
}
|
||||
|
||||
/* XXX - bad things can happen if the callback modifies ENTRY, e.g.,
|
||||
unsetting it or changing it to a non-indexed-array type. */
|
||||
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);
|
||||
|
||||
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;
|
||||
delim = '\n';
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "d:u:n:O:tC:c:s:")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'd':
|
||||
delim = *list_optarg;
|
||||
break;
|
||||
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, delim, flags);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int
|
||||
mapfile_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
builtin_error (_("array variable support required"));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
#endif /* ARRAY_VARS */
|
||||
@@ -1,555 +0,0 @@
|
||||
This file is setattr.def, from which is created setattr.c.
|
||||
It implements the builtins "export" and "readonly", 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 setattr.c
|
||||
|
||||
#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 "../flags.h"
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
extern int posixly_correct;
|
||||
extern int array_needs_making;
|
||||
extern char *this_command_name;
|
||||
extern sh_builtin_func_t *this_shell_builtin;
|
||||
|
||||
#ifdef ARRAY_VARS
|
||||
extern int declare_builtin __P((WORD_LIST *));
|
||||
#endif
|
||||
|
||||
#define READONLY_OR_EXPORT \
|
||||
(this_shell_builtin == readonly_builtin || this_shell_builtin == export_builtin)
|
||||
|
||||
$BUILTIN export
|
||||
$FUNCTION export_builtin
|
||||
$SHORT_DOC export [-fn] [name[=value] ...] or export -p
|
||||
Set export attribute for shell variables.
|
||||
|
||||
Marks each NAME for automatic export to the environment of subsequently
|
||||
executed commands. If VALUE is supplied, assign VALUE before exporting.
|
||||
|
||||
Options:
|
||||
-f refer to shell functions
|
||||
-n remove the export property from each NAME
|
||||
-p display a list of all exported variables and functions
|
||||
|
||||
An argument of `--' disables further option processing.
|
||||
|
||||
Exit Status:
|
||||
Returns success unless an invalid option is given or NAME is invalid.
|
||||
$END
|
||||
|
||||
/* For each variable name in LIST, make that variable appear in the
|
||||
environment passed to simple commands. If there is no LIST, then
|
||||
print all such variables. An argument of `-n' says to remove the
|
||||
exported attribute from variables named in LIST. An argument of
|
||||
-f indicates that the names present in LIST refer to functions. */
|
||||
int
|
||||
export_builtin (list)
|
||||
register WORD_LIST *list;
|
||||
{
|
||||
return (set_or_show_attributes (list, att_exported, 0));
|
||||
}
|
||||
|
||||
$BUILTIN readonly
|
||||
$FUNCTION readonly_builtin
|
||||
$SHORT_DOC readonly [-aAf] [name[=value] ...] or readonly -p
|
||||
Mark shell variables as unchangeable.
|
||||
|
||||
Mark each NAME as read-only; the values of these NAMEs may not be
|
||||
changed by subsequent assignment. If VALUE is supplied, assign VALUE
|
||||
before marking as read-only.
|
||||
|
||||
Options:
|
||||
-a refer to indexed array variables
|
||||
-A refer to associative array variables
|
||||
-f refer to shell functions
|
||||
-p display a list of all readonly variables or functions, depending on
|
||||
whether or not the -f option is given
|
||||
|
||||
An argument of `--' disables further option processing.
|
||||
|
||||
Exit Status:
|
||||
Returns success unless an invalid option is given or NAME is invalid.
|
||||
$END
|
||||
|
||||
/* For each variable name in LIST, make that variable readonly. Given an
|
||||
empty LIST, print out all existing readonly variables. */
|
||||
int
|
||||
readonly_builtin (list)
|
||||
register WORD_LIST *list;
|
||||
{
|
||||
return (set_or_show_attributes (list, att_readonly, 0));
|
||||
}
|
||||
|
||||
#if defined (ARRAY_VARS)
|
||||
# define ATTROPTS "aAfnp"
|
||||
#else
|
||||
# define ATTROPTS "fnp"
|
||||
#endif
|
||||
|
||||
/* For each variable name in LIST, make that variable have the specified
|
||||
ATTRIBUTE. An arg of `-n' says to remove the attribute from the the
|
||||
remaining names in LIST (doesn't work for readonly). */
|
||||
int
|
||||
set_or_show_attributes (list, attribute, nodefs)
|
||||
register WORD_LIST *list;
|
||||
int attribute, nodefs;
|
||||
{
|
||||
register SHELL_VAR *var;
|
||||
int assign, undo, any_failed, assign_error, opt;
|
||||
int functions_only, arrays_only, assoc_only;
|
||||
int aflags;
|
||||
char *name;
|
||||
#if defined (ARRAY_VARS)
|
||||
WORD_LIST *nlist, *tlist;
|
||||
WORD_DESC *w;
|
||||
#endif
|
||||
|
||||
functions_only = arrays_only = assoc_only = 0;
|
||||
undo = any_failed = assign_error = 0;
|
||||
/* Read arguments from the front of the list. */
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, ATTROPTS)) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'n':
|
||||
undo = 1;
|
||||
break;
|
||||
case 'f':
|
||||
functions_only = 1;
|
||||
break;
|
||||
#if defined (ARRAY_VARS)
|
||||
case 'a':
|
||||
arrays_only = 1;
|
||||
break;
|
||||
case 'A':
|
||||
assoc_only = 1;
|
||||
break;
|
||||
#endif
|
||||
case 'p':
|
||||
break;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
if (list)
|
||||
{
|
||||
if (attribute & att_exported)
|
||||
array_needs_making = 1;
|
||||
|
||||
/* Cannot undo readonly status, silently disallowed. */
|
||||
if (undo && (attribute & att_readonly))
|
||||
attribute &= ~att_readonly;
|
||||
|
||||
while (list)
|
||||
{
|
||||
name = list->word->word;
|
||||
|
||||
if (functions_only) /* xxx -f name */
|
||||
{
|
||||
var = find_function (name);
|
||||
if (var == 0)
|
||||
{
|
||||
builtin_error (_("%s: not a function"), name);
|
||||
any_failed++;
|
||||
}
|
||||
else
|
||||
SETVARATTR (var, attribute, undo);
|
||||
|
||||
list = list->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* xxx [-np] name[=value] */
|
||||
assign = assignment (name, 0);
|
||||
|
||||
aflags = 0;
|
||||
if (assign)
|
||||
{
|
||||
name[assign] = '\0';
|
||||
if (name[assign - 1] == '+')
|
||||
{
|
||||
aflags |= ASS_APPEND;
|
||||
name[assign - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (legal_identifier (name) == 0)
|
||||
{
|
||||
sh_invalidid (name);
|
||||
if (assign)
|
||||
assign_error++;
|
||||
else
|
||||
any_failed++;
|
||||
list = list->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (assign) /* xxx [-np] name=value */
|
||||
{
|
||||
name[assign] = '=';
|
||||
if (aflags & ASS_APPEND)
|
||||
name[assign - 1] = '+';
|
||||
#if defined (ARRAY_VARS)
|
||||
/* Let's try something here. Turn readonly -a xxx=yyy into
|
||||
declare -ra xxx=yyy and see what that gets us. */
|
||||
if (arrays_only || assoc_only)
|
||||
{
|
||||
tlist = list->next;
|
||||
list->next = (WORD_LIST *)NULL;
|
||||
w = arrays_only ? make_word ("-ra") : make_word ("-rA");
|
||||
nlist = make_word_list (w, list);
|
||||
opt = declare_builtin (nlist);
|
||||
if (opt != EXECUTION_SUCCESS)
|
||||
assign_error++;
|
||||
list->next = tlist;
|
||||
dispose_word (w);
|
||||
free (nlist);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
/* This word has already been expanded once with command
|
||||
and parameter expansion. Call do_assignment_no_expand (),
|
||||
which does not do command or parameter substitution. If
|
||||
the assignment is not performed correctly, flag an error. */
|
||||
if (do_assignment_no_expand (name) == 0)
|
||||
assign_error++;
|
||||
name[assign] = '\0';
|
||||
if (aflags & ASS_APPEND)
|
||||
name[assign - 1] = '\0';
|
||||
}
|
||||
|
||||
set_var_attribute (name, attribute, undo);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SHELL_VAR **variable_list;
|
||||
register int i;
|
||||
|
||||
if ((attribute & att_function) || functions_only)
|
||||
{
|
||||
variable_list = all_shell_functions ();
|
||||
if (attribute != att_function)
|
||||
attribute &= ~att_function; /* so declare -xf works, for example */
|
||||
}
|
||||
else
|
||||
variable_list = all_shell_variables ();
|
||||
|
||||
#if defined (ARRAY_VARS)
|
||||
if (attribute & att_array)
|
||||
{
|
||||
arrays_only++;
|
||||
if (attribute != att_array)
|
||||
attribute &= ~att_array;
|
||||
}
|
||||
else if (attribute & att_assoc)
|
||||
{
|
||||
assoc_only++;
|
||||
if (attribute != att_assoc)
|
||||
attribute &= ~att_assoc;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (variable_list)
|
||||
{
|
||||
for (i = 0; var = variable_list[i]; i++)
|
||||
{
|
||||
#if defined (ARRAY_VARS)
|
||||
if (arrays_only && array_p (var) == 0)
|
||||
continue;
|
||||
else if (assoc_only && assoc_p (var) == 0)
|
||||
continue;
|
||||
#endif
|
||||
if ((var->attributes & attribute))
|
||||
{
|
||||
show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
|
||||
if (any_failed = sh_chkwrite (any_failed))
|
||||
break;
|
||||
}
|
||||
}
|
||||
free (variable_list);
|
||||
}
|
||||
}
|
||||
|
||||
return (assign_error ? EX_BADASSIGN
|
||||
: ((any_failed == 0) ? EXECUTION_SUCCESS
|
||||
: EXECUTION_FAILURE));
|
||||
}
|
||||
|
||||
/* Show all variable variables (v == 1) or functions (v == 0) with
|
||||
attributes. */
|
||||
int
|
||||
show_all_var_attributes (v, nodefs)
|
||||
int v, nodefs;
|
||||
{
|
||||
SHELL_VAR **variable_list, *var;
|
||||
int any_failed;
|
||||
register int i;
|
||||
|
||||
variable_list = v ? all_shell_variables () : all_shell_functions ();
|
||||
if (variable_list == 0)
|
||||
return (EXECUTION_SUCCESS);
|
||||
|
||||
for (i = any_failed = 0; var = variable_list[i]; i++)
|
||||
{
|
||||
show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
|
||||
if (any_failed = sh_chkwrite (any_failed))
|
||||
break;
|
||||
}
|
||||
free (variable_list);
|
||||
return (any_failed == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
/* Show the attributes for shell variable VAR. If NODEFS is non-zero,
|
||||
don't show function definitions along with the name. If PATTR is
|
||||
non-zero, it indicates we're being called from `export' or `readonly'.
|
||||
In POSIX mode, this prints the name of the calling builtin (`export'
|
||||
or `readonly') instead of `declare', and doesn't print function defs
|
||||
when called by `export' or `readonly'. */
|
||||
int
|
||||
show_var_attributes (var, pattr, nodefs)
|
||||
SHELL_VAR *var;
|
||||
int pattr, nodefs;
|
||||
{
|
||||
char flags[16], *x;
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
|
||||
/* pattr == 0 means we are called from `declare'. */
|
||||
if (pattr == 0 || posixly_correct == 0)
|
||||
{
|
||||
#if defined (ARRAY_VARS)
|
||||
if (array_p (var))
|
||||
flags[i++] = 'a';
|
||||
|
||||
if (assoc_p (var))
|
||||
flags[i++] = 'A';
|
||||
#endif
|
||||
|
||||
if (function_p (var))
|
||||
flags[i++] = 'f';
|
||||
|
||||
if (integer_p (var))
|
||||
flags[i++] = 'i';
|
||||
|
||||
if (nameref_p (var))
|
||||
flags[i++] = 'n';
|
||||
|
||||
if (readonly_p (var))
|
||||
flags[i++] = 'r';
|
||||
|
||||
if (trace_p (var))
|
||||
flags[i++] = 't';
|
||||
|
||||
if (exported_p (var))
|
||||
flags[i++] = 'x';
|
||||
|
||||
if (capcase_p (var))
|
||||
flags[i++] = 'c';
|
||||
|
||||
if (lowercase_p (var))
|
||||
flags[i++] = 'l';
|
||||
|
||||
if (uppercase_p (var))
|
||||
flags[i++] = 'u';
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined (ARRAY_VARS)
|
||||
if (array_p (var))
|
||||
flags[i++] = 'a';
|
||||
|
||||
if (assoc_p (var))
|
||||
flags[i++] = 'A';
|
||||
#endif
|
||||
|
||||
if (function_p (var))
|
||||
flags[i++] = 'f';
|
||||
}
|
||||
|
||||
flags[i] = '\0';
|
||||
|
||||
/* If we're printing functions with definitions, print the function def
|
||||
first, then the attributes, instead of printing output that can't be
|
||||
reused as input to recreate the current state. */
|
||||
if (function_p (var) && nodefs == 0 && (pattr == 0 || posixly_correct == 0))
|
||||
{
|
||||
printf ("%s\n", named_function_string (var->name, function_cell (var), FUNC_MULTILINE|FUNC_EXTERNAL));
|
||||
nodefs++;
|
||||
if (pattr == 0 && i == 1 && flags[0] == 'f')
|
||||
return 0; /* don't print `declare -f name' */
|
||||
}
|
||||
|
||||
if (pattr == 0 || posixly_correct == 0)
|
||||
printf ("declare -%s ", i ? flags : "-");
|
||||
else if (i)
|
||||
printf ("%s -%s ", this_command_name, flags);
|
||||
else
|
||||
printf ("%s ", this_command_name);
|
||||
|
||||
#if defined (ARRAY_VARS)
|
||||
if (array_p (var))
|
||||
print_array_assignment (var, 1);
|
||||
else if (assoc_p (var))
|
||||
print_assoc_assignment (var, 1);
|
||||
else
|
||||
#endif
|
||||
/* force `readonly' and `export' to not print out function definitions
|
||||
when in POSIX mode. */
|
||||
if (nodefs || (function_p (var) && pattr != 0 && posixly_correct))
|
||||
printf ("%s\n", var->name);
|
||||
else if (function_p (var))
|
||||
printf ("%s\n", named_function_string (var->name, function_cell (var), FUNC_MULTILINE|FUNC_EXTERNAL));
|
||||
else if (invisible_p (var) && (array_p (var) || assoc_p (var)))
|
||||
printf ("%s\n", var->name);
|
||||
else if (invisible_p (var) || var_isset (var) == 0)
|
||||
printf ("%s\n", var->name);
|
||||
else
|
||||
{
|
||||
x = sh_double_quote (value_cell (var));
|
||||
printf ("%s=%s\n", var->name, x);
|
||||
free (x);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
show_name_attributes (name, nodefs)
|
||||
char *name;
|
||||
int nodefs;
|
||||
{
|
||||
SHELL_VAR *var;
|
||||
|
||||
#if 0
|
||||
var = find_variable_tempenv (name);
|
||||
#else
|
||||
var = find_variable_noref (name);
|
||||
#endif
|
||||
|
||||
if (var /* && invisible_p (var) == 0 */) /* XXX bash-4.4/bash-5.0 */
|
||||
{
|
||||
show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
|
||||
return (0);
|
||||
}
|
||||
else
|
||||
return (1);
|
||||
}
|
||||
|
||||
int
|
||||
show_func_attributes (name, nodefs)
|
||||
char *name;
|
||||
int nodefs;
|
||||
{
|
||||
SHELL_VAR *var;
|
||||
|
||||
var = find_function (name);
|
||||
|
||||
if (var)
|
||||
{
|
||||
show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
|
||||
return (0);
|
||||
}
|
||||
else
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
set_var_attribute (name, attribute, undo)
|
||||
char *name;
|
||||
int attribute, undo;
|
||||
{
|
||||
SHELL_VAR *var, *tv, *v;
|
||||
char *tvalue;
|
||||
|
||||
if (undo)
|
||||
var = find_variable (name);
|
||||
else
|
||||
{
|
||||
tv = find_tempenv_variable (name);
|
||||
/* XXX -- need to handle case where tv is a temp variable in a
|
||||
function-scope context, since function_env has been merged into
|
||||
the local variables table. */
|
||||
if (tv && tempvar_p (tv))
|
||||
{
|
||||
tvalue = var_isset (tv) ? savestring (value_cell (tv)) : savestring ("");
|
||||
|
||||
var = bind_variable (tv->name, tvalue, 0);
|
||||
var->attributes |= tv->attributes & ~att_tempvar;
|
||||
/* This avoids an error message when propagating a read-only var
|
||||
later on. */
|
||||
if (var->context == 0 && (attribute & att_readonly))
|
||||
{
|
||||
/* Don't bother to set the `propagate to the global variables
|
||||
table' flag if we've just bound the variable in that table */
|
||||
v = find_global_variable (tv->name);
|
||||
if (v != var)
|
||||
VSETATTR (tv, att_propagate);
|
||||
}
|
||||
else
|
||||
VSETATTR (tv, att_propagate);
|
||||
if (var->context != 0)
|
||||
VSETATTR (var, att_propagate);
|
||||
SETVARATTR (tv, attribute, undo); /* XXX */
|
||||
|
||||
stupidly_hack_special_variables (tv->name);
|
||||
|
||||
free (tvalue);
|
||||
}
|
||||
else
|
||||
{
|
||||
var = find_variable_notempenv (name);
|
||||
if (var == 0)
|
||||
{
|
||||
var = bind_variable (name, (char *)NULL, 0);
|
||||
if (no_invisible_vars == 0)
|
||||
VSETATTR (var, att_invisible);
|
||||
}
|
||||
else if (var->context != 0)
|
||||
VSETATTR (var, att_propagate);
|
||||
}
|
||||
}
|
||||
|
||||
if (var)
|
||||
SETVARATTR (var, attribute, undo);
|
||||
|
||||
if (var && (exported_p (var) || (attribute & att_exported)))
|
||||
array_needs_making++; /* XXX */
|
||||
}
|
||||
-141
@@ -1,141 +0,0 @@
|
||||
/* config-top.h - various user-settable options not under the control of autoconf. */
|
||||
|
||||
/* Copyright (C) 2002-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/>.
|
||||
*/
|
||||
|
||||
/* Define CONTINUE_AFTER_KILL_ERROR if you want the kill command to
|
||||
continue processing arguments after one of them fails. This is
|
||||
what POSIX.2 specifies. */
|
||||
#define CONTINUE_AFTER_KILL_ERROR
|
||||
|
||||
/* Define BREAK_COMPLAINS if you want the non-standard, but useful
|
||||
error messages about `break' and `continue' out of context. */
|
||||
#define BREAK_COMPLAINS
|
||||
|
||||
/* Define BUFFERED_INPUT if you want the shell to do its own input
|
||||
buffering, rather than using stdio. Do not undefine this; it's
|
||||
required to preserve semantics required by POSIX. */
|
||||
#define BUFFERED_INPUT
|
||||
|
||||
/* Define ONESHOT if you want sh -c 'command' to avoid forking to execute
|
||||
`command' whenever possible. This is a big efficiency improvement. */
|
||||
#define ONESHOT
|
||||
|
||||
/* Define V9_ECHO if you want to give the echo builtin backslash-escape
|
||||
interpretation using the -e option, in the style of the Bell Labs 9th
|
||||
Edition version of echo. You cannot emulate the System V echo behavior
|
||||
without this option. */
|
||||
#define V9_ECHO
|
||||
|
||||
/* Define DONT_REPORT_SIGPIPE if you don't want to see `Broken pipe' messages
|
||||
when a job like `cat jobs.c | exit 1' terminates due to a SIGPIPE. */
|
||||
#define DONT_REPORT_SIGPIPE
|
||||
|
||||
/* Define DONT_REPORT_SIGTERM if you don't want to see `Terminates' message
|
||||
when a job exits due to SIGTERM, since that's the default signal sent
|
||||
by the kill builtin. */
|
||||
/* #define DONT_REPORT_SIGTERM */
|
||||
|
||||
/* Define DONT_REPORT_BROKEN_PIPE_WRITE_ERRORS if you don't want builtins
|
||||
like `echo' and `printf' to report errors when output does not succeed
|
||||
due to EPIPE. */
|
||||
/* #define DONT_REPORT_BROKEN_PIPE_WRITE_ERRORS */
|
||||
|
||||
/* The default value of the PATH variable. */
|
||||
#ifndef DEFAULT_PATH_VALUE
|
||||
#define DEFAULT_PATH_VALUE \
|
||||
"/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:."
|
||||
#endif
|
||||
|
||||
/* The value for PATH when invoking `command -p'. This is only used when
|
||||
the Posix.2 confstr () function, or CS_PATH define are not present. */
|
||||
#ifndef STANDARD_UTILS_PATH
|
||||
#define STANDARD_UTILS_PATH \
|
||||
"/bin:/usr/bin:/sbin:/usr/sbin:/etc:/usr/etc"
|
||||
#endif
|
||||
|
||||
/* Default primary and secondary prompt strings. */
|
||||
#define PPROMPT "\\s-\\v\\$ "
|
||||
#define SPROMPT "> "
|
||||
|
||||
/* Undefine this if you don't want the ksh-compatible behavior of reprinting
|
||||
the select menu after a valid choice is made only if REPLY is set to NULL
|
||||
in the body of the select command. The menu is always reprinted if the
|
||||
reply to the select query is an empty line. */
|
||||
#define KSH_COMPATIBLE_SELECT
|
||||
|
||||
/* Default interactive shell startup file. */
|
||||
#define DEFAULT_BASHRC "~/.bashrc"
|
||||
|
||||
/* System-wide .bashrc file for interactive shells. */
|
||||
/* #define SYS_BASHRC "/etc/bash.bashrc" */
|
||||
|
||||
/* System-wide .bash_logout for login shells. */
|
||||
/* #define SYS_BASH_LOGOUT "/etc/bash.bash_logout" */
|
||||
|
||||
/* Define this to make non-interactive shells begun with argv[0][0] == '-'
|
||||
run the startup files when not in posix mode. */
|
||||
/* #define NON_INTERACTIVE_LOGIN_SHELLS */
|
||||
|
||||
/* Define this if you want bash to try to check whether it's being run by
|
||||
sshd and source the .bashrc if so (like the rshd behavior). This checks
|
||||
for the presence of SSH_CLIENT or SSH2_CLIENT in the initial environment,
|
||||
which can be fooled under certain not-uncommon circumstances. */
|
||||
/* #define SSH_SOURCE_BASHRC */
|
||||
|
||||
/* Define if you want the case-capitalizing operators (~[~]) and the
|
||||
`capcase' variable attribute (declare -c). */
|
||||
#define CASEMOD_CAPCASE
|
||||
|
||||
/* This is used as the name of a shell function to call when a command
|
||||
name is not found. If you want to name it something other than the
|
||||
default ("command_not_found_handle"), change it here. */
|
||||
/* #define NOTFOUND_HOOK "command_not_found_handle" */
|
||||
|
||||
/* Define if you want each line saved to the history list in bashhist.c:
|
||||
bash_add_history() to be sent to syslog(). */
|
||||
/* #define SYSLOG_HISTORY */
|
||||
#if defined (SYSLOG_HISTORY)
|
||||
# define SYSLOG_FACILITY LOG_USER
|
||||
# define SYSLOG_LEVEL LOG_INFO
|
||||
#endif
|
||||
|
||||
/* Define if you want to include code in shell.c to support wordexp(3) */
|
||||
/* #define WORDEXP_OPTION */
|
||||
|
||||
/* Define as 1 if you want to enable code that implements multiple coprocs */
|
||||
#ifndef MULTIPLE_COPROCS
|
||||
# define MULTIPLE_COPROCS 0
|
||||
#endif
|
||||
|
||||
/* Define to 0 if you want the checkwinsize option off by default, 1 if you
|
||||
want it on. */
|
||||
#define CHECKWINSIZE_DEFAULT 0
|
||||
|
||||
/* Define to 1 if you want to optimize for sequential array assignment when
|
||||
using indexed arrays, 0 if you want bash-4.2 behavior, which favors
|
||||
random access but is O(N) for each array assignment. */
|
||||
#define OPTIMIZE_SEQUENTIAL_ARRAY_ASSIGNMENT 1
|
||||
|
||||
/* Define to 1 if you want to be able to export indexed arrays to processes
|
||||
using the foo=([0]=one [1]=two) and so on */
|
||||
/* #define ARRAY_EXPORT 1 */
|
||||
|
||||
/* Defined to 1 if you want the shell to exit if it is running setuid and its
|
||||
attempt to drop privilege using setuid(getuid()) fails with errno == EAGAIN */
|
||||
/* #define EXIT_ON_SETUID_FAILURE 1 */
|
||||
@@ -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
-10345
File diff suppressed because it is too large
Load Diff
-8756
File diff suppressed because it is too large
Load Diff
@@ -1,10 +0,0 @@
|
||||
@ignore
|
||||
Copyright (C) 1988-2014 Free Software Foundation, Inc.
|
||||
@end ignore
|
||||
|
||||
@set LASTCHANGE Mon May 12 10:32:34 EDT 2014
|
||||
|
||||
@set EDITION 4.3
|
||||
@set VERSION 4.3
|
||||
@set UPDATED 12 May 2014
|
||||
@set UPDATED-MONTH May 2014
|
||||
@@ -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
|
||||
-5502
File diff suppressed because it is too large
Load Diff
-517
@@ -1,517 +0,0 @@
|
||||
/* externs.h -- extern function declarations which do not appear in their
|
||||
own header file. */
|
||||
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
/* Make sure that this is included *after* config.h! */
|
||||
|
||||
#if !defined (_EXTERNS_H_)
|
||||
# define _EXTERNS_H_
|
||||
|
||||
#include "stdc.h"
|
||||
|
||||
/* Functions from expr.c. */
|
||||
extern intmax_t evalexp __P((char *, int *));
|
||||
|
||||
/* Functions from print_cmd.c. */
|
||||
#define FUNC_MULTILINE 0x01
|
||||
#define FUNC_EXTERNAL 0x02
|
||||
|
||||
extern char *make_command_string __P((COMMAND *));
|
||||
extern char *named_function_string __P((char *, COMMAND *, int));
|
||||
|
||||
extern void print_command __P((COMMAND *));
|
||||
extern void print_simple_command __P((SIMPLE_COM *));
|
||||
extern void print_word_list __P((WORD_LIST *, char *));
|
||||
|
||||
/* debugger support */
|
||||
extern void print_for_command_head __P((FOR_COM *));
|
||||
#if defined (SELECT_COMMAND)
|
||||
extern void print_select_command_head __P((SELECT_COM *));
|
||||
#endif
|
||||
extern void print_case_command_head __P((CASE_COM *));
|
||||
#if defined (DPAREN_ARITHMETIC)
|
||||
extern void print_arith_command __P((WORD_LIST *));
|
||||
#endif
|
||||
#if defined (COND_COMMAND)
|
||||
extern void print_cond_command __P((COND_COM *));
|
||||
#endif
|
||||
|
||||
/* set -x support */
|
||||
extern void xtrace_init __P((void));
|
||||
#ifdef NEED_XTRACE_SET_DECL
|
||||
extern void xtrace_set __P((int, FILE *));
|
||||
#endif
|
||||
extern void xtrace_fdchk __P((int));
|
||||
extern void xtrace_reset __P((void));
|
||||
extern char *indirection_level_string __P((void));
|
||||
extern void xtrace_print_assignment __P((char *, char *, int, int));
|
||||
extern void xtrace_print_word_list __P((WORD_LIST *, int));
|
||||
extern void xtrace_print_for_command_head __P((FOR_COM *));
|
||||
#if defined (SELECT_COMMAND)
|
||||
extern void xtrace_print_select_command_head __P((SELECT_COM *));
|
||||
#endif
|
||||
extern void xtrace_print_case_command_head __P((CASE_COM *));
|
||||
#if defined (DPAREN_ARITHMETIC)
|
||||
extern void xtrace_print_arith_cmd __P((WORD_LIST *));
|
||||
#endif
|
||||
#if defined (COND_COMMAND)
|
||||
extern void xtrace_print_cond_term __P((int, int, WORD_DESC *, char *, char *));
|
||||
#endif
|
||||
|
||||
/* Functions from shell.c. */
|
||||
extern void exit_shell __P((int)) __attribute__((__noreturn__));
|
||||
extern void sh_exit __P((int)) __attribute__((__noreturn__));
|
||||
extern void subshell_exit __P((int)) __attribute__((__noreturn__));
|
||||
extern void disable_priv_mode __P((void));
|
||||
extern void unbind_args __P((void));
|
||||
|
||||
#if defined (RESTRICTED_SHELL)
|
||||
extern int shell_is_restricted __P((char *));
|
||||
extern int maybe_make_restricted __P((char *));
|
||||
#endif
|
||||
|
||||
extern void unset_bash_input __P((int));
|
||||
extern void get_current_user_info __P((void));
|
||||
|
||||
/* Functions from eval.c. */
|
||||
extern int reader_loop __P((void));
|
||||
extern int parse_command __P((void));
|
||||
extern int read_command __P((void));
|
||||
|
||||
/* Functions from braces.c. */
|
||||
#if defined (BRACE_EXPANSION)
|
||||
extern char **brace_expand __P((char *));
|
||||
#endif
|
||||
|
||||
/* Miscellaneous functions from parse.y */
|
||||
extern int yyparse __P((void));
|
||||
extern int return_EOF __P((void));
|
||||
extern char *xparse_dolparen __P((char *, char *, int *, int));
|
||||
extern void reset_parser __P((void));
|
||||
extern WORD_LIST *parse_string_to_word_list __P((char *, int, const char *));
|
||||
|
||||
extern int parser_in_command_position __P((void));
|
||||
|
||||
extern void free_pushed_string_input __P((void));
|
||||
|
||||
extern int parser_expanding_alias __P((void));
|
||||
extern void parser_save_alias __P((void));
|
||||
extern void parser_restore_alias __P((void));
|
||||
|
||||
extern char *decode_prompt_string __P((char *));
|
||||
|
||||
extern int get_current_prompt_level __P((void));
|
||||
extern void set_current_prompt_level __P((int));
|
||||
|
||||
#if defined (HISTORY)
|
||||
extern char *history_delimiting_chars __P((const char *));
|
||||
#endif
|
||||
|
||||
/* Declarations for functions defined in locale.c */
|
||||
extern void set_default_locale __P((void));
|
||||
extern void set_default_locale_vars __P((void));
|
||||
extern int set_locale_var __P((char *, char *));
|
||||
extern int set_lang __P((char *, char *));
|
||||
extern void set_default_lang __P((void));
|
||||
extern char *get_locale_var __P((char *));
|
||||
extern char *localetrans __P((char *, int, int *));
|
||||
extern char *mk_msgstr __P((char *, int *));
|
||||
extern char *localeexpand __P((char *, int, int, int, int *));
|
||||
|
||||
/* Declarations for functions defined in list.c. */
|
||||
extern void list_walk __P((GENERIC_LIST *, sh_glist_func_t *));
|
||||
extern void wlist_walk __P((WORD_LIST *, sh_icpfunc_t *));
|
||||
extern GENERIC_LIST *list_reverse ();
|
||||
extern int list_length ();
|
||||
extern GENERIC_LIST *list_append ();
|
||||
extern GENERIC_LIST *list_remove ();
|
||||
|
||||
/* Declarations for functions defined in stringlib.c */
|
||||
extern int find_string_in_alist __P((char *, STRING_INT_ALIST *, int));
|
||||
extern char *find_token_in_alist __P((int, STRING_INT_ALIST *, int));
|
||||
extern int find_index_in_alist __P((char *, STRING_INT_ALIST *, int));
|
||||
|
||||
extern char *substring __P((const char *, int, int));
|
||||
extern char *strsub __P((char *, char *, char *, int));
|
||||
extern char *strcreplace __P((char *, int, char *, int));
|
||||
extern void strip_leading __P((char *));
|
||||
extern void strip_trailing __P((char *, int, int));
|
||||
extern void xbcopy __P((char *, char *, int));
|
||||
|
||||
/* Functions from version.c. */
|
||||
extern char *shell_version_string __P((void));
|
||||
extern void show_shell_version __P((int));
|
||||
|
||||
/* Functions from the bash library, lib/sh/libsh.a. These should really
|
||||
go into a separate include file. */
|
||||
|
||||
/* declarations for functions defined in lib/sh/casemod.c */
|
||||
extern char *sh_modcase __P((const char *, char *, int));
|
||||
|
||||
/* Defines for flags argument to sh_modcase. These need to agree with what's
|
||||
in lib/sh/casemode.c */
|
||||
#define CASE_LOWER 0x0001
|
||||
#define CASE_UPPER 0x0002
|
||||
#define CASE_CAPITALIZE 0x0004
|
||||
#define CASE_UNCAP 0x0008
|
||||
#define CASE_TOGGLE 0x0010
|
||||
#define CASE_TOGGLEALL 0x0020
|
||||
#define CASE_UPFIRST 0x0040
|
||||
#define CASE_LOWFIRST 0x0080
|
||||
|
||||
#define CASE_USEWORDS 0x1000
|
||||
|
||||
/* declarations for functions defined in lib/sh/clktck.c */
|
||||
extern long get_clk_tck __P((void));
|
||||
|
||||
/* declarations for functions defined in lib/sh/clock.c */
|
||||
extern void clock_t_to_secs ();
|
||||
extern void print_clock_t ();
|
||||
|
||||
/* Declarations for functions defined in lib/sh/dprintf.c */
|
||||
#if !defined (HAVE_DPRINTF)
|
||||
extern void dprintf __P((int, const char *, ...)) __attribute__((__format__ (printf, 2, 3)));
|
||||
#endif
|
||||
|
||||
/* Declarations for functions defined in lib/sh/fmtulong.c */
|
||||
#define FL_PREFIX 0x01 /* add 0x, 0X, or 0 prefix as appropriate */
|
||||
#define FL_ADDBASE 0x02 /* add base# prefix to converted value */
|
||||
#define FL_HEXUPPER 0x04 /* use uppercase when converting to hex */
|
||||
#define FL_UNSIGNED 0x08 /* don't add any sign */
|
||||
|
||||
extern char *fmtulong __P((unsigned long int, int, char *, size_t, int));
|
||||
|
||||
/* Declarations for functions defined in lib/sh/fmtulong.c */
|
||||
#if defined (HAVE_LONG_LONG)
|
||||
extern char *fmtullong __P((unsigned long long int, int, char *, size_t, int));
|
||||
#endif
|
||||
|
||||
/* Declarations for functions defined in lib/sh/fmtumax.c */
|
||||
extern char *fmtumax __P((uintmax_t, int, char *, size_t, int));
|
||||
|
||||
/* Declarations for functions defined in lib/sh/fnxform.c */
|
||||
extern char *fnx_fromfs __P((char *, size_t));
|
||||
extern char *fnx_tofs __P((char *, size_t));
|
||||
|
||||
/* Declarations for functions defined in lib/sh/fpurge.c */
|
||||
|
||||
#if defined NEED_FPURGE_DECL
|
||||
#if !HAVE_DECL_FPURGE
|
||||
|
||||
#if HAVE_FPURGE
|
||||
# define fpurge _bash_fpurge
|
||||
#endif
|
||||
extern int fpurge __P((FILE *stream));
|
||||
|
||||
#endif /* HAVE_DECL_FPURGE */
|
||||
#endif /* NEED_FPURGE_DECL */
|
||||
|
||||
/* Declarations for functions defined in lib/sh/getcwd.c */
|
||||
#if !defined (HAVE_GETCWD)
|
||||
extern char *getcwd __P((char *, size_t));
|
||||
#endif
|
||||
|
||||
/* Declarations for functions defined in lib/sh/input_avail.c */
|
||||
extern int input_avail __P((int));
|
||||
|
||||
/* Declarations for functions defined in lib/sh/itos.c */
|
||||
extern char *inttostr __P((intmax_t, char *, size_t));
|
||||
extern char *itos __P((intmax_t));
|
||||
extern char *mitos __P((intmax_t));
|
||||
extern char *uinttostr __P((uintmax_t, char *, size_t));
|
||||
extern char *uitos __P((uintmax_t));
|
||||
|
||||
/* declarations for functions defined in lib/sh/makepath.c */
|
||||
#define MP_DOTILDE 0x01
|
||||
#define MP_DOCWD 0x02
|
||||
#define MP_RMDOT 0x04
|
||||
#define MP_IGNDOT 0x08
|
||||
|
||||
extern char *sh_makepath __P((const char *, const char *, int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/mbscasecmp.c */
|
||||
#if !defined (HAVE_MBSCASECMP)
|
||||
extern char *mbscasecmp __P((const char *, const char *));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/mbschr.c */
|
||||
#if !defined (HAVE_MBSCHR)
|
||||
extern char *mbschr __P((const char *, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/mbscmp.c */
|
||||
#if !defined (HAVE_MBSCMP)
|
||||
extern char *mbscmp __P((const char *, const char *));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/netconn.c */
|
||||
extern int isnetconn __P((int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/netopen.c */
|
||||
extern int netopen __P((char *));
|
||||
|
||||
/* Declarations for functions defined in lib/sh/oslib.c */
|
||||
|
||||
#if !defined (HAVE_DUP2) || defined (DUP2_BROKEN)
|
||||
extern int dup2 __P((int, int));
|
||||
#endif
|
||||
|
||||
#if !defined (HAVE_GETDTABLESIZE)
|
||||
extern int getdtablesize __P((void));
|
||||
#endif /* !HAVE_GETDTABLESIZE */
|
||||
|
||||
#if !defined (HAVE_GETHOSTNAME)
|
||||
extern int gethostname __P((char *, int));
|
||||
#endif /* !HAVE_GETHOSTNAME */
|
||||
|
||||
extern int getmaxgroups __P((void));
|
||||
extern long getmaxchild __P((void));
|
||||
|
||||
/* declarations for functions defined in lib/sh/pathcanon.c */
|
||||
#define PATH_CHECKDOTDOT 0x0001
|
||||
#define PATH_CHECKEXISTS 0x0002
|
||||
#define PATH_HARDPATH 0x0004
|
||||
#define PATH_NOALLOC 0x0008
|
||||
|
||||
extern char *sh_canonpath __P((char *, int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/pathphys.c */
|
||||
extern char *sh_physpath __P((char *, int));
|
||||
extern char *sh_realpath __P((const char *, char *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/setlinebuf.c */
|
||||
#ifdef NEED_SH_SETLINEBUF_DECL
|
||||
extern int sh_setlinebuf __P((FILE *));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/shaccess.c */
|
||||
extern int sh_eaccess __P((char *, int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/shmatch.c */
|
||||
extern int sh_regmatch __P((const char *, const char *, int));
|
||||
|
||||
/* defines for flags argument to sh_regmatch. */
|
||||
#define SHMAT_SUBEXP 0x001 /* save subexpressions in SH_REMATCH */
|
||||
#define SHMAT_PWARN 0x002 /* print a warning message on invalid regexp */
|
||||
|
||||
/* declarations for functions defined in lib/sh/shmbchar.c */
|
||||
extern size_t mbstrlen __P((const char *));
|
||||
extern char *mbsmbchar __P((const char *));
|
||||
extern int sh_mbsnlen __P((const char *, size_t, int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/shquote.c */
|
||||
extern char *sh_single_quote __P((const char *));
|
||||
extern char *sh_double_quote __P((const char *));
|
||||
extern char *sh_mkdoublequoted __P((const char *, int, int));
|
||||
extern char *sh_un_double_quote __P((char *));
|
||||
extern char *sh_backslash_quote __P((char *, const char *, int));
|
||||
extern char *sh_backslash_quote_for_double_quotes __P((char *));
|
||||
extern int sh_contains_shell_metas __P((char *));
|
||||
extern int sh_contains_quotes __P((char *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/spell.c */
|
||||
extern int spname __P((char *, char *));
|
||||
extern char *dirspell __P((char *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/strcasecmp.c */
|
||||
#if !defined (HAVE_STRCASECMP)
|
||||
extern int strncasecmp __P((const char *, const char *, int));
|
||||
extern int strcasecmp __P((const char *, const char *));
|
||||
#endif /* HAVE_STRCASECMP */
|
||||
|
||||
/* declarations for functions defined in lib/sh/strcasestr.c */
|
||||
#if ! HAVE_STRCASESTR
|
||||
extern char *strcasestr __P((const char *, const char *));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strchrnul.c */
|
||||
#if ! HAVE_STRCHRNUL
|
||||
extern char *strchrnul __P((const char *, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strerror.c */
|
||||
#if !defined (HAVE_STRERROR) && !defined (strerror)
|
||||
extern char *strerror __P((int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strftime.c */
|
||||
#if !defined (HAVE_STRFTIME) && defined (NEED_STRFTIME_DECL)
|
||||
extern size_t strftime __P((char *, size_t, const char *, const struct tm *));
|
||||
#endif
|
||||
|
||||
/* declarations for functions and structures defined in lib/sh/stringlist.c */
|
||||
|
||||
/* This is a general-purpose argv-style array struct. */
|
||||
typedef struct _list_of_strings {
|
||||
char **list;
|
||||
int list_size;
|
||||
int list_len;
|
||||
} STRINGLIST;
|
||||
|
||||
typedef int sh_strlist_map_func_t __P((char *));
|
||||
|
||||
extern STRINGLIST *strlist_create __P((int));
|
||||
extern STRINGLIST *strlist_resize __P((STRINGLIST *, int));
|
||||
extern void strlist_flush __P((STRINGLIST *));
|
||||
extern void strlist_dispose __P((STRINGLIST *));
|
||||
extern int strlist_remove __P((STRINGLIST *, char *));
|
||||
extern STRINGLIST *strlist_copy __P((STRINGLIST *));
|
||||
extern STRINGLIST *strlist_merge __P((STRINGLIST *, STRINGLIST *));
|
||||
extern STRINGLIST *strlist_append __P((STRINGLIST *, STRINGLIST *));
|
||||
extern STRINGLIST *strlist_prefix_suffix __P((STRINGLIST *, char *, char *));
|
||||
extern void strlist_print __P((STRINGLIST *, char *));
|
||||
extern void strlist_walk __P((STRINGLIST *, sh_strlist_map_func_t *));
|
||||
extern void strlist_sort __P((STRINGLIST *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/stringvec.c */
|
||||
|
||||
extern char **strvec_create __P((int));
|
||||
extern char **strvec_resize __P((char **, int));
|
||||
extern char **strvec_mcreate __P((int));
|
||||
extern char **strvec_mresize __P((char **, int));
|
||||
extern void strvec_flush __P((char **));
|
||||
extern void strvec_dispose __P((char **));
|
||||
extern int strvec_remove __P((char **, char *));
|
||||
extern int strvec_len __P((char **));
|
||||
extern int strvec_search __P((char **, char *));
|
||||
extern char **strvec_copy __P((char **));
|
||||
extern int strvec_strcmp __P((char **, char **));
|
||||
extern void strvec_sort __P((char **));
|
||||
|
||||
extern char **strvec_from_word_list __P((WORD_LIST *, int, int, int *));
|
||||
extern WORD_LIST *strvec_to_word_list __P((char **, int, int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/strnlen.c */
|
||||
#if !defined (HAVE_STRNLEN)
|
||||
extern size_t strnlen __P((const char *, size_t));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strpbrk.c */
|
||||
#if !defined (HAVE_STRPBRK)
|
||||
extern char *strpbrk __P((const char *, const char *));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strtod.c */
|
||||
#if !defined (HAVE_STRTOD)
|
||||
extern double strtod __P((const char *, char **));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strtol.c */
|
||||
#if !HAVE_DECL_STRTOL
|
||||
extern long strtol __P((const char *, char **, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strtoll.c */
|
||||
#if defined (HAVE_LONG_LONG) && !HAVE_DECL_STRTOLL
|
||||
extern long long strtoll __P((const char *, char **, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strtoul.c */
|
||||
#if !HAVE_DECL_STRTOUL
|
||||
extern unsigned long strtoul __P((const char *, char **, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strtoull.c */
|
||||
#if defined (HAVE_LONG_LONG) && !HAVE_DECL_STRTOULL
|
||||
extern unsigned long long strtoull __P((const char *, char **, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strimax.c */
|
||||
#if !HAVE_DECL_STRTOIMAX
|
||||
extern intmax_t strtoimax __P((const char *, char **, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strumax.c */
|
||||
#if !HAVE_DECL_STRTOUMAX
|
||||
extern uintmax_t strtoumax __P((const char *, char **, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/strtrans.c */
|
||||
extern char *ansicstr __P((char *, int, int, int *, int *));
|
||||
extern char *ansic_quote __P((char *, int, int *));
|
||||
extern int ansic_shouldquote __P((const char *));
|
||||
extern char *ansiexpand __P((char *, int, int, int *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/timeval.c. No prototypes
|
||||
so we don't have to count on having a definition of struct timeval in
|
||||
scope when this file is included. */
|
||||
extern void timeval_to_secs ();
|
||||
extern void print_timeval ();
|
||||
|
||||
/* declarations for functions defined in lib/sh/tmpfile.c */
|
||||
#define MT_USETMPDIR 0x0001
|
||||
#define MT_READWRITE 0x0002
|
||||
#define MT_USERANDOM 0x0004
|
||||
|
||||
extern char *sh_mktmpname __P((char *, int));
|
||||
extern int sh_mktmpfd __P((char *, int, char **));
|
||||
/* extern FILE *sh_mktmpfp __P((char *, int, char **)); */
|
||||
|
||||
/* declarations for functions defined in lib/sh/uconvert.c */
|
||||
extern int uconvert __P((char *, long *, long *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/ufuncs.c */
|
||||
extern unsigned int falarm __P((unsigned int, unsigned int));
|
||||
extern unsigned int fsleep __P((unsigned int, unsigned int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/unicode.c */
|
||||
extern int u32cconv __P((unsigned long, char *));
|
||||
extern void u32reset __P((void));
|
||||
|
||||
/* declarations for functions defined in lib/sh/wcsnwidth.c */
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
extern int wcsnwidth __P((const wchar_t *, size_t, int));
|
||||
#endif
|
||||
|
||||
/* declarations for functions defined in lib/sh/winsize.c */
|
||||
extern void get_new_window_size __P((int, int *, int *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/zcatfd.c */
|
||||
extern int zcatfd __P((int, int, char *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/zgetline.c */
|
||||
extern ssize_t zgetline __P((int, char **, size_t *, int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/zmapfd.c */
|
||||
extern int zmapfd __P((int, char **, char *));
|
||||
|
||||
/* declarations for functions defined in lib/sh/zread.c */
|
||||
extern ssize_t zread __P((int, char *, size_t));
|
||||
extern ssize_t zreadretry __P((int, char *, size_t));
|
||||
extern ssize_t zreadintr __P((int, char *, size_t));
|
||||
extern ssize_t zreadc __P((int, char *));
|
||||
extern ssize_t zreadcintr __P((int, char *));
|
||||
extern ssize_t zreadn __P((int, char *, size_t));
|
||||
extern void zreset __P((void));
|
||||
extern void zsyncfd __P((int));
|
||||
|
||||
/* declarations for functions defined in lib/sh/zwrite.c */
|
||||
extern int zwrite __P((int, char *, size_t));
|
||||
|
||||
/* declarations for functions defined in lib/glob/gmisc.c */
|
||||
extern int match_pattern_char __P((char *, char *));
|
||||
extern int umatchlen __P((char *, size_t));
|
||||
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
extern int match_pattern_wchar __P((wchar_t *, wchar_t *));
|
||||
extern int wmatchlen __P((wchar_t *, size_t));
|
||||
#endif
|
||||
|
||||
#endif /* _EXTERNS_H_ */
|
||||
-623
@@ -1,623 +0,0 @@
|
||||
/* findcmd.c -- Functions to search for commands by name. */
|
||||
|
||||
/* Copyright (C) 1997-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 <stdio.h>
|
||||
#include "chartypes.h"
|
||||
#include "bashtypes.h"
|
||||
#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
|
||||
# include <sys/file.h>
|
||||
#endif
|
||||
#include "filecntl.h"
|
||||
#include "posixstat.h"
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
|
||||
#include "bashansi.h"
|
||||
|
||||
#include "memalloc.h"
|
||||
#include "shell.h"
|
||||
#include "flags.h"
|
||||
#include "hashlib.h"
|
||||
#include "pathexp.h"
|
||||
#include "hashcmd.h"
|
||||
#include "findcmd.h" /* matching prototypes and declarations */
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
extern int posixly_correct;
|
||||
extern int last_command_exit_value;
|
||||
|
||||
/* Static functions defined and used in this file. */
|
||||
static char *_find_user_command_internal __P((const char *, int));
|
||||
static char *find_user_command_internal __P((const char *, int));
|
||||
static char *find_user_command_in_path __P((const char *, char *, int));
|
||||
static char *find_in_path_element __P((const char *, char *, int, int, struct stat *));
|
||||
static char *find_absolute_program __P((const char *, int));
|
||||
|
||||
static char *get_next_path_element __P((char *, int *));
|
||||
|
||||
/* The file name which we would try to execute, except that it isn't
|
||||
possible to execute it. This is the first file that matches the
|
||||
name that we are looking for while we are searching $PATH for a
|
||||
suitable one to execute. If we cannot find a suitable executable
|
||||
file, then we use this one. */
|
||||
static char *file_to_lose_on;
|
||||
|
||||
/* Non-zero if we should stat every command found in the hash table to
|
||||
make sure it still exists. */
|
||||
int check_hashed_filenames;
|
||||
|
||||
/* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command ()
|
||||
encounters a `.' as the directory pathname while scanning the
|
||||
list of possible pathnames; i.e., if `.' comes before the directory
|
||||
containing the file of interest. */
|
||||
int dot_found_in_search = 0;
|
||||
|
||||
/* Return some flags based on information about this file.
|
||||
The EXISTS bit is non-zero if the file is found.
|
||||
The EXECABLE bit is non-zero the file is executble.
|
||||
Zero is returned if the file is not found. */
|
||||
int
|
||||
file_status (name)
|
||||
const char *name;
|
||||
{
|
||||
struct stat finfo;
|
||||
int r;
|
||||
|
||||
/* Determine whether this file exists or not. */
|
||||
if (stat (name, &finfo) < 0)
|
||||
return (0);
|
||||
|
||||
/* If the file is a directory, then it is not "executable" in the
|
||||
sense of the shell. */
|
||||
if (S_ISDIR (finfo.st_mode))
|
||||
return (FS_EXISTS|FS_DIRECTORY);
|
||||
|
||||
r = FS_EXISTS;
|
||||
|
||||
#if defined (HAVE_EACCESS)
|
||||
/* Use eaccess(2) if we have it to take things like ACLs and other
|
||||
file access mechanisms into account. eaccess uses the effective
|
||||
user and group IDs, not the real ones. We could use sh_eaccess,
|
||||
but we don't want any special treatment for /dev/fd. */
|
||||
if (eaccess (name, X_OK) == 0)
|
||||
r |= FS_EXECABLE;
|
||||
if (eaccess (name, R_OK) == 0)
|
||||
r |= FS_READABLE;
|
||||
|
||||
return r;
|
||||
#elif defined (AFS)
|
||||
/* We have to use access(2) to determine access because AFS does not
|
||||
support Unix file system semantics. This may produce wrong
|
||||
answers for non-AFS files when ruid != euid. I hate AFS. */
|
||||
if (access (name, X_OK) == 0)
|
||||
r |= FS_EXECABLE;
|
||||
if (access (name, R_OK) == 0)
|
||||
r |= FS_READABLE;
|
||||
|
||||
return r;
|
||||
#else /* !HAVE_EACCESS && !AFS */
|
||||
|
||||
/* Find out if the file is actually executable. By definition, the
|
||||
only other criteria is that the file has an execute bit set that
|
||||
we can use. The same with whether or not a file is readable. */
|
||||
|
||||
/* Root only requires execute permission for any of owner, group or
|
||||
others to be able to exec a file, and can read any file. */
|
||||
if (current_user.euid == (uid_t)0)
|
||||
{
|
||||
r |= FS_READABLE;
|
||||
if (finfo.st_mode & S_IXUGO)
|
||||
r |= FS_EXECABLE;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* If we are the owner of the file, the owner bits apply. */
|
||||
if (current_user.euid == finfo.st_uid)
|
||||
{
|
||||
if (finfo.st_mode & S_IXUSR)
|
||||
r |= FS_EXECABLE;
|
||||
if (finfo.st_mode & S_IRUSR)
|
||||
r |= FS_READABLE;
|
||||
}
|
||||
|
||||
/* If we are in the owning group, the group permissions apply. */
|
||||
else if (group_member (finfo.st_gid))
|
||||
{
|
||||
if (finfo.st_mode & S_IXGRP)
|
||||
r |= FS_EXECABLE;
|
||||
if (finfo.st_mode & S_IRGRP)
|
||||
r |= FS_READABLE;
|
||||
}
|
||||
|
||||
/* Else we check whether `others' have permission to execute the file */
|
||||
else
|
||||
{
|
||||
if (finfo.st_mode & S_IXOTH)
|
||||
r |= FS_EXECABLE;
|
||||
if (finfo.st_mode & S_IROTH)
|
||||
r |= FS_READABLE;
|
||||
}
|
||||
|
||||
return r;
|
||||
#endif /* !AFS */
|
||||
}
|
||||
|
||||
/* Return non-zero if FILE exists and is executable.
|
||||
Note that this function is the definition of what an
|
||||
executable file is; do not change this unless YOU know
|
||||
what an executable file is. */
|
||||
int
|
||||
executable_file (file)
|
||||
const char *file;
|
||||
{
|
||||
int s;
|
||||
|
||||
s = file_status (file);
|
||||
#if defined EISDIR
|
||||
if (s & FS_DIRECTORY)
|
||||
errno = EISDIR; /* let's see if we can improve error messages */
|
||||
#endif
|
||||
return ((s & FS_EXECABLE) && ((s & FS_DIRECTORY) == 0));
|
||||
}
|
||||
|
||||
int
|
||||
is_directory (file)
|
||||
const char *file;
|
||||
{
|
||||
return (file_status (file) & FS_DIRECTORY);
|
||||
}
|
||||
|
||||
int
|
||||
executable_or_directory (file)
|
||||
const char *file;
|
||||
{
|
||||
int s;
|
||||
|
||||
s = file_status (file);
|
||||
return ((s & FS_EXECABLE) || (s & FS_DIRECTORY));
|
||||
}
|
||||
|
||||
/* Locate the executable file referenced by NAME, searching along
|
||||
the contents of the shell PATH variable. Return a new string
|
||||
which is the full pathname to the file, or NULL if the file
|
||||
couldn't be found. If a file is found that isn't executable,
|
||||
and that is the only match, then return that. */
|
||||
char *
|
||||
find_user_command (name)
|
||||
const char *name;
|
||||
{
|
||||
return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS));
|
||||
}
|
||||
|
||||
/* Locate the file referenced by NAME, searching along the contents
|
||||
of the shell PATH variable. Return a new string which is the full
|
||||
pathname to the file, or NULL if the file couldn't be found. This
|
||||
returns the first readable file found; designed to be used to look
|
||||
for shell scripts or files to source. */
|
||||
char *
|
||||
find_path_file (name)
|
||||
const char *name;
|
||||
{
|
||||
return (find_user_command_internal (name, FS_READABLE));
|
||||
}
|
||||
|
||||
static char *
|
||||
_find_user_command_internal (name, flags)
|
||||
const char *name;
|
||||
int flags;
|
||||
{
|
||||
char *path_list, *cmd;
|
||||
SHELL_VAR *var;
|
||||
|
||||
/* Search for the value of PATH in both the temporary environments and
|
||||
in the regular list of variables. */
|
||||
if (var = find_variable_tempenv ("PATH")) /* XXX could be array? */
|
||||
path_list = value_cell (var);
|
||||
else
|
||||
path_list = (char *)NULL;
|
||||
|
||||
if (path_list == 0 || *path_list == '\0')
|
||||
return (savestring (name));
|
||||
|
||||
cmd = find_user_command_in_path (name, path_list, flags);
|
||||
|
||||
return (cmd);
|
||||
}
|
||||
|
||||
static char *
|
||||
find_user_command_internal (name, flags)
|
||||
const char *name;
|
||||
int flags;
|
||||
{
|
||||
#ifdef __WIN32__
|
||||
char *res, *dotexe;
|
||||
|
||||
dotexe = (char *)xmalloc (strlen (name) + 5);
|
||||
strcpy (dotexe, name);
|
||||
strcat (dotexe, ".exe");
|
||||
res = _find_user_command_internal (dotexe, flags);
|
||||
free (dotexe);
|
||||
if (res == 0)
|
||||
res = _find_user_command_internal (name, flags);
|
||||
return res;
|
||||
#else
|
||||
return (_find_user_command_internal (name, flags));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Return the next element from PATH_LIST, a colon separated list of
|
||||
paths. PATH_INDEX_POINTER is the address of an index into PATH_LIST;
|
||||
the index is modified by this function.
|
||||
Return the next element of PATH_LIST or NULL if there are no more. */
|
||||
static char *
|
||||
get_next_path_element (path_list, path_index_pointer)
|
||||
char *path_list;
|
||||
int *path_index_pointer;
|
||||
{
|
||||
char *path;
|
||||
|
||||
path = extract_colon_unit (path_list, path_index_pointer);
|
||||
|
||||
if (path == 0)
|
||||
return (path);
|
||||
|
||||
if (*path == '\0')
|
||||
{
|
||||
free (path);
|
||||
path = savestring (".");
|
||||
}
|
||||
|
||||
return (path);
|
||||
}
|
||||
|
||||
/* Look for PATHNAME in $PATH. Returns either the hashed command
|
||||
corresponding to PATHNAME or the first instance of PATHNAME found
|
||||
in $PATH. If (FLAGS&1) is non-zero, insert the instance of PATHNAME
|
||||
found in $PATH into the command hash table. Returns a newly-allocated
|
||||
string. */
|
||||
char *
|
||||
search_for_command (pathname, flags)
|
||||
const char *pathname;
|
||||
int flags;
|
||||
{
|
||||
char *hashed_file, *command;
|
||||
int temp_path, st;
|
||||
SHELL_VAR *path;
|
||||
|
||||
hashed_file = command = (char *)NULL;
|
||||
|
||||
/* If PATH is in the temporary environment for this command, don't use the
|
||||
hash table to search for the full pathname. */
|
||||
path = find_variable_tempenv ("PATH");
|
||||
temp_path = path && tempvar_p (path);
|
||||
if (temp_path == 0 && path)
|
||||
path = (SHELL_VAR *)NULL;
|
||||
|
||||
/* Don't waste time trying to find hashed data for a pathname
|
||||
that is already completely specified or if we're using a command-
|
||||
specific value for PATH. */
|
||||
if (path == 0 && absolute_program (pathname) == 0)
|
||||
hashed_file = phash_search (pathname);
|
||||
|
||||
/* If a command found in the hash table no longer exists, we need to
|
||||
look for it in $PATH. Thank you Posix.2. This forces us to stat
|
||||
every command found in the hash table. */
|
||||
|
||||
if (hashed_file && (posixly_correct || check_hashed_filenames))
|
||||
{
|
||||
st = file_status (hashed_file);
|
||||
if ((st & (FS_EXISTS|FS_EXECABLE)) != (FS_EXISTS|FS_EXECABLE))
|
||||
{
|
||||
phash_remove (pathname);
|
||||
free (hashed_file);
|
||||
hashed_file = (char *)NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (hashed_file)
|
||||
command = hashed_file;
|
||||
else if (absolute_program (pathname))
|
||||
/* A command containing a slash is not looked up in PATH or saved in
|
||||
the hash table. */
|
||||
command = savestring (pathname);
|
||||
else
|
||||
{
|
||||
/* If $PATH is in the temporary environment, we've already retrieved
|
||||
it, so don't bother trying again. */
|
||||
if (temp_path)
|
||||
{
|
||||
command = find_user_command_in_path (pathname, value_cell (path),
|
||||
FS_EXEC_PREFERRED|FS_NODIRS);
|
||||
}
|
||||
else
|
||||
command = find_user_command (pathname);
|
||||
if (command && hashing_enabled && temp_path == 0 && (flags & 1))
|
||||
phash_insert ((char *)pathname, command, dot_found_in_search, 1); /* XXX fix const later */
|
||||
}
|
||||
return (command);
|
||||
}
|
||||
|
||||
char *
|
||||
user_command_matches (name, flags, state)
|
||||
const char *name;
|
||||
int flags, state;
|
||||
{
|
||||
register int i;
|
||||
int path_index, name_len;
|
||||
char *path_list, *path_element, *match;
|
||||
struct stat dotinfo;
|
||||
static char **match_list = NULL;
|
||||
static int match_list_size = 0;
|
||||
static int match_index = 0;
|
||||
|
||||
if (state == 0)
|
||||
{
|
||||
/* Create the list of matches. */
|
||||
if (match_list == 0)
|
||||
{
|
||||
match_list_size = 5;
|
||||
match_list = strvec_create (match_list_size);
|
||||
}
|
||||
|
||||
/* Clear out the old match list. */
|
||||
for (i = 0; i < match_list_size; i++)
|
||||
match_list[i] = 0;
|
||||
|
||||
/* We haven't found any files yet. */
|
||||
match_index = 0;
|
||||
|
||||
if (absolute_program (name))
|
||||
{
|
||||
match_list[0] = find_absolute_program (name, flags);
|
||||
match_list[1] = (char *)NULL;
|
||||
path_list = (char *)NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
name_len = strlen (name);
|
||||
file_to_lose_on = (char *)NULL;
|
||||
dot_found_in_search = 0;
|
||||
if (stat (".", &dotinfo) < 0)
|
||||
dotinfo.st_dev = dotinfo.st_ino = 0; /* so same_file won't match */
|
||||
path_list = get_string_value ("PATH");
|
||||
path_index = 0;
|
||||
}
|
||||
|
||||
while (path_list && path_list[path_index])
|
||||
{
|
||||
path_element = get_next_path_element (path_list, &path_index);
|
||||
|
||||
if (path_element == 0)
|
||||
break;
|
||||
|
||||
match = find_in_path_element (name, path_element, flags, name_len, &dotinfo);
|
||||
|
||||
free (path_element);
|
||||
|
||||
if (match == 0)
|
||||
continue;
|
||||
|
||||
if (match_index + 1 == match_list_size)
|
||||
{
|
||||
match_list_size += 10;
|
||||
match_list = strvec_resize (match_list, (match_list_size + 1));
|
||||
}
|
||||
|
||||
match_list[match_index++] = match;
|
||||
match_list[match_index] = (char *)NULL;
|
||||
FREE (file_to_lose_on);
|
||||
file_to_lose_on = (char *)NULL;
|
||||
}
|
||||
|
||||
/* We haven't returned any strings yet. */
|
||||
match_index = 0;
|
||||
}
|
||||
|
||||
match = match_list[match_index];
|
||||
|
||||
if (match)
|
||||
match_index++;
|
||||
|
||||
return (match);
|
||||
}
|
||||
|
||||
static char *
|
||||
find_absolute_program (name, flags)
|
||||
const char *name;
|
||||
int flags;
|
||||
{
|
||||
int st;
|
||||
|
||||
st = file_status (name);
|
||||
|
||||
/* If the file doesn't exist, quit now. */
|
||||
if ((st & FS_EXISTS) == 0)
|
||||
return ((char *)NULL);
|
||||
|
||||
/* If we only care about whether the file exists or not, return
|
||||
this filename. Otherwise, maybe we care about whether this
|
||||
file is executable. If it is, and that is what we want, return it. */
|
||||
if ((flags & FS_EXISTS) || ((flags & FS_EXEC_ONLY) && (st & FS_EXECABLE)))
|
||||
return (savestring (name));
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static char *
|
||||
find_in_path_element (name, path, flags, name_len, dotinfop)
|
||||
const char *name;
|
||||
char *path;
|
||||
int flags, name_len;
|
||||
struct stat *dotinfop;
|
||||
{
|
||||
int status;
|
||||
char *full_path, *xpath;
|
||||
|
||||
xpath = (*path == '~') ? bash_tilde_expand (path, 0) : path;
|
||||
|
||||
/* Remember the location of "." in the path, in all its forms
|
||||
(as long as they begin with a `.', e.g. `./.') */
|
||||
if (dot_found_in_search == 0 && *xpath == '.')
|
||||
dot_found_in_search = same_file (".", xpath, dotinfop, (struct stat *)NULL);
|
||||
|
||||
full_path = sh_makepath (xpath, name, 0);
|
||||
|
||||
status = file_status (full_path);
|
||||
|
||||
if (xpath != path)
|
||||
free (xpath);
|
||||
|
||||
if ((status & FS_EXISTS) == 0)
|
||||
{
|
||||
free (full_path);
|
||||
return ((char *)NULL);
|
||||
}
|
||||
|
||||
/* The file exists. If the caller simply wants the first file, here it is. */
|
||||
if (flags & FS_EXISTS)
|
||||
return (full_path);
|
||||
|
||||
/* If we have a readable file, and the caller wants a readable file, this
|
||||
is it. */
|
||||
if ((flags & FS_READABLE) && (status & FS_READABLE))
|
||||
return (full_path);
|
||||
|
||||
/* If the file is executable, then it satisfies the cases of
|
||||
EXEC_ONLY and EXEC_PREFERRED. Return this file unconditionally. */
|
||||
if ((status & FS_EXECABLE) && (flags & (FS_EXEC_ONLY|FS_EXEC_PREFERRED)) &&
|
||||
(((flags & FS_NODIRS) == 0) || ((status & FS_DIRECTORY) == 0)))
|
||||
{
|
||||
FREE (file_to_lose_on);
|
||||
file_to_lose_on = (char *)NULL;
|
||||
return (full_path);
|
||||
}
|
||||
|
||||
/* The file is not executable, but it does exist. If we prefer
|
||||
an executable, then remember this one if it is the first one
|
||||
we have found. */
|
||||
if ((flags & FS_EXEC_PREFERRED) && file_to_lose_on == 0)
|
||||
file_to_lose_on = savestring (full_path);
|
||||
|
||||
/* If we want only executable files, or we don't want directories and
|
||||
this file is a directory, or we want a readable file and this file
|
||||
isn't readable, fail. */
|
||||
if ((flags & (FS_EXEC_ONLY|FS_EXEC_PREFERRED)) ||
|
||||
((flags & FS_NODIRS) && (status & FS_DIRECTORY)) ||
|
||||
((flags & FS_READABLE) && (status & FS_READABLE) == 0))
|
||||
{
|
||||
free (full_path);
|
||||
return ((char *)NULL);
|
||||
}
|
||||
else
|
||||
return (full_path);
|
||||
}
|
||||
|
||||
/* This does the dirty work for find_user_command_internal () and
|
||||
user_command_matches ().
|
||||
NAME is the name of the file to search for.
|
||||
PATH_LIST is a colon separated list of directories to search.
|
||||
FLAGS contains bit fields which control the files which are eligible.
|
||||
Some values are:
|
||||
FS_EXEC_ONLY: The file must be an executable to be found.
|
||||
FS_EXEC_PREFERRED: If we can't find an executable, then the
|
||||
the first file matching NAME will do.
|
||||
FS_EXISTS: The first file found will do.
|
||||
FS_NODIRS: Don't find any directories.
|
||||
*/
|
||||
static char *
|
||||
find_user_command_in_path (name, path_list, flags)
|
||||
const char *name;
|
||||
char *path_list;
|
||||
int flags;
|
||||
{
|
||||
char *full_path, *path;
|
||||
int path_index, name_len;
|
||||
struct stat dotinfo;
|
||||
|
||||
/* We haven't started looking, so we certainly haven't seen
|
||||
a `.' as the directory path yet. */
|
||||
dot_found_in_search = 0;
|
||||
|
||||
if (absolute_program (name))
|
||||
{
|
||||
full_path = find_absolute_program (name, flags);
|
||||
return (full_path);
|
||||
}
|
||||
|
||||
if (path_list == 0 || *path_list == '\0')
|
||||
return (savestring (name)); /* XXX */
|
||||
|
||||
file_to_lose_on = (char *)NULL;
|
||||
name_len = strlen (name);
|
||||
if (stat (".", &dotinfo) < 0)
|
||||
dotinfo.st_dev = dotinfo.st_ino = 0;
|
||||
path_index = 0;
|
||||
|
||||
while (path_list[path_index])
|
||||
{
|
||||
/* Allow the user to interrupt out of a lengthy path search. */
|
||||
QUIT;
|
||||
|
||||
path = get_next_path_element (path_list, &path_index);
|
||||
if (path == 0)
|
||||
break;
|
||||
|
||||
/* Side effects: sets dot_found_in_search, possibly sets
|
||||
file_to_lose_on. */
|
||||
full_path = find_in_path_element (name, path, flags, name_len, &dotinfo);
|
||||
free (path);
|
||||
|
||||
/* This should really be in find_in_path_element, but there isn't the
|
||||
right combination of flags. */
|
||||
if (full_path && is_directory (full_path))
|
||||
{
|
||||
free (full_path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (full_path)
|
||||
{
|
||||
FREE (file_to_lose_on);
|
||||
return (full_path);
|
||||
}
|
||||
}
|
||||
|
||||
/* We didn't find exactly what the user was looking for. Return
|
||||
the contents of FILE_TO_LOSE_ON which is NULL when the search
|
||||
required an executable, or non-NULL if a file was found and the
|
||||
search would accept a non-executable as a last resort. If the
|
||||
caller specified FS_NODIRS, and file_to_lose_on is a directory,
|
||||
return NULL. */
|
||||
if (file_to_lose_on && (flags & FS_NODIRS) && is_directory (file_to_lose_on))
|
||||
{
|
||||
free (file_to_lose_on);
|
||||
file_to_lose_on = (char *)NULL;
|
||||
}
|
||||
|
||||
return (file_to_lose_on);
|
||||
}
|
||||
@@ -1,835 +0,0 @@
|
||||
/* Copyright (C) 1991-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/>.
|
||||
*/
|
||||
|
||||
int FCT __P((CHAR *, CHAR *, int));
|
||||
|
||||
static int GMATCH __P((CHAR *, CHAR *, CHAR *, CHAR *, int));
|
||||
static CHAR *PARSE_COLLSYM __P((CHAR *, INT *));
|
||||
static CHAR *BRACKMATCH __P((CHAR *, U_CHAR, int));
|
||||
static int EXTMATCH __P((INT, CHAR *, CHAR *, CHAR *, CHAR *, int));
|
||||
|
||||
/*static*/ CHAR *PATSCAN __P((CHAR *, CHAR *, INT));
|
||||
|
||||
int
|
||||
FCT (pattern, string, flags)
|
||||
CHAR *pattern;
|
||||
CHAR *string;
|
||||
int flags;
|
||||
{
|
||||
CHAR *se, *pe;
|
||||
|
||||
if (string == 0 || pattern == 0)
|
||||
return FNM_NOMATCH;
|
||||
|
||||
se = string + STRLEN ((XCHAR *)string);
|
||||
pe = pattern + STRLEN ((XCHAR *)pattern);
|
||||
|
||||
return (GMATCH (string, se, pattern, pe, flags));
|
||||
}
|
||||
|
||||
/* Match STRING against the filename pattern PATTERN, returning zero if
|
||||
it matches, FNM_NOMATCH if not. */
|
||||
static int
|
||||
GMATCH (string, se, pattern, pe, flags)
|
||||
CHAR *string, *se;
|
||||
CHAR *pattern, *pe;
|
||||
int flags;
|
||||
{
|
||||
CHAR *p, *n; /* pattern, string */
|
||||
INT c; /* current pattern character - XXX U_CHAR? */
|
||||
INT sc; /* current string character - XXX U_CHAR? */
|
||||
|
||||
p = pattern;
|
||||
n = string;
|
||||
|
||||
if (string == 0 || pattern == 0)
|
||||
return FNM_NOMATCH;
|
||||
|
||||
#if DEBUG_MATCHING
|
||||
fprintf(stderr, "gmatch: string = %s; se = %s\n", string, se);
|
||||
fprintf(stderr, "gmatch: pattern = %s; pe = %s\n", pattern, pe);
|
||||
#endif
|
||||
|
||||
while (p < pe)
|
||||
{
|
||||
c = *p++;
|
||||
c = FOLD (c);
|
||||
|
||||
sc = n < se ? *n : '\0';
|
||||
|
||||
#ifdef EXTENDED_GLOB
|
||||
/* EXTMATCH () will handle recursively calling GMATCH, so we can
|
||||
just return what EXTMATCH() returns. */
|
||||
if ((flags & FNM_EXTMATCH) && *p == L('(') &&
|
||||
(c == L('+') || c == L('*') || c == L('?') || c == L('@') || c == L('!'))) /* ) */
|
||||
{
|
||||
int lflags;
|
||||
/* If we're not matching the start of the string, we're not
|
||||
concerned about the special cases for matching `.' */
|
||||
lflags = (n == string) ? flags : (flags & ~FNM_PERIOD);
|
||||
return (EXTMATCH (c, n, se, p, pe, lflags));
|
||||
}
|
||||
#endif /* EXTENDED_GLOB */
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case L('?'): /* Match single character */
|
||||
if (sc == '\0')
|
||||
return FNM_NOMATCH;
|
||||
else if ((flags & FNM_PATHNAME) && sc == L('/'))
|
||||
/* If we are matching a pathname, `?' can never match a `/'. */
|
||||
return FNM_NOMATCH;
|
||||
else if ((flags & FNM_PERIOD) && sc == L('.') &&
|
||||
(n == string || ((flags & FNM_PATHNAME) && n[-1] == L('/'))))
|
||||
/* `?' cannot match a `.' if it is the first character of the
|
||||
string or if it is the first character following a slash and
|
||||
we are matching a pathname. */
|
||||
return FNM_NOMATCH;
|
||||
break;
|
||||
|
||||
case L('\\'): /* backslash escape removes special meaning */
|
||||
if (p == pe)
|
||||
return FNM_NOMATCH;
|
||||
|
||||
if ((flags & FNM_NOESCAPE) == 0)
|
||||
{
|
||||
c = *p++;
|
||||
/* A trailing `\' cannot match. */
|
||||
if (p > pe)
|
||||
return FNM_NOMATCH;
|
||||
c = FOLD (c);
|
||||
}
|
||||
if (FOLD (sc) != (U_CHAR)c)
|
||||
return FNM_NOMATCH;
|
||||
break;
|
||||
|
||||
case '*': /* Match zero or more characters */
|
||||
if (p == pe)
|
||||
return 0;
|
||||
|
||||
if ((flags & FNM_PERIOD) && sc == L('.') &&
|
||||
(n == string || ((flags & FNM_PATHNAME) && n[-1] == L('/'))))
|
||||
/* `*' cannot match a `.' if it is the first character of the
|
||||
string or if it is the first character following a slash and
|
||||
we are matching a pathname. */
|
||||
return FNM_NOMATCH;
|
||||
|
||||
/* Collapse multiple consecutive `*' and `?', but make sure that
|
||||
one character of the string is consumed for each `?'. */
|
||||
for (c = *p++; (c == L('?') || c == L('*')); c = *p++)
|
||||
{
|
||||
if ((flags & FNM_PATHNAME) && sc == L('/'))
|
||||
/* A slash does not match a wildcard under FNM_PATHNAME. */
|
||||
return FNM_NOMATCH;
|
||||
#ifdef EXTENDED_GLOB
|
||||
else if ((flags & FNM_EXTMATCH) && c == L('?') && *p == L('(')) /* ) */
|
||||
{
|
||||
CHAR *newn;
|
||||
for (newn = n; newn < se; ++newn)
|
||||
{
|
||||
if (EXTMATCH (c, newn, se, p, pe, flags) == 0)
|
||||
return (0);
|
||||
}
|
||||
/* We didn't match. If we have a `?(...)', we can match 0
|
||||
or 1 times. */
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
else if (c == L('?'))
|
||||
{
|
||||
if (sc == L('\0'))
|
||||
return FNM_NOMATCH;
|
||||
/* One character of the string is consumed in matching
|
||||
this ? wildcard, so *??? won't match if there are
|
||||
fewer than three characters. */
|
||||
n++;
|
||||
sc = n < se ? *n : '\0';
|
||||
}
|
||||
|
||||
#ifdef EXTENDED_GLOB
|
||||
/* Handle ******(patlist) */
|
||||
if ((flags & FNM_EXTMATCH) && c == L('*') && *p == L('(')) /*)*/
|
||||
{
|
||||
CHAR *newn;
|
||||
/* We need to check whether or not the extended glob
|
||||
pattern matches the remainder of the string.
|
||||
If it does, we match the entire pattern. */
|
||||
for (newn = n; newn < se; ++newn)
|
||||
{
|
||||
if (EXTMATCH (c, newn, se, p, pe, flags) == 0)
|
||||
return (0);
|
||||
}
|
||||
/* We didn't match the extended glob pattern, but
|
||||
that's OK, since we can match 0 or more occurrences.
|
||||
We need to skip the glob pattern and see if we
|
||||
match the rest of the string. */
|
||||
newn = PATSCAN (p + 1, pe, 0);
|
||||
/* If NEWN is 0, we have an ill-formed pattern. */
|
||||
p = newn ? newn : pe;
|
||||
}
|
||||
#endif
|
||||
if (p == pe)
|
||||
break;
|
||||
}
|
||||
|
||||
/* The wildcards are the last element of the pattern. The name
|
||||
cannot match completely if we are looking for a pathname and
|
||||
it contains another slash, unless FNM_LEADING_DIR is set. */
|
||||
if (c == L('\0'))
|
||||
{
|
||||
int r = (flags & FNM_PATHNAME) == 0 ? 0 : FNM_NOMATCH;
|
||||
if (flags & FNM_PATHNAME)
|
||||
{
|
||||
if (flags & FNM_LEADING_DIR)
|
||||
r = 0;
|
||||
else if (MEMCHR (n, L('/'), se - n) == NULL)
|
||||
r = 0;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* If we've hit the end of the pattern and the last character of
|
||||
the pattern was handled by the loop above, we've succeeded.
|
||||
Otherwise, we need to match that last character. */
|
||||
if (p == pe && (c == L('?') || c == L('*')))
|
||||
return (0);
|
||||
|
||||
/* If we've hit the end of the string and the rest of the pattern
|
||||
is something that matches the empty string, we can succeed. */
|
||||
#if defined (EXTENDED_GLOB)
|
||||
if (n == se && ((flags & FNM_EXTMATCH) && (c == L('!') || c == L('?')) && *p == L('(')))
|
||||
{
|
||||
--p;
|
||||
if (EXTMATCH (c, n, se, p, pe, flags) == 0)
|
||||
return (c == L('!') ? FNM_NOMATCH : 0);
|
||||
return (c == L('!') ? 0 : FNM_NOMATCH);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If we stop at a slash in the pattern and we are looking for a
|
||||
pathname ([star]/foo), then consume enough of the string to stop
|
||||
at any slash and then try to match the rest of the pattern */
|
||||
if (c == L('/') && (flags & FNM_PATHNAME))
|
||||
{
|
||||
while (n < se && *n != L('/'))
|
||||
++n;
|
||||
if (n < se && *n == L('/') && (GMATCH (n+1, se, p, pe, flags) == 0))
|
||||
return 0;
|
||||
return FNM_NOMATCH; /* XXX */
|
||||
}
|
||||
else
|
||||
/* General case, use recursion. */
|
||||
{
|
||||
U_CHAR c1;
|
||||
const CHAR *endp;
|
||||
|
||||
endp = MEMCHR (n, (flags & FNM_PATHNAME) ? L('/') : L('\0'), se - n);
|
||||
if (endp == 0)
|
||||
endp = se;
|
||||
c1 = ((flags & FNM_NOESCAPE) == 0 && c == L('\\')) ? *p : c;
|
||||
c1 = FOLD (c1);
|
||||
for (--p; n < endp; ++n)
|
||||
{
|
||||
/* Only call strmatch if the first character indicates a
|
||||
possible match. We can check the first character if
|
||||
we're not doing an extended glob match. */
|
||||
if ((flags & FNM_EXTMATCH) == 0 && c != L('[') && FOLD (*n) != c1) /*]*/
|
||||
continue;
|
||||
|
||||
/* If we're doing an extended glob match and the pattern is not
|
||||
one of the extended glob patterns, we can check the first
|
||||
character. */
|
||||
if ((flags & FNM_EXTMATCH) && p[1] != L('(') && /*)*/
|
||||
STRCHR (L("?*+@!"), *p) == 0 && c != L('[') && FOLD (*n) != c1) /*]*/
|
||||
continue;
|
||||
|
||||
/* Otherwise, we just recurse. */
|
||||
if (GMATCH (n, se, p, pe, flags & ~FNM_PERIOD) == 0)
|
||||
return (0);
|
||||
}
|
||||
return FNM_NOMATCH;
|
||||
}
|
||||
|
||||
case L('['):
|
||||
{
|
||||
if (sc == L('\0') || n == se)
|
||||
return FNM_NOMATCH;
|
||||
|
||||
/* A character class cannot match a `.' if it is the first
|
||||
character of the string or if it is the first character
|
||||
following a slash and we are matching a pathname. */
|
||||
if ((flags & FNM_PERIOD) && sc == L('.') &&
|
||||
(n == string || ((flags & FNM_PATHNAME) && n[-1] == L('/'))))
|
||||
return (FNM_NOMATCH);
|
||||
|
||||
p = BRACKMATCH (p, sc, flags);
|
||||
if (p == 0)
|
||||
return FNM_NOMATCH;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if ((U_CHAR)c != FOLD (sc))
|
||||
return (FNM_NOMATCH);
|
||||
}
|
||||
|
||||
++n;
|
||||
}
|
||||
|
||||
if (n == se)
|
||||
return (0);
|
||||
|
||||
if ((flags & FNM_LEADING_DIR) && *n == L('/'))
|
||||
/* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
|
||||
return 0;
|
||||
|
||||
return (FNM_NOMATCH);
|
||||
}
|
||||
|
||||
/* Parse a bracket expression collating symbol ([.sym.]) starting at P, find
|
||||
the value of the symbol, and move P past the collating symbol expression.
|
||||
The value is returned in *VP, if VP is not null. */
|
||||
static CHAR *
|
||||
PARSE_COLLSYM (p, vp)
|
||||
CHAR *p;
|
||||
INT *vp;
|
||||
{
|
||||
register int pc;
|
||||
INT val;
|
||||
|
||||
p++; /* move past the `.' */
|
||||
|
||||
for (pc = 0; p[pc]; pc++)
|
||||
if (p[pc] == L('.') && p[pc+1] == L(']'))
|
||||
break;
|
||||
val = COLLSYM (p, pc);
|
||||
if (vp)
|
||||
*vp = val;
|
||||
return (p + pc + 2);
|
||||
}
|
||||
|
||||
/* Use prototype definition here because of type promotion. */
|
||||
static CHAR *
|
||||
#if defined (PROTOTYPES)
|
||||
BRACKMATCH (CHAR *p, U_CHAR test, int flags)
|
||||
#else
|
||||
BRACKMATCH (p, test, flags)
|
||||
CHAR *p;
|
||||
U_CHAR test;
|
||||
int flags;
|
||||
#endif
|
||||
{
|
||||
register CHAR cstart, cend, c;
|
||||
register int not; /* Nonzero if the sense of the character class is inverted. */
|
||||
int brcnt, forcecoll;
|
||||
INT pc;
|
||||
CHAR *savep;
|
||||
|
||||
test = FOLD (test);
|
||||
|
||||
savep = p;
|
||||
|
||||
/* POSIX.2 3.13.1 says that an exclamation mark (`!') shall replace the
|
||||
circumflex (`^') in its role in a `nonmatching list'. A bracket
|
||||
expression starting with an unquoted circumflex character produces
|
||||
unspecified results. This implementation treats the two identically. */
|
||||
if (not = (*p == L('!') || *p == L('^')))
|
||||
++p;
|
||||
|
||||
c = *p++;
|
||||
for (;;)
|
||||
{
|
||||
/* Initialize cstart and cend in case `-' is the last
|
||||
character of the pattern. */
|
||||
cstart = cend = c;
|
||||
forcecoll = 0;
|
||||
|
||||
/* POSIX.2 equivalence class: [=c=]. See POSIX.2 2.8.3.2. Find
|
||||
the end of the equivalence class, move the pattern pointer past
|
||||
it, and check for equivalence. XXX - this handles only
|
||||
single-character equivalence classes, which is wrong, or at
|
||||
least incomplete. */
|
||||
if (c == L('[') && *p == L('=') && p[2] == L('=') && p[3] == L(']'))
|
||||
{
|
||||
pc = FOLD (p[1]);
|
||||
p += 4;
|
||||
if (COLLEQUIV (test, pc))
|
||||
{
|
||||
/*[*/ /* Move past the closing `]', since the first thing we do at
|
||||
the `matched:' label is back p up one. */
|
||||
p++;
|
||||
goto matched;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = *p++;
|
||||
if (c == L('\0'))
|
||||
return ((test == L('[')) ? savep : (CHAR *)0); /*]*/
|
||||
c = FOLD (c);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* POSIX.2 character class expression. See POSIX.2 2.8.3.2. */
|
||||
if (c == L('[') && *p == L(':'))
|
||||
{
|
||||
CHAR *close, *ccname;
|
||||
|
||||
pc = 0; /* make sure invalid char classes don't match. */
|
||||
/* Find end of character class name */
|
||||
for (close = p + 1; *close != '\0'; close++)
|
||||
if (*close == L(':') && *(close+1) == L(']'))
|
||||
break;
|
||||
|
||||
if (*close != L('\0'))
|
||||
{
|
||||
ccname = (CHAR *)malloc ((close - p) * sizeof (CHAR));
|
||||
if (ccname == 0)
|
||||
pc = 0;
|
||||
else
|
||||
{
|
||||
bcopy (p + 1, ccname, (close - p - 1) * sizeof (CHAR));
|
||||
*(ccname + (close - p - 1)) = L('\0');
|
||||
pc = IS_CCLASS (test, (XCHAR *)ccname);
|
||||
}
|
||||
if (pc == -1)
|
||||
pc = 0;
|
||||
else
|
||||
p = close + 2;
|
||||
|
||||
free (ccname);
|
||||
}
|
||||
|
||||
if (pc)
|
||||
{
|
||||
/*[*/ /* Move past the closing `]', since the first thing we do at
|
||||
the `matched:' label is back p up one. */
|
||||
p++;
|
||||
goto matched;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* continue the loop here, since this expression can't be
|
||||
the first part of a range expression. */
|
||||
c = *p++;
|
||||
if (c == L('\0'))
|
||||
return ((test == L('[')) ? savep : (CHAR *)0);
|
||||
else if (c == L(']'))
|
||||
break;
|
||||
c = FOLD (c);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* POSIX.2 collating symbols. See POSIX.2 2.8.3.2. Find the end of
|
||||
the symbol name, make sure it is terminated by `.]', translate
|
||||
the name to a character using the external table, and do the
|
||||
comparison. */
|
||||
if (c == L('[') && *p == L('.'))
|
||||
{
|
||||
p = PARSE_COLLSYM (p, &pc);
|
||||
/* An invalid collating symbol cannot be the first point of a
|
||||
range. If it is, we set cstart to one greater than `test',
|
||||
so any comparisons later will fail. */
|
||||
cstart = (pc == INVALID) ? test + 1 : pc;
|
||||
forcecoll = 1;
|
||||
}
|
||||
|
||||
if (!(flags & FNM_NOESCAPE) && c == L('\\'))
|
||||
{
|
||||
if (*p == '\0')
|
||||
return (CHAR *)0;
|
||||
cstart = cend = *p++;
|
||||
}
|
||||
|
||||
cstart = cend = FOLD (cstart);
|
||||
|
||||
/* POSIX.2 2.8.3.1.2 says: `An expression containing a `[' that
|
||||
is not preceded by a backslash and is not part of a bracket
|
||||
expression produces undefined results.' This implementation
|
||||
treats the `[' as just a character to be matched if there is
|
||||
not a closing `]'. */
|
||||
if (c == L('\0'))
|
||||
return ((test == L('[')) ? savep : (CHAR *)0);
|
||||
|
||||
c = *p++;
|
||||
c = FOLD (c);
|
||||
|
||||
if ((flags & FNM_PATHNAME) && c == L('/'))
|
||||
/* [/] can never match when matching a pathname. */
|
||||
return (CHAR *)0;
|
||||
|
||||
/* This introduces a range, unless the `-' is the last
|
||||
character of the class. Find the end of the range
|
||||
and move past it. */
|
||||
if (c == L('-') && *p != L(']'))
|
||||
{
|
||||
cend = *p++;
|
||||
if (!(flags & FNM_NOESCAPE) && cend == L('\\'))
|
||||
cend = *p++;
|
||||
if (cend == L('\0'))
|
||||
return (CHAR *)0;
|
||||
if (cend == L('[') && *p == L('.'))
|
||||
{
|
||||
p = PARSE_COLLSYM (p, &pc);
|
||||
/* An invalid collating symbol cannot be the second part of a
|
||||
range expression. If we get one, we set cend to one fewer
|
||||
than the test character to make sure the range test fails. */
|
||||
cend = (pc == INVALID) ? test - 1 : pc;
|
||||
forcecoll = 1;
|
||||
}
|
||||
cend = FOLD (cend);
|
||||
|
||||
c = *p++;
|
||||
|
||||
/* POSIX.2 2.8.3.2: ``The ending range point shall collate
|
||||
equal to or higher than the starting range point; otherwise
|
||||
the expression shall be treated as invalid.'' Note that this
|
||||
applies to only the range expression; the rest of the bracket
|
||||
expression is still checked for matches. */
|
||||
if (RANGECMP (cstart, cend, forcecoll) > 0)
|
||||
{
|
||||
if (c == L(']'))
|
||||
break;
|
||||
c = FOLD (c);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (RANGECMP (test, cstart, forcecoll) >= 0 && RANGECMP (test, cend, forcecoll) <= 0)
|
||||
goto matched;
|
||||
|
||||
if (c == L(']'))
|
||||
break;
|
||||
}
|
||||
/* No match. */
|
||||
return (!not ? (CHAR *)0 : p);
|
||||
|
||||
matched:
|
||||
/* Skip the rest of the [...] that already matched. */
|
||||
c = *--p;
|
||||
brcnt = 1;
|
||||
while (brcnt > 0)
|
||||
{
|
||||
/* A `[' without a matching `]' is just another character to match. */
|
||||
if (c == L('\0'))
|
||||
return ((test == L('[')) ? savep : (CHAR *)0);
|
||||
|
||||
c = *p++;
|
||||
if (c == L('[') && (*p == L('=') || *p == L(':') || *p == L('.')))
|
||||
brcnt++;
|
||||
else if (c == L(']'))
|
||||
brcnt--;
|
||||
else if (!(flags & FNM_NOESCAPE) && c == L('\\'))
|
||||
{
|
||||
if (*p == '\0')
|
||||
return (CHAR *)0;
|
||||
/* XXX 1003.2d11 is unclear if this is right. */
|
||||
++p;
|
||||
}
|
||||
}
|
||||
return (not ? (CHAR *)0 : p);
|
||||
}
|
||||
|
||||
#if defined (EXTENDED_GLOB)
|
||||
/* ksh-like extended pattern matching:
|
||||
|
||||
[?*+@!](pat-list)
|
||||
|
||||
where pat-list is a list of one or patterns separated by `|'. Operation
|
||||
is as follows:
|
||||
|
||||
?(patlist) match zero or one of the given patterns
|
||||
*(patlist) match zero or more of the given patterns
|
||||
+(patlist) match one or more of the given patterns
|
||||
@(patlist) match exactly one of the given patterns
|
||||
!(patlist) match anything except one of the given patterns
|
||||
*/
|
||||
|
||||
/* Scan a pattern starting at STRING and ending at END, keeping track of
|
||||
embedded () and []. If DELIM is 0, we scan until a matching `)'
|
||||
because we're scanning a `patlist'. Otherwise, we scan until we see
|
||||
DELIM. In all cases, we never scan past END. The return value is the
|
||||
first character after the matching DELIM or NULL if the pattern is
|
||||
empty or invalid. */
|
||||
/*static*/ CHAR *
|
||||
PATSCAN (string, end, delim)
|
||||
CHAR *string, *end;
|
||||
INT delim;
|
||||
{
|
||||
int pnest, bnest, skip;
|
||||
INT cchar;
|
||||
CHAR *s, c, *bfirst;
|
||||
|
||||
pnest = bnest = skip = 0;
|
||||
cchar = 0;
|
||||
bfirst = NULL;
|
||||
|
||||
if (string == end)
|
||||
return (NULL);
|
||||
|
||||
for (s = string; c = *s; s++)
|
||||
{
|
||||
if (s >= end)
|
||||
return (s);
|
||||
if (skip)
|
||||
{
|
||||
skip = 0;
|
||||
continue;
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case L('\\'):
|
||||
skip = 1;
|
||||
break;
|
||||
|
||||
case L('\0'):
|
||||
return ((CHAR *)NULL);
|
||||
|
||||
/* `[' is not special inside a bracket expression, but it may
|
||||
introduce one of the special POSIX bracket expressions
|
||||
([.SYM.], [=c=], [: ... :]) that needs special handling. */
|
||||
case L('['):
|
||||
if (bnest == 0)
|
||||
{
|
||||
bfirst = s + 1;
|
||||
if (*bfirst == L('!') || *bfirst == L('^'))
|
||||
bfirst++;
|
||||
bnest++;
|
||||
}
|
||||
else if (s[1] == L(':') || s[1] == L('.') || s[1] == L('='))
|
||||
cchar = s[1];
|
||||
break;
|
||||
|
||||
/* `]' is not special if it's the first char (after a leading `!'
|
||||
or `^') in a bracket expression or if it's part of one of the
|
||||
special POSIX bracket expressions ([.SYM.], [=c=], [: ... :]) */
|
||||
case L(']'):
|
||||
if (bnest)
|
||||
{
|
||||
if (cchar && s[-1] == cchar)
|
||||
cchar = 0;
|
||||
else if (s != bfirst)
|
||||
{
|
||||
bnest--;
|
||||
bfirst = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case L('('):
|
||||
if (bnest == 0)
|
||||
pnest++;
|
||||
break;
|
||||
|
||||
case L(')'):
|
||||
if (bnest == 0 && pnest-- <= 0)
|
||||
return ++s;
|
||||
break;
|
||||
|
||||
case L('|'):
|
||||
if (bnest == 0 && pnest == 0 && delim == L('|'))
|
||||
return ++s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Return 0 if dequoted pattern matches S in the current locale. */
|
||||
static int
|
||||
STRCOMPARE (p, pe, s, se)
|
||||
CHAR *p, *pe, *s, *se;
|
||||
{
|
||||
int ret;
|
||||
CHAR c1, c2;
|
||||
int l1, l2;
|
||||
|
||||
l1 = pe - p;
|
||||
l2 = se - s;
|
||||
|
||||
if (l1 != l2)
|
||||
return (FNM_NOMATCH); /* unequal lengths, can't be identical */
|
||||
|
||||
c1 = *pe;
|
||||
c2 = *se;
|
||||
|
||||
if (c1 != 0)
|
||||
*pe = '\0';
|
||||
if (c2 != 0)
|
||||
*se = '\0';
|
||||
|
||||
#if HAVE_MULTIBYTE || defined (HAVE_STRCOLL)
|
||||
ret = STRCOLL ((XCHAR *)p, (XCHAR *)s);
|
||||
#else
|
||||
ret = STRCMP ((XCHAR *)p, (XCHAR *)s);
|
||||
#endif
|
||||
|
||||
if (c1 != 0)
|
||||
*pe = c1;
|
||||
if (c2 != 0)
|
||||
*se = c2;
|
||||
|
||||
return (ret == 0 ? ret : FNM_NOMATCH);
|
||||
}
|
||||
|
||||
/* Match a ksh extended pattern specifier. Return FNM_NOMATCH on failure or
|
||||
0 on success. This is handed the entire rest of the pattern and string
|
||||
the first time an extended pattern specifier is encountered, so it calls
|
||||
gmatch recursively. */
|
||||
static int
|
||||
EXTMATCH (xc, s, se, p, pe, flags)
|
||||
INT xc; /* select which operation */
|
||||
CHAR *s, *se;
|
||||
CHAR *p, *pe;
|
||||
int flags;
|
||||
{
|
||||
CHAR *prest; /* pointer to rest of pattern */
|
||||
CHAR *psub; /* pointer to sub-pattern */
|
||||
CHAR *pnext; /* pointer to next sub-pattern */
|
||||
CHAR *srest; /* pointer to rest of string */
|
||||
int m1, m2, xflags; /* xflags = flags passed to recursive matches */
|
||||
|
||||
#if DEBUG_MATCHING
|
||||
fprintf(stderr, "extmatch: xc = %c\n", xc);
|
||||
fprintf(stderr, "extmatch: s = %s; se = %s\n", s, se);
|
||||
fprintf(stderr, "extmatch: p = %s; pe = %s\n", p, pe);
|
||||
fprintf(stderr, "extmatch: flags = %d\n", flags);
|
||||
#endif
|
||||
|
||||
prest = PATSCAN (p + (*p == L('(')), pe, 0); /* ) */
|
||||
if (prest == 0)
|
||||
/* If PREST is 0, we failed to scan a valid pattern. In this
|
||||
case, we just want to compare the two as strings. */
|
||||
return (STRCOMPARE (p - 1, pe, s, se));
|
||||
|
||||
switch (xc)
|
||||
{
|
||||
case L('+'): /* match one or more occurrences */
|
||||
case L('*'): /* match zero or more occurrences */
|
||||
/* If we can get away with no matches, don't even bother. Just
|
||||
call GMATCH on the rest of the pattern and return success if
|
||||
it succeeds. */
|
||||
if (xc == L('*') && (GMATCH (s, se, prest, pe, flags) == 0))
|
||||
return 0;
|
||||
|
||||
/* OK, we have to do this the hard way. First, we make sure one of
|
||||
the subpatterns matches, then we try to match the rest of the
|
||||
string. */
|
||||
for (psub = p + 1; ; psub = pnext)
|
||||
{
|
||||
pnext = PATSCAN (psub, pe, L('|'));
|
||||
for (srest = s; srest <= se; srest++)
|
||||
{
|
||||
/* Match this substring (S -> SREST) against this
|
||||
subpattern (psub -> pnext - 1) */
|
||||
m1 = GMATCH (s, srest, psub, pnext - 1, flags) == 0;
|
||||
/* OK, we matched a subpattern, so make sure the rest of the
|
||||
string matches the rest of the pattern. Also handle
|
||||
multiple matches of the pattern. */
|
||||
if (m1)
|
||||
{
|
||||
/* if srest > s, we are not at start of string */
|
||||
xflags = (srest > s) ? (flags & ~FNM_PERIOD) : flags;
|
||||
m2 = (GMATCH (srest, se, prest, pe, xflags) == 0) ||
|
||||
(s != srest && GMATCH (srest, se, p - 1, pe, xflags) == 0);
|
||||
}
|
||||
if (m1 && m2)
|
||||
return (0);
|
||||
}
|
||||
if (pnext == prest)
|
||||
break;
|
||||
}
|
||||
return (FNM_NOMATCH);
|
||||
|
||||
case L('?'): /* match zero or one of the patterns */
|
||||
case L('@'): /* match one (or more) of the patterns */
|
||||
/* If we can get away with no matches, don't even bother. Just
|
||||
call gmatch on the rest of the pattern and return success if
|
||||
it succeeds. */
|
||||
if (xc == L('?') && (GMATCH (s, se, prest, pe, flags) == 0))
|
||||
return 0;
|
||||
|
||||
/* OK, we have to do this the hard way. First, we see if one of
|
||||
the subpatterns matches, then, if it does, we try to match the
|
||||
rest of the string. */
|
||||
for (psub = p + 1; ; psub = pnext)
|
||||
{
|
||||
pnext = PATSCAN (psub, pe, L('|'));
|
||||
srest = (prest == pe) ? se : s;
|
||||
for ( ; srest <= se; srest++)
|
||||
{
|
||||
/* if srest > s, we are not at start of string */
|
||||
xflags = (srest > s) ? (flags & ~FNM_PERIOD) : flags;
|
||||
if (GMATCH (s, srest, psub, pnext - 1, flags) == 0 &&
|
||||
GMATCH (srest, se, prest, pe, xflags) == 0)
|
||||
return (0);
|
||||
}
|
||||
if (pnext == prest)
|
||||
break;
|
||||
}
|
||||
return (FNM_NOMATCH);
|
||||
|
||||
case '!': /* match anything *except* one of the patterns */
|
||||
for (srest = s; srest <= se; srest++)
|
||||
{
|
||||
m1 = 0;
|
||||
for (psub = p + 1; ; psub = pnext)
|
||||
{
|
||||
pnext = PATSCAN (psub, pe, L('|'));
|
||||
/* If one of the patterns matches, just bail immediately. */
|
||||
if (m1 = (GMATCH (s, srest, psub, pnext - 1, flags) == 0))
|
||||
break;
|
||||
if (pnext == prest)
|
||||
break;
|
||||
}
|
||||
/* if srest > s, we are not at start of string */
|
||||
xflags = (srest > s) ? (flags & ~FNM_PERIOD) : flags;
|
||||
if (m1 == 0 && GMATCH (srest, se, prest, pe, xflags) == 0)
|
||||
return (0);
|
||||
}
|
||||
return (FNM_NOMATCH);
|
||||
}
|
||||
|
||||
return (FNM_NOMATCH);
|
||||
}
|
||||
#endif /* EXTENDED_GLOB */
|
||||
|
||||
#undef IS_CCLASS
|
||||
#undef FOLD
|
||||
#undef CHAR
|
||||
#undef U_CHAR
|
||||
#undef XCHAR
|
||||
#undef INT
|
||||
#undef INVALID
|
||||
#undef FCT
|
||||
#undef GMATCH
|
||||
#undef COLLSYM
|
||||
#undef PARSE_COLLSYM
|
||||
#undef PATSCAN
|
||||
#undef STRCOMPARE
|
||||
#undef EXTMATCH
|
||||
#undef BRACKMATCH
|
||||
#undef STRCHR
|
||||
#undef STRCOLL
|
||||
#undef STRLEN
|
||||
#undef STRCMP
|
||||
#undef COLLEQUIV
|
||||
#undef RANGECMP
|
||||
#undef L
|
||||
@@ -1,415 +0,0 @@
|
||||
/* strmatch.c -- ksh-like extended pattern matching for the shell and filename
|
||||
globbing. */
|
||||
|
||||
/* Copyright (C) 1991-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 <stdio.h> /* for debugging */
|
||||
|
||||
#include "strmatch.h"
|
||||
#include <chartypes.h>
|
||||
|
||||
#include "bashansi.h"
|
||||
#include "shmbutil.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
/* First, compile `sm_loop.c' for single-byte characters. */
|
||||
#define CHAR unsigned char
|
||||
#define U_CHAR unsigned char
|
||||
#define XCHAR char
|
||||
#define INT int
|
||||
#define L(CS) CS
|
||||
#define INVALID -1
|
||||
|
||||
#undef STREQ
|
||||
#undef STREQN
|
||||
#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0)
|
||||
#define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, n) == 0)
|
||||
|
||||
#ifndef GLOBASCII_DEFAULT
|
||||
# define GLOBASCII_DEFAULT 0
|
||||
#endif
|
||||
|
||||
int glob_asciirange = GLOBASCII_DEFAULT;
|
||||
|
||||
/* We use strcoll(3) for range comparisons in bracket expressions,
|
||||
even though it can have unwanted side effects in locales
|
||||
other than POSIX or US. For instance, in the de locale, [A-Z] matches
|
||||
all characters. If GLOB_ASCIIRANGE is non-zero, and we're not forcing
|
||||
the use of strcoll (e.g., for explicit collating symbols), we use
|
||||
straight ordering as if in the C locale. */
|
||||
|
||||
#if defined (HAVE_STRCOLL)
|
||||
/* Helper function for collating symbol equivalence. */
|
||||
static int
|
||||
rangecmp (c1, c2, forcecoll)
|
||||
int c1, c2;
|
||||
int forcecoll;
|
||||
{
|
||||
static char s1[2] = { ' ', '\0' };
|
||||
static char s2[2] = { ' ', '\0' };
|
||||
int ret;
|
||||
|
||||
/* Eight bits only. Period. */
|
||||
c1 &= 0xFF;
|
||||
c2 &= 0xFF;
|
||||
|
||||
if (c1 == c2)
|
||||
return (0);
|
||||
|
||||
if (forcecoll == 0 && glob_asciirange)
|
||||
return (c1 - c2);
|
||||
|
||||
s1[0] = c1;
|
||||
s2[0] = c2;
|
||||
|
||||
if ((ret = strcoll (s1, s2)) != 0)
|
||||
return ret;
|
||||
return (c1 - c2);
|
||||
}
|
||||
#else /* !HAVE_STRCOLL */
|
||||
# define rangecmp(c1, c2, f) ((int)(c1) - (int)(c2))
|
||||
#endif /* !HAVE_STRCOLL */
|
||||
|
||||
#if defined (HAVE_STRCOLL)
|
||||
static int
|
||||
collequiv (c1, c2)
|
||||
int c1, c2;
|
||||
{
|
||||
return (rangecmp (c1, c2, 1) == 0);
|
||||
}
|
||||
#else
|
||||
# define collequiv(c1, c2) ((c1) == (c2))
|
||||
#endif
|
||||
|
||||
#define _COLLSYM _collsym
|
||||
#define __COLLSYM __collsym
|
||||
#define POSIXCOLL posix_collsyms
|
||||
#include "collsyms.h"
|
||||
|
||||
static int
|
||||
collsym (s, len)
|
||||
CHAR *s;
|
||||
int len;
|
||||
{
|
||||
register struct _collsym *csp;
|
||||
char *x;
|
||||
|
||||
x = (char *)s;
|
||||
for (csp = posix_collsyms; csp->name; csp++)
|
||||
{
|
||||
if (STREQN(csp->name, x, len) && csp->name[len] == '\0')
|
||||
return (csp->code);
|
||||
}
|
||||
if (len == 1)
|
||||
return s[0];
|
||||
return INVALID;
|
||||
}
|
||||
|
||||
/* unibyte character classification */
|
||||
#if !defined (isascii) && !defined (HAVE_ISASCII)
|
||||
# define isascii(c) ((unsigned int)(c) <= 0177)
|
||||
#endif
|
||||
|
||||
enum char_class
|
||||
{
|
||||
CC_NO_CLASS = 0,
|
||||
CC_ASCII, CC_ALNUM, CC_ALPHA, CC_BLANK, CC_CNTRL, CC_DIGIT, CC_GRAPH,
|
||||
CC_LOWER, CC_PRINT, CC_PUNCT, CC_SPACE, CC_UPPER, CC_WORD, CC_XDIGIT
|
||||
};
|
||||
|
||||
static char const *const cclass_name[] =
|
||||
{
|
||||
"",
|
||||
"ascii", "alnum", "alpha", "blank", "cntrl", "digit", "graph",
|
||||
"lower", "print", "punct", "space", "upper", "word", "xdigit"
|
||||
};
|
||||
|
||||
#define N_CHAR_CLASS (sizeof(cclass_name) / sizeof (cclass_name[0]))
|
||||
|
||||
static int
|
||||
is_cclass (c, name)
|
||||
int c;
|
||||
const char *name;
|
||||
{
|
||||
enum char_class char_class = CC_NO_CLASS;
|
||||
int i, result;
|
||||
|
||||
for (i = 1; i < N_CHAR_CLASS; i++)
|
||||
{
|
||||
if (STREQ (name, cclass_name[i]))
|
||||
{
|
||||
char_class = (enum char_class)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (char_class == 0)
|
||||
return -1;
|
||||
|
||||
switch (char_class)
|
||||
{
|
||||
case CC_ASCII:
|
||||
result = isascii (c);
|
||||
break;
|
||||
case CC_ALNUM:
|
||||
result = ISALNUM (c);
|
||||
break;
|
||||
case CC_ALPHA:
|
||||
result = ISALPHA (c);
|
||||
break;
|
||||
case CC_BLANK:
|
||||
result = ISBLANK (c);
|
||||
break;
|
||||
case CC_CNTRL:
|
||||
result = ISCNTRL (c);
|
||||
break;
|
||||
case CC_DIGIT:
|
||||
result = ISDIGIT (c);
|
||||
break;
|
||||
case CC_GRAPH:
|
||||
result = ISGRAPH (c);
|
||||
break;
|
||||
case CC_LOWER:
|
||||
result = ISLOWER (c);
|
||||
break;
|
||||
case CC_PRINT:
|
||||
result = ISPRINT (c);
|
||||
break;
|
||||
case CC_PUNCT:
|
||||
result = ISPUNCT (c);
|
||||
break;
|
||||
case CC_SPACE:
|
||||
result = ISSPACE (c);
|
||||
break;
|
||||
case CC_UPPER:
|
||||
result = ISUPPER (c);
|
||||
break;
|
||||
case CC_WORD:
|
||||
result = (ISALNUM (c) || c == '_');
|
||||
break;
|
||||
case CC_XDIGIT:
|
||||
result = ISXDIGIT (c);
|
||||
break;
|
||||
default:
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Now include `sm_loop.c' for single-byte characters. */
|
||||
/* The result of FOLD is an `unsigned char' */
|
||||
# define FOLD(c) ((flags & FNM_CASEFOLD) \
|
||||
? TOLOWER ((unsigned char)c) \
|
||||
: ((unsigned char)c))
|
||||
|
||||
#define FCT internal_strmatch
|
||||
#define GMATCH gmatch
|
||||
#define COLLSYM collsym
|
||||
#define PARSE_COLLSYM parse_collsym
|
||||
#define BRACKMATCH brackmatch
|
||||
#define PATSCAN glob_patscan
|
||||
#define STRCOMPARE strcompare
|
||||
#define EXTMATCH extmatch
|
||||
#define STRCHR(S, C) strchr((S), (C))
|
||||
#define STRCOLL(S1, S2) strcoll((S1), (S2))
|
||||
#define STRLEN(S) strlen(S)
|
||||
#define STRCMP(S1, S2) strcmp((S1), (S2))
|
||||
#define RANGECMP(C1, C2, F) rangecmp((C1), (C2), (F))
|
||||
#define COLLEQUIV(C1, C2) collequiv((C1), (C2))
|
||||
#define CTYPE_T enum char_class
|
||||
#define IS_CCLASS(C, S) is_cclass((C), (S))
|
||||
#include "sm_loop.c"
|
||||
|
||||
#if HANDLE_MULTIBYTE
|
||||
|
||||
# define CHAR wchar_t
|
||||
# define U_CHAR wint_t
|
||||
# define XCHAR wchar_t
|
||||
# define INT wint_t
|
||||
# define L(CS) L##CS
|
||||
# define INVALID WEOF
|
||||
|
||||
# undef STREQ
|
||||
# undef STREQN
|
||||
# define STREQ(s1, s2) ((wcscmp (s1, s2) == 0))
|
||||
# define STREQN(a, b, n) ((a)[0] == (b)[0] && wcsncmp(a, b, n) == 0)
|
||||
|
||||
extern char *mbsmbchar __P((const char *));
|
||||
|
||||
static int
|
||||
rangecmp_wc (c1, c2, forcecoll)
|
||||
wint_t c1, c2;
|
||||
int forcecoll;
|
||||
{
|
||||
static wchar_t s1[2] = { L' ', L'\0' };
|
||||
static wchar_t s2[2] = { L' ', L'\0' };
|
||||
|
||||
if (c1 == c2)
|
||||
return 0;
|
||||
|
||||
if (forcecoll == 0 && glob_asciirange && c1 <= UCHAR_MAX && c2 <= UCHAR_MAX)
|
||||
return ((int)(c1 - c2));
|
||||
|
||||
s1[0] = c1;
|
||||
s2[0] = c2;
|
||||
|
||||
return (wcscoll (s1, s2));
|
||||
}
|
||||
|
||||
static int
|
||||
collequiv_wc (c, equiv)
|
||||
wint_t c, equiv;
|
||||
{
|
||||
return (c == equiv);
|
||||
}
|
||||
|
||||
/* Helper function for collating symbol. */
|
||||
# define _COLLSYM _collwcsym
|
||||
# define __COLLSYM __collwcsym
|
||||
# define POSIXCOLL posix_collwcsyms
|
||||
# include "collsyms.h"
|
||||
|
||||
static wint_t
|
||||
collwcsym (s, len)
|
||||
wchar_t *s;
|
||||
int len;
|
||||
{
|
||||
register struct _collwcsym *csp;
|
||||
|
||||
for (csp = posix_collwcsyms; csp->name; csp++)
|
||||
{
|
||||
if (STREQN(csp->name, s, len) && csp->name[len] == L'\0')
|
||||
return (csp->code);
|
||||
}
|
||||
if (len == 1)
|
||||
return s[0];
|
||||
return INVALID;
|
||||
}
|
||||
|
||||
static int
|
||||
is_wcclass (wc, name)
|
||||
wint_t wc;
|
||||
wchar_t *name;
|
||||
{
|
||||
char *mbs;
|
||||
mbstate_t state;
|
||||
size_t mbslength;
|
||||
wctype_t desc;
|
||||
int want_word;
|
||||
|
||||
if ((wctype ("ascii") == (wctype_t)0) && (wcscmp (name, L"ascii") == 0))
|
||||
{
|
||||
int c;
|
||||
|
||||
if ((c = wctob (wc)) == EOF)
|
||||
return 0;
|
||||
else
|
||||
return (c <= 0x7F);
|
||||
}
|
||||
|
||||
want_word = (wcscmp (name, L"word") == 0);
|
||||
if (want_word)
|
||||
name = L"alnum";
|
||||
|
||||
memset (&state, '\0', sizeof (mbstate_t));
|
||||
mbs = (char *) malloc (wcslen(name) * MB_CUR_MAX + 1);
|
||||
mbslength = wcsrtombs (mbs, (const wchar_t **)&name, (wcslen(name) * MB_CUR_MAX + 1), &state);
|
||||
|
||||
if (mbslength == (size_t)-1 || mbslength == (size_t)-2)
|
||||
{
|
||||
free (mbs);
|
||||
return -1;
|
||||
}
|
||||
desc = wctype (mbs);
|
||||
free (mbs);
|
||||
|
||||
if (desc == (wctype_t)0)
|
||||
return -1;
|
||||
|
||||
if (want_word)
|
||||
return (iswctype (wc, desc) || wc == L'_');
|
||||
else
|
||||
return (iswctype (wc, desc));
|
||||
}
|
||||
|
||||
/* Now include `sm_loop.c' for multibyte characters. */
|
||||
#define FOLD(c) ((flags & FNM_CASEFOLD) && iswupper (c) ? towlower (c) : (c))
|
||||
#define FCT internal_wstrmatch
|
||||
#define GMATCH gmatch_wc
|
||||
#define COLLSYM collwcsym
|
||||
#define PARSE_COLLSYM parse_collwcsym
|
||||
#define BRACKMATCH brackmatch_wc
|
||||
#define PATSCAN glob_patscan_wc
|
||||
#define STRCOMPARE wscompare
|
||||
#define EXTMATCH extmatch_wc
|
||||
#define STRCHR(S, C) wcschr((S), (C))
|
||||
#define STRCOLL(S1, S2) wcscoll((S1), (S2))
|
||||
#define STRLEN(S) wcslen(S)
|
||||
#define STRCMP(S1, S2) wcscmp((S1), (S2))
|
||||
#define RANGECMP(C1, C2, F) rangecmp_wc((C1), (C2), (F))
|
||||
#define COLLEQUIV(C1, C2) collequiv_wc((C1), (C2))
|
||||
#define CTYPE_T enum char_class
|
||||
#define IS_CCLASS(C, S) is_wcclass((C), (S))
|
||||
#include "sm_loop.c"
|
||||
|
||||
#endif /* HAVE_MULTIBYTE */
|
||||
|
||||
int
|
||||
xstrmatch (pattern, string, flags)
|
||||
char *pattern;
|
||||
char *string;
|
||||
int flags;
|
||||
{
|
||||
#if HANDLE_MULTIBYTE
|
||||
int ret;
|
||||
size_t n;
|
||||
wchar_t *wpattern, *wstring;
|
||||
size_t plen, slen, mplen, mslen;
|
||||
|
||||
if (mbsmbchar (string) == 0 && mbsmbchar (pattern) == 0)
|
||||
return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags));
|
||||
|
||||
if (MB_CUR_MAX == 1)
|
||||
return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags));
|
||||
|
||||
n = xdupmbstowcs (&wpattern, NULL, pattern);
|
||||
if (n == (size_t)-1 || n == (size_t)-2)
|
||||
return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags));
|
||||
|
||||
n = xdupmbstowcs (&wstring, NULL, string);
|
||||
if (n == (size_t)-1 || n == (size_t)-2)
|
||||
{
|
||||
free (wpattern);
|
||||
return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags));
|
||||
}
|
||||
|
||||
ret = internal_wstrmatch (wpattern, wstring, flags);
|
||||
|
||||
free (wpattern);
|
||||
free (wstring);
|
||||
|
||||
return ret;
|
||||
#else
|
||||
return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags));
|
||||
#endif /* !HANDLE_MULTIBYTE */
|
||||
}
|
||||
@@ -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,673 +0,0 @@
|
||||
.\"
|
||||
.\" MAN PAGE COMMENTS to
|
||||
.\"
|
||||
.\" Chet Ramey
|
||||
.\" Information Network Services
|
||||
.\" Case Western Reserve University
|
||||
.\" chet.ramey@case.edu
|
||||
.\"
|
||||
.\" Last Change: Thu Thu Jun 27 10:34:44 EDT 2013
|
||||
.\"
|
||||
.TH HISTORY 3 "2013 June 27" "GNU History 6.3"
|
||||
.\"
|
||||
.\" File Name macro. This used to be `.PN', for Path Name,
|
||||
.\" but Sun doesn't seem to like that very much.
|
||||
.\"
|
||||
.de FN
|
||||
\fI\|\\$1\|\fP
|
||||
..
|
||||
.ds lp \fR\|(\fP
|
||||
.ds rp \fR\|)\fP
|
||||
.\" FnN return-value fun-name N arguments
|
||||
.de Fn1
|
||||
\fI\\$1\fP \fB\\$2\fP \\*(lp\fI\\$3\fP\\*(rp
|
||||
.br
|
||||
..
|
||||
.de Fn2
|
||||
.if t \fI\\$1\fP \fB\\$2\fP \\*(lp\fI\\$3,\|\\$4\fP\\*(rp
|
||||
.if n \fI\\$1\fP \fB\\$2\fP \\*(lp\fI\\$3, \\$4\fP\\*(rp
|
||||
.br
|
||||
..
|
||||
.de Fn3
|
||||
.if t \fI\\$1\fP \fB\\$2\fP \\*(lp\fI\\$3,\|\\$4,\|\\$5\fP\|\\*(rp
|
||||
.if n \fI\\$1\fP \fB\\$2\fP \\*(lp\fI\\$3, \\$4, \\$5\fP\\*(rp
|
||||
.br
|
||||
..
|
||||
.de Vb
|
||||
\fI\\$1\fP \fB\\$2\fP
|
||||
.br
|
||||
..
|
||||
.SH NAME
|
||||
history \- GNU History Library
|
||||
.SH COPYRIGHT
|
||||
.if t The GNU History Library is Copyright \(co 1989-2011 by the Free Software Foundation, Inc.
|
||||
.if n The GNU History Library is Copyright (C) 1989-2011 by the Free Software Foundation, Inc.
|
||||
.SH DESCRIPTION
|
||||
Many programs read input from the user a line at a time. The GNU
|
||||
History library is able to keep track of those lines, associate arbitrary
|
||||
data with each line, and utilize information from previous lines in
|
||||
composing new ones.
|
||||
.PP
|
||||
.SH "HISTORY EXPANSION"
|
||||
.PP
|
||||
The history library supports a history expansion feature that
|
||||
is identical to the history expansion in
|
||||
.BR bash.
|
||||
This section describes what syntax features are available.
|
||||
.PP
|
||||
History expansions introduce words from the history list into
|
||||
the input stream, making it easy to repeat commands, insert the
|
||||
arguments to a previous command into the current input line, or
|
||||
fix errors in previous commands quickly.
|
||||
.PP
|
||||
History expansion is usually performed immediately after a complete line
|
||||
is read.
|
||||
It takes place in two parts.
|
||||
The first is to determine which line from the history list
|
||||
to use during substitution.
|
||||
The second is to select portions of that line for inclusion into
|
||||
the current one.
|
||||
The line selected from the history is the \fIevent\fP,
|
||||
and the portions of that line that are acted upon are \fIwords\fP.
|
||||
Various \fImodifiers\fP are available to manipulate the selected words.
|
||||
The line is broken into words in the same fashion as \fBbash\fP
|
||||
does when reading input,
|
||||
so that several words that would otherwise be separated
|
||||
are considered one word when surrounded by quotes (see the
|
||||
description of \fBhistory_tokenize()\fP below).
|
||||
History expansions are introduced by the appearance of the
|
||||
history expansion character, which is \^\fB!\fP\^ by default.
|
||||
Only backslash (\^\fB\e\fP\^) and single quotes can quote
|
||||
the history expansion character.
|
||||
.SS Event Designators
|
||||
.PP
|
||||
An event designator is a reference to a command line entry in the
|
||||
history list.
|
||||
Unless the reference is absolute, events are relative to the current
|
||||
position in the history list.
|
||||
.PP
|
||||
.PD 0
|
||||
.TP
|
||||
.B !
|
||||
Start a history substitution, except when followed by a
|
||||
.BR blank ,
|
||||
newline, = or (.
|
||||
.TP
|
||||
.B !\fIn\fR
|
||||
Refer to command line
|
||||
.IR n .
|
||||
.TP
|
||||
.B !\-\fIn\fR
|
||||
Refer to the current command minus
|
||||
.IR n .
|
||||
.TP
|
||||
.B !!
|
||||
Refer to the previous command. This is a synonym for `!\-1'.
|
||||
.TP
|
||||
.B !\fIstring\fR
|
||||
Refer to the most recent command
|
||||
preceding the current position in the history list
|
||||
starting with
|
||||
.IR string .
|
||||
.TP
|
||||
.B !?\fIstring\fR\fB[?]\fR
|
||||
Refer to the most recent command
|
||||
preceding the current position in the history list
|
||||
containing
|
||||
.IR string .
|
||||
The trailing \fB?\fP may be omitted if
|
||||
.I string
|
||||
is followed immediately by a newline.
|
||||
.TP
|
||||
.B \d\s+2^\s-2\u\fIstring1\fP\d\s+2^\s-2\u\fIstring2\fP\d\s+2^\s-2\u
|
||||
Quick substitution. Repeat the last command, replacing
|
||||
.I string1
|
||||
with
|
||||
.IR string2 .
|
||||
Equivalent to
|
||||
``!!:s/\fIstring1\fP/\fIstring2\fP/''
|
||||
(see \fBModifiers\fP below).
|
||||
.TP
|
||||
.B !#
|
||||
The entire command line typed so far.
|
||||
.PD
|
||||
.SS Word Designators
|
||||
.PP
|
||||
Word designators are used to select desired words from the event.
|
||||
A
|
||||
.B :
|
||||
separates the event specification from the word designator.
|
||||
It may be omitted if the word designator begins with a
|
||||
.BR ^ ,
|
||||
.BR $ ,
|
||||
.BR * ,
|
||||
.BR \- ,
|
||||
or
|
||||
.BR % .
|
||||
Words are numbered from the beginning of the line,
|
||||
with the first word being denoted by 0 (zero).
|
||||
Words are inserted into the current line separated by single spaces.
|
||||
.PP
|
||||
.PD 0
|
||||
.TP
|
||||
.B 0 (zero)
|
||||
The zeroth word. For the shell, this is the command
|
||||
word.
|
||||
.TP
|
||||
.I n
|
||||
The \fIn\fRth word.
|
||||
.TP
|
||||
.B ^
|
||||
The first argument. That is, word 1.
|
||||
.TP
|
||||
.B $
|
||||
The last word. This is usually the last argument, but will expand to the
|
||||
zeroth word if there is only one word in the line.
|
||||
.TP
|
||||
.B %
|
||||
The word matched by the most recent `?\fIstring\fR?' search.
|
||||
.TP
|
||||
.I x\fB\-\fPy
|
||||
A range of words; `\-\fIy\fR' abbreviates `0\-\fIy\fR'.
|
||||
.TP
|
||||
.B *
|
||||
All of the words but the zeroth. This is a synonym
|
||||
for `\fI1\-$\fP'. It is not an error to use
|
||||
.B *
|
||||
if there is just one
|
||||
word in the event; the empty string is returned in that case.
|
||||
.TP
|
||||
.B x*
|
||||
Abbreviates \fIx\-$\fP.
|
||||
.TP
|
||||
.B x\-
|
||||
Abbreviates \fIx\-$\fP like \fBx*\fP, but omits the last word.
|
||||
.PD
|
||||
.PP
|
||||
If a word designator is supplied without an event specification, the
|
||||
previous command is used as the event.
|
||||
.SS Modifiers
|
||||
.PP
|
||||
After the optional word designator, there may appear a sequence of
|
||||
one or more of the following modifiers, each preceded by a `:'.
|
||||
.PP
|
||||
.PD 0
|
||||
.PP
|
||||
.TP
|
||||
.B h
|
||||
Remove a trailing file name component, leaving only the head.
|
||||
.TP
|
||||
.B t
|
||||
Remove all leading file name components, leaving the tail.
|
||||
.TP
|
||||
.B r
|
||||
Remove a trailing suffix of the form \fI.xxx\fP, leaving the
|
||||
basename.
|
||||
.TP
|
||||
.B e
|
||||
Remove all but the trailing suffix.
|
||||
.TP
|
||||
.B p
|
||||
Print the new command but do not execute it.
|
||||
.TP
|
||||
.B q
|
||||
Quote the substituted words, escaping further substitutions.
|
||||
.TP
|
||||
.B x
|
||||
Quote the substituted words as with
|
||||
.BR q ,
|
||||
but break into words at
|
||||
.B blanks
|
||||
and newlines.
|
||||
.TP
|
||||
.B s/\fIold\fP/\fInew\fP/
|
||||
Substitute
|
||||
.I new
|
||||
for the first occurrence of
|
||||
.I old
|
||||
in the event line. Any delimiter can be used in place of /. The
|
||||
final delimiter is optional if it is the last character of the
|
||||
event line. The delimiter may be quoted in
|
||||
.I old
|
||||
and
|
||||
.I new
|
||||
with a single backslash. If & appears in
|
||||
.IR new ,
|
||||
it is replaced by
|
||||
.IR old .
|
||||
A single backslash will quote the &. If
|
||||
.I old
|
||||
is null, it is set to the last
|
||||
.I old
|
||||
substituted, or, if no previous history substitutions took place,
|
||||
the last
|
||||
.I string
|
||||
in a
|
||||
.B !?\fIstring\fR\fB[?]\fR
|
||||
search.
|
||||
.TP
|
||||
.B &
|
||||
Repeat the previous substitution.
|
||||
.TP
|
||||
.B g
|
||||
Cause changes to be applied over the entire event line. This is
|
||||
used in conjunction with `\fB:s\fP' (e.g., `\fB:gs/\fIold\fP/\fInew\fP/\fR')
|
||||
or `\fB:&\fP'. If used with
|
||||
`\fB:s\fP', any delimiter can be used
|
||||
in place of /, and the final delimiter is optional
|
||||
if it is the last character of the event line.
|
||||
An \fBa\fP may be used as a synonym for \fBg\fP.
|
||||
.TP
|
||||
.B G
|
||||
Apply the following `\fBs\fP' modifier once to each word in the event line.
|
||||
.PD
|
||||
.SH "PROGRAMMING WITH HISTORY FUNCTIONS"
|
||||
This section describes how to use the History library in other programs.
|
||||
.SS Introduction to History
|
||||
.PP
|
||||
The programmer using the History library has available functions
|
||||
for remembering lines on a history list, associating arbitrary data
|
||||
with a line, removing lines from the list, searching through the list
|
||||
for a line containing an arbitrary text string, and referencing any line
|
||||
in the list directly. In addition, a history \fIexpansion\fP function
|
||||
is available which provides for a consistent user interface across
|
||||
different programs.
|
||||
.PP
|
||||
The user using programs written with the History library has the
|
||||
benefit of a consistent user interface with a set of well-known
|
||||
commands for manipulating the text of previous lines and using that text
|
||||
in new commands. The basic history manipulation commands are
|
||||
identical to
|
||||
the history substitution provided by \fBbash\fP.
|
||||
.PP
|
||||
If the programmer desires, he can use the Readline library, which
|
||||
includes some history manipulation by default, and has the added
|
||||
advantage of command line editing.
|
||||
.PP
|
||||
Before declaring any functions using any functionality the History
|
||||
library provides in other code, an application writer should include
|
||||
the file
|
||||
.FN <readline/history.h>
|
||||
in any file that uses the
|
||||
History library's features. It supplies extern declarations for all
|
||||
of the library's public functions and variables, and declares all of
|
||||
the public data structures.
|
||||
|
||||
.SS History Storage
|
||||
.PP
|
||||
The history list is an array of history entries. A history entry is
|
||||
declared as follows:
|
||||
.PP
|
||||
.Vb "typedef void *" histdata_t;
|
||||
.PP
|
||||
.nf
|
||||
typedef struct _hist_entry {
|
||||
char *line;
|
||||
char *timestamp;
|
||||
histdata_t data;
|
||||
} HIST_ENTRY;
|
||||
.fi
|
||||
.PP
|
||||
The history list itself might therefore be declared as
|
||||
.PP
|
||||
.Vb "HIST_ENTRY **" the_history_list;
|
||||
.PP
|
||||
The state of the History library is encapsulated into a single structure:
|
||||
.PP
|
||||
.nf
|
||||
/*
|
||||
* A structure used to pass around the current state of the history.
|
||||
*/
|
||||
typedef struct _hist_state {
|
||||
HIST_ENTRY **entries; /* Pointer to the entries themselves. */
|
||||
int offset; /* The location pointer within this array. */
|
||||
int length; /* Number of elements within this array. */
|
||||
int size; /* Number of slots allocated to this array. */
|
||||
int flags;
|
||||
} HISTORY_STATE;
|
||||
.fi
|
||||
.PP
|
||||
If the flags member includes \fBHS_STIFLED\fP, the history has been
|
||||
stifled.
|
||||
.SH "History Functions"
|
||||
.PP
|
||||
This section describes the calling sequence for the various functions
|
||||
exported by the GNU History library.
|
||||
.SS Initializing History and State Management
|
||||
This section describes functions used to initialize and manage
|
||||
the state of the History library when you want to use the history
|
||||
functions in your program.
|
||||
|
||||
.Fn1 void using_history void
|
||||
Begin a session in which the history functions might be used. This
|
||||
initializes the interactive variables.
|
||||
|
||||
.Fn1 "HISTORY_STATE *" history_get_history_state void
|
||||
Return a structure describing the current state of the input history.
|
||||
|
||||
.Fn1 void history_set_history_state "HISTORY_STATE *state"
|
||||
Set the state of the history list according to \fIstate\fP.
|
||||
|
||||
.SS History List Management
|
||||
|
||||
These functions manage individual entries on the history list, or set
|
||||
parameters managing the list itself.
|
||||
|
||||
.Fn1 void add_history "const char *string"
|
||||
Place \fIstring\fP at the end of the history list. The associated data
|
||||
field (if any) is set to \fBNULL\fP.
|
||||
|
||||
.Fn1 void add_history_time "const char *string"
|
||||
Change the time stamp associated with the most recent history entry to
|
||||
\fIstring\fP.
|
||||
|
||||
.Fn1 "HIST_ENTRY *" remove_history "int which"
|
||||
Remove history entry at offset \fIwhich\fP from the history. The
|
||||
removed element is returned so you can free the line, data,
|
||||
and containing structure.
|
||||
|
||||
.Fn1 "histdata_t" free_history_entry "HIST_ENTRY *histent"
|
||||
Free the history entry \fIhistent\fP and any history library private
|
||||
data associated with it. Returns the application-specific data
|
||||
so the caller can dispose of it.
|
||||
|
||||
.Fn3 "HIST_ENTRY *" replace_history_entry "int which" "const char *line" "histdata_t data"
|
||||
Make the history entry at offset \fIwhich\fP have \fIline\fP and \fIdata\fP.
|
||||
This returns the old entry so the caller can dispose of any
|
||||
application-specific data. In the case
|
||||
of an invalid \fIwhich\fP, a \fBNULL\fP pointer is returned.
|
||||
|
||||
.Fn1 void clear_history "void"
|
||||
Clear the history list by deleting all the entries.
|
||||
|
||||
.Fn1 void stifle_history "int max"
|
||||
Stifle the history list, remembering only the last \fImax\fP entries.
|
||||
|
||||
.Fn1 int unstifle_history "void"
|
||||
Stop stifling the history. This returns the previously-set
|
||||
maximum number of history entries (as set by \fBstifle_history()\fP).
|
||||
history was stifled. The value is positive if the history was
|
||||
stifled, negative if it wasn't.
|
||||
|
||||
.Fn1 int history_is_stifled "void"
|
||||
Returns non-zero if the history is stifled, zero if it is not.
|
||||
|
||||
.SS Information About the History List
|
||||
|
||||
These functions return information about the entire history list or
|
||||
individual list entries.
|
||||
|
||||
.Fn1 "HIST_ENTRY **" history_list "void"
|
||||
Return a \fBNULL\fP terminated array of \fIHIST_ENTRY *\fP which is the
|
||||
current input history. Element 0 of this list is the beginning of time.
|
||||
If there is no history, return \fBNULL\fP.
|
||||
|
||||
.Fn1 int where_history "void"
|
||||
Returns the offset of the current history element.
|
||||
|
||||
.Fn1 "HIST_ENTRY *" current_history "void"
|
||||
Return the history entry at the current position, as determined by
|
||||
\fBwhere_history()\fP. If there is no entry there, return a \fBNULL\fP
|
||||
pointer.
|
||||
|
||||
.Fn1 "HIST_ENTRY *" history_get "int offset"
|
||||
Return the history entry at position \fIoffset\fP, starting from
|
||||
\fBhistory_base\fP.
|
||||
If there is no entry there, or if \fIoffset\fP
|
||||
is greater than the history length, return a \fBNULL\fP pointer.
|
||||
|
||||
.Fn1 "time_t" history_get_time "HIST_ENTRY *"
|
||||
Return the time stamp associated with the history entry passed as the argument.
|
||||
|
||||
.Fn1 int history_total_bytes "void"
|
||||
Return the number of bytes that the primary history entries are using.
|
||||
This function returns the sum of the lengths of all the lines in the
|
||||
history.
|
||||
|
||||
.SS Moving Around the History List
|
||||
|
||||
These functions allow the current index into the history list to be
|
||||
set or changed.
|
||||
|
||||
.Fn1 int history_set_pos "int pos"
|
||||
Set the current history offset to \fIpos\fP, an absolute index
|
||||
into the list.
|
||||
Returns 1 on success, 0 if \fIpos\fP is less than zero or greater
|
||||
than the number of history entries.
|
||||
|
||||
.Fn1 "HIST_ENTRY *" previous_history "void"
|
||||
Back up the current history offset to the previous history entry, and
|
||||
return a pointer to that entry. If there is no previous entry, return
|
||||
a \fBNULL\fP pointer.
|
||||
|
||||
.Fn1 "HIST_ENTRY *" next_history "void"
|
||||
Move the current history offset forward to the next history entry, and
|
||||
return the a pointer to that entry. If there is no next entry, return
|
||||
a \fBNULL\fP pointer.
|
||||
|
||||
.SS Searching the History List
|
||||
|
||||
These functions allow searching of the history list for entries containing
|
||||
a specific string. Searching may be performed both forward and backward
|
||||
from the current history position. The search may be \fIanchored\fP,
|
||||
meaning that the string must match at the beginning of the history entry.
|
||||
|
||||
.Fn2 int history_search "const char *string" "int direction"
|
||||
Search the history for \fIstring\fP, starting at the current history offset.
|
||||
If \fIdirection\fP is less than 0, then the search is through
|
||||
previous entries, otherwise through subsequent entries.
|
||||
If \fIstring\fP is found, then
|
||||
the current history index is set to that history entry, and the value
|
||||
returned is the offset in the line of the entry where
|
||||
\fIstring\fP was found. Otherwise, nothing is changed, and a -1 is
|
||||
returned.
|
||||
|
||||
.Fn2 int history_search_prefix "const char *string" "int direction"
|
||||
Search the history for \fIstring\fP, starting at the current history
|
||||
offset. The search is anchored: matching lines must begin with
|
||||
\fIstring\fP. If \fIdirection\fP is less than 0, then the search is
|
||||
through previous entries, otherwise through subsequent entries.
|
||||
If \fIstring\fP is found, then the
|
||||
current history index is set to that entry, and the return value is 0.
|
||||
Otherwise, nothing is changed, and a -1 is returned.
|
||||
|
||||
.Fn3 int history_search_pos "const char *string" "int direction" "int pos"
|
||||
Search for \fIstring\fP in the history list, starting at \fIpos\fP, an
|
||||
absolute index into the list. If \fIdirection\fP is negative, the search
|
||||
proceeds backward from \fIpos\fP, otherwise forward. Returns the absolute
|
||||
index of the history element where \fIstring\fP was found, or -1 otherwise.
|
||||
|
||||
.SS Managing the History File
|
||||
The History library can read the history from and write it to a file.
|
||||
This section documents the functions for managing a history file.
|
||||
|
||||
.Fn1 int read_history "const char *filename"
|
||||
Add the contents of \fIfilename\fP to the history list, a line at a time.
|
||||
If \fIfilename\fP is \fBNULL\fP, then read from \fI~/.history\fP.
|
||||
Returns 0 if successful, or \fBerrno\fP if not.
|
||||
|
||||
.Fn3 int read_history_range "const char *filename" "int from" "int to"
|
||||
Read a range of lines from \fIfilename\fP, adding them to the history list.
|
||||
Start reading at line \fIfrom\fP and end at \fIto\fP.
|
||||
If \fIfrom\fP is zero, start at the beginning. If \fIto\fP is less than
|
||||
\fIfrom\fP, then read until the end of the file. If \fIfilename\fP is
|
||||
\fBNULL\fP, then read from \fI~/.history\fP. Returns 0 if successful,
|
||||
or \fBerrno\fP if not.
|
||||
|
||||
.Fn1 int write_history "const char *filename"
|
||||
Write the current history to \fIfilename\fP, overwriting \fIfilename\fP
|
||||
if necessary.
|
||||
If \fIfilename\fP is \fBNULL\fP, then write the history list to \fI~/.history\fP.
|
||||
Returns 0 on success, or \fBerrno\fP on a read or write error.
|
||||
|
||||
|
||||
.Fn2 int append_history "int nelements" "const char *filename"
|
||||
Append the last \fInelements\fP of the history list to \fIfilename\fP.
|
||||
If \fIfilename\fP is \fBNULL\fP, then append to \fI~/.history\fP.
|
||||
Returns 0 on success, or \fBerrno\fP on a read or write error.
|
||||
|
||||
.Fn2 int history_truncate_file "const char *filename" "int nlines"
|
||||
Truncate the history file \fIfilename\fP, leaving only the last
|
||||
\fInlines\fP lines.
|
||||
If \fIfilename\fP is \fBNULL\fP, then \fI~/.history\fP is truncated.
|
||||
Returns 0 on success, or \fBerrno\fP on failure.
|
||||
|
||||
.SS History Expansion
|
||||
|
||||
These functions implement history expansion.
|
||||
|
||||
.Fn2 int history_expand "char *string" "char **output"
|
||||
Expand \fIstring\fP, placing the result into \fIoutput\fP, a pointer
|
||||
to a string. Returns:
|
||||
.RS
|
||||
.PD 0
|
||||
.TP
|
||||
0
|
||||
If no expansions took place (or, if the only change in
|
||||
the text was the removal of escape characters preceding the history expansion
|
||||
character);
|
||||
.TP
|
||||
1
|
||||
if expansions did take place;
|
||||
.TP
|
||||
-1
|
||||
if there was an error in expansion;
|
||||
.TP
|
||||
2
|
||||
if the returned line should be displayed, but not executed,
|
||||
as with the \fB:p\fP modifier.
|
||||
.PD
|
||||
.RE
|
||||
If an error ocurred in expansion, then \fIoutput\fP contains a descriptive
|
||||
error message.
|
||||
|
||||
.Fn3 "char *" get_history_event "const char *string" "int *cindex" "int qchar"
|
||||
Returns the text of the history event beginning at \fIstring\fP +
|
||||
\fI*cindex\fP. \fI*cindex\fP is modified to point to after the event
|
||||
specifier. At function entry, \fIcindex\fP points to the index into
|
||||
\fIstring\fP where the history event specification begins. \fIqchar\fP
|
||||
is a character that is allowed to end the event specification in addition
|
||||
to the ``normal'' terminating characters.
|
||||
|
||||
.Fn1 "char **" history_tokenize "const char *string"
|
||||
Return an array of tokens parsed out of \fIstring\fP, much as the
|
||||
shell might.
|
||||
The tokens are split on the characters in the
|
||||
\fBhistory_word_delimiters\fP variable,
|
||||
and shell quoting conventions are obeyed.
|
||||
|
||||
.Fn3 "char *" history_arg_extract "int first" "int last" "const char *string"
|
||||
Extract a string segment consisting of the \fIfirst\fP through \fIlast\fP
|
||||
arguments present in \fIstring\fP. Arguments are split using
|
||||
\fBhistory_tokenize()\fP.
|
||||
|
||||
.SS History Variables
|
||||
|
||||
This section describes the externally-visible variables exported by
|
||||
the GNU History Library.
|
||||
|
||||
.Vb int history_base
|
||||
The logical offset of the first entry in the history list.
|
||||
|
||||
.Vb int history_length
|
||||
The number of entries currently stored in the history list.
|
||||
|
||||
.Vb int history_max_entries
|
||||
The maximum number of history entries. This must be changed using
|
||||
\fBstifle_history()\fP.
|
||||
|
||||
.Vb int history_wite_timestamps
|
||||
If non-zero, timestamps are written to the history file, so they can be
|
||||
preserved between sessions. The default value is 0, meaning that
|
||||
timestamps are not saved.
|
||||
The current timestamp format uses the value of \fIhistory_comment_char\fP
|
||||
to delimit timestamp entries in the history file. If that variable does
|
||||
not have a value (the default), timestamps will not be written.
|
||||
|
||||
.Vb char history_expansion_char
|
||||
The character that introduces a history event. The default is \fB!\fP.
|
||||
Setting this to 0 inhibits history expansion.
|
||||
|
||||
.Vb char history_subst_char
|
||||
The character that invokes word substitution if found at the start of
|
||||
a line. The default is \fB^\fP.
|
||||
|
||||
.Vb char history_comment_char
|
||||
During tokenization, if this character is seen as the first character
|
||||
of a word, then it and all subsequent characters up to a newline are
|
||||
ignored, suppressing history expansion for the remainder of the line.
|
||||
This is disabled by default.
|
||||
|
||||
.Vb "char *" history_word_delimiters
|
||||
The characters that separate tokens for \fBhistory_tokenize()\fP.
|
||||
The default value is \fB"\ \et\en()<>;&|"\fP.
|
||||
|
||||
.Vb "char *" history_no_expand_chars
|
||||
The list of characters which inhibit history expansion if found immediately
|
||||
following \fBhistory_expansion_char\fP. The default is space, tab, newline,
|
||||
\fB\er\fP, and \fB=\fP.
|
||||
|
||||
.Vb "char *" history_search_delimiter_chars
|
||||
The list of additional characters which can delimit a history search
|
||||
string, in addition to space, tab, \fI:\fP and \fI?\fP in the case of
|
||||
a substring search. The default is empty.
|
||||
|
||||
.Vb int history_quotes_inhibit_expansion
|
||||
If non-zero, double-quoted words are not scanned for the history expansion
|
||||
character or the history comment character. The default value is 0.
|
||||
|
||||
.Vb "rl_linebuf_func_t *" history_inhibit_expansion_function
|
||||
This should be set to the address of a function that takes two arguments:
|
||||
a \fBchar *\fP (\fIstring\fP)
|
||||
and an \fBint\fP index into that string (\fIi\fP).
|
||||
It should return a non-zero value if the history expansion starting at
|
||||
\fIstring[i]\fP should not be performed; zero if the expansion should
|
||||
be done.
|
||||
It is intended for use by applications like \fBbash\fP that use the history
|
||||
expansion character for additional purposes.
|
||||
By default, this variable is set to \fBNULL\fP.
|
||||
.SH FILES
|
||||
.PD 0
|
||||
.TP
|
||||
.FN ~/.history
|
||||
Default filename for reading and writing saved history
|
||||
.PD
|
||||
.SH "SEE ALSO"
|
||||
.PD 0
|
||||
.TP
|
||||
\fIThe Gnu Readline Library\fP, Brian Fox and Chet Ramey
|
||||
.TP
|
||||
\fIThe Gnu History Library\fP, Brian Fox and Chet Ramey
|
||||
.TP
|
||||
\fIbash\fP(1)
|
||||
.TP
|
||||
\fIreadline\fP(3)
|
||||
.PD
|
||||
.SH AUTHORS
|
||||
Brian Fox, Free Software Foundation
|
||||
.br
|
||||
bfox@gnu.org
|
||||
.PP
|
||||
Chet Ramey, Case Western Reserve University
|
||||
.br
|
||||
chet.ramey@case.edu
|
||||
.SH BUG REPORTS
|
||||
If you find a bug in the
|
||||
.B history
|
||||
library, you should report it. But first, you should
|
||||
make sure that it really is a bug, and that it appears in the latest
|
||||
version of the
|
||||
.B history
|
||||
library that you have.
|
||||
.PP
|
||||
Once you have determined that a bug actually exists, mail a
|
||||
bug report to \fIbug\-readline\fP@\fIgnu.org\fP.
|
||||
If you have a fix, you are welcome to mail that
|
||||
as well! Suggestions and `philosophical' bug reports may be mailed
|
||||
to \fPbug-readline\fP@\fIgnu.org\fP or posted to the Usenet
|
||||
newsgroup
|
||||
.BR gnu.bash.bug .
|
||||
.PP
|
||||
Comments and bug reports concerning
|
||||
this manual page should be directed to
|
||||
.IR chet.ramey@case.edu .
|
||||
@@ -1,577 +0,0 @@
|
||||
@ignore
|
||||
This file documents the user interface to the GNU History library.
|
||||
|
||||
Copyright (C) 1988-2014 Free Software Foundation, Inc.
|
||||
Authored by Brian Fox and Chet Ramey.
|
||||
|
||||
Permission is granted to make and distribute verbatim copies of this manual
|
||||
provided the copyright notice and this permission notice are preserved on
|
||||
all copies.
|
||||
|
||||
Permission is granted to process this file through Tex and print the
|
||||
results, provided the printed document carries copying permission notice
|
||||
identical to this one except for the removal of this paragraph (this
|
||||
paragraph not being relevant to the printed manual).
|
||||
|
||||
Permission is granted to copy and distribute modified versions of this
|
||||
manual under the conditions for verbatim copying, provided also that the
|
||||
GNU Copyright statement is available to the distributee, and provided that
|
||||
the entire resulting derived work is distributed under the terms of a
|
||||
permission notice identical to this one.
|
||||
|
||||
Permission is granted to copy and distribute translations of this manual
|
||||
into another language, under the above conditions for modified versions.
|
||||
@end ignore
|
||||
|
||||
@node Programming with GNU History
|
||||
@chapter Programming with GNU History
|
||||
|
||||
This chapter describes how to interface programs that you write
|
||||
with the @sc{gnu} History Library.
|
||||
It should be considered a technical guide.
|
||||
For information on the interactive use of @sc{gnu} History, @pxref{Using
|
||||
History Interactively}.
|
||||
|
||||
@menu
|
||||
* Introduction to History:: What is the GNU History library for?
|
||||
* History Storage:: How information is stored.
|
||||
* History Functions:: Functions that you can use.
|
||||
* History Variables:: Variables that control behaviour.
|
||||
* History Programming Example:: Example of using the GNU History Library.
|
||||
@end menu
|
||||
|
||||
@node Introduction to History
|
||||
@section Introduction to History
|
||||
|
||||
Many programs read input from the user a line at a time. The @sc{gnu}
|
||||
History library is able to keep track of those lines, associate arbitrary
|
||||
data with each line, and utilize information from previous lines in
|
||||
composing new ones.
|
||||
|
||||
The programmer using the History library has available functions
|
||||
for remembering lines on a history list, associating arbitrary data
|
||||
with a line, removing lines from the list, searching through the list
|
||||
for a line containing an arbitrary text string, and referencing any line
|
||||
in the list directly. In addition, a history @dfn{expansion} function
|
||||
is available which provides for a consistent user interface across
|
||||
different programs.
|
||||
|
||||
The user using programs written with the History library has the
|
||||
benefit of a consistent user interface with a set of well-known
|
||||
commands for manipulating the text of previous lines and using that text
|
||||
in new commands. The basic history manipulation commands are similar to
|
||||
the history substitution provided by @code{csh}.
|
||||
|
||||
If the programmer desires, he can use the Readline library, which
|
||||
includes some history manipulation by default, and has the added
|
||||
advantage of command line editing.
|
||||
|
||||
Before declaring any functions using any functionality the History
|
||||
library provides in other code, an application writer should include
|
||||
the file @code{<readline/history.h>} in any file that uses the
|
||||
History library's features. It supplies extern declarations for all
|
||||
of the library's public functions and variables, and declares all of
|
||||
the public data structures.
|
||||
|
||||
@node History Storage
|
||||
@section History Storage
|
||||
|
||||
The history list is an array of history entries. A history entry is
|
||||
declared as follows:
|
||||
|
||||
@example
|
||||
typedef void *histdata_t;
|
||||
|
||||
typedef struct _hist_entry @{
|
||||
char *line;
|
||||
char *timestamp;
|
||||
histdata_t data;
|
||||
@} HIST_ENTRY;
|
||||
@end example
|
||||
|
||||
The history list itself might therefore be declared as
|
||||
|
||||
@example
|
||||
HIST_ENTRY **the_history_list;
|
||||
@end example
|
||||
|
||||
The state of the History library is encapsulated into a single structure:
|
||||
|
||||
@example
|
||||
/*
|
||||
* A structure used to pass around the current state of the history.
|
||||
*/
|
||||
typedef struct _hist_state @{
|
||||
HIST_ENTRY **entries; /* Pointer to the entries themselves. */
|
||||
int offset; /* The location pointer within this array. */
|
||||
int length; /* Number of elements within this array. */
|
||||
int size; /* Number of slots allocated to this array. */
|
||||
int flags;
|
||||
@} HISTORY_STATE;
|
||||
@end example
|
||||
|
||||
If the flags member includes @code{HS_STIFLED}, the history has been
|
||||
stifled.
|
||||
|
||||
@node History Functions
|
||||
@section History Functions
|
||||
|
||||
This section describes the calling sequence for the various functions
|
||||
exported by the @sc{gnu} History library.
|
||||
|
||||
@menu
|
||||
* Initializing History and State Management:: Functions to call when you
|
||||
want to use history in a
|
||||
program.
|
||||
* History List Management:: Functions used to manage the list
|
||||
of history entries.
|
||||
* Information About the History List:: Functions returning information about
|
||||
the history list.
|
||||
* Moving Around the History List:: Functions used to change the position
|
||||
in the history list.
|
||||
* Searching the History List:: Functions to search the history list
|
||||
for entries containing a string.
|
||||
* Managing the History File:: Functions that read and write a file
|
||||
containing the history list.
|
||||
* History Expansion:: Functions to perform csh-like history
|
||||
expansion.
|
||||
@end menu
|
||||
|
||||
@node Initializing History and State Management
|
||||
@subsection Initializing History and State Management
|
||||
|
||||
This section describes functions used to initialize and manage
|
||||
the state of the History library when you want to use the history
|
||||
functions in your program.
|
||||
|
||||
@deftypefun void using_history (void)
|
||||
Begin a session in which the history functions might be used. This
|
||||
initializes the interactive variables.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun {HISTORY_STATE *} history_get_history_state (void)
|
||||
Return a structure describing the current state of the input history.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun void history_set_history_state (HISTORY_STATE *state)
|
||||
Set the state of the history list according to @var{state}.
|
||||
@end deftypefun
|
||||
|
||||
@node History List Management
|
||||
@subsection History List Management
|
||||
|
||||
These functions manage individual entries on the history list, or set
|
||||
parameters managing the list itself.
|
||||
|
||||
@deftypefun void add_history (const char *string)
|
||||
Place @var{string} at the end of the history list. The associated data
|
||||
field (if any) is set to @code{NULL}.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun void add_history_time (const char *string)
|
||||
Change the time stamp associated with the most recent history entry to
|
||||
@var{string}.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun {HIST_ENTRY *} remove_history (int which)
|
||||
Remove history entry at offset @var{which} from the history. The
|
||||
removed element is returned so you can free the line, data,
|
||||
and containing structure.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun {histdata_t} free_history_entry (HIST_ENTRY *histent)
|
||||
Free the history entry @var{histent} and any history library private
|
||||
data associated with it. Returns the application-specific data
|
||||
so the caller can dispose of it.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun {HIST_ENTRY *} replace_history_entry (int which, const char *line, histdata_t data)
|
||||
Make the history entry at offset @var{which} have @var{line} and @var{data}.
|
||||
This returns the old entry so the caller can dispose of any
|
||||
application-specific data. In the case
|
||||
of an invalid @var{which}, a @code{NULL} pointer is returned.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun void clear_history (void)
|
||||
Clear the history list by deleting all the entries.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun void stifle_history (int max)
|
||||
Stifle the history list, remembering only the last @var{max} entries.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun int unstifle_history (void)
|
||||
Stop stifling the history. This returns the previously-set
|
||||
maximum number of history entries (as set by @code{stifle_history()}).
|
||||
The value is positive if the history was
|
||||
stifled, negative if it wasn't.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun int history_is_stifled (void)
|
||||
Returns non-zero if the history is stifled, zero if it is not.
|
||||
@end deftypefun
|
||||
|
||||
@node Information About the History List
|
||||
@subsection Information About the History List
|
||||
|
||||
These functions return information about the entire history list or
|
||||
individual list entries.
|
||||
|
||||
@deftypefun {HIST_ENTRY **} history_list (void)
|
||||
Return a @code{NULL} terminated array of @code{HIST_ENTRY *} which is the
|
||||
current input history. Element 0 of this list is the beginning of time.
|
||||
If there is no history, return @code{NULL}.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun int where_history (void)
|
||||
Returns the offset of the current history element.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun {HIST_ENTRY *} current_history (void)
|
||||
Return the history entry at the current position, as determined by
|
||||
@code{where_history()}. If there is no entry there, return a @code{NULL}
|
||||
pointer.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun {HIST_ENTRY *} history_get (int offset)
|
||||
Return the history entry at position @var{offset}, starting from
|
||||
@code{history_base} (@pxref{History Variables}).
|
||||
If there is no entry there, or if @var{offset}
|
||||
is greater than the history length, return a @code{NULL} pointer.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun time_t history_get_time (HIST_ENTRY *entry)
|
||||
Return the time stamp associated with the history entry @var{entry}.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun int history_total_bytes (void)
|
||||
Return the number of bytes that the primary history entries are using.
|
||||
This function returns the sum of the lengths of all the lines in the
|
||||
history.
|
||||
@end deftypefun
|
||||
|
||||
@node Moving Around the History List
|
||||
@subsection Moving Around the History List
|
||||
|
||||
These functions allow the current index into the history list to be
|
||||
set or changed.
|
||||
|
||||
@deftypefun int history_set_pos (int pos)
|
||||
Set the current history offset to @var{pos}, an absolute index
|
||||
into the list.
|
||||
Returns 1 on success, 0 if @var{pos} is less than zero or greater
|
||||
than the number of history entries.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun {HIST_ENTRY *} previous_history (void)
|
||||
Back up the current history offset to the previous history entry, and
|
||||
return a pointer to that entry. If there is no previous entry, return
|
||||
a @code{NULL} pointer.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun {HIST_ENTRY *} next_history (void)
|
||||
Move the current history offset forward to the next history entry, and
|
||||
return the a pointer to that entry. If there is no next entry, return
|
||||
a @code{NULL} pointer.
|
||||
@end deftypefun
|
||||
|
||||
@node Searching the History List
|
||||
@subsection Searching the History List
|
||||
@cindex History Searching
|
||||
|
||||
These functions allow searching of the history list for entries containing
|
||||
a specific string. Searching may be performed both forward and backward
|
||||
from the current history position. The search may be @dfn{anchored},
|
||||
meaning that the string must match at the beginning of the history entry.
|
||||
@cindex anchored search
|
||||
|
||||
@deftypefun int history_search (const char *string, int direction)
|
||||
Search the history for @var{string}, starting at the current history offset.
|
||||
If @var{direction} is less than 0, then the search is through
|
||||
previous entries, otherwise through subsequent entries.
|
||||
If @var{string} is found, then
|
||||
the current history index is set to that history entry, and the value
|
||||
returned is the offset in the line of the entry where
|
||||
@var{string} was found. Otherwise, nothing is changed, and a -1 is
|
||||
returned.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun int history_search_prefix (const char *string, int direction)
|
||||
Search the history for @var{string}, starting at the current history
|
||||
offset. The search is anchored: matching lines must begin with
|
||||
@var{string}. If @var{direction} is less than 0, then the search is
|
||||
through previous entries, otherwise through subsequent entries.
|
||||
If @var{string} is found, then the
|
||||
current history index is set to that entry, and the return value is 0.
|
||||
Otherwise, nothing is changed, and a -1 is returned.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun int history_search_pos (const char *string, int direction, int pos)
|
||||
Search for @var{string} in the history list, starting at @var{pos}, an
|
||||
absolute index into the list. If @var{direction} is negative, the search
|
||||
proceeds backward from @var{pos}, otherwise forward. Returns the absolute
|
||||
index of the history element where @var{string} was found, or -1 otherwise.
|
||||
@end deftypefun
|
||||
|
||||
@node Managing the History File
|
||||
@subsection Managing the History File
|
||||
|
||||
The History library can read the history from and write it to a file.
|
||||
This section documents the functions for managing a history file.
|
||||
|
||||
@deftypefun int read_history (const char *filename)
|
||||
Add the contents of @var{filename} to the history list, a line at a time.
|
||||
If @var{filename} is @code{NULL}, then read from @file{~/.history}.
|
||||
Returns 0 if successful, or @code{errno} if not.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun int read_history_range (const char *filename, int from, int to)
|
||||
Read a range of lines from @var{filename}, adding them to the history list.
|
||||
Start reading at line @var{from} and end at @var{to}.
|
||||
If @var{from} is zero, start at the beginning. If @var{to} is less than
|
||||
@var{from}, then read until the end of the file. If @var{filename} is
|
||||
@code{NULL}, then read from @file{~/.history}. Returns 0 if successful,
|
||||
or @code{errno} if not.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun int write_history (const char *filename)
|
||||
Write the current history to @var{filename}, overwriting @var{filename}
|
||||
if necessary.
|
||||
If @var{filename} is @code{NULL}, then write the history list to
|
||||
@file{~/.history}.
|
||||
Returns 0 on success, or @code{errno} on a read or write error.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun int append_history (int nelements, const char *filename)
|
||||
Append the last @var{nelements} of the history list to @var{filename}.
|
||||
If @var{filename} is @code{NULL}, then append to @file{~/.history}.
|
||||
Returns 0 on success, or @code{errno} on a read or write error.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun int history_truncate_file (const char *filename, int nlines)
|
||||
Truncate the history file @var{filename}, leaving only the last
|
||||
@var{nlines} lines.
|
||||
If @var{filename} is @code{NULL}, then @file{~/.history} is truncated.
|
||||
Returns 0 on success, or @code{errno} on failure.
|
||||
@end deftypefun
|
||||
|
||||
@node History Expansion
|
||||
@subsection History Expansion
|
||||
|
||||
These functions implement history expansion.
|
||||
|
||||
@deftypefun int history_expand (char *string, char **output)
|
||||
Expand @var{string}, placing the result into @var{output}, a pointer
|
||||
to a string (@pxref{History Interaction}). Returns:
|
||||
@table @code
|
||||
@item 0
|
||||
If no expansions took place (or, if the only change in
|
||||
the text was the removal of escape characters preceding the history expansion
|
||||
character);
|
||||
@item 1
|
||||
if expansions did take place;
|
||||
@item -1
|
||||
if there was an error in expansion;
|
||||
@item 2
|
||||
if the returned line should be displayed, but not executed,
|
||||
as with the @code{:p} modifier (@pxref{Modifiers}).
|
||||
@end table
|
||||
|
||||
If an error occurred in expansion, then @var{output} contains a descriptive
|
||||
error message.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun {char *} get_history_event (const char *string, int *cindex, int qchar)
|
||||
Returns the text of the history event beginning at @var{string} +
|
||||
@var{*cindex}. @var{*cindex} is modified to point to after the event
|
||||
specifier. At function entry, @var{cindex} points to the index into
|
||||
@var{string} where the history event specification begins. @var{qchar}
|
||||
is a character that is allowed to end the event specification in addition
|
||||
to the ``normal'' terminating characters.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun {char **} history_tokenize (const char *string)
|
||||
Return an array of tokens parsed out of @var{string}, much as the
|
||||
shell might. The tokens are split on the characters in the
|
||||
@var{history_word_delimiters} variable,
|
||||
and shell quoting conventions are obeyed.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun {char *} history_arg_extract (int first, int last, const char *string)
|
||||
Extract a string segment consisting of the @var{first} through @var{last}
|
||||
arguments present in @var{string}. Arguments are split using
|
||||
@code{history_tokenize}.
|
||||
@end deftypefun
|
||||
|
||||
@node History Variables
|
||||
@section History Variables
|
||||
|
||||
This section describes the externally-visible variables exported by
|
||||
the @sc{gnu} History Library.
|
||||
|
||||
@deftypevar int history_base
|
||||
The logical offset of the first entry in the history list.
|
||||
@end deftypevar
|
||||
|
||||
@deftypevar int history_length
|
||||
The number of entries currently stored in the history list.
|
||||
@end deftypevar
|
||||
|
||||
@deftypevar int history_max_entries
|
||||
The maximum number of history entries. This must be changed using
|
||||
@code{stifle_history()}.
|
||||
@end deftypevar
|
||||
|
||||
@deftypevar int history_write_timestamps
|
||||
If non-zero, timestamps are written to the history file, so they can be
|
||||
preserved between sessions. The default value is 0, meaning that
|
||||
timestamps are not saved.
|
||||
|
||||
The current timestamp format uses the value of @var{history_comment_char}
|
||||
to delimit timestamp entries in the history file. If that variable does
|
||||
not have a value (the default), timestamps will not be written.
|
||||
@end deftypevar
|
||||
|
||||
@deftypevar char history_expansion_char
|
||||
The character that introduces a history event. The default is @samp{!}.
|
||||
Setting this to 0 inhibits history expansion.
|
||||
@end deftypevar
|
||||
|
||||
@deftypevar char history_subst_char
|
||||
The character that invokes word substitution if found at the start of
|
||||
a line. The default is @samp{^}.
|
||||
@end deftypevar
|
||||
|
||||
@deftypevar char history_comment_char
|
||||
During tokenization, if this character is seen as the first character
|
||||
of a word, then it and all subsequent characters up to a newline are
|
||||
ignored, suppressing history expansion for the remainder of the line.
|
||||
This is disabled by default.
|
||||
@end deftypevar
|
||||
|
||||
@deftypevar {char *} history_word_delimiters
|
||||
The characters that separate tokens for @code{history_tokenize()}.
|
||||
The default value is @code{" \t\n()<>;&|"}.
|
||||
@end deftypevar
|
||||
|
||||
@deftypevar {char *} history_search_delimiter_chars
|
||||
The list of additional characters which can delimit a history search
|
||||
string, in addition to space, TAB, @samp{:} and @samp{?} in the case of
|
||||
a substring search. The default is empty.
|
||||
@end deftypevar
|
||||
|
||||
@deftypevar {char *} history_no_expand_chars
|
||||
The list of characters which inhibit history expansion if found immediately
|
||||
following @var{history_expansion_char}. The default is space, tab, newline,
|
||||
carriage return, and @samp{=}.
|
||||
@end deftypevar
|
||||
|
||||
@deftypevar int history_quotes_inhibit_expansion
|
||||
If non-zero, single-quoted words are not scanned for the history expansion
|
||||
character. The default value is 0.
|
||||
@end deftypevar
|
||||
|
||||
@deftypevar {rl_linebuf_func_t *} history_inhibit_expansion_function
|
||||
This should be set to the address of a function that takes two arguments:
|
||||
a @code{char *} (@var{string})
|
||||
and an @code{int} index into that string (@var{i}).
|
||||
It should return a non-zero value if the history expansion starting at
|
||||
@var{string[i]} should not be performed; zero if the expansion should
|
||||
be done.
|
||||
It is intended for use by applications like Bash that use the history
|
||||
expansion character for additional purposes.
|
||||
By default, this variable is set to @code{NULL}.
|
||||
@end deftypevar
|
||||
|
||||
@node History Programming Example
|
||||
@section History Programming Example
|
||||
|
||||
The following program demonstrates simple use of the @sc{gnu} History Library.
|
||||
|
||||
@smallexample
|
||||
#include <stdio.h>
|
||||
#include <readline/history.h>
|
||||
|
||||
main (argc, argv)
|
||||
int argc;
|
||||
char **argv;
|
||||
@{
|
||||
char line[1024], *t;
|
||||
int len, done = 0;
|
||||
|
||||
line[0] = 0;
|
||||
|
||||
using_history ();
|
||||
while (!done)
|
||||
@{
|
||||
printf ("history$ ");
|
||||
fflush (stdout);
|
||||
t = fgets (line, sizeof (line) - 1, stdin);
|
||||
if (t && *t)
|
||||
@{
|
||||
len = strlen (t);
|
||||
if (t[len - 1] == '\n')
|
||||
t[len - 1] = '\0';
|
||||
@}
|
||||
|
||||
if (!t)
|
||||
strcpy (line, "quit");
|
||||
|
||||
if (line[0])
|
||||
@{
|
||||
char *expansion;
|
||||
int result;
|
||||
|
||||
result = history_expand (line, &expansion);
|
||||
if (result)
|
||||
fprintf (stderr, "%s\n", expansion);
|
||||
|
||||
if (result < 0 || result == 2)
|
||||
@{
|
||||
free (expansion);
|
||||
continue;
|
||||
@}
|
||||
|
||||
add_history (expansion);
|
||||
strncpy (line, expansion, sizeof (line) - 1);
|
||||
free (expansion);
|
||||
@}
|
||||
|
||||
if (strcmp (line, "quit") == 0)
|
||||
done = 1;
|
||||
else if (strcmp (line, "save") == 0)
|
||||
write_history ("history_file");
|
||||
else if (strcmp (line, "read") == 0)
|
||||
read_history ("history_file");
|
||||
else if (strcmp (line, "list") == 0)
|
||||
@{
|
||||
register HIST_ENTRY **the_list;
|
||||
register int i;
|
||||
|
||||
the_list = history_list ();
|
||||
if (the_list)
|
||||
for (i = 0; the_list[i]; i++)
|
||||
printf ("%d: %s\n", i + history_base, the_list[i]->line);
|
||||
@}
|
||||
else if (strncmp (line, "delete", 6) == 0)
|
||||
@{
|
||||
int which;
|
||||
if ((sscanf (line + 6, "%d", &which)) == 1)
|
||||
@{
|
||||
HIST_ENTRY *entry = remove_history (which);
|
||||
if (!entry)
|
||||
fprintf (stderr, "No such entry %d\n", which);
|
||||
else
|
||||
@{
|
||||
free (entry->line);
|
||||
free (entry);
|
||||
@}
|
||||
@}
|
||||
else
|
||||
@{
|
||||
fprintf (stderr, "non-numeric arg given to `delete'\n");
|
||||
@}
|
||||
@}
|
||||
@}
|
||||
@}
|
||||
@end smallexample
|
||||
@@ -1,10 +0,0 @@
|
||||
@ignore
|
||||
Copyright (C) 1988-2014 Free Software Foundation, Inc.
|
||||
@end ignore
|
||||
|
||||
@set EDITION 6.3
|
||||
@set VERSION 6.3
|
||||
@set UPDATED 16 May 2014
|
||||
@set UPDATED-MONTH May 2014
|
||||
|
||||
@set LASTCHANGE Fri May 16 09:36:19 EDT 2014
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,609 +0,0 @@
|
||||
/* histfile.c - functions to manipulate the history file. */
|
||||
|
||||
/* Copyright (C) 1989-2010 Free Software Foundation, Inc.
|
||||
|
||||
This file contains the GNU History Library (History), a set of
|
||||
routines for managing the text of previously typed lines.
|
||||
|
||||
History 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.
|
||||
|
||||
History 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 History. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* The goal is to make the implementation transparent, so that you
|
||||
don't have to know what data types are used, just what functions
|
||||
you can call. I think I have done that. */
|
||||
|
||||
#define READLINE_LIBRARY
|
||||
|
||||
#if defined (__TANDEM)
|
||||
# include <floss.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_CONFIG_H)
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#if ! defined (_MINIX) && defined (HAVE_SYS_FILE_H)
|
||||
# include <sys/file.h>
|
||||
#endif
|
||||
#include "posixstat.h"
|
||||
#include <fcntl.h>
|
||||
|
||||
#if defined (HAVE_STDLIB_H)
|
||||
# include <stdlib.h>
|
||||
#else
|
||||
# include "ansi_stdlib.h"
|
||||
#endif /* HAVE_STDLIB_H */
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#if defined (__EMX__)
|
||||
# undef HAVE_MMAP
|
||||
#endif
|
||||
|
||||
#ifdef HISTORY_USE_MMAP
|
||||
# include <sys/mman.h>
|
||||
|
||||
# ifdef MAP_FILE
|
||||
# define MAP_RFLAGS (MAP_FILE|MAP_PRIVATE)
|
||||
# define MAP_WFLAGS (MAP_FILE|MAP_SHARED)
|
||||
# else
|
||||
# define MAP_RFLAGS MAP_PRIVATE
|
||||
# define MAP_WFLAGS MAP_SHARED
|
||||
# endif
|
||||
|
||||
# ifndef MAP_FAILED
|
||||
# define MAP_FAILED ((void *)-1)
|
||||
# endif
|
||||
|
||||
#endif /* HISTORY_USE_MMAP */
|
||||
|
||||
/* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment
|
||||
on win 95/98/nt), we want to open files with O_BINARY mode so that there
|
||||
is no \n -> \r\n conversion performed. On other systems, we don't want to
|
||||
mess around with O_BINARY at all, so we ensure that it's defined to 0. */
|
||||
#if defined (__EMX__) || defined (__CYGWIN__)
|
||||
# ifndef O_BINARY
|
||||
# define O_BINARY 0
|
||||
# endif
|
||||
#else /* !__EMX__ && !__CYGWIN__ */
|
||||
# undef O_BINARY
|
||||
# define O_BINARY 0
|
||||
#endif /* !__EMX__ && !__CYGWIN__ */
|
||||
|
||||
#include <errno.h>
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif /* !errno */
|
||||
|
||||
#include "history.h"
|
||||
#include "histlib.h"
|
||||
|
||||
#include "rlshell.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
/* If non-zero, we write timestamps to the history file in history_do_write() */
|
||||
int history_write_timestamps = 0;
|
||||
|
||||
/* Does S look like the beginning of a history timestamp entry? Placeholder
|
||||
for more extensive tests. */
|
||||
#define HIST_TIMESTAMP_START(s) (*(s) == history_comment_char && isdigit ((s)[1]) )
|
||||
|
||||
/* Return the string that should be used in the place of this
|
||||
filename. This only matters when you don't specify the
|
||||
filename to read_history (), or write_history (). */
|
||||
static char *
|
||||
history_filename (filename)
|
||||
const char *filename;
|
||||
{
|
||||
char *return_val;
|
||||
const char *home;
|
||||
int home_len;
|
||||
|
||||
return_val = filename ? savestring (filename) : (char *)NULL;
|
||||
|
||||
if (return_val)
|
||||
return (return_val);
|
||||
|
||||
home = sh_get_env_value ("HOME");
|
||||
|
||||
if (home == 0)
|
||||
return (NULL);
|
||||
else
|
||||
home_len = strlen (home);
|
||||
|
||||
return_val = (char *)xmalloc (2 + home_len + 8); /* strlen(".history") == 8 */
|
||||
strcpy (return_val, home);
|
||||
return_val[home_len] = '/';
|
||||
#if defined (__MSDOS__)
|
||||
strcpy (return_val + home_len + 1, "_history");
|
||||
#else
|
||||
strcpy (return_val + home_len + 1, ".history");
|
||||
#endif
|
||||
|
||||
return (return_val);
|
||||
}
|
||||
|
||||
static char *
|
||||
history_backupfile (filename)
|
||||
const char *filename;
|
||||
{
|
||||
char *ret;
|
||||
size_t len;
|
||||
|
||||
len = strlen (filename);
|
||||
ret = xmalloc (len + 2);
|
||||
strcpy (ret, filename);
|
||||
ret[len] = '-';
|
||||
ret[len+1] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Add the contents of FILENAME to the history list, a line at a time.
|
||||
If FILENAME is NULL, then read from ~/.history. Returns 0 if
|
||||
successful, or errno if not. */
|
||||
int
|
||||
read_history (filename)
|
||||
const char *filename;
|
||||
{
|
||||
return (read_history_range (filename, 0, -1));
|
||||
}
|
||||
|
||||
/* Read a range of lines from FILENAME, adding them to the history list.
|
||||
Start reading at the FROM'th line and end at the TO'th. If FROM
|
||||
is zero, start at the beginning. If TO is less than FROM, read
|
||||
until the end of the file. If FILENAME is NULL, then read from
|
||||
~/.history. Returns 0 if successful, or errno if not. */
|
||||
int
|
||||
read_history_range (filename, from, to)
|
||||
const char *filename;
|
||||
int from, to;
|
||||
{
|
||||
register char *line_start, *line_end, *p;
|
||||
char *input, *buffer, *bufend, *last_ts;
|
||||
int file, current_line, chars_read;
|
||||
struct stat finfo;
|
||||
size_t file_size;
|
||||
#if defined (EFBIG)
|
||||
int overflow_errno = EFBIG;
|
||||
#elif defined (EOVERFLOW)
|
||||
int overflow_errno = EOVERFLOW;
|
||||
#else
|
||||
int overflow_errno = EIO;
|
||||
#endif
|
||||
|
||||
buffer = last_ts = (char *)NULL;
|
||||
input = history_filename (filename);
|
||||
file = input ? open (input, O_RDONLY|O_BINARY, 0666) : -1;
|
||||
|
||||
if ((file < 0) || (fstat (file, &finfo) == -1))
|
||||
goto error_and_exit;
|
||||
|
||||
file_size = (size_t)finfo.st_size;
|
||||
|
||||
/* check for overflow on very large files */
|
||||
if (file_size != finfo.st_size || file_size + 1 < file_size)
|
||||
{
|
||||
errno = overflow_errno;
|
||||
goto error_and_exit;
|
||||
}
|
||||
|
||||
#ifdef HISTORY_USE_MMAP
|
||||
/* We map read/write and private so we can change newlines to NULs without
|
||||
affecting the underlying object. */
|
||||
buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
|
||||
if ((void *)buffer == MAP_FAILED)
|
||||
{
|
||||
errno = overflow_errno;
|
||||
goto error_and_exit;
|
||||
}
|
||||
chars_read = file_size;
|
||||
#else
|
||||
buffer = (char *)malloc (file_size + 1);
|
||||
if (buffer == 0)
|
||||
{
|
||||
errno = overflow_errno;
|
||||
goto error_and_exit;
|
||||
}
|
||||
|
||||
chars_read = read (file, buffer, file_size);
|
||||
#endif
|
||||
if (chars_read < 0)
|
||||
{
|
||||
error_and_exit:
|
||||
if (errno != 0)
|
||||
chars_read = errno;
|
||||
else
|
||||
chars_read = EIO;
|
||||
if (file >= 0)
|
||||
close (file);
|
||||
|
||||
FREE (input);
|
||||
#ifndef HISTORY_USE_MMAP
|
||||
FREE (buffer);
|
||||
#endif
|
||||
|
||||
return (chars_read);
|
||||
}
|
||||
|
||||
close (file);
|
||||
|
||||
/* Set TO to larger than end of file if negative. */
|
||||
if (to < 0)
|
||||
to = chars_read;
|
||||
|
||||
/* Start at beginning of file, work to end. */
|
||||
bufend = buffer + chars_read;
|
||||
current_line = 0;
|
||||
|
||||
/* Skip lines until we are at FROM. */
|
||||
for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
|
||||
if (*line_end == '\n')
|
||||
{
|
||||
p = line_end + 1;
|
||||
/* If we see something we think is a timestamp, continue with this
|
||||
line. We should check more extensively here... */
|
||||
if (HIST_TIMESTAMP_START(p) == 0)
|
||||
current_line++;
|
||||
line_start = p;
|
||||
}
|
||||
|
||||
/* If there are lines left to gobble, then gobble them now. */
|
||||
for (line_end = line_start; line_end < bufend; line_end++)
|
||||
if (*line_end == '\n')
|
||||
{
|
||||
/* Change to allow Windows-like \r\n end of line delimiter. */
|
||||
if (line_end > line_start && line_end[-1] == '\r')
|
||||
line_end[-1] = '\0';
|
||||
else
|
||||
*line_end = '\0';
|
||||
|
||||
if (*line_start)
|
||||
{
|
||||
if (HIST_TIMESTAMP_START(line_start) == 0)
|
||||
{
|
||||
add_history (line_start);
|
||||
if (last_ts)
|
||||
{
|
||||
add_history_time (last_ts);
|
||||
last_ts = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
last_ts = line_start;
|
||||
current_line--;
|
||||
}
|
||||
}
|
||||
|
||||
current_line++;
|
||||
|
||||
if (current_line >= to)
|
||||
break;
|
||||
|
||||
line_start = line_end + 1;
|
||||
}
|
||||
|
||||
FREE (input);
|
||||
#ifndef HISTORY_USE_MMAP
|
||||
FREE (buffer);
|
||||
#else
|
||||
munmap (buffer, file_size);
|
||||
#endif
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Truncate the history file FNAME, leaving only LINES trailing lines.
|
||||
If FNAME is NULL, then use ~/.history. Returns 0 on success, errno
|
||||
on failure. */
|
||||
int
|
||||
history_truncate_file (fname, lines)
|
||||
const char *fname;
|
||||
int lines;
|
||||
{
|
||||
char *buffer, *filename, *bakname, *bp, *bp1; /* bp1 == bp+1 */
|
||||
int file, chars_read, rv;
|
||||
struct stat finfo;
|
||||
size_t file_size;
|
||||
|
||||
buffer = (char *)NULL;
|
||||
filename = history_filename (fname);
|
||||
bakname = 0;
|
||||
file = filename ? open (filename, O_RDONLY|O_BINARY, 0666) : -1;
|
||||
rv = 0;
|
||||
|
||||
/* Don't try to truncate non-regular files. */
|
||||
if (file == -1 || fstat (file, &finfo) == -1)
|
||||
{
|
||||
rv = errno;
|
||||
if (file != -1)
|
||||
close (file);
|
||||
goto truncate_exit;
|
||||
}
|
||||
|
||||
if (S_ISREG (finfo.st_mode) == 0)
|
||||
{
|
||||
close (file);
|
||||
#ifdef EFTYPE
|
||||
rv = EFTYPE;
|
||||
#else
|
||||
rv = EINVAL;
|
||||
#endif
|
||||
goto truncate_exit;
|
||||
}
|
||||
|
||||
file_size = (size_t)finfo.st_size;
|
||||
|
||||
/* check for overflow on very large files */
|
||||
if (file_size != finfo.st_size || file_size + 1 < file_size)
|
||||
{
|
||||
close (file);
|
||||
#if defined (EFBIG)
|
||||
rv = errno = EFBIG;
|
||||
#elif defined (EOVERFLOW)
|
||||
rv = errno = EOVERFLOW;
|
||||
#else
|
||||
rv = errno = EINVAL;
|
||||
#endif
|
||||
goto truncate_exit;
|
||||
}
|
||||
|
||||
buffer = (char *)malloc (file_size + 1);
|
||||
if (buffer == 0)
|
||||
{
|
||||
rv = errno;
|
||||
close (file);
|
||||
goto truncate_exit;
|
||||
}
|
||||
|
||||
chars_read = read (file, buffer, file_size);
|
||||
close (file);
|
||||
|
||||
if (chars_read <= 0)
|
||||
{
|
||||
rv = (chars_read < 0) ? errno : 0;
|
||||
goto truncate_exit;
|
||||
}
|
||||
|
||||
/* Count backwards from the end of buffer until we have passed
|
||||
LINES lines. bp1 is set funny initially. But since bp[1] can't
|
||||
be a comment character (since it's off the end) and *bp can't be
|
||||
both a newline and the history comment character, it should be OK. */
|
||||
for (bp1 = bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
|
||||
{
|
||||
if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
|
||||
lines--;
|
||||
bp1 = bp;
|
||||
}
|
||||
|
||||
/* If this is the first line, then the file contains exactly the
|
||||
number of lines we want to truncate to, so we don't need to do
|
||||
anything. It's the first line if we don't find a newline between
|
||||
the current value of i and 0. Otherwise, write from the start of
|
||||
this line until the end of the buffer. */
|
||||
for ( ; bp > buffer; bp--)
|
||||
{
|
||||
if (*bp == '\n' && HIST_TIMESTAMP_START(bp1) == 0)
|
||||
{
|
||||
bp++;
|
||||
break;
|
||||
}
|
||||
bp1 = bp;
|
||||
}
|
||||
|
||||
/* Write only if there are more lines in the file than we want to
|
||||
truncate to. */
|
||||
if (bp <= buffer)
|
||||
{
|
||||
rv = 0;
|
||||
goto truncate_exit;
|
||||
}
|
||||
|
||||
bakname = history_backupfile (filename);
|
||||
if (filename && bakname)
|
||||
rename (filename, bakname);
|
||||
|
||||
if ((file = open (filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600)) != -1)
|
||||
{
|
||||
if (write (file, bp, chars_read - (bp - buffer)) < 0)
|
||||
{
|
||||
rv = errno;
|
||||
rename (bakname, filename);
|
||||
}
|
||||
|
||||
#if defined (__BEOS__)
|
||||
/* BeOS ignores O_TRUNC. */
|
||||
ftruncate (file, chars_read - (bp - buffer));
|
||||
#endif
|
||||
|
||||
if (close (file) < 0 && rv == 0)
|
||||
{
|
||||
rv = errno;
|
||||
rename (bakname, filename);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = errno;
|
||||
rename (bakname, filename);
|
||||
}
|
||||
|
||||
truncate_exit:
|
||||
|
||||
FREE (buffer);
|
||||
|
||||
xfree (filename);
|
||||
FREE (bakname);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* Workhorse function for writing history. Writes NELEMENT entries
|
||||
from the history list to FILENAME. OVERWRITE is non-zero if you
|
||||
wish to replace FILENAME with the entries. */
|
||||
static int
|
||||
history_do_write (filename, nelements, overwrite)
|
||||
const char *filename;
|
||||
int nelements, overwrite;
|
||||
{
|
||||
register int i;
|
||||
char *output, *bakname;
|
||||
int file, mode, rv;
|
||||
#ifdef HISTORY_USE_MMAP
|
||||
size_t cursize;
|
||||
|
||||
mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
|
||||
#else
|
||||
mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
|
||||
#endif
|
||||
output = history_filename (filename);
|
||||
bakname = (overwrite && output) ? history_backupfile (output) : 0;
|
||||
|
||||
if (output && bakname)
|
||||
rename (output, bakname);
|
||||
|
||||
file = output ? open (output, mode, 0600) : -1;
|
||||
rv = 0;
|
||||
|
||||
if (file == -1)
|
||||
{
|
||||
rv = errno;
|
||||
if (output && bakname)
|
||||
rename (bakname, output);
|
||||
FREE (output);
|
||||
FREE (bakname);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
#ifdef HISTORY_USE_MMAP
|
||||
cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
|
||||
#endif
|
||||
|
||||
if (nelements > history_length)
|
||||
nelements = history_length;
|
||||
|
||||
/* Build a buffer of all the lines to write, and write them in one syscall.
|
||||
Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
|
||||
{
|
||||
HIST_ENTRY **the_history; /* local */
|
||||
register int j;
|
||||
int buffer_size;
|
||||
char *buffer;
|
||||
|
||||
the_history = history_list ();
|
||||
/* Calculate the total number of bytes to write. */
|
||||
for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
|
||||
#if 0
|
||||
buffer_size += 2 + HISTENT_BYTES (the_history[i]);
|
||||
#else
|
||||
{
|
||||
if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
|
||||
buffer_size += strlen (the_history[i]->timestamp) + 1;
|
||||
buffer_size += strlen (the_history[i]->line) + 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Allocate the buffer, and fill it. */
|
||||
#ifdef HISTORY_USE_MMAP
|
||||
if (ftruncate (file, buffer_size+cursize) == -1)
|
||||
goto mmap_error;
|
||||
buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
|
||||
if ((void *)buffer == MAP_FAILED)
|
||||
{
|
||||
mmap_error:
|
||||
rv = errno;
|
||||
close (file);
|
||||
if (output && bakname)
|
||||
rename (bakname, output);
|
||||
FREE (output);
|
||||
FREE (bakname);
|
||||
return rv;
|
||||
}
|
||||
#else
|
||||
buffer = (char *)malloc (buffer_size);
|
||||
if (buffer == 0)
|
||||
{
|
||||
rv = errno;
|
||||
close (file);
|
||||
if (output && bakname)
|
||||
rename (bakname, output);
|
||||
FREE (output);
|
||||
FREE (bakname);
|
||||
return rv;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (j = 0, i = history_length - nelements; i < history_length; i++)
|
||||
{
|
||||
if (history_write_timestamps && the_history[i]->timestamp && the_history[i]->timestamp[0])
|
||||
{
|
||||
strcpy (buffer + j, the_history[i]->timestamp);
|
||||
j += strlen (the_history[i]->timestamp);
|
||||
buffer[j++] = '\n';
|
||||
}
|
||||
strcpy (buffer + j, the_history[i]->line);
|
||||
j += strlen (the_history[i]->line);
|
||||
buffer[j++] = '\n';
|
||||
}
|
||||
|
||||
#ifdef HISTORY_USE_MMAP
|
||||
if (msync (buffer, buffer_size, MS_ASYNC) != 0 || munmap (buffer, buffer_size) != 0)
|
||||
rv = errno;
|
||||
#else
|
||||
if (write (file, buffer, buffer_size) < 0)
|
||||
rv = errno;
|
||||
xfree (buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (close (file) < 0 && rv == 0)
|
||||
rv = errno;
|
||||
|
||||
if (rv != 0 && output && bakname)
|
||||
rename (bakname, output);
|
||||
else if (rv == 0 && bakname)
|
||||
unlink (bakname);
|
||||
|
||||
FREE (output);
|
||||
FREE (bakname);
|
||||
|
||||
return (rv);
|
||||
}
|
||||
|
||||
/* Append NELEMENT entries to FILENAME. The entries appended are from
|
||||
the end of the list minus NELEMENTs up to the end of the list. */
|
||||
int
|
||||
append_history (nelements, filename)
|
||||
int nelements;
|
||||
const char *filename;
|
||||
{
|
||||
return (history_do_write (filename, nelements, HISTORY_APPEND));
|
||||
}
|
||||
|
||||
/* Overwrite FILENAME with the current history. If FILENAME is NULL,
|
||||
then write the history list to ~/.history. Values returned
|
||||
are as in read_history ().*/
|
||||
int
|
||||
write_history (filename)
|
||||
const char *filename;
|
||||
{
|
||||
return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
|
||||
}
|
||||
@@ -1,693 +0,0 @@
|
||||
/* misc.c -- miscellaneous bindable readline functions. */
|
||||
|
||||
/* Copyright (C) 1987-2012 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
|
||||
|
||||
#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 */
|
||||
|
||||
#if defined (HAVE_LOCALE_H)
|
||||
# include <locale.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* System-specific feature definitions and include files. */
|
||||
#include "rldefs.h"
|
||||
#include "rlmbutil.h"
|
||||
|
||||
/* Some standard library routines. */
|
||||
#include "readline.h"
|
||||
#include "history.h"
|
||||
|
||||
#include "rlprivate.h"
|
||||
#include "rlshell.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
static int rl_digit_loop PARAMS((void));
|
||||
static void _rl_history_set_point PARAMS((void));
|
||||
|
||||
extern int history_offset;
|
||||
|
||||
/* Forward declarations used in this file */
|
||||
void _rl_free_history_entry PARAMS((HIST_ENTRY *));
|
||||
|
||||
/* If non-zero, rl_get_previous_history and rl_get_next_history attempt
|
||||
to preserve the value of rl_point from line to line. */
|
||||
int _rl_history_preserve_point = 0;
|
||||
|
||||
_rl_arg_cxt _rl_argcxt;
|
||||
|
||||
/* Saved target point for when _rl_history_preserve_point is set. Special
|
||||
value of -1 means that point is at the end of the line. */
|
||||
int _rl_history_saved_point = -1;
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Numeric Arguments */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
int
|
||||
_rl_arg_overflow ()
|
||||
{
|
||||
if (rl_numeric_arg > 1000000)
|
||||
{
|
||||
_rl_argcxt = 0;
|
||||
rl_explicit_arg = rl_numeric_arg = 0;
|
||||
rl_ding ();
|
||||
rl_restore_prompt ();
|
||||
rl_clear_message ();
|
||||
RL_UNSETSTATE(RL_STATE_NUMERICARG);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
_rl_arg_init ()
|
||||
{
|
||||
rl_save_prompt ();
|
||||
_rl_argcxt = 0;
|
||||
RL_SETSTATE(RL_STATE_NUMERICARG);
|
||||
}
|
||||
|
||||
int
|
||||
_rl_arg_getchar ()
|
||||
{
|
||||
int c;
|
||||
|
||||
rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg);
|
||||
RL_SETSTATE(RL_STATE_MOREINPUT);
|
||||
c = rl_read_key ();
|
||||
RL_UNSETSTATE(RL_STATE_MOREINPUT);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Process C as part of the current numeric argument. Return -1 if the
|
||||
argument should be aborted, 0 if we should not read any more chars, and
|
||||
1 if we should continue to read chars. */
|
||||
int
|
||||
_rl_arg_dispatch (cxt, c)
|
||||
_rl_arg_cxt cxt;
|
||||
int c;
|
||||
{
|
||||
int key, r;
|
||||
|
||||
key = c;
|
||||
|
||||
/* If we see a key bound to `universal-argument' after seeing digits,
|
||||
it ends the argument but is otherwise ignored. */
|
||||
if (_rl_keymap[c].type == ISFUNC && _rl_keymap[c].function == rl_universal_argument)
|
||||
{
|
||||
if ((cxt & NUM_SAWDIGITS) == 0)
|
||||
{
|
||||
rl_numeric_arg *= 4;
|
||||
return 1;
|
||||
}
|
||||
else if (RL_ISSTATE (RL_STATE_CALLBACK))
|
||||
{
|
||||
_rl_argcxt |= NUM_READONE;
|
||||
return 0; /* XXX */
|
||||
}
|
||||
else
|
||||
{
|
||||
RL_SETSTATE(RL_STATE_MOREINPUT);
|
||||
key = rl_read_key ();
|
||||
RL_UNSETSTATE(RL_STATE_MOREINPUT);
|
||||
rl_restore_prompt ();
|
||||
rl_clear_message ();
|
||||
RL_UNSETSTATE(RL_STATE_NUMERICARG);
|
||||
if (key < 0)
|
||||
return -1;
|
||||
return (_rl_dispatch (key, _rl_keymap));
|
||||
}
|
||||
}
|
||||
|
||||
c = UNMETA (c);
|
||||
|
||||
if (_rl_digit_p (c))
|
||||
{
|
||||
r = _rl_digit_value (c);
|
||||
rl_numeric_arg = rl_explicit_arg ? (rl_numeric_arg * 10) + r : r;
|
||||
rl_explicit_arg = 1;
|
||||
_rl_argcxt |= NUM_SAWDIGITS;
|
||||
}
|
||||
else if (c == '-' && rl_explicit_arg == 0)
|
||||
{
|
||||
rl_numeric_arg = 1;
|
||||
_rl_argcxt |= NUM_SAWMINUS;
|
||||
rl_arg_sign = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Make M-- command equivalent to M--1 command. */
|
||||
if ((_rl_argcxt & NUM_SAWMINUS) && rl_numeric_arg == 1 && rl_explicit_arg == 0)
|
||||
rl_explicit_arg = 1;
|
||||
rl_restore_prompt ();
|
||||
rl_clear_message ();
|
||||
RL_UNSETSTATE(RL_STATE_NUMERICARG);
|
||||
|
||||
r = _rl_dispatch (key, _rl_keymap);
|
||||
if (RL_ISSTATE (RL_STATE_CALLBACK))
|
||||
{
|
||||
/* At worst, this will cause an extra redisplay. Otherwise,
|
||||
we have to wait until the next character comes in. */
|
||||
if (rl_done == 0)
|
||||
(*rl_redisplay_function) ();
|
||||
r = 0;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Handle C-u style numeric args, as well as M--, and M-digits. */
|
||||
static int
|
||||
rl_digit_loop ()
|
||||
{
|
||||
int c, r;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (_rl_arg_overflow ())
|
||||
return 1;
|
||||
|
||||
c = _rl_arg_getchar ();
|
||||
|
||||
if (c < 0)
|
||||
{
|
||||
_rl_abort_internal ();
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = _rl_arg_dispatch (_rl_argcxt, c);
|
||||
if (r <= 0 || (RL_ISSTATE (RL_STATE_NUMERICARG) == 0))
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Create a default argument. */
|
||||
void
|
||||
_rl_reset_argument ()
|
||||
{
|
||||
rl_numeric_arg = rl_arg_sign = 1;
|
||||
rl_explicit_arg = 0;
|
||||
_rl_argcxt = 0;
|
||||
}
|
||||
|
||||
/* Start a numeric argument with initial value KEY */
|
||||
int
|
||||
rl_digit_argument (ignore, key)
|
||||
int ignore, key;
|
||||
{
|
||||
_rl_arg_init ();
|
||||
if (RL_ISSTATE (RL_STATE_CALLBACK))
|
||||
{
|
||||
_rl_arg_dispatch (_rl_argcxt, key);
|
||||
rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rl_execute_next (key);
|
||||
return (rl_digit_loop ());
|
||||
}
|
||||
}
|
||||
|
||||
/* C-u, universal argument. Multiply the current argument by 4.
|
||||
Read a key. If the key has nothing to do with arguments, then
|
||||
dispatch on it. If the key is the abort character then abort. */
|
||||
int
|
||||
rl_universal_argument (count, key)
|
||||
int count, key;
|
||||
{
|
||||
_rl_arg_init ();
|
||||
rl_numeric_arg *= 4;
|
||||
|
||||
return (RL_ISSTATE (RL_STATE_CALLBACK) ? 0 : rl_digit_loop ());
|
||||
}
|
||||
|
||||
int
|
||||
_rl_arg_callback (cxt)
|
||||
_rl_arg_cxt cxt;
|
||||
{
|
||||
int c, r;
|
||||
|
||||
c = _rl_arg_getchar ();
|
||||
|
||||
if (_rl_argcxt & NUM_READONE)
|
||||
{
|
||||
_rl_argcxt &= ~NUM_READONE;
|
||||
rl_restore_prompt ();
|
||||
rl_clear_message ();
|
||||
RL_UNSETSTATE(RL_STATE_NUMERICARG);
|
||||
rl_execute_next (c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = _rl_arg_dispatch (cxt, c);
|
||||
return (r != 1);
|
||||
}
|
||||
|
||||
/* What to do when you abort reading an argument. */
|
||||
int
|
||||
rl_discard_argument ()
|
||||
{
|
||||
rl_ding ();
|
||||
rl_clear_message ();
|
||||
_rl_reset_argument ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* History Utilities */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* We already have a history library, and that is what we use to control
|
||||
the history features of readline. This is our local interface to
|
||||
the history mechanism. */
|
||||
|
||||
/* While we are editing the history, this is the saved
|
||||
version of the original line. */
|
||||
HIST_ENTRY *_rl_saved_line_for_history = (HIST_ENTRY *)NULL;
|
||||
|
||||
/* Set the history pointer back to the last entry in the history. */
|
||||
void
|
||||
_rl_start_using_history ()
|
||||
{
|
||||
using_history ();
|
||||
if (_rl_saved_line_for_history)
|
||||
_rl_free_history_entry (_rl_saved_line_for_history);
|
||||
|
||||
_rl_saved_line_for_history = (HIST_ENTRY *)NULL;
|
||||
}
|
||||
|
||||
/* Free the contents (and containing structure) of a HIST_ENTRY. */
|
||||
void
|
||||
_rl_free_history_entry (entry)
|
||||
HIST_ENTRY *entry;
|
||||
{
|
||||
if (entry == 0)
|
||||
return;
|
||||
|
||||
FREE (entry->line);
|
||||
FREE (entry->timestamp);
|
||||
|
||||
xfree (entry);
|
||||
}
|
||||
|
||||
/* Perhaps put back the current line if it has changed. */
|
||||
int
|
||||
rl_maybe_replace_line ()
|
||||
{
|
||||
HIST_ENTRY *temp;
|
||||
|
||||
temp = current_history ();
|
||||
/* If the current line has changed, save the changes. */
|
||||
if (temp && ((UNDO_LIST *)(temp->data) != rl_undo_list))
|
||||
{
|
||||
temp = replace_history_entry (where_history (), rl_line_buffer, (histdata_t)rl_undo_list);
|
||||
xfree (temp->line);
|
||||
FREE (temp->timestamp);
|
||||
xfree (temp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Restore the _rl_saved_line_for_history if there is one. */
|
||||
int
|
||||
rl_maybe_unsave_line ()
|
||||
{
|
||||
if (_rl_saved_line_for_history)
|
||||
{
|
||||
/* Can't call with `1' because rl_undo_list might point to an undo
|
||||
list from a history entry, as in rl_replace_from_history() below. */
|
||||
rl_replace_line (_rl_saved_line_for_history->line, 0);
|
||||
rl_undo_list = (UNDO_LIST *)_rl_saved_line_for_history->data;
|
||||
_rl_free_history_entry (_rl_saved_line_for_history);
|
||||
_rl_saved_line_for_history = (HIST_ENTRY *)NULL;
|
||||
rl_point = rl_end; /* rl_replace_line sets rl_end */
|
||||
}
|
||||
else
|
||||
rl_ding ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save the current line in _rl_saved_line_for_history. */
|
||||
int
|
||||
rl_maybe_save_line ()
|
||||
{
|
||||
if (_rl_saved_line_for_history == 0)
|
||||
{
|
||||
_rl_saved_line_for_history = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
|
||||
_rl_saved_line_for_history->line = savestring (rl_line_buffer);
|
||||
_rl_saved_line_for_history->timestamp = (char *)NULL;
|
||||
_rl_saved_line_for_history->data = (char *)rl_undo_list;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_rl_free_saved_history_line ()
|
||||
{
|
||||
if (_rl_saved_line_for_history)
|
||||
{
|
||||
_rl_free_history_entry (_rl_saved_line_for_history);
|
||||
_rl_saved_line_for_history = (HIST_ENTRY *)NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
_rl_history_set_point ()
|
||||
{
|
||||
rl_point = (_rl_history_preserve_point && _rl_history_saved_point != -1)
|
||||
? _rl_history_saved_point
|
||||
: rl_end;
|
||||
if (rl_point > rl_end)
|
||||
rl_point = rl_end;
|
||||
|
||||
#if defined (VI_MODE)
|
||||
if (rl_editing_mode == vi_mode && _rl_keymap != vi_insertion_keymap)
|
||||
rl_point = 0;
|
||||
#endif /* VI_MODE */
|
||||
|
||||
if (rl_editing_mode == emacs_mode)
|
||||
rl_mark = (rl_point == rl_end ? 0 : rl_end);
|
||||
}
|
||||
|
||||
void
|
||||
rl_replace_from_history (entry, flags)
|
||||
HIST_ENTRY *entry;
|
||||
int flags; /* currently unused */
|
||||
{
|
||||
/* Can't call with `1' because rl_undo_list might point to an undo list
|
||||
from a history entry, just like we're setting up here. */
|
||||
rl_replace_line (entry->line, 0);
|
||||
rl_undo_list = (UNDO_LIST *)entry->data;
|
||||
rl_point = rl_end;
|
||||
rl_mark = 0;
|
||||
|
||||
#if defined (VI_MODE)
|
||||
if (rl_editing_mode == vi_mode)
|
||||
{
|
||||
rl_point = 0;
|
||||
rl_mark = rl_end;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Process and free undo lists attached to each history entry prior to the
|
||||
current entry, inclusive, reverting each line to its saved state. This
|
||||
is destructive, and state about the current line is lost. This is not
|
||||
intended to be called while actively editing, and the current line is
|
||||
not assumed to have been added to the history list. */
|
||||
void
|
||||
_rl_revert_all_lines ()
|
||||
{
|
||||
int hpos;
|
||||
HIST_ENTRY *entry;
|
||||
UNDO_LIST *ul, *saved_undo_list;
|
||||
char *lbuf;
|
||||
|
||||
lbuf = savestring (rl_line_buffer);
|
||||
saved_undo_list = rl_undo_list;
|
||||
hpos = where_history ();
|
||||
|
||||
entry = (hpos == history_length) ? previous_history () : current_history ();
|
||||
while (entry)
|
||||
{
|
||||
if (ul = (UNDO_LIST *)entry->data)
|
||||
{
|
||||
if (ul == saved_undo_list)
|
||||
saved_undo_list = 0;
|
||||
/* Set up rl_line_buffer and other variables from history entry */
|
||||
rl_replace_from_history (entry, 0); /* entry->line is now current */
|
||||
/* entry->data = 0; /* entry->data is now current undo list */
|
||||
/* Undo all changes to this history entry */
|
||||
while (rl_undo_list)
|
||||
rl_do_undo ();
|
||||
/* And copy the reverted line back to the history entry, preserving
|
||||
the timestamp. */
|
||||
FREE (entry->line);
|
||||
entry->line = savestring (rl_line_buffer);
|
||||
entry->data = 0;
|
||||
}
|
||||
entry = previous_history ();
|
||||
}
|
||||
|
||||
/* Restore history state */
|
||||
rl_undo_list = saved_undo_list; /* may have been set to null */
|
||||
history_set_pos (hpos);
|
||||
|
||||
/* reset the line buffer */
|
||||
rl_replace_line (lbuf, 0);
|
||||
_rl_set_the_line ();
|
||||
|
||||
/* and clean up */
|
||||
xfree (lbuf);
|
||||
}
|
||||
|
||||
/* Free the history list, including private readline data and take care
|
||||
of pointer aliases to history data. Resets rl_undo_list if it points
|
||||
to an UNDO_LIST * saved as some history entry's data member. This
|
||||
should not be called while editing is active. */
|
||||
void
|
||||
rl_clear_history ()
|
||||
{
|
||||
HIST_ENTRY **hlist, *hent;
|
||||
register int i;
|
||||
UNDO_LIST *ul, *saved_undo_list;
|
||||
|
||||
saved_undo_list = rl_undo_list;
|
||||
hlist = history_list (); /* direct pointer, not copy */
|
||||
|
||||
for (i = 0; i < history_length; i++)
|
||||
{
|
||||
hent = hlist[i];
|
||||
if (ul = (UNDO_LIST *)hent->data)
|
||||
{
|
||||
if (ul == saved_undo_list)
|
||||
saved_undo_list = 0;
|
||||
_rl_free_undo_list (ul);
|
||||
hent->data = 0;
|
||||
}
|
||||
_rl_free_history_entry (hent);
|
||||
}
|
||||
|
||||
history_offset = history_length = 0;
|
||||
rl_undo_list = saved_undo_list; /* should be NULL */
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* History Commands */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* Meta-< goes to the start of the history. */
|
||||
int
|
||||
rl_beginning_of_history (count, key)
|
||||
int count, key;
|
||||
{
|
||||
return (rl_get_previous_history (1 + where_history (), key));
|
||||
}
|
||||
|
||||
/* Meta-> goes to the end of the history. (The current line). */
|
||||
int
|
||||
rl_end_of_history (count, key)
|
||||
int count, key;
|
||||
{
|
||||
rl_maybe_replace_line ();
|
||||
using_history ();
|
||||
rl_maybe_unsave_line ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Move down to the next history line. */
|
||||
int
|
||||
rl_get_next_history (count, key)
|
||||
int count, key;
|
||||
{
|
||||
HIST_ENTRY *temp;
|
||||
|
||||
if (count < 0)
|
||||
return (rl_get_previous_history (-count, key));
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
rl_maybe_replace_line ();
|
||||
|
||||
/* either not saved by rl_newline or at end of line, so set appropriately. */
|
||||
if (_rl_history_saved_point == -1 && (rl_point || rl_end))
|
||||
_rl_history_saved_point = (rl_point == rl_end) ? -1 : rl_point;
|
||||
|
||||
temp = (HIST_ENTRY *)NULL;
|
||||
while (count)
|
||||
{
|
||||
temp = next_history ();
|
||||
if (!temp)
|
||||
break;
|
||||
--count;
|
||||
}
|
||||
|
||||
if (temp == 0)
|
||||
rl_maybe_unsave_line ();
|
||||
else
|
||||
{
|
||||
rl_replace_from_history (temp, 0);
|
||||
_rl_history_set_point ();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the previous item out of our interactive history, making it the current
|
||||
line. If there is no previous history, just ding. */
|
||||
int
|
||||
rl_get_previous_history (count, key)
|
||||
int count, key;
|
||||
{
|
||||
HIST_ENTRY *old_temp, *temp;
|
||||
|
||||
if (count < 0)
|
||||
return (rl_get_next_history (-count, key));
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
/* either not saved by rl_newline or at end of line, so set appropriately. */
|
||||
if (_rl_history_saved_point == -1 && (rl_point || rl_end))
|
||||
_rl_history_saved_point = (rl_point == rl_end) ? -1 : rl_point;
|
||||
|
||||
/* If we don't have a line saved, then save this one. */
|
||||
rl_maybe_save_line ();
|
||||
|
||||
/* If the current line has changed, save the changes. */
|
||||
rl_maybe_replace_line ();
|
||||
|
||||
temp = old_temp = (HIST_ENTRY *)NULL;
|
||||
while (count)
|
||||
{
|
||||
temp = previous_history ();
|
||||
if (temp == 0)
|
||||
break;
|
||||
|
||||
old_temp = temp;
|
||||
--count;
|
||||
}
|
||||
|
||||
/* If there was a large argument, and we moved back to the start of the
|
||||
history, that is not an error. So use the last value found. */
|
||||
if (!temp && old_temp)
|
||||
temp = old_temp;
|
||||
|
||||
if (temp == 0)
|
||||
rl_ding ();
|
||||
else
|
||||
{
|
||||
rl_replace_from_history (temp, 0);
|
||||
_rl_history_set_point ();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Editing Modes */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
/* How to toggle back and forth between editing modes. */
|
||||
int
|
||||
rl_vi_editing_mode (count, key)
|
||||
int count, key;
|
||||
{
|
||||
#if defined (VI_MODE)
|
||||
_rl_set_insert_mode (RL_IM_INSERT, 1); /* vi mode ignores insert mode */
|
||||
rl_editing_mode = vi_mode;
|
||||
rl_vi_insert_mode (1, key);
|
||||
#endif /* VI_MODE */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
rl_emacs_editing_mode (count, key)
|
||||
int count, key;
|
||||
{
|
||||
rl_editing_mode = emacs_mode;
|
||||
_rl_set_insert_mode (RL_IM_INSERT, 1); /* emacs mode default is insert mode */
|
||||
_rl_keymap = emacs_standard_keymap;
|
||||
|
||||
if (_rl_show_mode_in_prompt)
|
||||
_rl_reset_prompt ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Function for the rest of the library to use to set insert/overwrite mode. */
|
||||
void
|
||||
_rl_set_insert_mode (im, force)
|
||||
int im, force;
|
||||
{
|
||||
#ifdef CURSOR_MODE
|
||||
_rl_set_cursor (im, force);
|
||||
#endif
|
||||
|
||||
rl_insert_mode = im;
|
||||
}
|
||||
|
||||
/* Toggle overwrite mode. A positive explicit argument selects overwrite
|
||||
mode. A negative or zero explicit argument selects insert mode. */
|
||||
int
|
||||
rl_overwrite_mode (count, key)
|
||||
int count, key;
|
||||
{
|
||||
if (rl_explicit_arg == 0)
|
||||
_rl_set_insert_mode (rl_insert_mode ^ 1, 0);
|
||||
else if (count > 0)
|
||||
_rl_set_insert_mode (RL_IM_OVERWRITE, 0);
|
||||
else
|
||||
_rl_set_insert_mode (RL_IM_INSERT, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/* zgetline - read a line of input from a specified file descriptor and return
|
||||
a pointer to a newly-allocated buffer containing the data. */
|
||||
|
||||
/* Copyright (C) 2008,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 <sys/types.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include "xmalloc.h"
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
extern ssize_t zread __P((int, char *, size_t));
|
||||
extern ssize_t zreadc __P((int, char *));
|
||||
extern ssize_t zreadintr __P((int, char *, size_t));
|
||||
extern ssize_t zreadcintr __P((int, char *));
|
||||
|
||||
typedef ssize_t breadfunc_t __P((int, char *, size_t));
|
||||
typedef ssize_t creadfunc_t __P((int, char *));
|
||||
|
||||
/* Initial memory allocation for automatic growing buffer in zreadlinec */
|
||||
#define GET_LINE_INITIAL_ALLOCATION 16
|
||||
|
||||
/* Derived from GNU libc's getline.
|
||||
The behavior is almost the same as getline. See man getline.
|
||||
The differences are
|
||||
(1) using file descriptor instead of FILE *;
|
||||
(2) the order of arguments: the file descriptor comes first;
|
||||
(3) the addition of a third argument, DELIM; sets the delimiter to
|
||||
be something other than newline if desired. If setting DELIM,
|
||||
the next argument should be 1; and
|
||||
(4) the addition of a fourth argument, UNBUFFERED_READ; this argument
|
||||
controls whether get_line uses buffering or not to get a byte data
|
||||
from FD. get_line uses zreadc if UNBUFFERED_READ is zero; and
|
||||
uses zread if UNBUFFERED_READ is non-zero.
|
||||
|
||||
Returns number of bytes read or -1 on error. */
|
||||
|
||||
ssize_t
|
||||
zgetline (fd, lineptr, n, delim, unbuffered_read)
|
||||
int fd;
|
||||
char **lineptr;
|
||||
size_t *n;
|
||||
int delim;
|
||||
int unbuffered_read;
|
||||
{
|
||||
int nr, retval;
|
||||
char *line, c;
|
||||
|
||||
if (lineptr == 0 || n == 0 || (*lineptr == 0 && *n != 0))
|
||||
return -1;
|
||||
|
||||
nr = 0;
|
||||
line = *lineptr;
|
||||
|
||||
while (1)
|
||||
{
|
||||
retval = unbuffered_read ? zread (fd, &c, 1) : zreadc(fd, &c);
|
||||
|
||||
if (retval <= 0)
|
||||
{
|
||||
if (line && nr > 0)
|
||||
line[nr] = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
if (nr + 2 >= *n)
|
||||
{
|
||||
size_t new_size;
|
||||
|
||||
new_size = (*n == 0) ? GET_LINE_INITIAL_ALLOCATION : *n * 2;
|
||||
line = (*n >= new_size) ? NULL : xrealloc (*lineptr, new_size);
|
||||
|
||||
if (line)
|
||||
{
|
||||
*lineptr = line;
|
||||
*n = new_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*n > 0)
|
||||
{
|
||||
(*lineptr)[*n - 1] = '\0';
|
||||
nr = *n - 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
line[nr] = c;
|
||||
nr++;
|
||||
|
||||
if (c == delim)
|
||||
{
|
||||
line[nr] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nr - 1;
|
||||
}
|
||||
-609
@@ -1,609 +0,0 @@
|
||||
/* pathexp.c -- The shell interface to the globbing library. */
|
||||
|
||||
/* Copyright (C) 1995-2014 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 <stdio.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "bashansi.h"
|
||||
|
||||
#include "shell.h"
|
||||
#include "pathexp.h"
|
||||
#include "flags.h"
|
||||
|
||||
#include "shmbutil.h"
|
||||
#include "bashintl.h"
|
||||
|
||||
#include <glob/strmatch.h>
|
||||
|
||||
static int glob_name_is_acceptable __P((const char *));
|
||||
static void ignore_globbed_names __P((char **, sh_ignore_func_t *));
|
||||
static char *split_ignorespec __P((char *, int *));
|
||||
|
||||
#if defined (USE_POSIX_GLOB_LIBRARY)
|
||||
# include <glob.h>
|
||||
typedef int posix_glob_errfunc_t __P((const char *, int));
|
||||
#else
|
||||
# include <glob/glob.h>
|
||||
#endif
|
||||
|
||||
/* Control whether * matches .files in globbing. */
|
||||
int glob_dot_filenames;
|
||||
|
||||
/* Control whether the extended globbing features are enabled. */
|
||||
int extended_glob = EXTGLOB_DEFAULT;
|
||||
|
||||
/* Control enabling special handling of `**' */
|
||||
int glob_star = 0;
|
||||
|
||||
/* Return nonzero if STRING has any unquoted special globbing chars in it. */
|
||||
int
|
||||
unquoted_glob_pattern_p (string)
|
||||
register char *string;
|
||||
{
|
||||
register int c;
|
||||
char *send;
|
||||
int open;
|
||||
|
||||
DECLARE_MBSTATE;
|
||||
|
||||
open = 0;
|
||||
send = string + strlen (string);
|
||||
|
||||
while (c = *string++)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '?':
|
||||
case '*':
|
||||
return (1);
|
||||
|
||||
case '[':
|
||||
open++;
|
||||
continue;
|
||||
|
||||
case ']':
|
||||
if (open)
|
||||
return (1);
|
||||
continue;
|
||||
|
||||
case '+':
|
||||
case '@':
|
||||
case '!':
|
||||
if (*string == '(') /*)*/
|
||||
return (1);
|
||||
continue;
|
||||
|
||||
case CTLESC:
|
||||
case '\\':
|
||||
if (*string++ == '\0')
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Advance one fewer byte than an entire multibyte character to
|
||||
account for the auto-increment in the loop above. */
|
||||
#ifdef HANDLE_MULTIBYTE
|
||||
string--;
|
||||
ADVANCE_CHAR_P (string, send - string);
|
||||
string++;
|
||||
#else
|
||||
ADVANCE_CHAR_P (string, send - string);
|
||||
#endif
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Return 1 if C is a character that is `special' in a POSIX ERE and needs to
|
||||
be quoted to match itself. */
|
||||
static inline int
|
||||
ere_char (c)
|
||||
int c;
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '.':
|
||||
case '[':
|
||||
case '\\':
|
||||
case '(':
|
||||
case ')':
|
||||
case '*':
|
||||
case '+':
|
||||
case '?':
|
||||
case '{':
|
||||
case '|':
|
||||
case '^':
|
||||
case '$':
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
glob_char_p (s)
|
||||
const char *s;
|
||||
{
|
||||
switch (*s)
|
||||
{
|
||||
case '*':
|
||||
case '[':
|
||||
case ']':
|
||||
case '?':
|
||||
case '\\':
|
||||
return 1;
|
||||
case '+':
|
||||
case '@':
|
||||
case '!':
|
||||
if (s[1] == '(') /*(*/
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* PATHNAME can contain characters prefixed by CTLESC; this indicates
|
||||
that the character is to be quoted. We quote it here in the style
|
||||
that the glob library recognizes. If flags includes QGLOB_CVTNULL,
|
||||
we change quoted null strings (pathname[0] == CTLNUL) into empty
|
||||
strings (pathname[0] == 0). If this is called after quote removal
|
||||
is performed, (flags & QGLOB_CVTNULL) should be 0; if called when quote
|
||||
removal has not been done (for example, before attempting to match a
|
||||
pattern while executing a case statement), flags should include
|
||||
QGLOB_CVTNULL. If flags includes QGLOB_FILENAME, appropriate quoting
|
||||
to match a filename should be performed. QGLOB_REGEXP means we're
|
||||
quoting for a Posix ERE (for [[ string =~ pat ]]) and that requires
|
||||
some special handling. */
|
||||
char *
|
||||
quote_string_for_globbing (pathname, qflags)
|
||||
const char *pathname;
|
||||
int qflags;
|
||||
{
|
||||
char *temp;
|
||||
register int i, j;
|
||||
int brack, cclass, collsym, equiv, c, last_was_backslash;
|
||||
|
||||
temp = (char *)xmalloc (2 * strlen (pathname) + 1);
|
||||
|
||||
if ((qflags & QGLOB_CVTNULL) && QUOTED_NULL (pathname))
|
||||
{
|
||||
temp[0] = '\0';
|
||||
return temp;
|
||||
}
|
||||
|
||||
brack = cclass = collsym = equiv = last_was_backslash = 0;
|
||||
for (i = j = 0; pathname[i]; i++)
|
||||
{
|
||||
/* Fix for CTLESC at the end of the string? */
|
||||
if (pathname[i] == CTLESC && pathname[i+1] == '\0')
|
||||
{
|
||||
temp[j++] = pathname[i++];
|
||||
break;
|
||||
}
|
||||
/* If we are parsing regexp, turn CTLESC CTLESC into CTLESC. It's not an
|
||||
ERE special character, so we should just be able to pass it through. */
|
||||
else if ((qflags & QGLOB_REGEXP) && pathname[i] == CTLESC && pathname[i+1] == CTLESC)
|
||||
{
|
||||
i++;
|
||||
temp[j++] = pathname[i];
|
||||
continue;
|
||||
}
|
||||
else if (pathname[i] == CTLESC)
|
||||
{
|
||||
if ((qflags & QGLOB_FILENAME) && pathname[i+1] == '/')
|
||||
continue;
|
||||
/* What to do if preceding char is backslash? */
|
||||
if (pathname[i+1] != CTLESC && (qflags & QGLOB_REGEXP) && ere_char (pathname[i+1]) == 0)
|
||||
continue;
|
||||
temp[j++] = '\\';
|
||||
i++;
|
||||
if (pathname[i] == '\0')
|
||||
break;
|
||||
}
|
||||
else if ((qflags & QGLOB_REGEXP) && (i == 0 || pathname[i-1] != CTLESC) && pathname[i] == '[') /*]*/
|
||||
{
|
||||
brack = 1;
|
||||
temp[j++] = pathname[i++]; /* open bracket */
|
||||
c = pathname[i++]; /* c == char after open bracket */
|
||||
do
|
||||
{
|
||||
if (c == 0)
|
||||
goto endpat;
|
||||
else if (c == CTLESC)
|
||||
{
|
||||
/* skip c, check for EOS, let assignment at end of loop */
|
||||
/* pathname[i] == backslash-escaped character */
|
||||
if (pathname[i] == 0)
|
||||
goto endpat;
|
||||
temp[j++] = pathname[i++];
|
||||
}
|
||||
else if (c == '[' && pathname[i] == ':')
|
||||
{
|
||||
temp[j++] = c;
|
||||
temp[j++] = pathname[i++];
|
||||
cclass = 1;
|
||||
}
|
||||
else if (cclass && c == ':' && pathname[i] == ']')
|
||||
{
|
||||
temp[j++] = c;
|
||||
temp[j++] = pathname[i++];
|
||||
cclass = 0;
|
||||
}
|
||||
else if (c == '[' && pathname[i] == '=')
|
||||
{
|
||||
temp[j++] = c;
|
||||
temp[j++] = pathname[i++];
|
||||
if (pathname[i] == ']')
|
||||
temp[j++] = pathname[i++]; /* right brack can be in equiv */
|
||||
equiv = 1;
|
||||
}
|
||||
else if (equiv && c == '=' && pathname[i] == ']')
|
||||
{
|
||||
temp[j++] = c;
|
||||
temp[j++] = pathname[i++];
|
||||
equiv = 0;
|
||||
}
|
||||
else if (c == '[' && pathname[i] == '.')
|
||||
{
|
||||
temp[j++] = c;
|
||||
temp[j++] = pathname[i++];
|
||||
if (pathname[i] == ']')
|
||||
temp[j++] = pathname[i++]; /* right brack can be in collsym */
|
||||
collsym = 1;
|
||||
}
|
||||
else if (collsym && c == '.' && pathname[i] == ']')
|
||||
{
|
||||
temp[j++] = c;
|
||||
temp[j++] = pathname[i++];
|
||||
collsym = 0;
|
||||
}
|
||||
else
|
||||
temp[j++] = c;
|
||||
}
|
||||
while ((c = pathname[i++]) != ']');
|
||||
temp[j++] = c; /* closing right bracket */
|
||||
i--; /* increment will happen above in loop */
|
||||
continue; /* skip double assignment below */
|
||||
}
|
||||
else if (pathname[i] == '\\' && (qflags & QGLOB_REGEXP) == 0)
|
||||
{
|
||||
/* XXX - if not quoting regexp, use backslash as quote char. Should
|
||||
we just pass it through without treating it as special? That is
|
||||
what ksh93 seems to do. */
|
||||
|
||||
/* If we want to pass through backslash unaltered, comment out these
|
||||
lines. */
|
||||
temp[j++] = '\\';
|
||||
|
||||
i++;
|
||||
if (pathname[i] == '\0')
|
||||
break;
|
||||
}
|
||||
else if (pathname[i] == '\\' && (qflags & QGLOB_REGEXP))
|
||||
last_was_backslash = 1;
|
||||
temp[j++] = pathname[i];
|
||||
}
|
||||
endpat:
|
||||
temp[j] = '\0';
|
||||
|
||||
return (temp);
|
||||
}
|
||||
|
||||
char *
|
||||
quote_globbing_chars (string)
|
||||
char *string;
|
||||
{
|
||||
size_t slen;
|
||||
char *temp, *s, *t, *send;
|
||||
DECLARE_MBSTATE;
|
||||
|
||||
slen = strlen (string);
|
||||
send = string + slen;
|
||||
|
||||
temp = (char *)xmalloc (slen * 2 + 1);
|
||||
for (t = temp, s = string; *s; )
|
||||
{
|
||||
if (glob_char_p (s))
|
||||
*t++ = '\\';
|
||||
|
||||
/* Copy a single (possibly multibyte) character from s to t,
|
||||
incrementing both. */
|
||||
COPY_CHAR_P (t, s, send);
|
||||
}
|
||||
*t = '\0';
|
||||
return temp;
|
||||
}
|
||||
|
||||
/* Call the glob library to do globbing on PATHNAME. */
|
||||
char **
|
||||
shell_glob_filename (pathname)
|
||||
const char *pathname;
|
||||
{
|
||||
#if defined (USE_POSIX_GLOB_LIBRARY)
|
||||
register int i;
|
||||
char *temp, **results;
|
||||
glob_t filenames;
|
||||
int glob_flags;
|
||||
|
||||
temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
|
||||
|
||||
filenames.gl_offs = 0;
|
||||
|
||||
# if defined (GLOB_PERIOD)
|
||||
glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0;
|
||||
# else
|
||||
glob_flags = 0;
|
||||
# endif /* !GLOB_PERIOD */
|
||||
|
||||
glob_flags |= (GLOB_ERR | GLOB_DOOFFS);
|
||||
|
||||
i = glob (temp, glob_flags, (posix_glob_errfunc_t *)NULL, &filenames);
|
||||
|
||||
free (temp);
|
||||
|
||||
if (i == GLOB_NOSPACE || i == GLOB_ABORTED)
|
||||
return ((char **)NULL);
|
||||
else if (i == GLOB_NOMATCH)
|
||||
filenames.gl_pathv = (char **)NULL;
|
||||
else if (i != 0) /* other error codes not in POSIX.2 */
|
||||
filenames.gl_pathv = (char **)NULL;
|
||||
|
||||
results = filenames.gl_pathv;
|
||||
|
||||
if (results && ((GLOB_FAILED (results)) == 0))
|
||||
{
|
||||
if (should_ignore_glob_matches ())
|
||||
ignore_glob_matches (results);
|
||||
if (results && results[0])
|
||||
strvec_sort (results);
|
||||
else
|
||||
{
|
||||
FREE (results);
|
||||
results = (char **)NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return (results);
|
||||
|
||||
#else /* !USE_POSIX_GLOB_LIBRARY */
|
||||
|
||||
char *temp, **results;
|
||||
|
||||
noglob_dot_filenames = glob_dot_filenames == 0;
|
||||
|
||||
temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
|
||||
results = glob_filename (temp, glob_star ? GX_GLOBSTAR : 0);
|
||||
free (temp);
|
||||
|
||||
if (results && ((GLOB_FAILED (results)) == 0))
|
||||
{
|
||||
if (should_ignore_glob_matches ())
|
||||
ignore_glob_matches (results);
|
||||
if (results && results[0])
|
||||
strvec_sort (results);
|
||||
else
|
||||
{
|
||||
FREE (results);
|
||||
results = (char **)&glob_error_return;
|
||||
}
|
||||
}
|
||||
|
||||
return (results);
|
||||
#endif /* !USE_POSIX_GLOB_LIBRARY */
|
||||
}
|
||||
|
||||
/* Stuff for GLOBIGNORE. */
|
||||
|
||||
static struct ignorevar globignore =
|
||||
{
|
||||
"GLOBIGNORE",
|
||||
(struct ign *)0,
|
||||
0,
|
||||
(char *)0,
|
||||
(sh_iv_item_func_t *)0,
|
||||
};
|
||||
|
||||
/* Set up to ignore some glob matches because the value of GLOBIGNORE
|
||||
has changed. If GLOBIGNORE is being unset, we also need to disable
|
||||
the globbing of filenames beginning with a `.'. */
|
||||
void
|
||||
setup_glob_ignore (name)
|
||||
char *name;
|
||||
{
|
||||
char *v;
|
||||
|
||||
v = get_string_value (name);
|
||||
setup_ignore_patterns (&globignore);
|
||||
|
||||
if (globignore.num_ignores)
|
||||
glob_dot_filenames = 1;
|
||||
else if (v == 0)
|
||||
glob_dot_filenames = 0;
|
||||
}
|
||||
|
||||
int
|
||||
should_ignore_glob_matches ()
|
||||
{
|
||||
return globignore.num_ignores;
|
||||
}
|
||||
|
||||
/* Return 0 if NAME matches a pattern in the globignore.ignores list. */
|
||||
static int
|
||||
glob_name_is_acceptable (name)
|
||||
const char *name;
|
||||
{
|
||||
struct ign *p;
|
||||
int flags;
|
||||
|
||||
/* . and .. are never matched */
|
||||
if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
|
||||
return (0);
|
||||
|
||||
flags = FNM_PATHNAME | FNMATCH_EXTFLAG;
|
||||
for (p = globignore.ignores; p->val; p++)
|
||||
{
|
||||
if (strmatch (p->val, (char *)name, flags) != FNM_NOMATCH)
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Internal function to test whether filenames in NAMES should be
|
||||
ignored. NAME_FUNC is a pointer to a function to call with each
|
||||
name. It returns non-zero if the name is acceptable to the particular
|
||||
ignore function which called _ignore_names; zero if the name should
|
||||
be removed from NAMES. */
|
||||
|
||||
static void
|
||||
ignore_globbed_names (names, name_func)
|
||||
char **names;
|
||||
sh_ignore_func_t *name_func;
|
||||
{
|
||||
char **newnames;
|
||||
int n, i;
|
||||
|
||||
for (i = 0; names[i]; i++)
|
||||
;
|
||||
newnames = strvec_create (i + 1);
|
||||
|
||||
for (n = i = 0; names[i]; i++)
|
||||
{
|
||||
if ((*name_func) (names[i]))
|
||||
newnames[n++] = names[i];
|
||||
else
|
||||
free (names[i]);
|
||||
}
|
||||
|
||||
newnames[n] = (char *)NULL;
|
||||
|
||||
if (n == 0)
|
||||
{
|
||||
names[0] = (char *)NULL;
|
||||
free (newnames);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Copy the acceptable names from NEWNAMES back to NAMES and set the
|
||||
new array end. */
|
||||
for (n = 0; newnames[n]; n++)
|
||||
names[n] = newnames[n];
|
||||
names[n] = (char *)NULL;
|
||||
free (newnames);
|
||||
}
|
||||
|
||||
void
|
||||
ignore_glob_matches (names)
|
||||
char **names;
|
||||
{
|
||||
if (globignore.num_ignores == 0)
|
||||
return;
|
||||
|
||||
ignore_globbed_names (names, glob_name_is_acceptable);
|
||||
}
|
||||
|
||||
static char *
|
||||
split_ignorespec (s, ip)
|
||||
char *s;
|
||||
int *ip;
|
||||
{
|
||||
char *t;
|
||||
int n, i;
|
||||
|
||||
if (s == 0)
|
||||
return 0;
|
||||
|
||||
i = *ip;
|
||||
if (s[i] == 0)
|
||||
return 0;
|
||||
|
||||
n = skip_to_delim (s, i, ":", SD_NOJMP|SD_EXTGLOB);
|
||||
t = substring (s, i, n);
|
||||
|
||||
if (s[n] == ':')
|
||||
n++;
|
||||
*ip = n;
|
||||
return t;
|
||||
}
|
||||
|
||||
void
|
||||
setup_ignore_patterns (ivp)
|
||||
struct ignorevar *ivp;
|
||||
{
|
||||
int numitems, maxitems, ptr;
|
||||
char *colon_bit, *this_ignoreval;
|
||||
struct ign *p;
|
||||
|
||||
this_ignoreval = get_string_value (ivp->varname);
|
||||
|
||||
/* If nothing has changed then just exit now. */
|
||||
if ((this_ignoreval && ivp->last_ignoreval && STREQ (this_ignoreval, ivp->last_ignoreval)) ||
|
||||
(!this_ignoreval && !ivp->last_ignoreval))
|
||||
return;
|
||||
|
||||
/* Oops. The ignore variable has changed. Re-parse it. */
|
||||
ivp->num_ignores = 0;
|
||||
|
||||
if (ivp->ignores)
|
||||
{
|
||||
for (p = ivp->ignores; p->val; p++)
|
||||
free(p->val);
|
||||
free (ivp->ignores);
|
||||
ivp->ignores = (struct ign *)NULL;
|
||||
}
|
||||
|
||||
if (ivp->last_ignoreval)
|
||||
{
|
||||
free (ivp->last_ignoreval);
|
||||
ivp->last_ignoreval = (char *)NULL;
|
||||
}
|
||||
|
||||
if (this_ignoreval == 0 || *this_ignoreval == '\0')
|
||||
return;
|
||||
|
||||
ivp->last_ignoreval = savestring (this_ignoreval);
|
||||
|
||||
numitems = maxitems = ptr = 0;
|
||||
|
||||
#if 0
|
||||
while (colon_bit = extract_colon_unit (this_ignoreval, &ptr))
|
||||
#else
|
||||
while (colon_bit = split_ignorespec (this_ignoreval, &ptr))
|
||||
#endif
|
||||
{
|
||||
if (numitems + 1 >= maxitems)
|
||||
{
|
||||
maxitems += 10;
|
||||
ivp->ignores = (struct ign *)xrealloc (ivp->ignores, maxitems * sizeof (struct ign));
|
||||
}
|
||||
ivp->ignores[numitems].val = colon_bit;
|
||||
ivp->ignores[numitems].len = strlen (colon_bit);
|
||||
ivp->ignores[numitems].flags = 0;
|
||||
if (ivp->item_func)
|
||||
(*ivp->item_func) (&ivp->ignores[numitems]);
|
||||
numitems++;
|
||||
}
|
||||
ivp->ignores[numitems].val = (char *)NULL;
|
||||
ivp->num_ignores = numitems;
|
||||
}
|
||||
@@ -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,402 +0,0 @@
|
||||
# this is needed so that the bad assignments (b[]=bcde, for example) do not
|
||||
# cause fatal shell errors when in posix mode
|
||||
set +o posix
|
||||
|
||||
set +a
|
||||
# The calls to egrep -v are to filter out builtin array variables that are
|
||||
# automatically set and possibly contain values that vary.
|
||||
|
||||
# first make sure we handle the basics
|
||||
x=()
|
||||
echo ${x[@]}
|
||||
unset x
|
||||
|
||||
# this should be an error
|
||||
test=(first & second)
|
||||
echo $?
|
||||
unset test
|
||||
|
||||
# make sure declare -a converts an existing variable to an array
|
||||
unset a
|
||||
a=abcde
|
||||
declare -a a
|
||||
echo ${a[0]}
|
||||
|
||||
unset a
|
||||
a=abcde
|
||||
a[2]=bdef
|
||||
|
||||
unset b
|
||||
declare -a b[256]
|
||||
|
||||
unset c[2]
|
||||
unset c[*]
|
||||
|
||||
a[1]=
|
||||
|
||||
_ENV=/bin/true
|
||||
x=${_ENV[(_$-=0)+(_=1)-_${-%%*i*}]}
|
||||
|
||||
declare -r c[100]
|
||||
|
||||
echo ${a[0]} ${a[4]}
|
||||
echo ${a[@]}
|
||||
|
||||
echo ${a[*]}
|
||||
|
||||
# this should print out values, too
|
||||
declare -a | egrep -v '(BASH_VERSINFO|PIPESTATUS|GROUPS|FUNCNAME)'
|
||||
|
||||
unset a[7]
|
||||
echo ${a[*]}
|
||||
|
||||
unset a[4]
|
||||
echo ${a[*]}
|
||||
|
||||
echo ${a}
|
||||
echo "${a}"
|
||||
echo $a
|
||||
|
||||
unset a[0]
|
||||
echo ${a}
|
||||
|
||||
echo ${a[@]}
|
||||
|
||||
a[5]="hello world"
|
||||
echo ${a[5]}
|
||||
echo ${#a[5]}
|
||||
|
||||
echo ${#a[@]}
|
||||
|
||||
a[4+5/2]="test expression"
|
||||
declare a["7 + 8"]="test 2"
|
||||
a[7 + 8]="test 2"
|
||||
echo ${a[@]}
|
||||
|
||||
readonly a[5]
|
||||
readonly a
|
||||
# these two lines should output `declare' commands
|
||||
readonly -a | egrep -v '(BASH_VERSINFO|PIPESTATUS|GROUPS)'
|
||||
declare -ar | egrep -v '(BASH_VERSINFO|PIPESTATUS|GROUPS)'
|
||||
# this line should output `readonly' commands, even for arrays
|
||||
set -o posix
|
||||
readonly -a | egrep -v '(BASH_VERSINFO|PIPESTATUS|GROUPS)'
|
||||
set +o posix
|
||||
|
||||
declare -a d='([1]="" [2]="bdef" [5]="hello world" "test")'
|
||||
d[9]="ninth element"
|
||||
|
||||
declare -a e[10]=test # this works in post-bash-2.05 versions
|
||||
declare -a e[10]='(test)'
|
||||
|
||||
pass=/etc/passwd
|
||||
declare -a f='("${d[@]}")'
|
||||
b=([0]=this [1]=is [2]=a [3]=test [4]="$PS1" [5]=$pass)
|
||||
|
||||
echo ${b[@]:2:3}
|
||||
|
||||
declare -pa | egrep -v '(BASH_VERSINFO|PIPESTATUS|GROUPS)'
|
||||
|
||||
a[3]="this is a test"
|
||||
|
||||
b[]=bcde
|
||||
b[*]=aaa
|
||||
echo ${b[ ]}
|
||||
|
||||
c[-2]=4
|
||||
echo ${c[-4]}
|
||||
|
||||
d[7]=(abdedfegeee)
|
||||
|
||||
d=([]=abcde [1]="test test" [*]=last [-65]=negative )
|
||||
|
||||
unset d[12]
|
||||
unset e[*]
|
||||
|
||||
declare -a | egrep -v '(BASH_VERSINFO|PIPESTATUS|GROUPS)'
|
||||
|
||||
ps1='hello'
|
||||
unset ps1[2]
|
||||
unset ${ps1[2]}
|
||||
|
||||
declare +a ps1
|
||||
declare +a c
|
||||
|
||||
# the prompt should not print when using a here doc
|
||||
read -p "array test: " -a rv <<!
|
||||
this is a test of read using arrays
|
||||
!
|
||||
|
||||
echo ${rv[0]} ${rv[4]}
|
||||
echo ${rv[@]}
|
||||
|
||||
# the variable should be converted to an array when `read -a' is done
|
||||
vv=1
|
||||
read -a vv <<!
|
||||
this is a test of arrays
|
||||
!
|
||||
echo ${vv[0]} ${vv[3]}
|
||||
echo ${vv[@]}
|
||||
unset vv
|
||||
|
||||
declare -a | egrep -v '(BASH_VERSINFO|PIPESTATUS|GROUPS)'
|
||||
|
||||
export rv
|
||||
#set
|
||||
|
||||
x[4]=bbb
|
||||
x=abde
|
||||
echo $x
|
||||
echo ${x[0]}
|
||||
echo ${x[4]}
|
||||
echo efgh | ( read x[1] ; echo ${x[1]} )
|
||||
echo wxyz | ( declare -a x ; read x ; echo $x ; echo ${x[0]} )
|
||||
|
||||
# Make sure that arrays can be used to save the positional paramters verbatim
|
||||
set -- a 'b c' d 'e f g' h
|
||||
|
||||
ARGV=( [0]=$0 "$@" )
|
||||
|
||||
for z in "${ARGV[@]}"
|
||||
do
|
||||
echo "$z"
|
||||
done
|
||||
|
||||
echo "$0"
|
||||
for z in "$@"
|
||||
do
|
||||
echo "$z"
|
||||
done
|
||||
|
||||
# do various pattern removal and length tests
|
||||
XPATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:.:/sbin:/usr/sbin
|
||||
|
||||
xpath=( $( IFS=: ; echo $XPATH ) )
|
||||
|
||||
echo ${xpath[@]}
|
||||
echo ${xpath[@]##*/}
|
||||
echo ${xpath[0]##*/}
|
||||
echo ${xpath[@]%%[!/]*}
|
||||
echo ${xpath[0]%%[!/]*}
|
||||
recho ${xpath##*/}
|
||||
recho ${xpath%%[!/]*}
|
||||
recho ${xpath[5]##*/}
|
||||
recho ${xpath[5]%%[!/]*}
|
||||
|
||||
# let's try to make it a DOS-style path
|
||||
|
||||
zecho "${xpath[@]/\//\\}"
|
||||
zecho "${xpath[@]//\//\\}"
|
||||
zecho "${xpath[@]//[\/]/\\}"
|
||||
|
||||
# length of the first element of the array, since array without subscript
|
||||
# is equivalent to referencing first element
|
||||
echo ${#xpath} -- ${#xpath[0]}
|
||||
|
||||
# number of elements in the array
|
||||
nelem=${#xpath[@]}
|
||||
echo ${#xpath[@]} -- $nelem
|
||||
|
||||
# total length of all elements in the array, including space separators
|
||||
xx="${xpath[*]}"
|
||||
echo ${#xx}
|
||||
|
||||
# total length of all elements in the array
|
||||
xx=$( IFS='' ; echo "${xpath[*]}" )
|
||||
echo ${#xx}
|
||||
|
||||
unset xpath[nelem-1]
|
||||
|
||||
nelem=${#xpath[@]}
|
||||
echo ${#xpath[@]} -- $nelem
|
||||
|
||||
# arrays and things that look like index assignments
|
||||
array=(42 [1]=14 [2]=44)
|
||||
|
||||
array2=(grep [ 123 ] \*)
|
||||
|
||||
echo ${array[@]}
|
||||
echo "${array2[@]}"
|
||||
|
||||
# arrays and implicit arithmetic evaluation
|
||||
declare -i -a iarray
|
||||
|
||||
iarray=( 2+4 1+6 7+2 )
|
||||
echo ${iarray[@]}
|
||||
|
||||
iarray[4]=4+1
|
||||
echo ${iarray[@]}
|
||||
|
||||
# make sure assignment using the compound assignment syntax removes all
|
||||
# of the old elements from the array value
|
||||
barray=(old1 old2 old3 old4 old5)
|
||||
barray=(new1 new2 new3)
|
||||
echo "length = ${#barray[@]}"
|
||||
echo "value = ${barray[*]}"
|
||||
|
||||
# make sure the array code behaves correctly with respect to unset variables
|
||||
set -u
|
||||
( echo ${#narray[4]} )
|
||||
|
||||
${THIS_SH} ./array1.sub
|
||||
${THIS_SH} ./array2.sub
|
||||
|
||||
# some old bugs and ksh93 compatibility tests
|
||||
${THIS_SH} ./array3.sub
|
||||
|
||||
# some compound assingment parsing problems that showed up in bash-3.1-release
|
||||
${THIS_SH} ./array4.sub
|
||||
|
||||
set +u
|
||||
cd /tmp
|
||||
|
||||
touch 1=bar
|
||||
foo=([10]="bar")
|
||||
echo ${foo[0]}
|
||||
rm 1=bar
|
||||
|
||||
cd $OLDPWD
|
||||
|
||||
foo=(a b c d e f g)
|
||||
echo ${foo[@]}
|
||||
|
||||
# quoted reserved words are ok
|
||||
foo=(\for \case \if \then \else)
|
||||
echo ${foo[@]}
|
||||
|
||||
# quoted metacharacters are ok
|
||||
foo=( [1]='<>' [2]='<' [3]='>' [4]='!' )
|
||||
echo ${foo[@]}
|
||||
|
||||
# numbers are just words when not in a redirection context
|
||||
foo=( 12 14 16 18 20 )
|
||||
echo ${foo[@]}
|
||||
|
||||
foo=( 4414758999202 )
|
||||
echo ${foo[@]}
|
||||
|
||||
# this was a bug in all versions of bash 2.x up to and including bash-2.04
|
||||
declare -a ddd=(aaa
|
||||
bbb)
|
||||
echo ${ddd[@]}
|
||||
|
||||
# errors until post-bash-2.05a; now reserved words are OK
|
||||
foo=(a b c for case if then else)
|
||||
|
||||
foo=(for case if then else)
|
||||
|
||||
# errors
|
||||
metas=( <> < > ! )
|
||||
metas=( [1]=<> [2]=< [3]=> [4]=! )
|
||||
|
||||
# various expansions that didn't really work right until post-bash-2.04
|
||||
foo='abc'
|
||||
echo ${foo[0]} ${#foo[0]}
|
||||
echo ${foo[1]} ${#foo[1]}
|
||||
echo ${foo[@]} ${#foo[@]}
|
||||
echo ${foo[*]} ${#foo[*]}
|
||||
|
||||
foo=''
|
||||
echo ${foo[0]} ${#foo[0]}
|
||||
echo ${foo[1]} ${#foo[1]}
|
||||
echo ${foo[@]} ${#foo[@]}
|
||||
echo ${foo[*]} ${#foo[*]}
|
||||
|
||||
# new expansions added after bash-2.05b
|
||||
x[0]=zero
|
||||
x[1]=one
|
||||
x[4]=four
|
||||
x[10]=ten
|
||||
|
||||
recho ${!x[@]}
|
||||
recho "${!x[@]}"
|
||||
recho ${!x[*]}
|
||||
recho "${!x[*]}"
|
||||
|
||||
# sparse array tests for code fixed in bash-3.0
|
||||
unset av
|
||||
av[1]='one'
|
||||
av[2]=''
|
||||
|
||||
av[3]=three
|
||||
av[5]=five
|
||||
av[7]=seven
|
||||
|
||||
echo include null element -- expect one
|
||||
echo ${av[@]:1:2} # what happens when we include a null element?
|
||||
echo include unset element -- expect three five
|
||||
echo ${av[@]:3:2} # what happens when we include an unset element?
|
||||
echo start at unset element -- expect five seven
|
||||
echo ${av[@]:4:2} # what happens when we start at an unset element?
|
||||
|
||||
echo too many elements -- expect three five seven
|
||||
echo ${av[@]:3:5} # how about too many elements?
|
||||
|
||||
echo positive offset - expect five seven
|
||||
echo ${av[@]:5:2}
|
||||
echo negative offset to unset element - expect seven
|
||||
echo ${av[@]: -2:2}
|
||||
|
||||
echo positive offset 2 - expect seven
|
||||
echo ${av[@]: 6:2}
|
||||
echo negative offset 2 - expect seven
|
||||
echo ${av[@]: -1:2}
|
||||
|
||||
echo out-of-range offset
|
||||
echo ${av[@]:12}
|
||||
|
||||
# parsing problems and other inconsistencies not fixed until post bash-3.0
|
||||
unset x
|
||||
declare -a x=(')' $$)
|
||||
[ ${x[1]} -eq $$ ] || echo bad
|
||||
|
||||
unset x
|
||||
declare -a x=(a b c d e)
|
||||
echo ${x[4]}
|
||||
|
||||
z=([1]=one [4]=four [7]=seven [10]=ten)
|
||||
|
||||
echo ${#z[@]}
|
||||
|
||||
echo ${!z[@]}
|
||||
|
||||
unset x
|
||||
declare -a x=(a \'b c\')
|
||||
|
||||
echo "${x[1]}"
|
||||
|
||||
unset x
|
||||
declare -a x=(a 'b c')
|
||||
|
||||
echo "${x[1]}"
|
||||
|
||||
unset x
|
||||
declare -a x=($0)
|
||||
[ "${x[@]}" = $0 ] || echo double expansion of \$0
|
||||
declare -a x=(\$0)
|
||||
echo "${x[@]}"
|
||||
|
||||
# tests for bash-3.1 problems
|
||||
${THIS_SH} ./array5.sub
|
||||
|
||||
# tests for post-bash-3.2 problems, most fixed in bash-3.2 patches
|
||||
${THIS_SH} ./array6.sub
|
||||
${THIS_SH} ./array7.sub
|
||||
|
||||
${THIS_SH} ./array8.sub
|
||||
|
||||
${THIS_SH} ./array9.sub
|
||||
|
||||
${THIS_SH} ./array10.sub
|
||||
|
||||
${THIS_SH} ./array11.sub
|
||||
|
||||
${THIS_SH} ./array12.sub
|
||||
|
||||
${THIS_SH} ./array13.sub
|
||||
|
||||
${THIS_SH} ./array14.sub
|
||||
|
||||
${THIS_SH} ./array15.sub
|
||||
|
||||
${THIS_SH} ./array16.sub
|
||||
@@ -1,42 +0,0 @@
|
||||
declare -a A
|
||||
mapfile A < mapfile.data
|
||||
for (( i = 0 ; i < ${#A[@]} ; i++ )); do
|
||||
echo -n "${A[${i}]}"
|
||||
done
|
||||
|
||||
declare -a B
|
||||
mapfile -t B < mapfile.data
|
||||
for (( i = 0 ; i < ${#B[@]} ; i++ )); do
|
||||
echo "${B[${i}]}"
|
||||
done
|
||||
|
||||
mapfile -C "echo" -c 1 A < mapfile.data
|
||||
mapfile -C "echo" -c 3 A < mapfile.data
|
||||
mapfile -C "echo" -c 19 A < mapfile.data
|
||||
|
||||
declare -a C
|
||||
mapfile -t -u 3 C 3< mapfile.data < mapfile.tests
|
||||
for (( i = 0 ; i < ${#C[@]} ; i++ )); do
|
||||
echo "${C[${i}]}"
|
||||
done
|
||||
|
||||
|
||||
declare -a D
|
||||
for (( i = 0 ; i < 30; i++ )); do
|
||||
D[${i}]="[$i] aaa"
|
||||
done
|
||||
mapfile -O 10 -t D < mapfile.data
|
||||
for (( i = 0 ; i < ${#D[@]} ; i++ )); do
|
||||
echo "${D[${i}]}"
|
||||
done
|
||||
|
||||
declare -a E
|
||||
for (( i = 0 ; i < 30; i++ )); do
|
||||
E[${i}]="[$i] aaa"
|
||||
done
|
||||
mapfile -O 10 -n 5 -t E < mapfile.data
|
||||
for (( i = 0 ; i < ${#E[@]} ; i++ )); do
|
||||
echo "${E[${i}]}"
|
||||
done
|
||||
|
||||
${THIS_SH} ./mapfile1.sub
|
||||
@@ -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
|
||||
-372
@@ -1,372 +0,0 @@
|
||||
/* unwind_prot.c - a simple unwind-protect system for internal variables */
|
||||
|
||||
/* I can't stand it anymore! Please can't we just write the
|
||||
whole Unix system in lisp or something? */
|
||||
|
||||
/* Copyright (C) 1987-2009 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Unwind Protection Scheme for Bash */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
#include "config.h"
|
||||
|
||||
#include "bashtypes.h"
|
||||
#include "bashansi.h"
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if STDC_HEADERS
|
||||
# include <stddef.h>
|
||||
#endif
|
||||
|
||||
#ifndef offsetof
|
||||
# define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
|
||||
#include "command.h"
|
||||
#include "general.h"
|
||||
#include "unwind_prot.h"
|
||||
#include "sig.h"
|
||||
#include "quit.h"
|
||||
#include "error.h" /* for internal_warning */
|
||||
|
||||
/* Structure describing a saved variable and the value to restore it to. */
|
||||
typedef struct {
|
||||
char *variable;
|
||||
int size;
|
||||
char desired_setting[1]; /* actual size is `size' */
|
||||
} SAVED_VAR;
|
||||
|
||||
/* If HEAD.CLEANUP is null, then ARG.V contains a tag to throw back to.
|
||||
If HEAD.CLEANUP is restore_variable, then SV.V contains the saved
|
||||
variable. Otherwise, call HEAD.CLEANUP (ARG.V) to clean up. */
|
||||
typedef union uwp {
|
||||
struct uwp_head {
|
||||
union uwp *next;
|
||||
Function *cleanup;
|
||||
} head;
|
||||
struct {
|
||||
struct uwp_head uwp_head;
|
||||
char *v;
|
||||
} arg;
|
||||
struct {
|
||||
struct uwp_head uwp_head;
|
||||
SAVED_VAR v;
|
||||
} sv;
|
||||
} UNWIND_ELT;
|
||||
|
||||
|
||||
static void without_interrupts __P((VFunction *, char *, char *));
|
||||
static void unwind_frame_discard_internal __P((char *, char *));
|
||||
static void unwind_frame_run_internal __P((char *, char *));
|
||||
static void add_unwind_protect_internal __P((Function *, char *));
|
||||
static void remove_unwind_protect_internal __P((char *, char *));
|
||||
static void run_unwind_protects_internal __P((char *, char *));
|
||||
static void clear_unwind_protects_internal __P((char *, char *));
|
||||
static inline void restore_variable __P((SAVED_VAR *));
|
||||
static void unwind_protect_mem_internal __P((char *, char *));
|
||||
|
||||
static UNWIND_ELT *unwind_protect_list = (UNWIND_ELT *)NULL;
|
||||
|
||||
#define uwpalloc(elt) (elt) = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT))
|
||||
#define uwpfree(elt) free(elt)
|
||||
|
||||
/* Run a function without interrupts. This relies on the fact that the
|
||||
FUNCTION cannot change the value of interrupt_immediately. (I.e., does
|
||||
not call QUIT (). */
|
||||
static void
|
||||
without_interrupts (function, arg1, arg2)
|
||||
VFunction *function;
|
||||
char *arg1, *arg2;
|
||||
{
|
||||
int old_interrupt_immediately;
|
||||
|
||||
old_interrupt_immediately = interrupt_immediately;
|
||||
interrupt_immediately = 0;
|
||||
|
||||
(*function)(arg1, arg2);
|
||||
|
||||
interrupt_immediately = old_interrupt_immediately;
|
||||
}
|
||||
|
||||
/* Start the beginning of a region. */
|
||||
void
|
||||
begin_unwind_frame (tag)
|
||||
char *tag;
|
||||
{
|
||||
add_unwind_protect ((Function *)NULL, tag);
|
||||
}
|
||||
|
||||
/* Discard the unwind protects back to TAG. */
|
||||
void
|
||||
discard_unwind_frame (tag)
|
||||
char *tag;
|
||||
{
|
||||
if (unwind_protect_list)
|
||||
without_interrupts (unwind_frame_discard_internal, tag, (char *)NULL);
|
||||
}
|
||||
|
||||
/* Run the unwind protects back to TAG. */
|
||||
void
|
||||
run_unwind_frame (tag)
|
||||
char *tag;
|
||||
{
|
||||
if (unwind_protect_list)
|
||||
without_interrupts (unwind_frame_run_internal, tag, (char *)NULL);
|
||||
}
|
||||
|
||||
/* Add the function CLEANUP with ARG to the list of unwindable things. */
|
||||
void
|
||||
add_unwind_protect (cleanup, arg)
|
||||
Function *cleanup;
|
||||
char *arg;
|
||||
{
|
||||
without_interrupts (add_unwind_protect_internal, (char *)cleanup, arg);
|
||||
}
|
||||
|
||||
/* Remove the top unwind protect from the list. */
|
||||
void
|
||||
remove_unwind_protect ()
|
||||
{
|
||||
if (unwind_protect_list)
|
||||
without_interrupts
|
||||
(remove_unwind_protect_internal, (char *)NULL, (char *)NULL);
|
||||
}
|
||||
|
||||
/* Run the list of cleanup functions in unwind_protect_list. */
|
||||
void
|
||||
run_unwind_protects ()
|
||||
{
|
||||
if (unwind_protect_list)
|
||||
without_interrupts
|
||||
(run_unwind_protects_internal, (char *)NULL, (char *)NULL);
|
||||
}
|
||||
|
||||
/* Erase the unwind-protect list. If flags is 1, free the elements. */
|
||||
void
|
||||
clear_unwind_protect_list (flags)
|
||||
int flags;
|
||||
{
|
||||
char *flag;
|
||||
|
||||
if (unwind_protect_list)
|
||||
{
|
||||
flag = flags ? "" : (char *)NULL;
|
||||
without_interrupts
|
||||
(clear_unwind_protects_internal, flag, (char *)NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
have_unwind_protects ()
|
||||
{
|
||||
return (unwind_protect_list != 0);
|
||||
}
|
||||
|
||||
int
|
||||
unwind_protect_tag_on_stack (tag)
|
||||
const char *tag;
|
||||
{
|
||||
UNWIND_ELT *elt;
|
||||
|
||||
elt = unwind_protect_list;
|
||||
while (elt)
|
||||
{
|
||||
if (elt->head.cleanup == 0 && STREQ (elt->arg.v, tag))
|
||||
return 1;
|
||||
elt = unwind_protect_list->head.next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* The Actual Functions */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
static void
|
||||
add_unwind_protect_internal (cleanup, arg)
|
||||
Function *cleanup;
|
||||
char *arg;
|
||||
{
|
||||
UNWIND_ELT *elt;
|
||||
|
||||
uwpalloc (elt);
|
||||
elt->head.next = unwind_protect_list;
|
||||
elt->head.cleanup = cleanup;
|
||||
elt->arg.v = arg;
|
||||
unwind_protect_list = elt;
|
||||
}
|
||||
|
||||
static void
|
||||
remove_unwind_protect_internal (ignore1, ignore2)
|
||||
char *ignore1, *ignore2;
|
||||
{
|
||||
UNWIND_ELT *elt;
|
||||
|
||||
elt = unwind_protect_list;
|
||||
if (elt)
|
||||
{
|
||||
unwind_protect_list = unwind_protect_list->head.next;
|
||||
uwpfree (elt);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
run_unwind_protects_internal (ignore1, ignore2)
|
||||
char *ignore1, *ignore2;
|
||||
{
|
||||
unwind_frame_run_internal ((char *) NULL, (char *) NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
clear_unwind_protects_internal (flag, ignore)
|
||||
char *flag, *ignore;
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
while (unwind_protect_list)
|
||||
remove_unwind_protect_internal ((char *)NULL, (char *)NULL);
|
||||
}
|
||||
unwind_protect_list = (UNWIND_ELT *)NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
unwind_frame_discard_internal (tag, ignore)
|
||||
char *tag, *ignore;
|
||||
{
|
||||
UNWIND_ELT *elt;
|
||||
int found;
|
||||
|
||||
found = 0;
|
||||
while (elt = unwind_protect_list)
|
||||
{
|
||||
unwind_protect_list = unwind_protect_list->head.next;
|
||||
if (elt->head.cleanup == 0 && (STREQ (elt->arg.v, tag)))
|
||||
{
|
||||
uwpfree (elt);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
uwpfree (elt);
|
||||
}
|
||||
|
||||
if (found == 0)
|
||||
internal_warning ("unwind_frame_discard: %s: frame not found", tag);
|
||||
}
|
||||
|
||||
/* Restore the value of a variable, based on the contents of SV.
|
||||
sv->desired_setting is a block of memory SIZE bytes long holding the
|
||||
value itself. This block of memory is copied back into the variable. */
|
||||
static inline void
|
||||
restore_variable (sv)
|
||||
SAVED_VAR *sv;
|
||||
{
|
||||
FASTCOPY (sv->desired_setting, sv->variable, sv->size);
|
||||
}
|
||||
|
||||
static void
|
||||
unwind_frame_run_internal (tag, ignore)
|
||||
char *tag, *ignore;
|
||||
{
|
||||
UNWIND_ELT *elt;
|
||||
int found;
|
||||
|
||||
found = 0;
|
||||
while (elt = unwind_protect_list)
|
||||
{
|
||||
unwind_protect_list = elt->head.next;
|
||||
|
||||
/* If tag, then compare. */
|
||||
if (elt->head.cleanup == 0)
|
||||
{
|
||||
if (tag && STREQ (elt->arg.v, tag))
|
||||
{
|
||||
uwpfree (elt);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (elt->head.cleanup == (Function *) restore_variable)
|
||||
restore_variable (&elt->sv.v);
|
||||
else
|
||||
(*(elt->head.cleanup)) (elt->arg.v);
|
||||
}
|
||||
|
||||
uwpfree (elt);
|
||||
}
|
||||
if (tag && found == 0)
|
||||
internal_warning ("unwind_frame_run: %s: frame not found", tag);
|
||||
}
|
||||
|
||||
static void
|
||||
unwind_protect_mem_internal (var, psize)
|
||||
char *var;
|
||||
char *psize;
|
||||
{
|
||||
int size, allocated;
|
||||
UNWIND_ELT *elt;
|
||||
|
||||
size = *(int *) psize;
|
||||
allocated = size + offsetof (UNWIND_ELT, sv.v.desired_setting[0]);
|
||||
elt = (UNWIND_ELT *)xmalloc (allocated);
|
||||
elt->head.next = unwind_protect_list;
|
||||
elt->head.cleanup = (Function *) restore_variable;
|
||||
elt->sv.v.variable = var;
|
||||
elt->sv.v.size = size;
|
||||
FASTCOPY (var, elt->sv.v.desired_setting, size);
|
||||
unwind_protect_list = elt;
|
||||
}
|
||||
|
||||
/* Save the value of a variable so it will be restored when unwind-protects
|
||||
are run. VAR is a pointer to the variable. SIZE is the size in
|
||||
bytes of VAR. */
|
||||
void
|
||||
unwind_protect_mem (var, size)
|
||||
char *var;
|
||||
int size;
|
||||
{
|
||||
without_interrupts (unwind_protect_mem_internal, var, (char *) &size);
|
||||
}
|
||||
|
||||
#if defined (DEBUG)
|
||||
#include <stdio.h>
|
||||
|
||||
void
|
||||
print_unwind_protect_tags ()
|
||||
{
|
||||
UNWIND_ELT *elt;
|
||||
|
||||
elt = unwind_protect_list;
|
||||
while (elt)
|
||||
{
|
||||
if (elt->head.cleanup == 0)
|
||||
fprintf(stderr, "tag: %s\n", elt->arg.v);
|
||||
elt = elt->head.next;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,49 +0,0 @@
|
||||
/* unwind_prot.h - Macros and functions for hacking unwind protection. */
|
||||
|
||||
/* Copyright (C) 1993-2009 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if !defined (_UNWIND_PROT_H)
|
||||
#define _UNWIND_PROT_H
|
||||
|
||||
/* Run a function without interrupts. */
|
||||
extern void begin_unwind_frame __P((char *));
|
||||
extern void discard_unwind_frame __P((char *));
|
||||
extern void run_unwind_frame __P((char *));
|
||||
extern void add_unwind_protect (); /* Not portable to arbitrary C99 hosts. */
|
||||
extern void remove_unwind_protect __P((void));
|
||||
extern void run_unwind_protects __P((void));
|
||||
extern void clear_unwind_protect_list __P((int));
|
||||
extern int have_unwind_protects __P((void));
|
||||
extern void uwp_init __P((void));
|
||||
|
||||
/* Define for people who like their code to look a certain way. */
|
||||
#define end_unwind_frame()
|
||||
|
||||
/* How to protect a variable. */
|
||||
#define unwind_protect_var(X) unwind_protect_mem ((char *)&(X), sizeof (X))
|
||||
extern void unwind_protect_mem __P((char *, int));
|
||||
|
||||
/* Backwards compatibility */
|
||||
#define unwind_protect_int unwind_protect_var
|
||||
#define unwind_protect_short unwind_protect_var
|
||||
#define unwind_protect_string unwind_protect_var
|
||||
#define unwind_protect_pointer unwind_protect_var
|
||||
#define unwind_protect_jmp_buf unwind_protect_var
|
||||
|
||||
#endif /* _UNWIND_PROT_H */
|
||||
Reference in New Issue
Block a user