mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-06-29 00:19:51 +02:00
commit bash-20100318 snapshot
This commit is contained in:
@@ -1066,6 +1066,7 @@ tests/type.tests f
|
||||
tests/type.right f
|
||||
tests/type1.sub f
|
||||
tests/type2.sub f
|
||||
tests/type3.sub f
|
||||
tests/varenv.right f
|
||||
tests/varenv.sh f
|
||||
tests/varenv1.sub f
|
||||
|
||||
@@ -415,6 +415,7 @@ lib/sh/snprintf.c f
|
||||
lib/sh/spell.c f
|
||||
lib/sh/strcasecmp.c f
|
||||
lib/sh/strcasestr.c f
|
||||
lib/sh/strchrnul.c f
|
||||
lib/sh/strerror.c f
|
||||
lib/sh/strftime.c f
|
||||
lib/sh/stringlist.c f
|
||||
|
||||
@@ -751,6 +751,9 @@ bash_add_history (line)
|
||||
chars_to_add = "";
|
||||
}
|
||||
|
||||
if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\n' && *chars_to_add == ';')
|
||||
chars_to_add++;
|
||||
|
||||
new_line = (char *)xmalloc (1
|
||||
+ curlen
|
||||
+ strlen (line)
|
||||
|
||||
+898
@@ -0,0 +1,898 @@
|
||||
/* bashhist.c -- bash interface to the GNU history library. */
|
||||
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if defined (HISTORY)
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# ifdef _MINIX
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "bashtypes.h"
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include "bashansi.h"
|
||||
#include "posixstat.h"
|
||||
#include "filecntl.h"
|
||||
|
||||
#include "bashintl.h"
|
||||
|
||||
#if defined (SYSLOG_HISTORY)
|
||||
# include <syslog.h>
|
||||
#endif
|
||||
|
||||
#include "shell.h"
|
||||
#include "flags.h"
|
||||
#include "input.h"
|
||||
#include "parser.h" /* for the struct dstack stuff. */
|
||||
#include "pathexp.h" /* for the struct ignorevar stuff */
|
||||
#include "bashhist.h" /* matching prototypes and declarations */
|
||||
#include "builtins/common.h"
|
||||
|
||||
#include <readline/history.h>
|
||||
#include <glob/glob.h>
|
||||
#include <glob/strmatch.h>
|
||||
|
||||
#if defined (READLINE)
|
||||
# include "bashline.h"
|
||||
extern int rl_done, rl_dispatching; /* should really include readline.h */
|
||||
#endif
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
static int histignore_item_func __P((struct ign *));
|
||||
static int check_history_control __P((char *));
|
||||
static void hc_erasedups __P((char *));
|
||||
static void really_add_history __P((char *));
|
||||
|
||||
static struct ignorevar histignore =
|
||||
{
|
||||
"HISTIGNORE",
|
||||
(struct ign *)0,
|
||||
0,
|
||||
(char *)0,
|
||||
(sh_iv_item_func_t *)histignore_item_func,
|
||||
};
|
||||
|
||||
#define HIGN_EXPAND 0x01
|
||||
|
||||
/* Declarations of bash history variables. */
|
||||
/* Non-zero means to remember lines typed to the shell on the history
|
||||
list. This is different than the user-controlled behaviour; this
|
||||
becomes zero when we read lines from a file, for example. */
|
||||
int remember_on_history = 1;
|
||||
int enable_history_list = 1; /* value for `set -o history' */
|
||||
|
||||
/* The number of lines that Bash has added to this history session. The
|
||||
difference between the number of the top element in the history list
|
||||
(offset from history_base) and the number of lines in the history file.
|
||||
Appending this session's history to the history file resets this to 0. */
|
||||
int history_lines_this_session;
|
||||
|
||||
/* The number of lines that Bash has read from the history file. */
|
||||
int history_lines_in_file;
|
||||
|
||||
#if defined (BANG_HISTORY)
|
||||
/* Non-zero means do no history expansion on this line, regardless
|
||||
of what history_expansion says. */
|
||||
int history_expansion_inhibited;
|
||||
#endif
|
||||
|
||||
/* With the old default, every line was saved in the history individually.
|
||||
I.e., if the user enters:
|
||||
bash$ for i in a b c
|
||||
> do
|
||||
> echo $i
|
||||
> done
|
||||
Each line will be individually saved in the history.
|
||||
bash$ history
|
||||
10 for i in a b c
|
||||
11 do
|
||||
12 echo $i
|
||||
13 done
|
||||
14 history
|
||||
If the variable command_oriented_history is set, multiple lines
|
||||
which form one command will be saved as one history entry.
|
||||
bash$ for i in a b c
|
||||
> do
|
||||
> echo $i
|
||||
> done
|
||||
bash$ history
|
||||
10 for i in a b c
|
||||
do
|
||||
echo $i
|
||||
done
|
||||
11 history
|
||||
The user can then recall the whole command all at once instead
|
||||
of just being able to recall one line at a time.
|
||||
|
||||
This is now enabled by default.
|
||||
*/
|
||||
int command_oriented_history = 1;
|
||||
|
||||
/* Set to 1 if the first line of a possibly-multi-line command was saved
|
||||
in the history list. Managed by maybe_add_history(), but global so
|
||||
the history-manipluating builtins can see it. */
|
||||
int current_command_first_line_saved = 0;
|
||||
|
||||
/* Non-zero means to store newlines in the history list when using
|
||||
command_oriented_history rather than trying to use semicolons. */
|
||||
int literal_history;
|
||||
|
||||
/* Non-zero means to append the history to the history file at shell
|
||||
exit, even if the history has been stifled. */
|
||||
int force_append_history;
|
||||
|
||||
/* A nit for picking at history saving. Flags have the following values:
|
||||
|
||||
Value == 0 means save all lines parsed by the shell on the history.
|
||||
Value & HC_IGNSPACE means save all lines that do not start with a space.
|
||||
Value & HC_IGNDUPS means save all lines that do not match the last
|
||||
line saved.
|
||||
Value & HC_ERASEDUPS means to remove all other matching lines from the
|
||||
history list before saving the latest line. */
|
||||
int history_control;
|
||||
|
||||
/* Set to 1 if the last command was added to the history list successfully
|
||||
as a separate history entry; set to 0 if the line was ignored or added
|
||||
to a previous entry as part of command-oriented-history processing. */
|
||||
int hist_last_line_added;
|
||||
|
||||
/* Set to 1 if builtins/history.def:push_history added the last history
|
||||
entry. */
|
||||
int hist_last_line_pushed;
|
||||
|
||||
#if defined (READLINE)
|
||||
/* If non-zero, and readline is being used, the user is offered the
|
||||
chance to re-edit a failed history expansion. */
|
||||
int history_reediting;
|
||||
|
||||
/* If non-zero, and readline is being used, don't directly execute a
|
||||
line with history substitution. Reload it into the editing buffer
|
||||
instead and let the user further edit and confirm with a newline. */
|
||||
int hist_verify;
|
||||
|
||||
#endif /* READLINE */
|
||||
|
||||
/* Non-zero means to not save function definitions in the history list. */
|
||||
int dont_save_function_defs;
|
||||
|
||||
/* Variables declared in other files used here. */
|
||||
extern int current_command_line_count;
|
||||
|
||||
extern struct dstack dstack;
|
||||
|
||||
static int bash_history_inhibit_expansion __P((char *, int));
|
||||
#if defined (READLINE)
|
||||
static void re_edit __P((char *));
|
||||
#endif
|
||||
static int history_expansion_p __P((char *));
|
||||
static int shell_comment __P((char *));
|
||||
static int should_expand __P((char *));
|
||||
static HIST_ENTRY *last_history_entry __P((void));
|
||||
static char *expand_histignore_pattern __P((char *));
|
||||
static int history_should_ignore __P((char *));
|
||||
|
||||
/* Is the history expansion starting at string[i] one that should not
|
||||
be expanded? */
|
||||
static int
|
||||
bash_history_inhibit_expansion (string, i)
|
||||
char *string;
|
||||
int i;
|
||||
{
|
||||
/* The shell uses ! as a pattern negation character in globbing [...]
|
||||
expressions, so let those pass without expansion. */
|
||||
if (i > 0 && (string[i - 1] == '[') && member (']', string + i + 1))
|
||||
return (1);
|
||||
/* The shell uses ! as the indirect expansion character, so let those
|
||||
expansions pass as well. */
|
||||
else if (i > 1 && string[i - 1] == '{' && string[i - 2] == '$' &&
|
||||
member ('}', string + i + 1))
|
||||
return (1);
|
||||
#if defined (EXTENDED_GLOB)
|
||||
else if (extended_glob && i > 1 && string[i+1] == '(' && member (')', string + i + 2))
|
||||
return (1);
|
||||
#endif
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
bash_initialize_history ()
|
||||
{
|
||||
history_quotes_inhibit_expansion = 1;
|
||||
history_search_delimiter_chars = ";&()|<>";
|
||||
history_inhibit_expansion_function = bash_history_inhibit_expansion;
|
||||
#if defined (BANG_HISTORY)
|
||||
sv_histchars ("histchars");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
bash_history_reinit (interact)
|
||||
int interact;
|
||||
{
|
||||
#if defined (BANG_HISTORY)
|
||||
history_expansion = interact != 0;
|
||||
history_expansion_inhibited = 1;
|
||||
#endif
|
||||
remember_on_history = enable_history_list = interact != 0;
|
||||
history_inhibit_expansion_function = bash_history_inhibit_expansion;
|
||||
}
|
||||
|
||||
void
|
||||
bash_history_disable ()
|
||||
{
|
||||
remember_on_history = 0;
|
||||
#if defined (BANG_HISTORY)
|
||||
history_expansion_inhibited = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
bash_history_enable ()
|
||||
{
|
||||
remember_on_history = 1;
|
||||
#if defined (BANG_HISTORY)
|
||||
history_expansion_inhibited = 0;
|
||||
#endif
|
||||
history_inhibit_expansion_function = bash_history_inhibit_expansion;
|
||||
sv_history_control ("HISTCONTROL");
|
||||
sv_histignore ("HISTIGNORE");
|
||||
}
|
||||
|
||||
/* Load the history list from the history file. */
|
||||
void
|
||||
load_history ()
|
||||
{
|
||||
char *hf;
|
||||
|
||||
/* Truncate history file for interactive shells which desire it.
|
||||
Note that the history file is automatically truncated to the
|
||||
size of HISTSIZE if the user does not explicitly set the size
|
||||
differently. */
|
||||
set_if_not ("HISTSIZE", "500");
|
||||
sv_histsize ("HISTSIZE");
|
||||
|
||||
set_if_not ("HISTFILESIZE", get_string_value ("HISTSIZE"));
|
||||
sv_histsize ("HISTFILESIZE");
|
||||
|
||||
/* Read the history in HISTFILE into the history list. */
|
||||
hf = get_string_value ("HISTFILE");
|
||||
|
||||
if (hf && *hf && file_exists (hf))
|
||||
{
|
||||
read_history (hf);
|
||||
using_history ();
|
||||
history_lines_in_file = where_history ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
bash_clear_history ()
|
||||
{
|
||||
clear_history ();
|
||||
history_lines_this_session = 0;
|
||||
}
|
||||
|
||||
/* Delete and free the history list entry at offset I. */
|
||||
int
|
||||
bash_delete_histent (i)
|
||||
int i;
|
||||
{
|
||||
HIST_ENTRY *discard;
|
||||
|
||||
discard = remove_history (i);
|
||||
if (discard)
|
||||
free_history_entry (discard);
|
||||
history_lines_this_session--;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
bash_delete_last_history ()
|
||||
{
|
||||
register int i;
|
||||
HIST_ENTRY **hlist, *histent;
|
||||
int r;
|
||||
|
||||
hlist = history_list ();
|
||||
if (hlist == NULL)
|
||||
return 0;
|
||||
|
||||
for (i = 0; hlist[i]; i++)
|
||||
;
|
||||
i--;
|
||||
|
||||
/* History_get () takes a parameter that must be offset by history_base. */
|
||||
histent = history_get (history_base + i); /* Don't free this */
|
||||
if (histent == NULL)
|
||||
return 0;
|
||||
|
||||
r = bash_delete_histent (i);
|
||||
|
||||
if (where_history () > history_length)
|
||||
history_set_pos (history_length);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_UNUSED
|
||||
/* Write the existing history out to the history file. */
|
||||
void
|
||||
save_history ()
|
||||
{
|
||||
char *hf;
|
||||
|
||||
hf = get_string_value ("HISTFILE");
|
||||
if (hf && *hf && file_exists (hf))
|
||||
{
|
||||
/* Append only the lines that occurred this session to
|
||||
the history file. */
|
||||
using_history ();
|
||||
|
||||
if (history_lines_this_session < where_history () || force_append_history)
|
||||
append_history (history_lines_this_session, hf);
|
||||
else
|
||||
write_history (hf);
|
||||
sv_histsize ("HISTFILESIZE");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
maybe_append_history (filename)
|
||||
char *filename;
|
||||
{
|
||||
int fd, result;
|
||||
struct stat buf;
|
||||
|
||||
result = EXECUTION_SUCCESS;
|
||||
if (history_lines_this_session && (history_lines_this_session < where_history ()))
|
||||
{
|
||||
/* If the filename was supplied, then create it if necessary. */
|
||||
if (stat (filename, &buf) == -1 && errno == ENOENT)
|
||||
{
|
||||
fd = open (filename, O_WRONLY|O_CREAT, 0600);
|
||||
if (fd < 0)
|
||||
{
|
||||
builtin_error (_("%s: cannot create: %s"), filename, strerror (errno));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
close (fd);
|
||||
}
|
||||
result = append_history (history_lines_this_session, filename);
|
||||
history_lines_in_file += history_lines_this_session;
|
||||
history_lines_this_session = 0;
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
||||
/* If this is an interactive shell, then append the lines executed
|
||||
this session to the history file. */
|
||||
int
|
||||
maybe_save_shell_history ()
|
||||
{
|
||||
int result;
|
||||
char *hf;
|
||||
|
||||
result = 0;
|
||||
if (history_lines_this_session)
|
||||
{
|
||||
hf = get_string_value ("HISTFILE");
|
||||
|
||||
if (hf && *hf)
|
||||
{
|
||||
/* If the file doesn't exist, then create it. */
|
||||
if (file_exists (hf) == 0)
|
||||
{
|
||||
int file;
|
||||
file = open (hf, O_CREAT | O_TRUNC | O_WRONLY, 0600);
|
||||
if (file != -1)
|
||||
close (file);
|
||||
}
|
||||
|
||||
/* Now actually append the lines if the history hasn't been
|
||||
stifled. If the history has been stifled, rewrite the
|
||||
history file. */
|
||||
using_history ();
|
||||
if (history_lines_this_session <= where_history () || force_append_history)
|
||||
{
|
||||
result = append_history (history_lines_this_session, hf);
|
||||
history_lines_in_file += history_lines_this_session;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = write_history (hf);
|
||||
history_lines_in_file = history_lines_this_session;
|
||||
}
|
||||
history_lines_this_session = 0;
|
||||
|
||||
sv_histsize ("HISTFILESIZE");
|
||||
}
|
||||
}
|
||||
return (result);
|
||||
}
|
||||
|
||||
#if defined (READLINE)
|
||||
/* Tell readline () that we have some text for it to edit. */
|
||||
static void
|
||||
re_edit (text)
|
||||
char *text;
|
||||
{
|
||||
if (bash_input.type == st_stdin)
|
||||
bash_re_edit (text);
|
||||
}
|
||||
#endif /* READLINE */
|
||||
|
||||
/* Return 1 if this line needs history expansion. */
|
||||
static int
|
||||
history_expansion_p (line)
|
||||
char *line;
|
||||
{
|
||||
register char *s;
|
||||
|
||||
for (s = line; *s; s++)
|
||||
if (*s == history_expansion_char || *s == history_subst_char)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Do pre-processing on LINE. If PRINT_CHANGES is non-zero, then
|
||||
print the results of expanding the line if there were any changes.
|
||||
If there is an error, return NULL, otherwise the expanded line is
|
||||
returned. If ADDIT is non-zero the line is added to the history
|
||||
list after history expansion. ADDIT is just a suggestion;
|
||||
REMEMBER_ON_HISTORY can veto, and does.
|
||||
Right now this does history expansion. */
|
||||
char *
|
||||
pre_process_line (line, print_changes, addit)
|
||||
char *line;
|
||||
int print_changes, addit;
|
||||
{
|
||||
char *history_value;
|
||||
char *return_value;
|
||||
int expanded;
|
||||
|
||||
return_value = line;
|
||||
expanded = 0;
|
||||
|
||||
# if defined (BANG_HISTORY)
|
||||
/* History expand the line. If this results in no errors, then
|
||||
add that line to the history if ADDIT is non-zero. */
|
||||
if (!history_expansion_inhibited && history_expansion && history_expansion_p (line))
|
||||
{
|
||||
expanded = history_expand (line, &history_value);
|
||||
|
||||
if (expanded)
|
||||
{
|
||||
if (print_changes)
|
||||
{
|
||||
if (expanded < 0)
|
||||
internal_error ("%s", history_value);
|
||||
#if defined (READLINE)
|
||||
else if (hist_verify == 0 || expanded == 2)
|
||||
#else
|
||||
else
|
||||
#endif
|
||||
fprintf (stderr, "%s\n", history_value);
|
||||
}
|
||||
|
||||
/* If there was an error, return NULL. */
|
||||
if (expanded < 0 || expanded == 2) /* 2 == print only */
|
||||
{
|
||||
# if defined (READLINE)
|
||||
if (expanded == 2 && rl_dispatching == 0 && *history_value)
|
||||
# else
|
||||
if (expanded == 2 && *history_value)
|
||||
# endif /* !READLINE */
|
||||
maybe_add_history (history_value);
|
||||
|
||||
free (history_value);
|
||||
|
||||
# if defined (READLINE)
|
||||
/* New hack. We can allow the user to edit the
|
||||
failed history expansion. */
|
||||
if (history_reediting && expanded < 0 && rl_done)
|
||||
re_edit (line);
|
||||
# endif /* READLINE */
|
||||
return ((char *)NULL);
|
||||
}
|
||||
|
||||
# if defined (READLINE)
|
||||
if (hist_verify && expanded == 1)
|
||||
{
|
||||
re_edit (history_value);
|
||||
return ((char *)NULL);
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
/* Let other expansions know that return_value can be free'ed,
|
||||
and that a line has been added to the history list. Note
|
||||
that we only add lines that have something in them. */
|
||||
expanded = 1;
|
||||
return_value = history_value;
|
||||
}
|
||||
# endif /* BANG_HISTORY */
|
||||
|
||||
if (addit && remember_on_history && *return_value)
|
||||
maybe_add_history (return_value);
|
||||
|
||||
#if 0
|
||||
if (expanded == 0)
|
||||
return_value = savestring (line);
|
||||
#endif
|
||||
|
||||
return (return_value);
|
||||
}
|
||||
|
||||
/* Return 1 if the first non-whitespace character in LINE is a `#', indicating
|
||||
* that the line is a shell comment. */
|
||||
static int
|
||||
shell_comment (line)
|
||||
char *line;
|
||||
{
|
||||
char *p;
|
||||
|
||||
for (p = line; p && *p && whitespace (*p); p++)
|
||||
;
|
||||
return (p && *p == '#');
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_UNUSED
|
||||
/* Remove shell comments from LINE. A `#' and anything after it is a comment.
|
||||
This isn't really useful yet, since it doesn't handle quoting. */
|
||||
static char *
|
||||
filter_comments (line)
|
||||
char *line;
|
||||
{
|
||||
char *p;
|
||||
|
||||
for (p = line; p && *p && *p != '#'; p++)
|
||||
;
|
||||
if (p && *p == '#')
|
||||
*p = '\0';
|
||||
return (line);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check LINE against what HISTCONTROL says to do. Returns 1 if the line
|
||||
should be saved; 0 if it should be discarded. */
|
||||
static int
|
||||
check_history_control (line)
|
||||
char *line;
|
||||
{
|
||||
HIST_ENTRY *temp;
|
||||
int r;
|
||||
|
||||
if (history_control == 0)
|
||||
return 1;
|
||||
|
||||
/* ignorespace or ignoreboth */
|
||||
if ((history_control & HC_IGNSPACE) && *line == ' ')
|
||||
return 0;
|
||||
|
||||
/* ignoredups or ignoreboth */
|
||||
if (history_control & HC_IGNDUPS)
|
||||
{
|
||||
using_history ();
|
||||
temp = previous_history ();
|
||||
|
||||
r = (temp == 0 || STREQ (temp->line, line) == 0);
|
||||
|
||||
using_history ();
|
||||
|
||||
if (r == 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Remove all entries matching LINE from the history list. Triggered when
|
||||
HISTCONTROL includes `erasedups'. */
|
||||
static void
|
||||
hc_erasedups (line)
|
||||
char *line;
|
||||
{
|
||||
HIST_ENTRY *temp;
|
||||
int r;
|
||||
|
||||
using_history ();
|
||||
while (temp = previous_history ())
|
||||
{
|
||||
if (STREQ (temp->line, line))
|
||||
{
|
||||
r = where_history ();
|
||||
remove_history (r);
|
||||
}
|
||||
}
|
||||
using_history ();
|
||||
}
|
||||
|
||||
/* Add LINE to the history list, handling possibly multi-line compound
|
||||
commands. We note whether or not we save the first line of each command
|
||||
(which is usually the entire command and history entry), and don't add
|
||||
the second and subsequent lines of a multi-line compound command if we
|
||||
didn't save the first line. We don't usually save shell comment lines in
|
||||
compound commands in the history, because they could have the effect of
|
||||
commenting out the rest of the command when the entire command is saved as
|
||||
a single history entry (when COMMAND_ORIENTED_HISTORY is enabled). If
|
||||
LITERAL_HISTORY is set, we're saving lines in the history with embedded
|
||||
newlines, so it's OK to save comment lines. We also make sure to save
|
||||
multiple-line quoted strings or other constructs. */
|
||||
void
|
||||
maybe_add_history (line)
|
||||
char *line;
|
||||
{
|
||||
hist_last_line_added = 0;
|
||||
|
||||
/* Don't use the value of history_control to affect the second
|
||||
and subsequent lines of a multi-line command (old code did
|
||||
this only when command_oriented_history is enabled). */
|
||||
if (current_command_line_count > 1)
|
||||
{
|
||||
if (current_command_first_line_saved &&
|
||||
(literal_history || dstack.delimiter_depth != 0 || shell_comment (line) == 0))
|
||||
bash_add_history (line);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This is the first line of a (possible multi-line) command. Note whether
|
||||
or not we should save the first line and remember it. */
|
||||
current_command_first_line_saved = check_add_history (line, 0);
|
||||
}
|
||||
|
||||
/* Just check LINE against HISTCONTROL and HISTIGNORE and add it to the
|
||||
history if it's OK. Used by `history -s' as well as maybe_add_history().
|
||||
Returns 1 if the line was saved in the history, 0 otherwise. */
|
||||
int
|
||||
check_add_history (line, force)
|
||||
char *line;
|
||||
int force;
|
||||
{
|
||||
if (check_history_control (line) && history_should_ignore (line) == 0)
|
||||
{
|
||||
/* We're committed to saving the line. If the user has requested it,
|
||||
remove other matching lines from the history. */
|
||||
if (history_control & HC_ERASEDUPS)
|
||||
hc_erasedups (line);
|
||||
|
||||
if (force)
|
||||
{
|
||||
really_add_history (line);
|
||||
using_history ();
|
||||
}
|
||||
else
|
||||
bash_add_history (line);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined (SYSLOG_HISTORY)
|
||||
#define SYSLOG_MAXLEN 600
|
||||
|
||||
void
|
||||
bash_syslog_history (line)
|
||||
const char *line;
|
||||
{
|
||||
char trunc[SYSLOG_MAXLEN];
|
||||
|
||||
if (strlen(line) < SYSLOG_MAXLEN)
|
||||
syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY: PID=%d UID=%d %s", getpid(), current_user.uid, line);
|
||||
else
|
||||
{
|
||||
strncpy (trunc, line, SYSLOG_MAXLEN);
|
||||
trunc[SYSLOG_MAXLEN - 1] = '\0';
|
||||
syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY (TRUNCATED): PID=%d UID=%d %s", getpid(), current_user.uid, trunc);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Add a line to the history list.
|
||||
The variable COMMAND_ORIENTED_HISTORY controls the style of history
|
||||
remembering; when non-zero, and LINE is not the first line of a
|
||||
complete parser construct, append LINE to the last history line instead
|
||||
of adding it as a new line. */
|
||||
void
|
||||
bash_add_history (line)
|
||||
char *line;
|
||||
{
|
||||
int add_it, offset, curlen;
|
||||
HIST_ENTRY *current, *old;
|
||||
char *chars_to_add, *new_line;
|
||||
|
||||
add_it = 1;
|
||||
if (command_oriented_history && current_command_line_count > 1)
|
||||
{
|
||||
chars_to_add = literal_history ? "\n" : history_delimiting_chars ();
|
||||
|
||||
using_history ();
|
||||
current = previous_history ();
|
||||
|
||||
if (current)
|
||||
{
|
||||
/* If the previous line ended with an escaped newline (escaped
|
||||
with backslash, but otherwise unquoted), then remove the quoted
|
||||
newline, since that is what happens when the line is parsed. */
|
||||
curlen = strlen (current->line);
|
||||
|
||||
if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\\' &&
|
||||
current->line[curlen - 2] != '\\')
|
||||
{
|
||||
current->line[curlen - 1] = '\0';
|
||||
curlen--;
|
||||
chars_to_add = "";
|
||||
}
|
||||
|
||||
new_line = (char *)xmalloc (1
|
||||
+ curlen
|
||||
+ strlen (line)
|
||||
+ strlen (chars_to_add));
|
||||
sprintf (new_line, "%s%s%s", current->line, chars_to_add, line);
|
||||
offset = where_history ();
|
||||
old = replace_history_entry (offset, new_line, current->data);
|
||||
free (new_line);
|
||||
|
||||
if (old)
|
||||
free_history_entry (old);
|
||||
|
||||
add_it = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (add_it)
|
||||
really_add_history (line);
|
||||
|
||||
#if defined (SYSLOG_HISTORY)
|
||||
bash_syslog_history (line);
|
||||
#endif
|
||||
|
||||
using_history ();
|
||||
}
|
||||
|
||||
static void
|
||||
really_add_history (line)
|
||||
char *line;
|
||||
{
|
||||
hist_last_line_added = 1;
|
||||
hist_last_line_pushed = 0;
|
||||
add_history (line);
|
||||
history_lines_this_session++;
|
||||
}
|
||||
|
||||
int
|
||||
history_number ()
|
||||
{
|
||||
using_history ();
|
||||
return (remember_on_history ? history_base + where_history () : 1);
|
||||
}
|
||||
|
||||
static int
|
||||
should_expand (s)
|
||||
char *s;
|
||||
{
|
||||
char *p;
|
||||
|
||||
for (p = s; p && *p; p++)
|
||||
{
|
||||
if (*p == '\\')
|
||||
p++;
|
||||
else if (*p == '&')
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
histignore_item_func (ign)
|
||||
struct ign *ign;
|
||||
{
|
||||
if (should_expand (ign->val))
|
||||
ign->flags |= HIGN_EXPAND;
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
setup_history_ignore (varname)
|
||||
char *varname;
|
||||
{
|
||||
setup_ignore_patterns (&histignore);
|
||||
}
|
||||
|
||||
static HIST_ENTRY *
|
||||
last_history_entry ()
|
||||
{
|
||||
HIST_ENTRY *he;
|
||||
|
||||
using_history ();
|
||||
he = previous_history ();
|
||||
using_history ();
|
||||
return he;
|
||||
}
|
||||
|
||||
char *
|
||||
last_history_line ()
|
||||
{
|
||||
HIST_ENTRY *he;
|
||||
|
||||
he = last_history_entry ();
|
||||
if (he == 0)
|
||||
return ((char *)NULL);
|
||||
return he->line;
|
||||
}
|
||||
|
||||
static char *
|
||||
expand_histignore_pattern (pat)
|
||||
char *pat;
|
||||
{
|
||||
HIST_ENTRY *phe;
|
||||
char *ret;
|
||||
|
||||
phe = last_history_entry ();
|
||||
|
||||
if (phe == (HIST_ENTRY *)0)
|
||||
return (savestring (pat));
|
||||
|
||||
ret = strcreplace (pat, '&', phe->line, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Return 1 if we should not put LINE into the history according to the
|
||||
patterns in HISTIGNORE. */
|
||||
static int
|
||||
history_should_ignore (line)
|
||||
char *line;
|
||||
{
|
||||
register int i, match;
|
||||
char *npat;
|
||||
|
||||
if (histignore.num_ignores == 0)
|
||||
return 0;
|
||||
|
||||
for (i = match = 0; i < histignore.num_ignores; i++)
|
||||
{
|
||||
if (histignore.ignores[i].flags & HIGN_EXPAND)
|
||||
npat = expand_histignore_pattern (histignore.ignores[i].val);
|
||||
else
|
||||
npat = histignore.ignores[i].val;
|
||||
|
||||
match = strmatch (npat, line, FNMATCH_EXTFLAG) != FNM_NOMATCH;
|
||||
|
||||
if (histignore.ignores[i].flags & HIGN_EXPAND)
|
||||
free (npat);
|
||||
|
||||
if (match)
|
||||
break;
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
#endif /* HISTORY */
|
||||
+2
-1
@@ -615,13 +615,14 @@ add_char:
|
||||
if (unbuffered_read == 0)
|
||||
zsyncfd (fd);
|
||||
|
||||
discard_unwind_frame ("read_builtin");
|
||||
|
||||
retval = eof ? EXECUTION_FAILURE : EXECUTION_SUCCESS;
|
||||
|
||||
assign_vars:
|
||||
|
||||
interrupt_immediately--;
|
||||
terminate_immediately--;
|
||||
discard_unwind_frame ("read_builtin");
|
||||
|
||||
#if defined (ARRAY_VARS)
|
||||
/* If -a was given, take the string read, break it into a list of words,
|
||||
|
||||
+1
-1
@@ -3902,7 +3902,7 @@ turned on to be used in an expression.
|
||||
.PP
|
||||
Constants with a leading 0 are interpreted as octal numbers.
|
||||
A leading 0x or 0X denotes hexadecimal.
|
||||
Otherwise, numbers take the form [\fIbase#\fP]n, where \fIbase\fP
|
||||
Otherwise, numbers take the form [\fIbase#\fP]n, where the optional \fIbase\fP
|
||||
is a decimal number between 2 and 64 representing the arithmetic
|
||||
base, and \fIn\fP is a number in that base.
|
||||
If \fIbase#\fP is omitted, then base 10 is used.
|
||||
|
||||
+6
-5
@@ -6055,7 +6055,7 @@ exit status of 124. If a shell function returns 124, and changes
|
||||
the compspec associated with the command on which completion is being
|
||||
attempted (supplied as the first argument when the function is executed),
|
||||
programmable completion restarts from the beginning, with an
|
||||
attempt to find a compspec for that command. This allows a set of
|
||||
attempt to find a new compspec for that command. This allows a set of
|
||||
completions to be built dynamically as completion is attempted, rather than
|
||||
being loaded all at once.
|
||||
.PP
|
||||
@@ -7642,12 +7642,13 @@ It returns false if the end of options is encountered or an
|
||||
error occurs.
|
||||
.TP
|
||||
\fBhash\fP [\fB\-lr\fP] [\fB\-p\fP \fIfilename\fP] [\fB\-dt\fP] [\fIname\fP]
|
||||
For each
|
||||
.IR name ,
|
||||
the full file name of the command is determined by searching
|
||||
Each time \fBhash\fP is invoked,
|
||||
the full pathname of the command
|
||||
.I name
|
||||
is determined by searching
|
||||
the directories in
|
||||
.B $PATH
|
||||
and remembered.
|
||||
and remembered. Any previously-remembered pathname is discarded.
|
||||
If the
|
||||
.B \-p
|
||||
option is supplied, no path search is performed, and
|
||||
|
||||
+1
-1
@@ -5966,7 +5966,7 @@ to be used in an expression.
|
||||
|
||||
Constants with a leading 0 are interpreted as octal numbers.
|
||||
A leading @samp{0x} or @samp{0X} denotes hexadecimal. Otherwise,
|
||||
numbers take the form [@var{base}@code{#}]@var{n}, where @var{base}
|
||||
numbers take the form [@var{base}@code{#}]@var{n}, where the optional @var{base}
|
||||
is a decimal number between 2 and 64 representing the arithmetic
|
||||
base, and @var{n} is a number in that base. If @var{base}@code{#} is
|
||||
omitted, then base 10 is used.
|
||||
|
||||
+7
-6
@@ -2986,10 +2986,12 @@ If @code{getopts} is silent, then a colon (@samp{:}) is placed in
|
||||
@example
|
||||
hash [-r] [-p @var{filename}] [-dt] [@var{name}]
|
||||
@end example
|
||||
Remember the full pathnames of commands specified as @var{name} arguments,
|
||||
Each time @code{hash} is invoked, it remembers the full pathnames of the
|
||||
commands specified as @var{name} arguments,
|
||||
so they need not be searched for on subsequent invocations.
|
||||
The commands are found by searching through the directories listed in
|
||||
@env{$PATH}.
|
||||
Any previously-remembered pathname is discarded.
|
||||
The @option{-p} option inhibits the path search, and @var{filename} is
|
||||
used as the location of @var{name}.
|
||||
The @option{-r} option causes the shell to forget all remembered locations.
|
||||
@@ -5316,8 +5318,9 @@ bash [long-opt] [-abefhkmnptuvxdBCDHP] [-o @var{option}] [-O @var{shopt_option}]
|
||||
bash [long-opt] -s [-abefhkmnptuvxdBCDHP] [-o @var{option}] [-O @var{shopt_option}] [@var{argument} @dots{}]
|
||||
@end example
|
||||
|
||||
In addition to the single-character shell command-line options
|
||||
(@pxref{The Set Builtin}), there are several multi-character
|
||||
All of the single-character options used with the @code{set} builtin
|
||||
(@pxref{The Set Builtin}) can be used as options when the shell is invoked.
|
||||
In addition, there are several multi-character
|
||||
options that you can use. These options must appear on the command
|
||||
line before the single-character options to be recognized.
|
||||
|
||||
@@ -5326,9 +5329,7 @@ line before the single-character options to be recognized.
|
||||
Arrange for the debugger profile to be executed before the shell
|
||||
starts. Turns on extended debugging mode (see @ref{The Shopt Builtin}
|
||||
for a description of the @code{extdebug} option to the @code{shopt}
|
||||
builtin) and shell function tracing
|
||||
(see @ref{The Set Builtin} for a description of the @code{-o functrace}
|
||||
option).
|
||||
builtin).
|
||||
|
||||
@item --dump-po-strings
|
||||
A list of all double-quoted strings preceded by @samp{$}
|
||||
|
||||
@@ -4816,7 +4816,15 @@ history_delimiting_chars ()
|
||||
add the first line of the body of the here document (the second line
|
||||
of the command). */
|
||||
if (parser_state & PST_HEREDOC)
|
||||
#if 0
|
||||
{
|
||||
if (last_read_token == WORD && (token_before_that == LESS_LESS || token_before_that == LESS_LESS_MINUS))
|
||||
return ("\n"); /* XXX -- need to clean up in bash_add_history */
|
||||
#endif
|
||||
return (current_command_line_count == 2 ? "\n" : "");
|
||||
#if 0
|
||||
}
|
||||
#endif
|
||||
|
||||
/* First, handle some special cases. */
|
||||
/*(*/
|
||||
|
||||
@@ -4816,7 +4816,11 @@ history_delimiting_chars ()
|
||||
add the first line of the body of the here document (the second line
|
||||
of the command). */
|
||||
if (parser_state & PST_HEREDOC)
|
||||
{
|
||||
if (last_read_token == WORD && (token_before_that == LESS_LESS || token_before_that == LESS_LESS_MINUS))
|
||||
return ("\n"); /* XXX -- need to clean up in bash_add_history */
|
||||
return (current_command_line_count == 2 ? "\n" : "");
|
||||
}
|
||||
|
||||
/* First, handle some special cases. */
|
||||
/*(*/
|
||||
@@ -5780,10 +5784,6 @@ sh_parser_state_t *
|
||||
save_parser_state (ps)
|
||||
sh_parser_state_t *ps;
|
||||
{
|
||||
#if defined (ARRAY_VARS)
|
||||
SHELL_VAR *v;
|
||||
#endif
|
||||
|
||||
if (ps == 0)
|
||||
ps = (sh_parser_state_t *)xmalloc (sizeof (sh_parser_state_t));
|
||||
if (ps == 0)
|
||||
@@ -5806,11 +5806,7 @@ save_parser_state (ps)
|
||||
|
||||
ps->last_command_exit_value = last_command_exit_value;
|
||||
#if defined (ARRAY_VARS)
|
||||
v = find_variable ("PIPESTATUS");
|
||||
if (v && array_p (v) && array_cell (v))
|
||||
ps->pipestatus = array_copy (array_cell (v));
|
||||
else
|
||||
ps->pipestatus = (ARRAY *)NULL;
|
||||
ps->pipestatus = save_pipestatus_array ();
|
||||
#endif
|
||||
|
||||
ps->last_shell_builtin = last_shell_builtin;
|
||||
@@ -5826,10 +5822,6 @@ void
|
||||
restore_parser_state (ps)
|
||||
sh_parser_state_t *ps;
|
||||
{
|
||||
#if defined (ARRAY_VARS)
|
||||
SHELL_VAR *v;
|
||||
#endif
|
||||
|
||||
if (ps == 0)
|
||||
return;
|
||||
|
||||
@@ -5854,12 +5846,7 @@ restore_parser_state (ps)
|
||||
|
||||
last_command_exit_value = ps->last_command_exit_value;
|
||||
#if defined (ARRAY_VARS)
|
||||
v = find_variable ("PIPESTATUS");
|
||||
if (v && array_p (v) && array_cell (v))
|
||||
{
|
||||
array_dispose (array_cell (v));
|
||||
var_setarray (v, ps->pipestatus);
|
||||
}
|
||||
restore_pipestatus_array (ps->pipestatus);
|
||||
#endif
|
||||
|
||||
last_shell_builtin = ps->last_shell_builtin;
|
||||
|
||||
+35
-37
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: bash 4.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-12-30 08:25-0500\n"
|
||||
"PO-Revision-Date: 2010-01-28 10:27+0800\n"
|
||||
"PO-Revision-Date: 2010-03-14 16:16+0800\n"
|
||||
"Last-Translator: Xin Ye <alyex.ye@gmail.com>\n"
|
||||
"Language-Team: Chinese (simplified) <i18n-zh@googlegroups.com>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -101,7 +101,7 @@ msgstr "%s 未与任何键绑定。\n"
|
||||
#: builtins/bind.def:307
|
||||
#, c-format
|
||||
msgid "%s can be invoked via "
|
||||
msgstr ""
|
||||
msgstr "%s 可以被调用,通过"
|
||||
|
||||
#: builtins/break.def:77 builtins/break.def:117
|
||||
msgid "loop count"
|
||||
@@ -439,17 +439,17 @@ msgstr "%s: 选项需要一个参数 -- %c\n"
|
||||
|
||||
#: builtins/hash.def:92
|
||||
msgid "hashing disabled"
|
||||
msgstr ""
|
||||
msgstr "哈希已禁用"
|
||||
|
||||
#: builtins/hash.def:138
|
||||
#, c-format
|
||||
msgid "%s: hash table empty\n"
|
||||
msgstr ""
|
||||
msgstr "%s: 哈希表为空\n"
|
||||
|
||||
#: builtins/hash.def:244
|
||||
#, c-format
|
||||
msgid "hits\tcommand\n"
|
||||
msgstr ""
|
||||
msgstr "命中\t命令\n"
|
||||
|
||||
#: builtins/help.def:130
|
||||
#, c-format
|
||||
@@ -495,7 +495,7 @@ msgstr "%s: 历史扩展失败"
|
||||
#: builtins/inlib.def:71
|
||||
#, c-format
|
||||
msgid "%s: inlib failed"
|
||||
msgstr ""
|
||||
msgstr "%s: inlib 失败"
|
||||
|
||||
#: builtins/jobs.def:109
|
||||
msgid "no other options allowed with `-x'"
|
||||
@@ -537,12 +537,12 @@ msgstr "%s: 无效的行数"
|
||||
#: builtins/mapfile.def:277
|
||||
#, c-format
|
||||
msgid "%s: invalid array origin"
|
||||
msgstr ""
|
||||
msgstr "%s: 无效的数组基数"
|
||||
|
||||
#: builtins/mapfile.def:294
|
||||
#, c-format
|
||||
msgid "%s: invalid callback quantum"
|
||||
msgstr ""
|
||||
msgstr "%s: 无效的回调量子"
|
||||
|
||||
#: builtins/mapfile.def:326
|
||||
msgid "empty array variable name"
|
||||
@@ -569,7 +569,7 @@ msgstr "警告: %s: %s"
|
||||
|
||||
#: builtins/printf.def:757
|
||||
msgid "missing hex digit for \\x"
|
||||
msgstr ""
|
||||
msgstr "使用了\\x但缺少十六进制数"
|
||||
|
||||
#: builtins/pushd.def:195
|
||||
msgid "no other directory"
|
||||
@@ -695,7 +695,7 @@ msgstr "%s: 不是函数"
|
||||
|
||||
#: builtins/shift.def:71 builtins/shift.def:77
|
||||
msgid "shift count"
|
||||
msgstr ""
|
||||
msgstr "位移计数"
|
||||
|
||||
#: builtins/shopt.def:260
|
||||
msgid "cannot set and unset shell options simultaneously"
|
||||
@@ -720,7 +720,6 @@ msgid "cannot suspend"
|
||||
msgstr "无法挂起"
|
||||
|
||||
#: builtins/suspend.def:111
|
||||
#, fuzzy
|
||||
msgid "cannot suspend a login shell"
|
||||
msgstr "无法挂起一个登录shell"
|
||||
|
||||
@@ -752,12 +751,12 @@ msgstr "%s 是 %s\n"
|
||||
#: builtins/type.def:337
|
||||
#, c-format
|
||||
msgid "%s is hashed (%s)\n"
|
||||
msgstr ""
|
||||
msgstr "%s 已被哈希 (%s)\n"
|
||||
|
||||
#: builtins/ulimit.def:372
|
||||
#, c-format
|
||||
msgid "%s: invalid limit argument"
|
||||
msgstr ""
|
||||
msgstr "%s: 无效的 limit 参数"
|
||||
|
||||
#: builtins/ulimit.def:398
|
||||
#, c-format
|
||||
@@ -767,16 +766,16 @@ msgstr "`%c': 坏的命令"
|
||||
#: builtins/ulimit.def:427
|
||||
#, c-format
|
||||
msgid "%s: cannot get limit: %s"
|
||||
msgstr ""
|
||||
msgstr "%s: 无法获取 limit 值: %s"
|
||||
|
||||
#: builtins/ulimit.def:453
|
||||
msgid "limit"
|
||||
msgstr ""
|
||||
msgstr "limit"
|
||||
|
||||
#: builtins/ulimit.def:465 builtins/ulimit.def:765
|
||||
#, c-format
|
||||
msgid "%s: cannot modify limit: %s"
|
||||
msgstr ""
|
||||
msgstr "%s: 无法修改 limit 值: %s"
|
||||
|
||||
#: builtins/umask.def:118
|
||||
msgid "octal number"
|
||||
@@ -785,12 +784,12 @@ msgstr "八进制数"
|
||||
#: builtins/umask.def:231
|
||||
#, c-format
|
||||
msgid "`%c': invalid symbolic mode operator"
|
||||
msgstr ""
|
||||
msgstr "`%c': 无效的符号状态运算符"
|
||||
|
||||
#: builtins/umask.def:286
|
||||
#, c-format
|
||||
msgid "`%c': invalid symbolic mode character"
|
||||
msgstr ""
|
||||
msgstr "`%c': 无效的符号状态字符"
|
||||
|
||||
#: error.c:90 error.c:321 error.c:323 error.c:325
|
||||
msgid " line "
|
||||
@@ -816,11 +815,11 @@ msgstr "坏的命令类型"
|
||||
|
||||
#: error.c:408
|
||||
msgid "bad connector"
|
||||
msgstr ""
|
||||
msgstr "坏的条件连接符"
|
||||
|
||||
#: error.c:409
|
||||
msgid "bad jump"
|
||||
msgstr ""
|
||||
msgstr "坏的跳转"
|
||||
|
||||
#: error.c:447
|
||||
#, c-format
|
||||
@@ -830,7 +829,7 @@ msgstr "%s: 为绑定变量"
|
||||
#: eval.c:181
|
||||
#, c-format
|
||||
msgid "\atimed out waiting for input: auto-logout\n"
|
||||
msgstr ""
|
||||
msgstr "\a 等待输入超时:自动登出\n"
|
||||
|
||||
#: execute_cmd.c:497
|
||||
#, c-format
|
||||
@@ -864,7 +863,7 @@ msgstr "%s: %s: 坏的解释器"
|
||||
#: execute_cmd.c:4976
|
||||
#, c-format
|
||||
msgid "cannot duplicate fd %d to fd %d"
|
||||
msgstr ""
|
||||
msgstr "无法复制文件描述符 %d 到 文件描述符 %d"
|
||||
|
||||
#: expr.c:241
|
||||
msgid "expression recursion level exceeded"
|
||||
@@ -872,7 +871,7 @@ msgstr "表达式递归层次越界"
|
||||
|
||||
#: expr.c:265
|
||||
msgid "recursion stack underflow"
|
||||
msgstr ""
|
||||
msgstr "递归栈下溢"
|
||||
|
||||
#: expr.c:379
|
||||
msgid "syntax error in expression"
|
||||
@@ -888,7 +887,7 @@ msgstr "除0"
|
||||
|
||||
#: expr.c:471
|
||||
msgid "bug: bad expassign token"
|
||||
msgstr ""
|
||||
msgstr "错误:坏的表达式赋值符号"
|
||||
|
||||
#: expr.c:513
|
||||
msgid "`:' expected for conditional expression"
|
||||
@@ -896,11 +895,11 @@ msgstr "条件表达式期待 `:'"
|
||||
|
||||
#: expr.c:781
|
||||
msgid "exponent less than 0"
|
||||
msgstr ""
|
||||
msgstr "指数小于0"
|
||||
|
||||
#: expr.c:826
|
||||
msgid "identifier expected after pre-increment or pre-decrement"
|
||||
msgstr ""
|
||||
msgstr "预增符或预减符后应跟有标识符"
|
||||
|
||||
#: expr.c:854
|
||||
msgid "missing `)'"
|
||||
@@ -917,15 +916,15 @@ msgstr "语法错误: 无效的算术运算符"
|
||||
#: expr.c:1202
|
||||
#, c-format
|
||||
msgid "%s%s%s: %s (error token is \"%s\")"
|
||||
msgstr ""
|
||||
msgstr "%s%s%s: %s (错误符号是 \"%s\")"
|
||||
|
||||
#: expr.c:1260
|
||||
msgid "invalid arithmetic base"
|
||||
msgstr ""
|
||||
msgstr "无效的算数进制"
|
||||
|
||||
#: expr.c:1280
|
||||
msgid "value too great for base"
|
||||
msgstr ""
|
||||
msgstr "数值太大不可为算数进制的基"
|
||||
|
||||
#: expr.c:1329
|
||||
#, c-format
|
||||
@@ -939,17 +938,17 @@ msgstr "getcwd: 无法访问父目录"
|
||||
#: input.c:94 subst.c:4857
|
||||
#, c-format
|
||||
msgid "cannot reset nodelay mode for fd %d"
|
||||
msgstr ""
|
||||
msgstr "无法为文件描述符 %d 重置nodelay模式"
|
||||
|
||||
#: input.c:258
|
||||
#, c-format
|
||||
msgid "cannot allocate new file descriptor for bash input from fd %d"
|
||||
msgstr ""
|
||||
msgstr "无法从文件描述符 %d 为 bash 的输入获取一个新的文件描述符"
|
||||
|
||||
#: input.c:266
|
||||
#, c-format
|
||||
msgid "save_bash_input: buffer already exists for new fd %d"
|
||||
msgstr ""
|
||||
msgstr "save_bash_input: 已经存在新的文件描述符 %d 的缓冲区"
|
||||
|
||||
#: jobs.c:466
|
||||
msgid "start_pipeline: pgrp pipe"
|
||||
@@ -1203,7 +1202,6 @@ msgid "setlocale: %s: cannot change locale (%s): %s"
|
||||
msgstr ""
|
||||
|
||||
#: mailcheck.c:433
|
||||
#, fuzzy
|
||||
msgid "You have mail in $_"
|
||||
msgstr "您在 $_ 中有邮件"
|
||||
|
||||
@@ -1837,17 +1835,17 @@ msgstr ""
|
||||
|
||||
#: version.c:47
|
||||
msgid "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n"
|
||||
msgstr ""
|
||||
msgstr "许可证 GPLv3+: GNU GPL 许可证版本3或者更高 <http://gnu.org/licenses/gpl.html>\n"
|
||||
|
||||
#: version.c:86 version2.c:83
|
||||
#, c-format
|
||||
msgid "GNU bash, version %s (%s)\n"
|
||||
msgstr ""
|
||||
msgstr "GNU bash, 版本 %s (%s)\n"
|
||||
|
||||
#: version.c:91 version2.c:88
|
||||
#, c-format
|
||||
msgid "This is free software; you are free to change and redistribute it.\n"
|
||||
msgstr ""
|
||||
msgstr "这是自由软件,您可以自由地更改和重新发布。\n"
|
||||
|
||||
#: version.c:92 version2.c:89
|
||||
#, c-format
|
||||
@@ -1862,7 +1860,7 @@ msgstr ""
|
||||
#: version2.c:87
|
||||
#, c-format
|
||||
msgid "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
|
||||
msgstr ""
|
||||
msgstr "许可证 GPLv2+: GNU GPL 许可证版本2或者更高 <http://gnu.org/licenses/gpl.html>\n"
|
||||
|
||||
#: xmalloc.c:91
|
||||
#, c-format
|
||||
|
||||
+17
-4
@@ -113,6 +113,12 @@ FILE *xtrace_fp = 0;
|
||||
|
||||
#define CHECK_XTRACE_FP xtrace_fp = (xtrace_fp ? xtrace_fp : stderr)
|
||||
|
||||
#define PRINT_DEFERRED_HEREDOCS(x) \
|
||||
do { \
|
||||
if (deferred_heredocs) \
|
||||
print_deferred_heredocs (x); \
|
||||
} while (0)
|
||||
|
||||
/* Non-zero means the stuff being printed is inside of a function def. */
|
||||
static int inside_function_def;
|
||||
static int skip_this_indent;
|
||||
@@ -293,8 +299,7 @@ make_command_string_internal (command)
|
||||
}
|
||||
|
||||
make_command_string_internal (command->value.Connection->second);
|
||||
if (deferred_heredocs)
|
||||
print_deferred_heredocs ("");
|
||||
PRINT_DEFERRED_HEREDOCS ("");
|
||||
printing_connection--;
|
||||
break;
|
||||
|
||||
@@ -560,13 +565,15 @@ print_for_command (for_command)
|
||||
FOR_COM *for_command;
|
||||
{
|
||||
print_for_command_head (for_command);
|
||||
|
||||
cprintf (";");
|
||||
newline ("do\n");
|
||||
|
||||
indentation += indentation_amount;
|
||||
make_command_string_internal (for_command->action);
|
||||
PRINT_DEFERRED_HEREDOCS ("");
|
||||
semicolon ();
|
||||
indentation -= indentation_amount;
|
||||
|
||||
newline ("done");
|
||||
}
|
||||
|
||||
@@ -620,6 +627,7 @@ print_select_command (select_command)
|
||||
newline ("do\n");
|
||||
indentation += indentation_amount;
|
||||
make_command_string_internal (select_command->action);
|
||||
PRINT_DEFERRED_HEREDOCS ("");
|
||||
semicolon ();
|
||||
indentation -= indentation_amount;
|
||||
newline ("done");
|
||||
@@ -703,6 +711,7 @@ print_case_clauses (clauses)
|
||||
indentation += indentation_amount;
|
||||
make_command_string_internal (clauses->action);
|
||||
indentation -= indentation_amount;
|
||||
PRINT_DEFERRED_HEREDOCS ("");
|
||||
if (clauses->flags & CASEPAT_FALLTHROUGH)
|
||||
newline (";&");
|
||||
else if (clauses->flags & CASEPAT_TESTNEXT)
|
||||
@@ -736,10 +745,12 @@ print_until_or_while (while_command, which)
|
||||
cprintf ("%s ", which);
|
||||
skip_this_indent++;
|
||||
make_command_string_internal (while_command->test);
|
||||
PRINT_DEFERRED_HEREDOCS ("");
|
||||
semicolon ();
|
||||
cprintf (" do\n"); /* was newline ("do\n"); */
|
||||
indentation += indentation_amount;
|
||||
make_command_string_internal (while_command->action);
|
||||
PRINT_DEFERRED_HEREDOCS ("");
|
||||
indentation -= indentation_amount;
|
||||
semicolon ();
|
||||
newline ("done");
|
||||
@@ -756,6 +767,7 @@ print_if_command (if_command)
|
||||
cprintf (" then\n");
|
||||
indentation += indentation_amount;
|
||||
make_command_string_internal (if_command->true_case);
|
||||
PRINT_DEFERRED_HEREDOCS ("");
|
||||
indentation -= indentation_amount;
|
||||
|
||||
if (if_command->false_case)
|
||||
@@ -764,6 +776,7 @@ print_if_command (if_command)
|
||||
newline ("else\n");
|
||||
indentation += indentation_amount;
|
||||
make_command_string_internal (if_command->false_case);
|
||||
PRINT_DEFERRED_HEREDOCS ("");
|
||||
indentation -= indentation_amount;
|
||||
}
|
||||
semicolon ();
|
||||
@@ -943,7 +956,7 @@ print_deferred_heredocs (cstring)
|
||||
cprintf (" ");
|
||||
print_heredoc_header (hdtail);
|
||||
}
|
||||
if (cstring[0] != ';' || cstring[1])
|
||||
if (cstring[0] && (cstring[0] != ';' || cstring[1]))
|
||||
cprintf ("%s", cstring);
|
||||
if (deferred_heredocs)
|
||||
cprintf ("\n");
|
||||
|
||||
+4
-3
@@ -155,7 +155,7 @@ static void
|
||||
make_command_string_internal (command)
|
||||
COMMAND *command;
|
||||
{
|
||||
char s[3], *op;
|
||||
char s[3];
|
||||
|
||||
if (command == 0)
|
||||
cprintf ("");
|
||||
@@ -565,6 +565,8 @@ print_for_command (for_command)
|
||||
newline ("do\n");
|
||||
indentation += indentation_amount;
|
||||
make_command_string_internal (for_command->action);
|
||||
if (deferred_heredocs)
|
||||
print_deferred_heredocs ("");
|
||||
semicolon ();
|
||||
indentation -= indentation_amount;
|
||||
newline ("done");
|
||||
@@ -1052,10 +1054,9 @@ static void
|
||||
print_redirection (redirect)
|
||||
REDIRECT *redirect;
|
||||
{
|
||||
int kill_leading, redirector, redir_fd;
|
||||
int redirector, redir_fd;
|
||||
WORD_DESC *redirectee, *redir_word;
|
||||
|
||||
kill_leading = 0;
|
||||
redirectee = redirect->redirectee.filename;
|
||||
redir_fd = redirect->redirectee.dest;
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
BUILD_DIR=/usr/local/build/chet/bash/bash-current
|
||||
BUILD_DIR=/usr/local/build/bash/bash-current
|
||||
THIS_SH=$BUILD_DIR/bash
|
||||
PATH=$PATH:$BUILD_DIR
|
||||
|
||||
|
||||
@@ -80,3 +80,19 @@ qux
|
||||
|
||||
bar
|
||||
qux
|
||||
foo is a function
|
||||
foo ()
|
||||
{
|
||||
rm -f a b c;
|
||||
for f in a b c;
|
||||
do
|
||||
cat >> ${f} <<-EOF
|
||||
file
|
||||
EOF
|
||||
|
||||
done
|
||||
grep . a b c
|
||||
}
|
||||
a:file
|
||||
b:file
|
||||
c:file
|
||||
|
||||
@@ -91,3 +91,5 @@ type f | cat -v
|
||||
${THIS_SH} type1.sub
|
||||
|
||||
${THIS_SH} type2.sub
|
||||
|
||||
${THIS_SH} type3.sub
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
set +o posix
|
||||
|
||||
hash -r
|
||||
unalias -a
|
||||
|
||||
# this should echo nothing
|
||||
type
|
||||
# this should be a usage error
|
||||
type -r ${THIS_SH}
|
||||
|
||||
# these should behave identically
|
||||
type notthere
|
||||
command -v notthere
|
||||
|
||||
alias m=more
|
||||
|
||||
unset -f func 2>/dev/null
|
||||
func() { echo this is func; }
|
||||
|
||||
type -t func
|
||||
type -t while
|
||||
type -t builtin
|
||||
type -t /bin/sh
|
||||
type -t ${THIS_SH}
|
||||
type -t mv
|
||||
|
||||
type func
|
||||
# the following two should produce identical output
|
||||
type while
|
||||
type -a while
|
||||
type builtin
|
||||
type /bin/sh
|
||||
|
||||
command -v func
|
||||
command -V func
|
||||
command -v while
|
||||
command -V while
|
||||
|
||||
# the following two lines should produce the same output
|
||||
# post-3.0 patch makes command -v silent, as posix specifies
|
||||
# first test with alias expansion off (should all fail or produce no output)
|
||||
type -t m
|
||||
type m
|
||||
command -v m
|
||||
alias -p
|
||||
alias m
|
||||
|
||||
# then test with alias expansion on
|
||||
shopt -s expand_aliases
|
||||
type m
|
||||
type -t m
|
||||
command -v m
|
||||
alias -p
|
||||
alias m
|
||||
|
||||
command -V m
|
||||
shopt -u expand_aliases
|
||||
|
||||
command -v builtin
|
||||
command -V builtin
|
||||
command -v /bin/sh
|
||||
command -V /bin/sh
|
||||
|
||||
unset -f func
|
||||
type func
|
||||
unalias m
|
||||
type m
|
||||
|
||||
hash -r
|
||||
|
||||
hash -p /bin/sh sh
|
||||
type -p sh
|
||||
|
||||
SHBASE=${THIS_SH##*/}
|
||||
hash -p /tmp/$SHBASE $SHBASE
|
||||
type -p $SHBASE
|
||||
type $SHBASE
|
||||
|
||||
type -t $SHBASE
|
||||
|
||||
# make sure the hash table looks right
|
||||
hash
|
||||
|
||||
# bug in versions of bash up to and including bash-3.2
|
||||
f() {
|
||||
v=$'\001'
|
||||
}
|
||||
|
||||
type f | cat -v
|
||||
|
||||
${THIS_SH} type1.sub
|
||||
|
||||
${THIS_SH} type2.sub
|
||||
@@ -0,0 +1,16 @@
|
||||
foo() {
|
||||
rm -f a b c
|
||||
for f in a b c; do
|
||||
cat <<-EOF >> ${f}
|
||||
file
|
||||
EOF
|
||||
done
|
||||
grep . a b c
|
||||
}
|
||||
|
||||
type foo
|
||||
|
||||
eval "$(type foo | sed 1d)"
|
||||
foo
|
||||
|
||||
rm -f a b c
|
||||
Reference in New Issue
Block a user