mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-07-13 23:20:49 +02:00
commit bash-20111104 snapshot
This commit is contained in:
+142
@@ -12407,3 +12407,145 @@ make_cmd.c
|
||||
when breaking the string between the double parens into three
|
||||
separate components instead of a simple character loop. Fixes
|
||||
bug reported by Dan Douglas <ormaaj@gmail.com>
|
||||
|
||||
11/2
|
||||
----
|
||||
Makefile.in
|
||||
- make libbuiltins.a depend on builtext.h to serialize its creation
|
||||
and avoid conflict between multiple invocations of mkbuiltins.
|
||||
Fix from Mike Frysinger <vapier@gentoo.org>
|
||||
|
||||
11/5
|
||||
----
|
||||
findcmd.c
|
||||
- user_command_matches: if stat(".", ...) returns -1, set st_dev
|
||||
and st_ino fields in dotinfo to 0 to avoid same_file matches
|
||||
- find_user_command_in_path: check stat(2) return the same way
|
||||
|
||||
lib/glob/glob.c
|
||||
- glob_vector: don't call strlen(pat) without checking pat == 0
|
||||
- glob_dir_to_array: make sure to free `result' and all allocated
|
||||
members before returning error due to malloc failure
|
||||
- glob_vector: make sure to free `nextname' and `npat' on errors
|
||||
(mostly when setting lose = 1)
|
||||
- glob_vector: if flags & GX_MATCHDIRS but not GX_ALLDIRS, make
|
||||
sure we free `subdir'
|
||||
- glob_filename: when expanding ** (GX_ALLDIRS), make sure we
|
||||
free temp_results (return value from glob_vector)
|
||||
|
||||
lib/glob/xmbsrtowcs.c
|
||||
- xdupmbstowcs: fix call to realloc to use sizeof (char *) instead
|
||||
of sizeof (char **) when assigning idxtmp
|
||||
|
||||
execute_cmd.c
|
||||
- print_index_and_element: return 0 right away if L == 0
|
||||
- is_dirname: fix memory leak by freeing `temp'
|
||||
- time_command: don't try to deref NULL `command' when assigning
|
||||
to `posix_time'
|
||||
- shell_execve: null-terminate `sample' after READ_SAMPLE_BUF so it's
|
||||
terminated for functions that expect that
|
||||
|
||||
builtins/read.def
|
||||
- read_builtin: don't call bind_read_variable with a potentially-null
|
||||
string
|
||||
|
||||
pcomplete.c
|
||||
- gen_command_matches: don't call dispose_word_desc with a NULL arg
|
||||
- gen_compspec_completions: fix memory leak by freeing `ret' before
|
||||
calling gen_action_completions (tcs, ...). happens when
|
||||
performing directory completion as default and no completions
|
||||
have been generated
|
||||
- gen_progcomp_completions: make sure to set foundp to 0 whenever
|
||||
returning NULL
|
||||
- it_init_aliases: fix memory leak by freeing alias_list before
|
||||
returning
|
||||
|
||||
bashline.c
|
||||
- command_word_completion_function: don't call restore_tilde with a
|
||||
NULL directory_part argument
|
||||
- bash_directory_expansion: bugfix: don't throw away results of
|
||||
rl_directory_rewrite_hook if it's set and returns non-zero
|
||||
- bind_keyseq_to_unix_command: free `kseq' before returning error
|
||||
|
||||
arrayfunc.c
|
||||
- assign_array_element_internal: make sure `akey' is freed if non-null
|
||||
before returning error
|
||||
- assign_compound_array_list: free `akey' before returning error
|
||||
- array_value_internal: free `akey' before returning error
|
||||
- unbind_array_element: free `akey' before returning error
|
||||
|
||||
subst.c
|
||||
- array_length_reference: free `akey' before returning error in case
|
||||
of expand_assignment_string_to_string error
|
||||
- array_length_reference: free `akey' after call to assoc_reference
|
||||
- skip_to_delim: if skipping process and command substitution, free
|
||||
return value from extract_process_subst
|
||||
- parameter_brace_substring: free `val' (vtype == VT_VARIABLE) before
|
||||
returning if verify_substring_values fails
|
||||
- parameter_brace_expand: remove two duplicate lines that allocate
|
||||
ret in parameter_brace_substring case
|
||||
- parameter_brace_expand: convert `free (name); name = xmalloc (...)'
|
||||
to use `xrealloc (name, ...)'
|
||||
- parameter_brace_expand: free `name' before returning when handling
|
||||
${!PREFIX*} expansion
|
||||
- split_at_delims: fix memory leak by freeing `d2' before returning
|
||||
|
||||
redir.c
|
||||
- redirection_error: free `filename' if the redirection operator is
|
||||
REDIR_VARASSIGN by assigning allocname
|
||||
|
||||
eval.c
|
||||
- send_pwd_to_eterm: fix memory leak by freeing value returned by
|
||||
get_working_directory()
|
||||
|
||||
builtins/cd.def
|
||||
- change_to_directory: fix memory leak by freeing return value from
|
||||
resetpwd()
|
||||
- cd_builtin: fix memory leak by freeing value returned by dirspell()
|
||||
- cd_builtin: fix memory leak by freeing `directory' if appropriate
|
||||
before overwriting with return value from resetpwd()
|
||||
|
||||
builtins/type.def
|
||||
- describe_command: free `full_path' before overwriting it with return
|
||||
value from sh_makepath
|
||||
|
||||
builtins/complete.def
|
||||
- compgen_builtin: fix memory leak by calling strlist_dispose (sl)
|
||||
before overwriting sl with return value from completions_to_stringlist
|
||||
|
||||
builtins/hash.def
|
||||
- list_hashed_filename_targets: fix memory leak by freeing `target'
|
||||
|
||||
make_cmd.c
|
||||
- make_arith_for_command: free `init', `test', and `step' before
|
||||
returning error on parse error
|
||||
|
||||
jobs.c
|
||||
- initialize_job_control: don't call move_to_high_fd if shell_tty == -1
|
||||
|
||||
general.c
|
||||
- check_dev_tty: don't call close with an fd < 0
|
||||
- legal_number: deal with NULL `string' argument, return invalid
|
||||
|
||||
lib/sh/fmtulong.c
|
||||
- fmtulong: if the `base' argument is invalid, make sure we index
|
||||
buf by `len-1' at maximum
|
||||
|
||||
print_cmd.c
|
||||
- print_deferred_heredocs: don't try to dereference a NULL `cstring'
|
||||
- cprintf: make sure to call va_end (args)
|
||||
|
||||
variables.c
|
||||
- push_dollar_vars: fix call to xrealloc to use sizeof (WORD_LIST *)
|
||||
instead of sizeof (WORD_LIST **)
|
||||
|
||||
lib/sh/zmapfd.c
|
||||
- zmapfd: if read returns error, free result and return -1 immediately
|
||||
instead of trying to reallocate it
|
||||
|
||||
11/6
|
||||
----
|
||||
execute_cmd.c
|
||||
- cpl_reap: rewrote to avoid using pointer after freeing it; now builds
|
||||
new coproc list on the fly while traversing the old one and sets the
|
||||
right values for coproc_list when done
|
||||
|
||||
@@ -12399,3 +12399,146 @@ lib/readline/histexpand.c
|
||||
we assume that the double quote is followed by a character in
|
||||
history_no_expand_chars. tcsh and csh seem to do this. This
|
||||
answers a persistent complaint about history expansion
|
||||
|
||||
10/29
|
||||
-----
|
||||
make_cmd.c
|
||||
- make_arith_for_command: use skip_to_delim to find the next `;'
|
||||
when breaking the string between the double parens into three
|
||||
separate components instead of a simple character loop. Fixes
|
||||
bug reported by Dan Douglas <ormaaj@gmail.com>
|
||||
|
||||
11/2
|
||||
----
|
||||
Makefile.in
|
||||
- make libbuiltins.a depend on builtext.h to serialize its creation
|
||||
and avoid conflict between multiple invocations of mkbuiltins.
|
||||
Fix from Mike Frysinger <vapier@gentoo.org>
|
||||
|
||||
11/5
|
||||
----
|
||||
findcmd.c
|
||||
- user_command_matches: if stat(".", ...) returns -1, set st_dev
|
||||
and st_ino fields in dotinfo to 0 to avoid same_file matches
|
||||
- find_user_command_in_path: check stat(2) return the same way
|
||||
|
||||
lib/glob/glob.c
|
||||
- glob_vector: don't call strlen(pat) without checking pat == 0
|
||||
- glob_dir_to_array: make sure to free `result' and all allocated
|
||||
members before returning error due to malloc failure
|
||||
- glob_vector: make sure to free `nextname' and `npat' on errors
|
||||
(mostly when setting lose = 1)
|
||||
- glob_vector: if flags & GX_MATCHDIRS but not GX_ALLDIRS, make
|
||||
sure we free `subdir'
|
||||
- glob_filename: when expanding ** (GX_ALLDIRS), make sure we
|
||||
free temp_results (return value from glob_vector)
|
||||
|
||||
lib/glob/xmbsrtowcs.c
|
||||
- xdupmbstowcs: fix call to realloc to use sizeof (char *) instead
|
||||
of sizeof (char **) when assigning idxtmp
|
||||
|
||||
execute_cmd.c
|
||||
- print_index_and_element: return 0 right away if L == 0
|
||||
- is_dirname: fix memory leak by freeing `temp'
|
||||
- time_command: don't try to deref NULL `command' when assigning
|
||||
to `posix_time'
|
||||
- shell_execve: null-terminate `sample' after READ_SAMPLE_BUF so it's
|
||||
terminated for functions that expect that
|
||||
|
||||
builtins/read.def
|
||||
- read_builtin: don't call bind_read_variable with a potentially-null
|
||||
string
|
||||
|
||||
pcomplete.c
|
||||
- gen_command_matches: don't call dispose_word_desc with a NULL arg
|
||||
- gen_compspec_completions: fix memory leak by freeing `ret' before
|
||||
calling gen_action_completions (tcs, ...). happens when
|
||||
performing directory completion as default and no completions
|
||||
have been generated
|
||||
- gen_progcomp_completions: make sure to set foundp to 0 whenever
|
||||
returning NULL
|
||||
- it_init_aliases: fix memory leak by freeing alias_list before
|
||||
returning
|
||||
|
||||
bashline.c
|
||||
- command_word_completion_function: don't call restore_tilde with a
|
||||
NULL directory_part argument
|
||||
- bash_directory_expansion: bugfix: don't throw away results of
|
||||
rl_directory_rewrite_hook if it's set and returns non-zero
|
||||
- bind_keyseq_to_unix_command: free `kseq' before returning error
|
||||
|
||||
arrayfunc.c
|
||||
- assign_array_element_internal: make sure `akey' is freed if non-null
|
||||
before returning error
|
||||
- assign_compound_array_list: free `akey' before returning error
|
||||
- array_value_internal: free `akey' before returning error
|
||||
- unbind_array_element: free `akey' before returning error
|
||||
|
||||
subst.c
|
||||
- array_length_reference: free `akey' before returning error in case
|
||||
of expand_assignment_string_to_string error
|
||||
- array_length_reference: free `akey' after call to assoc_reference
|
||||
- skip_to_delim: if skipping process and command substitution, free
|
||||
return value from extract_process_subst
|
||||
- parameter_brace_substring: free `val' (vtype == VT_VARIABLE) before
|
||||
returning if verify_substring_values fails
|
||||
- parameter_brace_expand: remove two duplicate lines that allocate
|
||||
ret in parameter_brace_substring case
|
||||
- parameter_brace_expand: convert `free (name); name = xmalloc (...)'
|
||||
to use `xrealloc (name, ...)'
|
||||
- parameter_brace_expand: free `name' before returning when handling
|
||||
${!PREFIX*} expansion
|
||||
- split_at_delims: fix memory leak by freeing `d2' before returning
|
||||
|
||||
redir.c
|
||||
- redirection_error: free `filename' if the redirection operator is
|
||||
REDIR_VARASSIGN by assigning allocname
|
||||
|
||||
eval.c
|
||||
- send_pwd_to_eterm: fix memory leak by freeing value returned by
|
||||
get_working_directory()
|
||||
|
||||
builtins/cd.def
|
||||
- change_to_directory: fix memory leak by freeing return value from
|
||||
resetpwd()
|
||||
- cd_builtin: fix memory leak by freeing value returned by dirspell()
|
||||
- cd_builtin: fix memory leak by freeing `directory' if appropriate
|
||||
before overwriting with return value from resetpwd()
|
||||
|
||||
builtins/type.def
|
||||
- describe_command: free `full_path' before overwriting it with return
|
||||
value from sh_makepath
|
||||
|
||||
builtins/complete.def
|
||||
- compgen_builtin: fix memory leak by calling strlist_dispose (sl)
|
||||
before overwriting sl with return value from completions_to_stringlist
|
||||
|
||||
builtins/hash.def
|
||||
- list_hashed_filename_targets: fix memory leak by freeing `target'
|
||||
|
||||
make_cmd.c
|
||||
- make_arith_for_command: free `init', `test', and `step' before
|
||||
returning error on parse error
|
||||
|
||||
jobs.c
|
||||
- initialize_job_control: don't call move_to_high_fd if shell_tty == -1
|
||||
|
||||
general.c
|
||||
- check_dev_tty: don't call close with an fd < 0
|
||||
- legal_number: deal with NULL `string' argument, return invalid
|
||||
|
||||
lib/sh/fmtulong.c
|
||||
- fmtulong: if the `base' argument is invalid, make sure we index
|
||||
buf by `len-1' at maximum
|
||||
|
||||
print_cmd.c
|
||||
- print_deferred_heredocs: don't try to dereference a NULL `cstring'
|
||||
- cprintf: make sure to call va_end (args)
|
||||
|
||||
variables.c
|
||||
- push_dollar_vars: fix call to xrealloc to use sizeof (WORD_LIST *)
|
||||
instead of sizeof (WORD_LIST **)
|
||||
|
||||
lib/sh/zmapfd.c
|
||||
- zmapfd: if read returns error, free result and return -1 immediately
|
||||
instead of trying to reallocate it
|
||||
|
||||
+1
-1
@@ -672,7 +672,7 @@ syntax.c: mksyntax${EXEEXT} $(srcdir)/syntax.h
|
||||
$(RM) $@
|
||||
./mksyntax$(EXEEXT) -o $@
|
||||
|
||||
$(BUILTINS_LIBRARY): $(BUILTIN_DEFS) $(BUILTIN_C_SRC) config.h ${BASHINCDIR}/memalloc.h version.h
|
||||
$(BUILTINS_LIBRARY): $(BUILTIN_DEFS) $(BUILTIN_C_SRC) config.h ${BASHINCDIR}/memalloc.h $(DEFDIR)/builtext.h version.h
|
||||
@(cd $(DEFDIR) && $(MAKE) $(MFLAGS) DEBUG=${DEBUG} libbuiltins.a ) || exit 1
|
||||
|
||||
# these require special rules to circumvent make builtin rules
|
||||
|
||||
@@ -110,7 +110,7 @@ add_alias (name, value)
|
||||
alias_t *temp;
|
||||
int n;
|
||||
|
||||
if (!aliases)
|
||||
if (aliases == 0)
|
||||
{
|
||||
initialize_aliases ();
|
||||
temp = (alias_t *)NULL;
|
||||
|
||||
@@ -0,0 +1,580 @@
|
||||
/* alias.c -- Not a full alias, but just the kind that we use in the
|
||||
shell. Csh style alias is somewhere else (`over there, in a box'). */
|
||||
|
||||
/* Copyright (C) 1987-2009 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if defined (ALIAS)
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# ifdef _MINIX
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "chartypes.h"
|
||||
#include "bashansi.h"
|
||||
#include "command.h"
|
||||
#include "general.h"
|
||||
#include "externs.h"
|
||||
#include "alias.h"
|
||||
|
||||
#if defined (PROGRAMMABLE_COMPLETION)
|
||||
# include "pcomplete.h"
|
||||
#endif
|
||||
|
||||
#define ALIAS_HASH_BUCKETS 16 /* must be power of two */
|
||||
|
||||
typedef int sh_alias_map_func_t __P((alias_t *));
|
||||
|
||||
static void free_alias_data __P((PTR_T));
|
||||
static alias_t **map_over_aliases __P((sh_alias_map_func_t *));
|
||||
static void sort_aliases __P((alias_t **));
|
||||
static int qsort_alias_compare __P((alias_t **, alias_t **));
|
||||
|
||||
#if defined (READLINE)
|
||||
static int skipquotes __P((char *, int));
|
||||
static int skipws __P((char *, int));
|
||||
static int rd_token __P((char *, int));
|
||||
#endif
|
||||
|
||||
/* Non-zero means expand all words on the line. Otherwise, expand
|
||||
after first expansion if the expansion ends in a space. */
|
||||
int alias_expand_all = 0;
|
||||
|
||||
/* The list of aliases that we have. */
|
||||
HASH_TABLE *aliases = (HASH_TABLE *)NULL;
|
||||
|
||||
void
|
||||
initialize_aliases ()
|
||||
{
|
||||
if (aliases == 0)
|
||||
aliases = hash_create (ALIAS_HASH_BUCKETS);
|
||||
}
|
||||
|
||||
/* Scan the list of aliases looking for one with NAME. Return NULL
|
||||
if the alias doesn't exist, else a pointer to the alias_t. */
|
||||
alias_t *
|
||||
find_alias (name)
|
||||
char *name;
|
||||
{
|
||||
BUCKET_CONTENTS *al;
|
||||
|
||||
if (aliases == 0)
|
||||
return ((alias_t *)NULL);
|
||||
|
||||
al = hash_search (name, aliases, 0);
|
||||
return (al ? (alias_t *)al->data : (alias_t *)NULL);
|
||||
}
|
||||
|
||||
/* Return the value of the alias for NAME, or NULL if there is none. */
|
||||
char *
|
||||
get_alias_value (name)
|
||||
char *name;
|
||||
{
|
||||
alias_t *alias;
|
||||
|
||||
if (aliases == 0)
|
||||
return ((char *)NULL);
|
||||
|
||||
alias = find_alias (name);
|
||||
return (alias ? alias->value : (char *)NULL);
|
||||
}
|
||||
|
||||
/* Make a new alias from NAME and VALUE. If NAME can be found,
|
||||
then replace its value. */
|
||||
void
|
||||
add_alias (name, value)
|
||||
char *name, *value;
|
||||
{
|
||||
BUCKET_CONTENTS *elt;
|
||||
alias_t *temp;
|
||||
int n;
|
||||
|
||||
if (!aliases)
|
||||
{
|
||||
initialize_aliases ();
|
||||
temp = (alias_t *)NULL;
|
||||
}
|
||||
else
|
||||
temp = find_alias (name);
|
||||
|
||||
if (temp)
|
||||
{
|
||||
free (temp->value);
|
||||
temp->value = savestring (value);
|
||||
temp->flags &= ~AL_EXPANDNEXT;
|
||||
n = value[strlen (value) - 1];
|
||||
if (n == ' ' || n == '\t')
|
||||
temp->flags |= AL_EXPANDNEXT;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp = (alias_t *)xmalloc (sizeof (alias_t));
|
||||
temp->name = savestring (name);
|
||||
temp->value = savestring (value);
|
||||
temp->flags = 0;
|
||||
|
||||
n = value[strlen (value) - 1];
|
||||
if (n == ' ' || n == '\t')
|
||||
temp->flags |= AL_EXPANDNEXT;
|
||||
|
||||
elt = hash_insert (savestring (name), aliases, HASH_NOSRCH);
|
||||
elt->data = temp;
|
||||
#if defined (PROGRAMMABLE_COMPLETION)
|
||||
set_itemlist_dirty (&it_aliases);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete a single alias structure. */
|
||||
static void
|
||||
free_alias_data (data)
|
||||
PTR_T data;
|
||||
{
|
||||
register alias_t *a;
|
||||
|
||||
a = (alias_t *)data;
|
||||
free (a->value);
|
||||
free (a->name);
|
||||
free (data);
|
||||
}
|
||||
|
||||
/* Remove the alias with name NAME from the alias table. Returns
|
||||
the number of aliases left in the table, or -1 if the alias didn't
|
||||
exist. */
|
||||
int
|
||||
remove_alias (name)
|
||||
char *name;
|
||||
{
|
||||
BUCKET_CONTENTS *elt;
|
||||
|
||||
if (aliases == 0)
|
||||
return (-1);
|
||||
|
||||
elt = hash_remove (name, aliases, 0);
|
||||
if (elt)
|
||||
{
|
||||
free_alias_data (elt->data);
|
||||
free (elt->key); /* alias name */
|
||||
free (elt); /* XXX */
|
||||
#if defined (PROGRAMMABLE_COMPLETION)
|
||||
set_itemlist_dirty (&it_aliases);
|
||||
#endif
|
||||
return (aliases->nentries);
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Delete all aliases. */
|
||||
void
|
||||
delete_all_aliases ()
|
||||
{
|
||||
if (aliases == 0)
|
||||
return;
|
||||
|
||||
hash_flush (aliases, free_alias_data);
|
||||
hash_dispose (aliases);
|
||||
aliases = (HASH_TABLE *)NULL;
|
||||
#if defined (PROGRAMMABLE_COMPLETION)
|
||||
set_itemlist_dirty (&it_aliases);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Return an array of aliases that satisfy the conditions tested by FUNCTION.
|
||||
If FUNCTION is NULL, return all aliases. */
|
||||
static alias_t **
|
||||
map_over_aliases (function)
|
||||
sh_alias_map_func_t *function;
|
||||
{
|
||||
register int i;
|
||||
register BUCKET_CONTENTS *tlist;
|
||||
alias_t *alias, **list;
|
||||
int list_index;
|
||||
|
||||
i = HASH_ENTRIES (aliases);
|
||||
if (i == 0)
|
||||
return ((alias_t **)NULL);
|
||||
|
||||
list = (alias_t **)xmalloc ((i + 1) * sizeof (alias_t *));
|
||||
for (i = list_index = 0; i < aliases->nbuckets; i++)
|
||||
{
|
||||
for (tlist = hash_items (i, aliases); tlist; tlist = tlist->next)
|
||||
{
|
||||
alias = (alias_t *)tlist->data;
|
||||
|
||||
if (!function || (*function) (alias))
|
||||
{
|
||||
list[list_index++] = alias;
|
||||
list[list_index] = (alias_t *)NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (list);
|
||||
}
|
||||
|
||||
static void
|
||||
sort_aliases (array)
|
||||
alias_t **array;
|
||||
{
|
||||
qsort (array, strvec_len ((char **)array), sizeof (alias_t *), (QSFUNC *)qsort_alias_compare);
|
||||
}
|
||||
|
||||
static int
|
||||
qsort_alias_compare (as1, as2)
|
||||
alias_t **as1, **as2;
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result = (*as1)->name[0] - (*as2)->name[0]) == 0)
|
||||
result = strcmp ((*as1)->name, (*as2)->name);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/* Return a sorted list of all defined aliases */
|
||||
alias_t **
|
||||
all_aliases ()
|
||||
{
|
||||
alias_t **list;
|
||||
|
||||
if (aliases == 0 || HASH_ENTRIES (aliases) == 0)
|
||||
return ((alias_t **)NULL);
|
||||
|
||||
list = map_over_aliases ((sh_alias_map_func_t *)NULL);
|
||||
if (list)
|
||||
sort_aliases (list);
|
||||
return (list);
|
||||
}
|
||||
|
||||
char *
|
||||
alias_expand_word (s)
|
||||
char *s;
|
||||
{
|
||||
alias_t *r;
|
||||
|
||||
r = find_alias (s);
|
||||
return (r ? savestring (r->value) : (char *)NULL);
|
||||
}
|
||||
|
||||
/* Readline support functions -- expand all aliases in a line. */
|
||||
|
||||
#if defined (READLINE)
|
||||
|
||||
/* Return non-zero if CHARACTER is a member of the class of characters
|
||||
that are self-delimiting in the shell (this really means that these
|
||||
characters delimit tokens). */
|
||||
#define self_delimiting(character) (member ((character), " \t\n\r;|&()"))
|
||||
|
||||
/* Return non-zero if CHARACTER is a member of the class of characters
|
||||
that delimit commands in the shell. */
|
||||
#define command_separator(character) (member ((character), "\r\n;|&("))
|
||||
|
||||
/* If this is 1, we are checking the next token read for alias expansion
|
||||
because it is the first word in a command. */
|
||||
static int command_word;
|
||||
|
||||
/* This is for skipping quoted strings in alias expansions. */
|
||||
#define quote_char(c) (((c) == '\'') || ((c) == '"'))
|
||||
|
||||
/* Consume a quoted string from STRING, starting at string[START] (so
|
||||
string[START] is the opening quote character), and return the index
|
||||
of the closing quote character matching the opening quote character.
|
||||
This handles single matching pairs of unquoted quotes; it could afford
|
||||
to be a little smarter... This skips words between balanced pairs of
|
||||
quotes, words where the first character is quoted with a `\', and other
|
||||
backslash-escaped characters. */
|
||||
|
||||
static int
|
||||
skipquotes (string, start)
|
||||
char *string;
|
||||
int start;
|
||||
{
|
||||
register int i;
|
||||
int delimiter = string[start];
|
||||
|
||||
/* i starts at START + 1 because string[START] is the opening quote
|
||||
character. */
|
||||
for (i = start + 1 ; string[i] ; i++)
|
||||
{
|
||||
if (string[i] == '\\')
|
||||
{
|
||||
i++; /* skip backslash-quoted quote characters, too */
|
||||
if (string[i] == 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string[i] == delimiter)
|
||||
return i;
|
||||
}
|
||||
return (i);
|
||||
}
|
||||
|
||||
/* Skip the white space and any quoted characters in STRING, starting at
|
||||
START. Return the new index into STRING, after zero or more characters
|
||||
have been skipped. */
|
||||
static int
|
||||
skipws (string, start)
|
||||
char *string;
|
||||
int start;
|
||||
{
|
||||
register int i;
|
||||
int pass_next, backslash_quoted_word;
|
||||
unsigned char peekc;
|
||||
|
||||
/* skip quoted strings, in ' or ", and words in which a character is quoted
|
||||
with a `\'. */
|
||||
i = backslash_quoted_word = pass_next = 0;
|
||||
|
||||
/* Skip leading whitespace (or separator characters), and quoted words.
|
||||
But save it in the output. */
|
||||
|
||||
for (i = start; string[i]; i++)
|
||||
{
|
||||
if (pass_next)
|
||||
{
|
||||
pass_next = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (whitespace (string[i]))
|
||||
{
|
||||
backslash_quoted_word = 0; /* we are no longer in a backslash-quoted word */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string[i] == '\\')
|
||||
{
|
||||
peekc = string[i+1];
|
||||
if (peekc == 0)
|
||||
break;
|
||||
if (ISLETTER (peekc))
|
||||
backslash_quoted_word++; /* this is a backslash-quoted word */
|
||||
else
|
||||
pass_next++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* This only handles single pairs of non-escaped quotes. This
|
||||
overloads backslash_quoted_word to also mean that a word like
|
||||
""f is being scanned, so that the quotes will inhibit any expansion
|
||||
of the word. */
|
||||
if (quote_char(string[i]))
|
||||
{
|
||||
i = skipquotes (string, i);
|
||||
/* This could be a line that contains a single quote character,
|
||||
in which case skipquotes () terminates with string[i] == '\0'
|
||||
(the end of the string). Check for that here. */
|
||||
if (string[i] == '\0')
|
||||
break;
|
||||
|
||||
peekc = string[i + 1];
|
||||
if (ISLETTER (peekc))
|
||||
backslash_quoted_word++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we're in the middle of some kind of quoted word, let it
|
||||
pass through. */
|
||||
if (backslash_quoted_word)
|
||||
continue;
|
||||
|
||||
/* If this character is a shell command separator, then set a hint for
|
||||
alias_expand that the next token is the first word in a command. */
|
||||
|
||||
if (command_separator (string[i]))
|
||||
{
|
||||
command_word++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (i);
|
||||
}
|
||||
|
||||
/* Characters that may appear in a token. Basically, anything except white
|
||||
space and a token separator. */
|
||||
#define token_char(c) (!((whitespace (string[i]) || self_delimiting (string[i]))))
|
||||
|
||||
/* Read from START in STRING until the next separator character, and return
|
||||
the index of that separator. Skip backslash-quoted characters. Call
|
||||
skipquotes () for quoted strings in the middle or at the end of tokens,
|
||||
so all characters show up (e.g. foo'' and foo""bar) */
|
||||
static int
|
||||
rd_token (string, start)
|
||||
char *string;
|
||||
int start;
|
||||
{
|
||||
register int i;
|
||||
|
||||
/* From here to next separator character is a token. */
|
||||
for (i = start; string[i] && token_char (string[i]); i++)
|
||||
{
|
||||
if (string[i] == '\\')
|
||||
{
|
||||
i++; /* skip backslash-escaped character */
|
||||
if (string[i] == 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If this character is a quote character, we want to call skipquotes
|
||||
to get the whole quoted portion as part of this word. That word
|
||||
will not generally match an alias, even if te unquoted word would
|
||||
have. The presence of the quotes in the token serves then to
|
||||
inhibit expansion. */
|
||||
if (quote_char (string[i]))
|
||||
{
|
||||
i = skipquotes (string, i);
|
||||
/* This could be a line that contains a single quote character,
|
||||
in which case skipquotes () terminates with string[i] == '\0'
|
||||
(the end of the string). Check for that here. */
|
||||
if (string[i] == '\0')
|
||||
break;
|
||||
|
||||
/* Now string[i] is the matching quote character, and the
|
||||
quoted portion of the token has been scanned. */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return (i);
|
||||
}
|
||||
|
||||
/* Return a new line, with any aliases substituted. */
|
||||
char *
|
||||
alias_expand (string)
|
||||
char *string;
|
||||
{
|
||||
register int i, j, start;
|
||||
char *line, *token;
|
||||
int line_len, tl, real_start, expand_next, expand_this_token;
|
||||
alias_t *alias;
|
||||
|
||||
line_len = strlen (string) + 1;
|
||||
line = (char *)xmalloc (line_len);
|
||||
token = (char *)xmalloc (line_len);
|
||||
|
||||
line[0] = i = 0;
|
||||
expand_next = 0;
|
||||
command_word = 1; /* initialized to expand the first word on the line */
|
||||
|
||||
/* Each time through the loop we find the next word in line. If it
|
||||
has an alias, substitute the alias value. If the value ends in ` ',
|
||||
then try again with the next word. Else, if there is no value, or if
|
||||
the value does not end in space, we are done. */
|
||||
|
||||
for (;;)
|
||||
{
|
||||
|
||||
token[0] = 0;
|
||||
start = i;
|
||||
|
||||
/* Skip white space and quoted characters */
|
||||
i = skipws (string, start);
|
||||
|
||||
if (start == i && string[i] == '\0')
|
||||
{
|
||||
free (token);
|
||||
return (line);
|
||||
}
|
||||
|
||||
/* copy the just-skipped characters into the output string,
|
||||
expanding it if there is not enough room. */
|
||||
j = strlen (line);
|
||||
tl = i - start; /* number of characters just skipped */
|
||||
RESIZE_MALLOCED_BUFFER (line, j, (tl + 1), line_len, (tl + 50));
|
||||
strncpy (line + j, string + start, tl);
|
||||
line[j + tl] = '\0';
|
||||
|
||||
real_start = i;
|
||||
|
||||
command_word = command_word || (command_separator (string[i]));
|
||||
expand_this_token = (command_word || expand_next);
|
||||
expand_next = 0;
|
||||
|
||||
/* Read the next token, and copy it into TOKEN. */
|
||||
start = i;
|
||||
i = rd_token (string, start);
|
||||
|
||||
tl = i - start; /* token length */
|
||||
|
||||
/* If tl == 0, but we're not at the end of the string, then we have a
|
||||
single-character token, probably a delimiter */
|
||||
if (tl == 0 && string[i] != '\0')
|
||||
{
|
||||
tl = 1;
|
||||
i++; /* move past it */
|
||||
}
|
||||
|
||||
strncpy (token, string + start, tl);
|
||||
token [tl] = '\0';
|
||||
|
||||
/* If there is a backslash-escaped character quoted in TOKEN,
|
||||
then we don't do alias expansion. This should check for all
|
||||
other quoting characters, too. */
|
||||
if (mbschr (token, '\\'))
|
||||
expand_this_token = 0;
|
||||
|
||||
/* If we should be expanding here, if we are expanding all words, or if
|
||||
we are in a location in the string where an expansion is supposed to
|
||||
take place, see if this word has a substitution. If it does, then do
|
||||
the expansion. Note that we defer the alias value lookup until we
|
||||
are sure we are expanding this token. */
|
||||
|
||||
if ((token[0]) &&
|
||||
(expand_this_token || alias_expand_all) &&
|
||||
(alias = find_alias (token)))
|
||||
{
|
||||
char *v;
|
||||
int vlen, llen;
|
||||
|
||||
v = alias->value;
|
||||
vlen = strlen (v);
|
||||
llen = strlen (line);
|
||||
|
||||
/* +3 because we possibly add one more character below. */
|
||||
RESIZE_MALLOCED_BUFFER (line, llen, (vlen + 3), line_len, (vlen + 50));
|
||||
|
||||
strcpy (line + llen, v);
|
||||
|
||||
if ((expand_this_token && vlen && whitespace (v[vlen - 1])) ||
|
||||
alias_expand_all)
|
||||
expand_next = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int llen, tlen;
|
||||
|
||||
llen = strlen (line);
|
||||
tlen = i - real_start; /* tlen == strlen(token) */
|
||||
|
||||
RESIZE_MALLOCED_BUFFER (line, llen, (tlen + 1), line_len, (llen + tlen + 50));
|
||||
|
||||
strncpy (line + llen, string + real_start, tlen);
|
||||
line[llen + tlen] = '\0';
|
||||
}
|
||||
command_word = 0;
|
||||
}
|
||||
}
|
||||
#endif /* READLINE */
|
||||
#endif /* ALIAS */
|
||||
@@ -816,7 +816,7 @@ int quoted;
|
||||
rsize, rsize);
|
||||
strcpy(result + rlen, t);
|
||||
rlen += reg;
|
||||
if (quoted && t)
|
||||
if (quoted)
|
||||
free(t);
|
||||
/*
|
||||
* Add a separator only after non-null elements.
|
||||
|
||||
+14
-4
@@ -282,6 +282,7 @@ assign_array_element_internal (entry, name, vname, sub, sublen, value, flags)
|
||||
if (akey == 0 || *akey == 0)
|
||||
{
|
||||
err_badarraysub (name);
|
||||
FREE (akey);
|
||||
return ((SHELL_VAR *)NULL);
|
||||
}
|
||||
entry = bind_assoc_variable (entry, vname, akey, value, flags);
|
||||
@@ -534,6 +535,7 @@ assign_compound_array_list (var, nlist, flags)
|
||||
if (akey == 0 || *akey == 0)
|
||||
{
|
||||
err_badarraysub (w);
|
||||
FREE (akey);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -700,6 +702,7 @@ unbind_array_element (var, sub)
|
||||
if (akey == 0 || *akey == 0)
|
||||
{
|
||||
builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
|
||||
FREE (akey);
|
||||
return -1;
|
||||
}
|
||||
assoc_remove (assoc_cell (var), akey);
|
||||
@@ -935,6 +938,7 @@ array_value_internal (s, quoted, flags, rtype, indp)
|
||||
return ((char *)NULL); /* error message already printed */
|
||||
|
||||
/* [ */
|
||||
akey = 0;
|
||||
if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
|
||||
{
|
||||
if (rtype)
|
||||
@@ -964,11 +968,11 @@ array_value_internal (s, quoted, flags, rtype, indp)
|
||||
if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
|
||||
{
|
||||
temp = string_list_dollar_star (l);
|
||||
retval = quote_string (temp);
|
||||
retval = quote_string (temp); /* XXX - leak here */
|
||||
free (temp);
|
||||
}
|
||||
else /* ${name[@]} or unquoted ${name[*]} */
|
||||
retval = string_list_dollar_at (l, quoted);
|
||||
retval = string_list_dollar_at (l, quoted); /* XXX - leak here */
|
||||
|
||||
dispose_words (l);
|
||||
}
|
||||
@@ -1001,11 +1005,17 @@ array_value_internal (s, quoted, flags, rtype, indp)
|
||||
akey = expand_assignment_string_to_string (t, 0); /* [ */
|
||||
t[len - 1] = ']';
|
||||
if (akey == 0 || *akey == 0)
|
||||
INDEX_ERROR();
|
||||
{
|
||||
FREE (akey);
|
||||
INDEX_ERROR();
|
||||
}
|
||||
}
|
||||
|
||||
if (var == 0 || value_cell (var) == 0) /* XXX - check invisible_p(var) ? */
|
||||
return ((char *)NULL);
|
||||
{
|
||||
FREE (akey);
|
||||
return ((char *)NULL);
|
||||
}
|
||||
if (array_p (var) == 0 && assoc_p (var) == 0)
|
||||
return (ind == 0 ? value_cell (var) : (char *)NULL);
|
||||
else if (assoc_p (var))
|
||||
|
||||
+1097
File diff suppressed because it is too large
Load Diff
@@ -533,7 +533,7 @@ assoc_to_string (h, sep, quoted)
|
||||
return (savestring (""));
|
||||
|
||||
result = NULL;
|
||||
list = NULL;
|
||||
l = list = NULL;
|
||||
/* This might be better implemented directly, but it's simple to implement
|
||||
by converting to a word list first, possibly quoting the data, then
|
||||
using list_string */
|
||||
@@ -551,6 +551,8 @@ assoc_to_string (h, sep, quoted)
|
||||
l = REVERSE_LIST(list, WORD_LIST *);
|
||||
|
||||
result = l ? string_list_internal (l, sep) : savestring ("");
|
||||
dispose_words (l);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,557 @@
|
||||
/*
|
||||
* assoc.c - functions to manipulate associative arrays
|
||||
*
|
||||
* Associative arrays are standard shell hash tables.
|
||||
*
|
||||
* Chet Ramey
|
||||
* chet@ins.cwru.edu
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2008,2009,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"
|
||||
|
||||
#if defined (ARRAY_VARS)
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# ifdef _MINIX
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "bashansi.h"
|
||||
|
||||
#include "shell.h"
|
||||
#include "array.h"
|
||||
#include "assoc.h"
|
||||
#include "builtins/common.h"
|
||||
|
||||
static WORD_LIST *assoc_to_word_list_internal __P((HASH_TABLE *, int));
|
||||
|
||||
/* assoc_create == hash_create */
|
||||
|
||||
void
|
||||
assoc_dispose (hash)
|
||||
HASH_TABLE *hash;
|
||||
{
|
||||
if (hash)
|
||||
{
|
||||
hash_flush (hash, 0);
|
||||
hash_dispose (hash);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
assoc_flush (hash)
|
||||
HASH_TABLE *hash;
|
||||
{
|
||||
hash_flush (hash, 0);
|
||||
}
|
||||
|
||||
int
|
||||
assoc_insert (hash, key, value)
|
||||
HASH_TABLE *hash;
|
||||
char *key;
|
||||
char *value;
|
||||
{
|
||||
BUCKET_CONTENTS *b;
|
||||
|
||||
b = hash_search (key, hash, HASH_CREATE);
|
||||
if (b == 0)
|
||||
return -1;
|
||||
/* If we are overwriting an existing element's value, we're not going to
|
||||
use the key. Nothing in the array assignment code path frees the key
|
||||
string, so we can free it here to avoid a memory leak. */
|
||||
if (b->key != key)
|
||||
free (key);
|
||||
FREE (b->data);
|
||||
b->data = value ? savestring (value) : (char *)0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Like assoc_insert, but returns b->data instead of freeing it */
|
||||
PTR_T
|
||||
assoc_replace (hash, key, value)
|
||||
HASH_TABLE *hash;
|
||||
char *key;
|
||||
char *value;
|
||||
{
|
||||
BUCKET_CONTENTS *b;
|
||||
PTR_T t;
|
||||
|
||||
b = hash_search (key, hash, HASH_CREATE);
|
||||
if (b == 0)
|
||||
return (PTR_T)0;
|
||||
/* If we are overwriting an existing element's value, we're not going to
|
||||
use the key. Nothing in the array assignment code path frees the key
|
||||
string, so we can free it here to avoid a memory leak. */
|
||||
if (b->key != key)
|
||||
free (key);
|
||||
t = b->data;
|
||||
b->data = value ? savestring (value) : (char *)0;
|
||||
return t;
|
||||
}
|
||||
|
||||
void
|
||||
assoc_remove (hash, string)
|
||||
HASH_TABLE *hash;
|
||||
char *string;
|
||||
{
|
||||
BUCKET_CONTENTS *b;
|
||||
|
||||
b = hash_remove (string, hash, 0);
|
||||
if (b)
|
||||
{
|
||||
free ((char *)b->data);
|
||||
free (b->key);
|
||||
free (b);
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
assoc_reference (hash, string)
|
||||
HASH_TABLE *hash;
|
||||
char *string;
|
||||
{
|
||||
BUCKET_CONTENTS *b;
|
||||
|
||||
if (hash == 0)
|
||||
return (char *)0;
|
||||
|
||||
b = hash_search (string, hash, 0);
|
||||
return (b ? (char *)b->data : 0);
|
||||
}
|
||||
|
||||
/* Quote the data associated with each element of the hash table ASSOC,
|
||||
using quote_string */
|
||||
HASH_TABLE *
|
||||
assoc_quote (h)
|
||||
HASH_TABLE *h;
|
||||
{
|
||||
int i;
|
||||
BUCKET_CONTENTS *tlist;
|
||||
char *t;
|
||||
|
||||
if (h == 0 || assoc_empty (h))
|
||||
return ((HASH_TABLE *)NULL);
|
||||
|
||||
for (i = 0; i < h->nbuckets; i++)
|
||||
for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
|
||||
{
|
||||
t = quote_string ((char *)tlist->data);
|
||||
FREE (tlist->data);
|
||||
tlist->data = t;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
/* Quote escape characters in the data associated with each element
|
||||
of the hash table ASSOC, using quote_escapes */
|
||||
HASH_TABLE *
|
||||
assoc_quote_escapes (h)
|
||||
HASH_TABLE *h;
|
||||
{
|
||||
int i;
|
||||
BUCKET_CONTENTS *tlist;
|
||||
char *t;
|
||||
|
||||
if (h == 0 || assoc_empty (h))
|
||||
return ((HASH_TABLE *)NULL);
|
||||
|
||||
for (i = 0; i < h->nbuckets; i++)
|
||||
for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
|
||||
{
|
||||
t = quote_escapes ((char *)tlist->data);
|
||||
FREE (tlist->data);
|
||||
tlist->data = t;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
HASH_TABLE *
|
||||
assoc_dequote (h)
|
||||
HASH_TABLE *h;
|
||||
{
|
||||
int i;
|
||||
BUCKET_CONTENTS *tlist;
|
||||
char *t;
|
||||
|
||||
if (h == 0 || assoc_empty (h))
|
||||
return ((HASH_TABLE *)NULL);
|
||||
|
||||
for (i = 0; i < h->nbuckets; i++)
|
||||
for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
|
||||
{
|
||||
t = dequote_string ((char *)tlist->data);
|
||||
FREE (tlist->data);
|
||||
tlist->data = t;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
HASH_TABLE *
|
||||
assoc_dequote_escapes (h)
|
||||
HASH_TABLE *h;
|
||||
{
|
||||
int i;
|
||||
BUCKET_CONTENTS *tlist;
|
||||
char *t;
|
||||
|
||||
if (h == 0 || assoc_empty (h))
|
||||
return ((HASH_TABLE *)NULL);
|
||||
|
||||
for (i = 0; i < h->nbuckets; i++)
|
||||
for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
|
||||
{
|
||||
t = dequote_escapes ((char *)tlist->data);
|
||||
FREE (tlist->data);
|
||||
tlist->data = t;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
HASH_TABLE *
|
||||
assoc_remove_quoted_nulls (h)
|
||||
HASH_TABLE *h;
|
||||
{
|
||||
int i;
|
||||
BUCKET_CONTENTS *tlist;
|
||||
char *t;
|
||||
|
||||
if (h == 0 || assoc_empty (h))
|
||||
return ((HASH_TABLE *)NULL);
|
||||
|
||||
for (i = 0; i < h->nbuckets; i++)
|
||||
for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
|
||||
{
|
||||
t = remove_quoted_nulls ((char *)tlist->data);
|
||||
tlist->data = t;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a string whose elements are the members of array H beginning at
|
||||
* the STARTth element and spanning NELEM members. Null elements are counted.
|
||||
*/
|
||||
char *
|
||||
assoc_subrange (hash, start, nelem, starsub, quoted)
|
||||
HASH_TABLE *hash;
|
||||
arrayind_t start, nelem;
|
||||
int starsub, quoted;
|
||||
{
|
||||
WORD_LIST *l, *save, *h, *t;
|
||||
int i, j;
|
||||
char *ret;
|
||||
|
||||
if (assoc_empty (hash))
|
||||
return ((char *)NULL);
|
||||
|
||||
save = l = assoc_to_word_list (hash);
|
||||
if (save == 0)
|
||||
return ((char *)NULL);
|
||||
|
||||
for (i = 1; l && i < start; i++)
|
||||
l = l->next;
|
||||
if (l == 0)
|
||||
return ((char *)NULL);
|
||||
for (j = 0,h = t = l; l && j < nelem; j++)
|
||||
{
|
||||
t = l;
|
||||
l = l->next;
|
||||
}
|
||||
|
||||
t->next = (WORD_LIST *)NULL;
|
||||
|
||||
ret = string_list_pos_params (starsub ? '*' : '@', h, quoted);
|
||||
|
||||
if (t != l)
|
||||
t->next = l;
|
||||
|
||||
dispose_words (save);
|
||||
return (ret);
|
||||
|
||||
}
|
||||
|
||||
char *
|
||||
assoc_patsub (h, pat, rep, mflags)
|
||||
HASH_TABLE *h;
|
||||
char *pat, *rep;
|
||||
int mflags;
|
||||
{
|
||||
BUCKET_CONTENTS *tlist;
|
||||
int i, slen;
|
||||
HASH_TABLE *h2;
|
||||
char *t, *sifs, *ifs;
|
||||
|
||||
if (h == 0 || assoc_empty (h))
|
||||
return ((char *)NULL);
|
||||
|
||||
h2 = assoc_copy (h);
|
||||
for (i = 0; i < h2->nbuckets; i++)
|
||||
for (tlist = hash_items (i, h2); tlist; tlist = tlist->next)
|
||||
{
|
||||
t = pat_subst ((char *)tlist->data, pat, rep, mflags);
|
||||
FREE (tlist->data);
|
||||
tlist->data = t;
|
||||
}
|
||||
|
||||
if (mflags & MATCH_QUOTED)
|
||||
assoc_quote (h2);
|
||||
else
|
||||
assoc_quote_escapes (h2);
|
||||
|
||||
if (mflags & MATCH_STARSUB)
|
||||
{
|
||||
assoc_remove_quoted_nulls (h2);
|
||||
sifs = ifs_firstchar ((int *)NULL);
|
||||
t = assoc_to_string (h2, sifs, 0);
|
||||
free (sifs);
|
||||
}
|
||||
else if (mflags & MATCH_QUOTED)
|
||||
{
|
||||
/* ${array[@]} */
|
||||
sifs = ifs_firstchar (&slen);
|
||||
ifs = getifs ();
|
||||
if (ifs == 0 || *ifs == 0)
|
||||
{
|
||||
if (slen < 2)
|
||||
sifs = xrealloc (sifs, 2);
|
||||
sifs[0] = ' ';
|
||||
sifs[1] = '\0';
|
||||
}
|
||||
t = assoc_to_string (h2, sifs, 0);
|
||||
free(sifs);
|
||||
}
|
||||
else
|
||||
t = assoc_to_string (h2, " ", 0);
|
||||
|
||||
assoc_dispose (h2);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
char *
|
||||
assoc_modcase (h, pat, modop, mflags)
|
||||
HASH_TABLE *h;
|
||||
char *pat;
|
||||
int modop;
|
||||
int mflags;
|
||||
{
|
||||
BUCKET_CONTENTS *tlist;
|
||||
int i, slen;
|
||||
HASH_TABLE *h2;
|
||||
char *t, *sifs, *ifs;
|
||||
|
||||
if (h == 0 || assoc_empty (h))
|
||||
return ((char *)NULL);
|
||||
|
||||
h2 = assoc_copy (h);
|
||||
for (i = 0; i < h2->nbuckets; i++)
|
||||
for (tlist = hash_items (i, h2); tlist; tlist = tlist->next)
|
||||
{
|
||||
t = sh_modcase ((char *)tlist->data, pat, modop);
|
||||
FREE (tlist->data);
|
||||
tlist->data = t;
|
||||
}
|
||||
|
||||
if (mflags & MATCH_QUOTED)
|
||||
assoc_quote (h2);
|
||||
else
|
||||
assoc_quote_escapes (h2);
|
||||
|
||||
if (mflags & MATCH_STARSUB)
|
||||
{
|
||||
assoc_remove_quoted_nulls (h2);
|
||||
sifs = ifs_firstchar ((int *)NULL);
|
||||
t = assoc_to_string (h2, sifs, 0);
|
||||
free (sifs);
|
||||
}
|
||||
else if (mflags & MATCH_QUOTED)
|
||||
{
|
||||
/* ${array[@]} */
|
||||
sifs = ifs_firstchar (&slen);
|
||||
ifs = getifs ();
|
||||
if (ifs == 0 || *ifs == 0)
|
||||
{
|
||||
if (slen < 2)
|
||||
sifs = xrealloc (sifs, 2);
|
||||
sifs[0] = ' ';
|
||||
sifs[1] = '\0';
|
||||
}
|
||||
t = assoc_to_string (h2, sifs, 0);
|
||||
free(sifs);
|
||||
}
|
||||
else
|
||||
t = assoc_to_string (h2, " ", 0);
|
||||
|
||||
assoc_dispose (h2);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
char *
|
||||
assoc_to_assign (hash, quoted)
|
||||
HASH_TABLE *hash;
|
||||
int quoted;
|
||||
{
|
||||
char *ret;
|
||||
char *istr, *vstr;
|
||||
int i, rsize, rlen, elen;
|
||||
BUCKET_CONTENTS *tlist;
|
||||
|
||||
if (hash == 0 || assoc_empty (hash))
|
||||
return (char *)0;
|
||||
|
||||
ret = xmalloc (rsize = 128);
|
||||
ret[0] = '(';
|
||||
rlen = 1;
|
||||
|
||||
for (i = 0; i < hash->nbuckets; i++)
|
||||
for (tlist = hash_items (i, hash); tlist; tlist = tlist->next)
|
||||
{
|
||||
#if 1
|
||||
if (sh_contains_shell_metas (tlist->key))
|
||||
istr = sh_double_quote (tlist->key);
|
||||
else
|
||||
istr = tlist->key;
|
||||
#else
|
||||
istr = tlist->key;
|
||||
#endif
|
||||
vstr = tlist->data ? sh_double_quote ((char *)tlist->data) : (char *)0;
|
||||
|
||||
elen = STRLEN (istr) + 8 + STRLEN (vstr);
|
||||
RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
|
||||
|
||||
ret[rlen++] = '[';
|
||||
strcpy (ret+rlen, istr);
|
||||
rlen += STRLEN (istr);
|
||||
ret[rlen++] = ']';
|
||||
ret[rlen++] = '=';
|
||||
if (vstr)
|
||||
{
|
||||
strcpy (ret + rlen, vstr);
|
||||
rlen += STRLEN (vstr);
|
||||
}
|
||||
ret[rlen++] = ' ';
|
||||
|
||||
|
||||
if (istr != tlist->key)
|
||||
FREE (istr);
|
||||
|
||||
FREE (vstr);
|
||||
}
|
||||
|
||||
RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
|
||||
ret[rlen++] = ')';
|
||||
ret[rlen] = '\0';
|
||||
|
||||
if (quoted)
|
||||
{
|
||||
vstr = sh_single_quote (ret);
|
||||
free (ret);
|
||||
ret = vstr;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static WORD_LIST *
|
||||
assoc_to_word_list_internal (h, t)
|
||||
HASH_TABLE *h;
|
||||
int t;
|
||||
{
|
||||
WORD_LIST *list;
|
||||
int i;
|
||||
BUCKET_CONTENTS *tlist;
|
||||
char *w;
|
||||
|
||||
if (h == 0 || assoc_empty (h))
|
||||
return((WORD_LIST *)NULL);
|
||||
list = (WORD_LIST *)NULL;
|
||||
|
||||
for (i = 0; i < h->nbuckets; i++)
|
||||
for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
|
||||
{
|
||||
w = (t == 0) ? (char *)tlist->data : (char *)tlist->key;
|
||||
list = make_word_list (make_bare_word(w), list);
|
||||
}
|
||||
return (REVERSE_LIST(list, WORD_LIST *));
|
||||
}
|
||||
|
||||
WORD_LIST *
|
||||
assoc_to_word_list (h)
|
||||
HASH_TABLE *h;
|
||||
{
|
||||
return (assoc_to_word_list_internal (h, 0));
|
||||
}
|
||||
|
||||
WORD_LIST *
|
||||
assoc_keys_to_word_list (h)
|
||||
HASH_TABLE *h;
|
||||
{
|
||||
return (assoc_to_word_list_internal (h, 1));
|
||||
}
|
||||
|
||||
char *
|
||||
assoc_to_string (h, sep, quoted)
|
||||
HASH_TABLE *h;
|
||||
char *sep;
|
||||
int quoted;
|
||||
{
|
||||
BUCKET_CONTENTS *tlist;
|
||||
int i;
|
||||
char *result, *t, *w;
|
||||
WORD_LIST *list, *l;
|
||||
|
||||
if (h == 0)
|
||||
return ((char *)NULL);
|
||||
if (assoc_empty (h))
|
||||
return (savestring (""));
|
||||
|
||||
result = NULL;
|
||||
list = NULL;
|
||||
/* This might be better implemented directly, but it's simple to implement
|
||||
by converting to a word list first, possibly quoting the data, then
|
||||
using list_string */
|
||||
for (i = 0; i < h->nbuckets; i++)
|
||||
for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
|
||||
{
|
||||
w = (char *)tlist->data;
|
||||
if (w == 0)
|
||||
continue;
|
||||
t = quoted ? quote_string (w) : savestring (w);
|
||||
list = make_word_list (make_bare_word(t), list);
|
||||
FREE (t);
|
||||
}
|
||||
|
||||
l = REVERSE_LIST(list, WORD_LIST *);
|
||||
|
||||
result = l ? string_list_internal (l, sep) : savestring ("");
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* ARRAY_VARS */
|
||||
+13
-5
@@ -1807,7 +1807,7 @@ globword:
|
||||
{
|
||||
if (executable_or_directory (val))
|
||||
{
|
||||
if (*hint_text == '~')
|
||||
if (*hint_text == '~' && directory_part)
|
||||
{
|
||||
temp = restore_tilde (val, directory_part);
|
||||
free (val);
|
||||
@@ -2773,8 +2773,11 @@ bash_directory_expansion (dirname)
|
||||
|
||||
d = savestring (*dirname);
|
||||
|
||||
if (rl_directory_rewrite_hook)
|
||||
(*rl_directory_rewrite_hook) (&d);
|
||||
if ((rl_directory_rewrite_hook) && (*rl_directory_rewrite_hook) (&d))
|
||||
{
|
||||
free (*dirname);
|
||||
*dirname = d;
|
||||
}
|
||||
else if (rl_directory_completion_hook && (*rl_directory_completion_hook) (&d))
|
||||
{
|
||||
free (*dirname);
|
||||
@@ -3904,12 +3907,16 @@ bind_keyseq_to_unix_command (line)
|
||||
if (line[i] != ':')
|
||||
{
|
||||
builtin_error (_("%s: missing colon separator"), line);
|
||||
FREE (kseq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
i = isolate_sequence (line, i + 1, 0, &kstart);
|
||||
if (i < 0)
|
||||
return -1;
|
||||
{
|
||||
FREE (kseq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create the value string containing the command to execute. */
|
||||
value = substring (line, kstart, i);
|
||||
@@ -3920,7 +3927,8 @@ bind_keyseq_to_unix_command (line)
|
||||
/* and bind the key sequence in the current keymap to a function that
|
||||
understands how to execute from CMD_XMAP */
|
||||
rl_bind_keyseq_in_map (kseq, bash_execute_unix_command, kmap);
|
||||
|
||||
|
||||
free (kseq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+13
-4
@@ -526,9 +526,8 @@ initialize_readline ()
|
||||
set_directory_hook ();
|
||||
|
||||
rl_filename_rewrite_hook = bash_filename_rewrite_hook;
|
||||
#if 0
|
||||
|
||||
rl_filename_stat_hook = bash_filename_stat_hook;
|
||||
#endif
|
||||
|
||||
/* Tell the filename completer we want a chance to ignore some names. */
|
||||
rl_ignore_some_completions_function = filename_completion_ignore;
|
||||
@@ -1340,6 +1339,7 @@ attempt_shell_completion (text, start, end)
|
||||
|
||||
rl_filename_quote_characters = default_filename_quote_characters;
|
||||
set_filename_bstab (rl_filename_quote_characters);
|
||||
set_directory_hook ();
|
||||
|
||||
/* Determine if this could be a command word. It is if it appears at
|
||||
the start of the line (ignoring preceding whitespace), or if it
|
||||
@@ -1671,6 +1671,12 @@ command_word_completion_function (hint_text, state)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dircomplete_expand && path_dot_or_dotdot (filename_hint))
|
||||
{
|
||||
dircomplete_expand = 0;
|
||||
set_directory_hook ();
|
||||
dircomplete_expand = 1;
|
||||
}
|
||||
mapping_over = 4;
|
||||
goto inner;
|
||||
}
|
||||
@@ -1801,7 +1807,7 @@ globword:
|
||||
{
|
||||
if (executable_or_directory (val))
|
||||
{
|
||||
if (*hint_text == '~')
|
||||
if (*hint_text == '~' && directory_part)
|
||||
{
|
||||
temp = restore_tilde (val, directory_part);
|
||||
free (val);
|
||||
@@ -1871,6 +1877,9 @@ globword:
|
||||
|
||||
inner:
|
||||
val = rl_filename_completion_function (filename_hint, istate);
|
||||
if (mapping_over == 4 && dircomplete_expand)
|
||||
set_directory_hook ();
|
||||
|
||||
istate = 1;
|
||||
|
||||
if (val == 0)
|
||||
@@ -2029,7 +2038,7 @@ command_subst_completion_function (text, state)
|
||||
rl_completion_suppress_append = 1;
|
||||
}
|
||||
|
||||
if (!matches || !matches[cmd_index])
|
||||
if (matches == 0 || matches[cmd_index] == 0)
|
||||
{
|
||||
rl_filename_quoting_desired = 0; /* disable quoting */
|
||||
return ((char *)NULL);
|
||||
|
||||
+9
-2
@@ -1,7 +1,7 @@
|
||||
This file is cd.def, from which is created cd.c. It implements the
|
||||
builtins "cd" and "pwd" in Bash.
|
||||
|
||||
Copyright (C) 1987-2010 Free Software Foundation, Inc.
|
||||
Copyright (C) 1987-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
@@ -338,6 +338,7 @@ cd_builtin (list)
|
||||
if (temp && change_to_directory (temp, no_symlinks))
|
||||
{
|
||||
printf ("%s\n", temp);
|
||||
free (temp);
|
||||
return (bindpwd (no_symlinks));
|
||||
}
|
||||
else
|
||||
@@ -406,7 +407,11 @@ pwd_builtin (list)
|
||||
the file system has changed state underneath bash). */
|
||||
if ((tcwd && directory == 0) ||
|
||||
(posixly_correct && same_file (".", tcwd, (struct stat *)0, (struct stat *)0) == 0))
|
||||
directory = resetpwd ("pwd");
|
||||
{
|
||||
if (directory && directory != tcwd)
|
||||
free (directory);
|
||||
directory = resetpwd ("pwd");
|
||||
}
|
||||
|
||||
#undef tcwd
|
||||
|
||||
@@ -495,6 +500,8 @@ change_to_directory (newdir, nolinks)
|
||||
t = resetpwd ("cd");
|
||||
if (t == 0)
|
||||
set_working_directory (tdir);
|
||||
else
|
||||
free (t);
|
||||
}
|
||||
else
|
||||
set_working_directory (tdir);
|
||||
|
||||
+5
-2
@@ -1,7 +1,7 @@
|
||||
This file is cd.def, from which is created cd.c. It implements the
|
||||
builtins "cd" and "pwd" in Bash.
|
||||
|
||||
Copyright (C) 1987-2010 Free Software Foundation, Inc.
|
||||
Copyright (C) 1987-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
@@ -290,7 +290,8 @@ cd_builtin (list)
|
||||
free (temp);
|
||||
}
|
||||
|
||||
#if 0 /* changed for bash-4.2 Posix cd description steps 5-6 */
|
||||
#if 0
|
||||
/* changed for bash-4.2 Posix cd description steps 5-6 */
|
||||
/* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
|
||||
try the current directory, so we just punt now with an error
|
||||
message if POSIXLY_CORRECT is non-zero. The check for cdpath[0]
|
||||
@@ -494,6 +495,8 @@ change_to_directory (newdir, nolinks)
|
||||
t = resetpwd ("cd");
|
||||
if (t == 0)
|
||||
set_working_directory (tdir);
|
||||
else
|
||||
free (t);
|
||||
}
|
||||
else
|
||||
set_working_directory (tdir);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
This file is complete.def, from which is created complete.c.
|
||||
It implements the builtins "complete", "compgen", and "compopt" in Bash.
|
||||
|
||||
Copyright (C) 1999-2010 Free Software Foundation, Inc.
|
||||
Copyright (C) 1999-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
@@ -728,6 +728,7 @@ compgen_builtin (list)
|
||||
if ((sl == 0 || sl->list_len == 0) && (copts & COPT_DEFAULT))
|
||||
{
|
||||
matches = rl_completion_matches (word, rl_filename_completion_function);
|
||||
strlist_dispose (sl);
|
||||
sl = completions_to_stringlist (matches);
|
||||
strvec_dispose (matches);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,869 @@
|
||||
This file is complete.def, from which is created complete.c.
|
||||
It implements the builtins "complete", "compgen", and "compopt" in Bash.
|
||||
|
||||
Copyright (C) 1999-2010 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$PRODUCES complete.c
|
||||
|
||||
$BUILTIN complete
|
||||
$DEPENDS_ON PROGRAMMABLE_COMPLETION
|
||||
$FUNCTION complete_builtin
|
||||
$SHORT_DOC complete [-abcdefgjksuv] [-pr] [-DE] [-o option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [name ...]
|
||||
Specify how arguments are to be completed by Readline.
|
||||
|
||||
For each NAME, specify how arguments are to be completed. If no options
|
||||
are supplied, existing completion specifications are printed in a way that
|
||||
allows them to be reused as input.
|
||||
|
||||
Options:
|
||||
-p print existing completion specifications in a reusable format
|
||||
-r remove a completion specification for each NAME, or, if no
|
||||
NAMEs are supplied, all completion specifications
|
||||
-D apply the completions and actions as the default for commands
|
||||
without any specific completion defined
|
||||
-E apply the completions and actions to "empty" commands --
|
||||
completion attempted on a blank line
|
||||
|
||||
When completion is attempted, the actions are applied in the order the
|
||||
uppercase-letter options are listed above. The -D option takes
|
||||
precedence over -E.
|
||||
|
||||
Exit Status:
|
||||
Returns success unless an invalid option is supplied or an error occurs.
|
||||
$END
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../bashtypes.h"
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "../bashansi.h"
|
||||
#include "../bashintl.h"
|
||||
|
||||
#include "../shell.h"
|
||||
#include "../builtins.h"
|
||||
#include "../pcomplete.h"
|
||||
#include "../bashline.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
#include <readline/readline.h>
|
||||
|
||||
#define STRDUP(x) ((x) ? savestring (x) : (char *)NULL)
|
||||
|
||||
/* Structure containing all the non-action (binary) options; filled in by
|
||||
build_actions(). */
|
||||
struct _optflags {
|
||||
int pflag;
|
||||
int rflag;
|
||||
int Dflag;
|
||||
int Eflag;
|
||||
};
|
||||
|
||||
static int find_compact __P((char *));
|
||||
static int find_compopt __P((char *));
|
||||
|
||||
static int build_actions __P((WORD_LIST *, struct _optflags *, unsigned long *, unsigned long *));
|
||||
|
||||
static int remove_cmd_completions __P((WORD_LIST *));
|
||||
|
||||
static int print_one_completion __P((char *, COMPSPEC *));
|
||||
static int print_compitem __P((BUCKET_CONTENTS *));
|
||||
static void print_compopts __P((const char *, COMPSPEC *, int));
|
||||
static void print_all_completions __P((void));
|
||||
static int print_cmd_completions __P((WORD_LIST *));
|
||||
|
||||
static char *Garg, *Warg, *Parg, *Sarg, *Xarg, *Farg, *Carg;
|
||||
|
||||
static const struct _compacts {
|
||||
const char * const actname;
|
||||
int actflag;
|
||||
int actopt;
|
||||
} compacts[] = {
|
||||
{ "alias", CA_ALIAS, 'a' },
|
||||
{ "arrayvar", CA_ARRAYVAR, 0 },
|
||||
{ "binding", CA_BINDING, 0 },
|
||||
{ "builtin", CA_BUILTIN, 'b' },
|
||||
{ "command", CA_COMMAND, 'c' },
|
||||
{ "directory", CA_DIRECTORY, 'd' },
|
||||
{ "disabled", CA_DISABLED, 0 },
|
||||
{ "enabled", CA_ENABLED, 0 },
|
||||
{ "export", CA_EXPORT, 'e' },
|
||||
{ "file", CA_FILE, 'f' },
|
||||
{ "function", CA_FUNCTION, 0 },
|
||||
{ "helptopic", CA_HELPTOPIC, 0 },
|
||||
{ "hostname", CA_HOSTNAME, 0 },
|
||||
{ "group", CA_GROUP, 'g' },
|
||||
{ "job", CA_JOB, 'j' },
|
||||
{ "keyword", CA_KEYWORD, 'k' },
|
||||
{ "running", CA_RUNNING, 0 },
|
||||
{ "service", CA_SERVICE, 's' },
|
||||
{ "setopt", CA_SETOPT, 0 },
|
||||
{ "shopt", CA_SHOPT, 0 },
|
||||
{ "signal", CA_SIGNAL, 0 },
|
||||
{ "stopped", CA_STOPPED, 0 },
|
||||
{ "user", CA_USER, 'u' },
|
||||
{ "variable", CA_VARIABLE, 'v' },
|
||||
{ (char *)NULL, 0, 0 },
|
||||
};
|
||||
|
||||
/* This should be a STRING_INT_ALIST */
|
||||
static const struct _compopt {
|
||||
const char * const optname;
|
||||
int optflag;
|
||||
} compopts[] = {
|
||||
{ "bashdefault", COPT_BASHDEFAULT },
|
||||
{ "default", COPT_DEFAULT },
|
||||
{ "dirnames", COPT_DIRNAMES },
|
||||
{ "filenames",COPT_FILENAMES},
|
||||
{ "nospace", COPT_NOSPACE },
|
||||
{ "plusdirs", COPT_PLUSDIRS },
|
||||
{ (char *)NULL, 0 },
|
||||
};
|
||||
|
||||
static int
|
||||
find_compact (name)
|
||||
char *name;
|
||||
{
|
||||
register int i;
|
||||
|
||||
for (i = 0; compacts[i].actname; i++)
|
||||
if (STREQ (name, compacts[i].actname))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
find_compopt (name)
|
||||
char *name;
|
||||
{
|
||||
register int i;
|
||||
|
||||
for (i = 0; compopts[i].optname; i++)
|
||||
if (STREQ (name, compopts[i].optname))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Build the actions and compspec options from the options specified in LIST.
|
||||
ACTP is a pointer to an unsigned long in which to place the bitmap of
|
||||
actions. OPTP is a pointer to an unsigned long in which to place the
|
||||
btmap of compspec options (arguments to `-o'). PP, if non-null, gets 1
|
||||
if -p is supplied; RP, if non-null, gets 1 if -r is supplied.
|
||||
If either is null, the corresponding option generates an error.
|
||||
This also sets variables corresponding to options that take arguments as
|
||||
a side effect; the caller should ensure that those variables are set to
|
||||
NULL before calling build_actions. Return value:
|
||||
EX_USAGE = bad option
|
||||
EXECUTION_SUCCESS = some options supplied
|
||||
EXECUTION_FAILURE = no options supplied
|
||||
*/
|
||||
|
||||
static int
|
||||
build_actions (list, flagp, actp, optp)
|
||||
WORD_LIST *list;
|
||||
struct _optflags *flagp;
|
||||
unsigned long *actp, *optp;
|
||||
{
|
||||
int opt, ind, opt_given;
|
||||
unsigned long acts, copts;
|
||||
|
||||
acts = copts = (unsigned long)0L;
|
||||
opt_given = 0;
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "abcdefgjko:prsuvA:G:W:P:S:X:F:C:DE")) != -1)
|
||||
{
|
||||
opt_given = 1;
|
||||
switch (opt)
|
||||
{
|
||||
case 'r':
|
||||
if (flagp)
|
||||
{
|
||||
flagp->rflag = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
sh_invalidopt ("-r");
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
case 'p':
|
||||
if (flagp)
|
||||
{
|
||||
flagp->pflag = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
sh_invalidopt ("-p");
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
case 'a':
|
||||
acts |= CA_ALIAS;
|
||||
break;
|
||||
case 'b':
|
||||
acts |= CA_BUILTIN;
|
||||
break;
|
||||
case 'c':
|
||||
acts |= CA_COMMAND;
|
||||
break;
|
||||
case 'd':
|
||||
acts |= CA_DIRECTORY;
|
||||
break;
|
||||
case 'e':
|
||||
acts |= CA_EXPORT;
|
||||
break;
|
||||
case 'f':
|
||||
acts |= CA_FILE;
|
||||
break;
|
||||
case 'g':
|
||||
acts |= CA_GROUP;
|
||||
break;
|
||||
case 'j':
|
||||
acts |= CA_JOB;
|
||||
break;
|
||||
case 'k':
|
||||
acts |= CA_KEYWORD;
|
||||
break;
|
||||
case 's':
|
||||
acts |= CA_SERVICE;
|
||||
break;
|
||||
case 'u':
|
||||
acts |= CA_USER;
|
||||
break;
|
||||
case 'v':
|
||||
acts |= CA_VARIABLE;
|
||||
break;
|
||||
case 'o':
|
||||
ind = find_compopt (list_optarg);
|
||||
if (ind < 0)
|
||||
{
|
||||
sh_invalidoptname (list_optarg);
|
||||
return (EX_USAGE);
|
||||
}
|
||||
copts |= compopts[ind].optflag;
|
||||
break;
|
||||
case 'A':
|
||||
ind = find_compact (list_optarg);
|
||||
if (ind < 0)
|
||||
{
|
||||
builtin_error (_("%s: invalid action name"), list_optarg);
|
||||
return (EX_USAGE);
|
||||
}
|
||||
acts |= compacts[ind].actflag;
|
||||
break;
|
||||
case 'C':
|
||||
Carg = list_optarg;
|
||||
break;
|
||||
case 'D':
|
||||
if (flagp)
|
||||
{
|
||||
flagp->Dflag = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
sh_invalidopt ("-D");
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
case 'E':
|
||||
if (flagp)
|
||||
{
|
||||
flagp->Eflag = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
sh_invalidopt ("-E");
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
case 'F':
|
||||
Farg = list_optarg;
|
||||
break;
|
||||
case 'G':
|
||||
Garg = list_optarg;
|
||||
break;
|
||||
case 'P':
|
||||
Parg = list_optarg;
|
||||
break;
|
||||
case 'S':
|
||||
Sarg = list_optarg;
|
||||
break;
|
||||
case 'W':
|
||||
Warg = list_optarg;
|
||||
break;
|
||||
case 'X':
|
||||
Xarg = list_optarg;
|
||||
break;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
*actp = acts;
|
||||
*optp = copts;
|
||||
|
||||
return (opt_given ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
/* Add, remove, and display completion specifiers. */
|
||||
int
|
||||
complete_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int opt_given, rval;
|
||||
unsigned long acts, copts;
|
||||
COMPSPEC *cs;
|
||||
struct _optflags oflags;
|
||||
WORD_LIST *l, *wl;
|
||||
|
||||
if (list == 0)
|
||||
{
|
||||
print_all_completions ();
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
opt_given = oflags.pflag = oflags.rflag = oflags.Dflag = oflags.Eflag = 0;
|
||||
|
||||
acts = copts = (unsigned long)0L;
|
||||
Garg = Warg = Parg = Sarg = Xarg = Farg = Carg = (char *)NULL;
|
||||
cs = (COMPSPEC *)NULL;
|
||||
|
||||
/* Build the actions from the arguments. Also sets the [A-Z]arg variables
|
||||
as a side effect if they are supplied as options. */
|
||||
rval = build_actions (list, &oflags, &acts, &copts);
|
||||
if (rval == EX_USAGE)
|
||||
return (rval);
|
||||
opt_given = rval != EXECUTION_FAILURE;
|
||||
|
||||
list = loptend;
|
||||
|
||||
wl = oflags.Dflag ? make_word_list (make_bare_word (DEFAULTCMD), (WORD_LIST *)NULL)
|
||||
: (oflags.Eflag ? make_word_list (make_bare_word (EMPTYCMD), (WORD_LIST *)NULL) : 0);
|
||||
|
||||
/* -p overrides everything else */
|
||||
if (oflags.pflag || (list == 0 && opt_given == 0))
|
||||
{
|
||||
if (wl)
|
||||
{
|
||||
rval = print_cmd_completions (wl);
|
||||
dispose_words (wl);
|
||||
return rval;
|
||||
}
|
||||
else if (list == 0)
|
||||
{
|
||||
print_all_completions ();
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
return (print_cmd_completions (list));
|
||||
}
|
||||
|
||||
/* next, -r overrides everything else. */
|
||||
if (oflags.rflag)
|
||||
{
|
||||
if (wl)
|
||||
{
|
||||
rval = remove_cmd_completions (wl);
|
||||
dispose_words (wl);
|
||||
return rval;
|
||||
}
|
||||
else if (list == 0)
|
||||
{
|
||||
progcomp_flush ();
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
return (remove_cmd_completions (list));
|
||||
}
|
||||
|
||||
if (wl == 0 && list == 0 && opt_given)
|
||||
{
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
/* If we get here, we need to build a compspec and add it for each
|
||||
remaining argument. */
|
||||
cs = compspec_create ();
|
||||
cs->actions = acts;
|
||||
cs->options = copts;
|
||||
|
||||
cs->globpat = STRDUP (Garg);
|
||||
cs->words = STRDUP (Warg);
|
||||
cs->prefix = STRDUP (Parg);
|
||||
cs->suffix = STRDUP (Sarg);
|
||||
cs->funcname = STRDUP (Farg);
|
||||
cs->command = STRDUP (Carg);
|
||||
cs->filterpat = STRDUP (Xarg);
|
||||
|
||||
for (rval = EXECUTION_SUCCESS, l = wl ? wl : list ; l; l = l->next)
|
||||
{
|
||||
/* Add CS as the compspec for the specified commands. */
|
||||
if (progcomp_insert (l->word->word, cs) == 0)
|
||||
rval = EXECUTION_FAILURE;
|
||||
}
|
||||
|
||||
dispose_words (wl);
|
||||
return (rval);
|
||||
}
|
||||
|
||||
static int
|
||||
remove_cmd_completions (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
WORD_LIST *l;
|
||||
int ret;
|
||||
|
||||
for (ret = EXECUTION_SUCCESS, l = list; l; l = l->next)
|
||||
{
|
||||
if (progcomp_remove (l->word->word) == 0)
|
||||
{
|
||||
builtin_error (_("%s: no completion specification"), l->word->word);
|
||||
ret = EXECUTION_FAILURE;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define SQPRINTARG(a, f) \
|
||||
do { \
|
||||
if (a) \
|
||||
{ \
|
||||
x = sh_single_quote (a); \
|
||||
printf ("%s %s ", f, x); \
|
||||
free (x); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PRINTARG(a, f) \
|
||||
do { \
|
||||
if (a) \
|
||||
printf ("%s %s ", f, a); \
|
||||
} while (0)
|
||||
|
||||
#define PRINTOPT(a, f) \
|
||||
do { \
|
||||
if (acts & a) \
|
||||
printf ("%s ", f); \
|
||||
} while (0)
|
||||
|
||||
#define PRINTACT(a, f) \
|
||||
do { \
|
||||
if (acts & a) \
|
||||
printf ("-A %s ", f); \
|
||||
} while (0)
|
||||
|
||||
#define PRINTCOMPOPT(a, f) \
|
||||
do { \
|
||||
if (copts & a) \
|
||||
printf ("-o %s ", f); \
|
||||
} while (0)
|
||||
|
||||
#define XPRINTCOMPOPT(a, f) \
|
||||
do { \
|
||||
if (copts & a) \
|
||||
printf ("-o %s ", f); \
|
||||
else \
|
||||
printf ("+o %s ", f); \
|
||||
} while (0)
|
||||
|
||||
static int
|
||||
print_one_completion (cmd, cs)
|
||||
char *cmd;
|
||||
COMPSPEC *cs;
|
||||
{
|
||||
unsigned long acts, copts;
|
||||
char *x;
|
||||
|
||||
printf ("complete ");
|
||||
|
||||
copts = cs->options;
|
||||
|
||||
/* First, print the -o options. */
|
||||
PRINTCOMPOPT (COPT_BASHDEFAULT, "bashdefault");
|
||||
PRINTCOMPOPT (COPT_DEFAULT, "default");
|
||||
PRINTCOMPOPT (COPT_DIRNAMES, "dirnames");
|
||||
PRINTCOMPOPT (COPT_FILENAMES, "filenames");
|
||||
PRINTCOMPOPT (COPT_NOSPACE, "nospace");
|
||||
PRINTCOMPOPT (COPT_PLUSDIRS, "plusdirs");
|
||||
|
||||
acts = cs->actions;
|
||||
|
||||
/* simple flags next */
|
||||
PRINTOPT (CA_ALIAS, "-a");
|
||||
PRINTOPT (CA_BUILTIN, "-b");
|
||||
PRINTOPT (CA_COMMAND, "-c");
|
||||
PRINTOPT (CA_DIRECTORY, "-d");
|
||||
PRINTOPT (CA_EXPORT, "-e");
|
||||
PRINTOPT (CA_FILE, "-f");
|
||||
PRINTOPT (CA_GROUP, "-g");
|
||||
PRINTOPT (CA_JOB, "-j");
|
||||
PRINTOPT (CA_KEYWORD, "-k");
|
||||
PRINTOPT (CA_SERVICE, "-s");
|
||||
PRINTOPT (CA_USER, "-u");
|
||||
PRINTOPT (CA_VARIABLE, "-v");
|
||||
|
||||
/* now the rest of the actions */
|
||||
PRINTACT (CA_ARRAYVAR, "arrayvar");
|
||||
PRINTACT (CA_BINDING, "binding");
|
||||
PRINTACT (CA_DISABLED, "disabled");
|
||||
PRINTACT (CA_ENABLED, "enabled");
|
||||
PRINTACT (CA_FUNCTION, "function");
|
||||
PRINTACT (CA_HELPTOPIC, "helptopic");
|
||||
PRINTACT (CA_HOSTNAME, "hostname");
|
||||
PRINTACT (CA_RUNNING, "running");
|
||||
PRINTACT (CA_SETOPT, "setopt");
|
||||
PRINTACT (CA_SHOPT, "shopt");
|
||||
PRINTACT (CA_SIGNAL, "signal");
|
||||
PRINTACT (CA_STOPPED, "stopped");
|
||||
|
||||
/* now the rest of the arguments */
|
||||
|
||||
/* arguments that require quoting */
|
||||
SQPRINTARG (cs->globpat, "-G");
|
||||
SQPRINTARG (cs->words, "-W");
|
||||
SQPRINTARG (cs->prefix, "-P");
|
||||
SQPRINTARG (cs->suffix, "-S");
|
||||
SQPRINTARG (cs->filterpat, "-X");
|
||||
|
||||
SQPRINTARG (cs->command, "-C");
|
||||
|
||||
/* simple arguments that don't require quoting */
|
||||
PRINTARG (cs->funcname, "-F");
|
||||
|
||||
if (STREQ (cmd, EMPTYCMD))
|
||||
printf ("-E\n");
|
||||
else if (STREQ (cmd, DEFAULTCMD))
|
||||
printf ("-D\n");
|
||||
else
|
||||
printf ("%s\n", cmd);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
print_compopts (cmd, cs, full)
|
||||
const char *cmd;
|
||||
COMPSPEC *cs;
|
||||
int full;
|
||||
{
|
||||
int copts;
|
||||
|
||||
printf ("compopt ");
|
||||
copts = cs->options;
|
||||
|
||||
if (full)
|
||||
{
|
||||
XPRINTCOMPOPT (COPT_BASHDEFAULT, "bashdefault");
|
||||
XPRINTCOMPOPT (COPT_DEFAULT, "default");
|
||||
XPRINTCOMPOPT (COPT_DIRNAMES, "dirnames");
|
||||
XPRINTCOMPOPT (COPT_FILENAMES, "filenames");
|
||||
XPRINTCOMPOPT (COPT_NOSPACE, "nospace");
|
||||
XPRINTCOMPOPT (COPT_PLUSDIRS, "plusdirs");
|
||||
}
|
||||
else
|
||||
{
|
||||
PRINTCOMPOPT (COPT_BASHDEFAULT, "bashdefault");
|
||||
PRINTCOMPOPT (COPT_DEFAULT, "default");
|
||||
PRINTCOMPOPT (COPT_DIRNAMES, "dirnames");
|
||||
PRINTCOMPOPT (COPT_FILENAMES, "filenames");
|
||||
PRINTCOMPOPT (COPT_NOSPACE, "nospace");
|
||||
PRINTCOMPOPT (COPT_PLUSDIRS, "plusdirs");
|
||||
}
|
||||
|
||||
if (STREQ (cmd, EMPTYCMD))
|
||||
printf ("-E\n");
|
||||
else if (STREQ (cmd, DEFAULTCMD))
|
||||
printf ("-D\n");
|
||||
else
|
||||
printf ("%s\n", cmd);
|
||||
}
|
||||
|
||||
static int
|
||||
print_compitem (item)
|
||||
BUCKET_CONTENTS *item;
|
||||
{
|
||||
COMPSPEC *cs;
|
||||
char *cmd;
|
||||
|
||||
cmd = item->key;
|
||||
cs = (COMPSPEC *)item->data;
|
||||
|
||||
return (print_one_completion (cmd, cs));
|
||||
}
|
||||
|
||||
static void
|
||||
print_all_completions ()
|
||||
{
|
||||
progcomp_walk (print_compitem);
|
||||
}
|
||||
|
||||
static int
|
||||
print_cmd_completions (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
WORD_LIST *l;
|
||||
COMPSPEC *cs;
|
||||
int ret;
|
||||
|
||||
for (ret = EXECUTION_SUCCESS, l = list; l; l = l->next)
|
||||
{
|
||||
cs = progcomp_search (l->word->word);
|
||||
if (cs)
|
||||
print_one_completion (l->word->word, cs);
|
||||
else
|
||||
{
|
||||
builtin_error (_("%s: no completion specification"), l->word->word);
|
||||
ret = EXECUTION_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return (sh_chkwrite (ret));
|
||||
}
|
||||
|
||||
$BUILTIN compgen
|
||||
$DEPENDS_ON PROGRAMMABLE_COMPLETION
|
||||
$FUNCTION compgen_builtin
|
||||
$SHORT_DOC compgen [-abcdefgjksuv] [-o option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [word]
|
||||
Display possible completions depending on the options.
|
||||
|
||||
Intended to be used from within a shell function generating possible
|
||||
completions. If the optional WORD argument is supplied, matches against
|
||||
WORD are generated.
|
||||
|
||||
Exit Status:
|
||||
Returns success unless an invalid option is supplied or an error occurs.
|
||||
$END
|
||||
|
||||
int
|
||||
compgen_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int rval;
|
||||
unsigned long acts, copts;
|
||||
COMPSPEC *cs;
|
||||
STRINGLIST *sl;
|
||||
char *word, **matches;
|
||||
|
||||
if (list == 0)
|
||||
return (EXECUTION_SUCCESS);
|
||||
|
||||
acts = copts = (unsigned long)0L;
|
||||
Garg = Warg = Parg = Sarg = Xarg = Farg = Carg = (char *)NULL;
|
||||
cs = (COMPSPEC *)NULL;
|
||||
|
||||
/* Build the actions from the arguments. Also sets the [A-Z]arg variables
|
||||
as a side effect if they are supplied as options. */
|
||||
rval = build_actions (list, (struct _optflags *)NULL, &acts, &copts);
|
||||
if (rval == EX_USAGE)
|
||||
return (rval);
|
||||
if (rval == EXECUTION_FAILURE)
|
||||
return (EXECUTION_SUCCESS);
|
||||
|
||||
list = loptend;
|
||||
|
||||
word = (list && list->word) ? list->word->word : "";
|
||||
|
||||
if (Farg)
|
||||
builtin_error (_("warning: -F option may not work as you expect"));
|
||||
if (Carg)
|
||||
builtin_error (_("warning: -C option may not work as you expect"));
|
||||
|
||||
/* If we get here, we need to build a compspec and evaluate it. */
|
||||
cs = compspec_create ();
|
||||
cs->actions = acts;
|
||||
cs->options = copts;
|
||||
cs->refcount = 1;
|
||||
|
||||
cs->globpat = STRDUP (Garg);
|
||||
cs->words = STRDUP (Warg);
|
||||
cs->prefix = STRDUP (Parg);
|
||||
cs->suffix = STRDUP (Sarg);
|
||||
cs->funcname = STRDUP (Farg);
|
||||
cs->command = STRDUP (Carg);
|
||||
cs->filterpat = STRDUP (Xarg);
|
||||
|
||||
rval = EXECUTION_FAILURE;
|
||||
sl = gen_compspec_completions (cs, "compgen", word, 0, 0, 0);
|
||||
|
||||
/* If the compspec wants the bash default completions, temporarily
|
||||
turn off programmable completion and call the bash completion code. */
|
||||
if ((sl == 0 || sl->list_len == 0) && (copts & COPT_BASHDEFAULT))
|
||||
{
|
||||
matches = bash_default_completion (word, 0, 0, 0, 0);
|
||||
sl = completions_to_stringlist (matches);
|
||||
strvec_dispose (matches);
|
||||
}
|
||||
|
||||
/* This isn't perfect, but it's the best we can do, given what readline
|
||||
exports from its set of completion utility functions. */
|
||||
if ((sl == 0 || sl->list_len == 0) && (copts & COPT_DEFAULT))
|
||||
{
|
||||
matches = rl_completion_matches (word, rl_filename_completion_function);
|
||||
sl = completions_to_stringlist (matches);
|
||||
strvec_dispose (matches);
|
||||
}
|
||||
|
||||
if (sl)
|
||||
{
|
||||
if (sl->list && sl->list_len)
|
||||
{
|
||||
rval = EXECUTION_SUCCESS;
|
||||
strlist_print (sl, (char *)NULL);
|
||||
}
|
||||
strlist_dispose (sl);
|
||||
}
|
||||
|
||||
compspec_dispose (cs);
|
||||
return (rval);
|
||||
}
|
||||
|
||||
$BUILTIN compopt
|
||||
$DEPENDS_ON PROGRAMMABLE_COMPLETION
|
||||
$FUNCTION compopt_builtin
|
||||
$SHORT_DOC compopt [-o|+o option] [-DE] [name ...]
|
||||
Modify or display completion options.
|
||||
|
||||
Modify the completion options for each NAME, or, if no NAMEs are supplied,
|
||||
the completion currently being executed. If no OPTIONs are given, print
|
||||
the completion options for each NAME or the current completion specification.
|
||||
|
||||
Options:
|
||||
-o option Set completion option OPTION for each NAME
|
||||
-D Change options for the "default" command completion
|
||||
-E Change options for the "empty" command completion
|
||||
|
||||
Using `+o' instead of `-o' turns off the specified option.
|
||||
|
||||
Arguments:
|
||||
|
||||
Each NAME refers to a command for which a completion specification must
|
||||
have previously been defined using the `complete' builtin. If no NAMEs
|
||||
are supplied, compopt must be called by a function currently generating
|
||||
completions, and the options for that currently-executing completion
|
||||
generator are modified.
|
||||
|
||||
Exit Status:
|
||||
Returns success unless an invalid option is supplied or NAME does not
|
||||
have a completion specification defined.
|
||||
$END
|
||||
|
||||
int
|
||||
compopt_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int opts_on, opts_off, *opts, opt, oind, ret, Dflag, Eflag;
|
||||
WORD_LIST *l, *wl;
|
||||
COMPSPEC *cs;
|
||||
|
||||
opts_on = opts_off = Eflag = Dflag = 0;
|
||||
ret = EXECUTION_SUCCESS;
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "+o:DE")) != EOF)
|
||||
{
|
||||
opts = (list_opttype == '-') ? &opts_on : &opts_off;
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
case 'o':
|
||||
oind = find_compopt (list_optarg);
|
||||
if (oind < 0)
|
||||
{
|
||||
sh_invalidoptname (list_optarg);
|
||||
return (EX_USAGE);
|
||||
}
|
||||
*opts |= compopts[oind].optflag;
|
||||
break;
|
||||
case 'D':
|
||||
Dflag = 1;
|
||||
break;
|
||||
case 'E':
|
||||
Eflag = 1;
|
||||
break;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
wl = Dflag ? make_word_list (make_bare_word (DEFAULTCMD), (WORD_LIST *)NULL)
|
||||
: (Eflag ? make_word_list (make_bare_word (EMPTYCMD), (WORD_LIST *)NULL) : 0);
|
||||
|
||||
if (list == 0 && wl == 0)
|
||||
{
|
||||
if (RL_ISSTATE (RL_STATE_COMPLETING) == 0 || pcomp_curcs == 0)
|
||||
{
|
||||
builtin_error (_("not currently executing completion function"));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
cs = pcomp_curcs;
|
||||
|
||||
if (opts_on == 0 && opts_off == 0)
|
||||
{
|
||||
print_compopts (pcomp_curcmd, cs, 1);
|
||||
return (sh_chkwrite (ret));
|
||||
}
|
||||
|
||||
/* Set the compspec options */
|
||||
pcomp_set_compspec_options (cs, opts_on, 1);
|
||||
pcomp_set_compspec_options (cs, opts_off, 0);
|
||||
|
||||
/* And change the readline variables the options control */
|
||||
pcomp_set_readline_variables (opts_on, 1);
|
||||
pcomp_set_readline_variables (opts_off, 0);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
for (l = wl ? wl : list; l; l = l->next)
|
||||
{
|
||||
cs = progcomp_search (l->word->word);
|
||||
if (cs == 0)
|
||||
{
|
||||
builtin_error (_("%s: no completion specification"), l->word->word);
|
||||
ret = EXECUTION_FAILURE;
|
||||
continue;
|
||||
}
|
||||
if (opts_on == 0 && opts_off == 0)
|
||||
{
|
||||
print_compopts (l->word->word, cs, 1);
|
||||
continue; /* XXX -- fill in later */
|
||||
}
|
||||
|
||||
/* Set the compspec options */
|
||||
pcomp_set_compspec_options (cs, opts_on, 1);
|
||||
pcomp_set_compspec_options (cs, opts_off, 0);
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
@@ -276,6 +276,7 @@ list_hashed_filename_targets (list, fmt)
|
||||
printf ("%s\t", l->word->word);
|
||||
printf ("%s\n", target);
|
||||
}
|
||||
free (target);
|
||||
}
|
||||
|
||||
return (all_found ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
This file is hash.def, from which is created hash.c.
|
||||
It implements the builtin "hash" in Bash.
|
||||
|
||||
Copyright (C) 1987-2010 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$PRODUCES hash.c
|
||||
|
||||
$BUILTIN hash
|
||||
$FUNCTION hash_builtin
|
||||
$SHORT_DOC hash [-lr] [-p pathname] [-dt] [name ...]
|
||||
Remember or display program locations.
|
||||
|
||||
Determine and remember the full pathname of each command NAME. If
|
||||
no arguments are given, information about remembered commands is displayed.
|
||||
|
||||
Options:
|
||||
-d forget the remembered location of each NAME
|
||||
-l display in a format that may be reused as input
|
||||
-p pathname use PATHNAME is the full pathname of NAME
|
||||
-r forget all remembered locations
|
||||
-t print the remembered location of each NAME, preceding
|
||||
each location with the corresponding NAME if multiple
|
||||
NAMEs are given
|
||||
Arguments:
|
||||
NAME Each NAME is searched for in $PATH and added to the list
|
||||
of remembered commands.
|
||||
|
||||
Exit Status:
|
||||
Returns success unless NAME is not found or an invalid option is given.
|
||||
$END
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../bashtypes.h"
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "../bashansi.h"
|
||||
#include "../bashintl.h"
|
||||
|
||||
#include "../shell.h"
|
||||
#include "../builtins.h"
|
||||
#include "../flags.h"
|
||||
#include "../findcmd.h"
|
||||
#include "../hashcmd.h"
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
extern int posixly_correct;
|
||||
extern int dot_found_in_search;
|
||||
extern char *this_command_name;
|
||||
|
||||
static int add_hashed_command __P((char *, int));
|
||||
static int print_hash_info __P((BUCKET_CONTENTS *));
|
||||
static int print_portable_hash_info __P((BUCKET_CONTENTS *));
|
||||
static int print_hashed_commands __P((int));
|
||||
static int list_hashed_filename_targets __P((WORD_LIST *, int));
|
||||
|
||||
/* Print statistics on the current state of hashed commands. If LIST is
|
||||
not empty, then rehash (or hash in the first place) the specified
|
||||
commands. */
|
||||
int
|
||||
hash_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int expunge_hash_table, list_targets, list_portably, delete, opt;
|
||||
char *w, *pathname;
|
||||
|
||||
if (hashing_enabled == 0)
|
||||
{
|
||||
builtin_error (_("hashing disabled"));
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
expunge_hash_table = list_targets = list_portably = delete = 0;
|
||||
pathname = (char *)NULL;
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "dlp:rt")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'd':
|
||||
delete = 1;
|
||||
break;
|
||||
case 'l':
|
||||
list_portably = 1;
|
||||
break;
|
||||
case 'p':
|
||||
pathname = list_optarg;
|
||||
break;
|
||||
case 'r':
|
||||
expunge_hash_table = 1;
|
||||
break;
|
||||
case 't':
|
||||
list_targets = 1;
|
||||
break;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
/* hash -t requires at least one argument. */
|
||||
if (list == 0 && list_targets)
|
||||
{
|
||||
sh_needarg ("-t");
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
/* We want hash -r to be silent, but hash -- to print hashing info, so
|
||||
we test expunge_hash_table. */
|
||||
if (list == 0 && expunge_hash_table == 0)
|
||||
{
|
||||
opt = print_hashed_commands (list_portably);
|
||||
if (opt == 0 && posixly_correct == 0)
|
||||
printf (_("%s: hash table empty\n"), this_command_name);
|
||||
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
if (expunge_hash_table)
|
||||
phash_flush ();
|
||||
|
||||
/* If someone runs `hash -r -t xyz' he will be disappointed. */
|
||||
if (list_targets)
|
||||
return (list_hashed_filename_targets (list, list_portably));
|
||||
|
||||
#if defined (RESTRICTED_SHELL)
|
||||
if (restricted && pathname && strchr (pathname, '/'))
|
||||
{
|
||||
sh_restricted (pathname);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (opt = EXECUTION_SUCCESS; list; list = list->next)
|
||||
{
|
||||
/* Add, remove or rehash the specified commands. */
|
||||
w = list->word->word;
|
||||
if (absolute_program (w))
|
||||
continue;
|
||||
else if (pathname)
|
||||
{
|
||||
if (is_directory (pathname))
|
||||
{
|
||||
#ifdef EISDIR
|
||||
builtin_error ("%s: %s", pathname, strerror (EISDIR));
|
||||
#else
|
||||
builtin_error (_("%s: is a directory"), pathname);
|
||||
#endif
|
||||
opt = EXECUTION_FAILURE;
|
||||
}
|
||||
else
|
||||
phash_insert (w, pathname, 0, 0);
|
||||
}
|
||||
else if (delete)
|
||||
{
|
||||
if (phash_remove (w))
|
||||
{
|
||||
sh_notfound (w);
|
||||
opt = EXECUTION_FAILURE;
|
||||
}
|
||||
}
|
||||
else if (add_hashed_command (w, 0))
|
||||
opt = EXECUTION_FAILURE;
|
||||
}
|
||||
|
||||
fflush (stdout);
|
||||
return (opt);
|
||||
}
|
||||
|
||||
static int
|
||||
add_hashed_command (w, quiet)
|
||||
char *w;
|
||||
int quiet;
|
||||
{
|
||||
int rv;
|
||||
char *full_path;
|
||||
|
||||
rv = 0;
|
||||
if (find_function (w) == 0 && find_shell_builtin (w) == 0)
|
||||
{
|
||||
phash_remove (w);
|
||||
full_path = find_user_command (w);
|
||||
if (full_path && executable_file (full_path))
|
||||
phash_insert (w, full_path, dot_found_in_search, 0);
|
||||
else
|
||||
{
|
||||
if (quiet == 0)
|
||||
sh_notfound (w);
|
||||
rv++;
|
||||
}
|
||||
FREE (full_path);
|
||||
}
|
||||
return (rv);
|
||||
}
|
||||
|
||||
/* Print information about current hashed info. */
|
||||
static int
|
||||
print_hash_info (item)
|
||||
BUCKET_CONTENTS *item;
|
||||
{
|
||||
printf ("%4d\t%s\n", item->times_found, pathdata(item)->path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
print_portable_hash_info (item)
|
||||
BUCKET_CONTENTS *item;
|
||||
{
|
||||
printf ("builtin hash -p %s %s\n", pathdata(item)->path, item->key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
print_hashed_commands (fmt)
|
||||
int fmt;
|
||||
{
|
||||
if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0)
|
||||
return (0);
|
||||
|
||||
if (fmt == 0)
|
||||
printf (_("hits\tcommand\n"));
|
||||
hash_walk (hashed_filenames, fmt ? print_portable_hash_info : print_hash_info);
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
list_hashed_filename_targets (list, fmt)
|
||||
WORD_LIST *list;
|
||||
int fmt;
|
||||
{
|
||||
int all_found, multiple;
|
||||
char *target;
|
||||
WORD_LIST *l;
|
||||
|
||||
all_found = 1;
|
||||
multiple = list->next != 0;
|
||||
|
||||
for (l = list; l; l = l->next)
|
||||
{
|
||||
target = phash_search (l->word->word);
|
||||
if (target == 0)
|
||||
{
|
||||
all_found = 0;
|
||||
sh_notfound (l->word->word);
|
||||
continue;
|
||||
}
|
||||
if (fmt)
|
||||
printf ("builtin hash -p %s %s\n", target, l->word->word);
|
||||
else
|
||||
{
|
||||
if (multiple)
|
||||
printf ("%s\t", l->word->word);
|
||||
printf ("%s\n", target);
|
||||
}
|
||||
}
|
||||
|
||||
return (all_found ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
This file is help.def, from which is created help.c.
|
||||
It implements the builtin "help" in Bash.
|
||||
|
||||
Copyright (C) 1987-2009 Free Software Foundation, Inc.
|
||||
Copyright (C) 1987-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
@@ -209,7 +209,7 @@ show_longdoc (i)
|
||||
zcatfd (fd, 1, doc[0]);
|
||||
close (fd);
|
||||
}
|
||||
else
|
||||
else if (doc)
|
||||
for (j = 0; doc[j]; j++)
|
||||
printf ("%*s%s\n", BASE_INDENT, " ", _(doc[j]));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
This file is help.def, from which is created help.c.
|
||||
It implements the builtin "help" in Bash.
|
||||
|
||||
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/>.
|
||||
|
||||
$PRODUCES help.c
|
||||
|
||||
$BUILTIN help
|
||||
$FUNCTION help_builtin
|
||||
$DEPENDS_ON HELP_BUILTIN
|
||||
$SHORT_DOC help [-dms] [pattern ...]
|
||||
Display information about builtin commands.
|
||||
|
||||
Displays brief summaries of builtin commands. If PATTERN is
|
||||
specified, gives detailed help on all commands matching PATTERN,
|
||||
otherwise the list of help topics is printed.
|
||||
|
||||
Options:
|
||||
-d output short description for each topic
|
||||
-m display usage in pseudo-manpage format
|
||||
-s output only a short usage synopsis for each topic matching
|
||||
PATTERN
|
||||
|
||||
Arguments:
|
||||
PATTERN Pattern specifiying a help topic
|
||||
|
||||
Exit Status:
|
||||
Returns success unless PATTERN is not found or an invalid option is given.
|
||||
$END
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if defined (HELP_BUILTIN)
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# ifdef _MINIX
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <filecntl.h>
|
||||
|
||||
#include "../bashintl.h"
|
||||
|
||||
#include "../shell.h"
|
||||
#include "../builtins.h"
|
||||
#include "../pathexp.h"
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
#include <glob/strmatch.h>
|
||||
#include <glob/glob.h>
|
||||
|
||||
#ifndef errno
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
extern const char * const bash_copyright;
|
||||
extern const char * const bash_license;
|
||||
|
||||
static void show_builtin_command_help __P((void));
|
||||
static int open_helpfile __P((char *));
|
||||
static void show_desc __P((char *, int));
|
||||
static void show_manpage __P((char *, int));
|
||||
static void show_longdoc __P((int));
|
||||
|
||||
/* Print out a list of the known functions in the shell, and what they do.
|
||||
If LIST is supplied, print out the list which matches for each pattern
|
||||
specified. */
|
||||
int
|
||||
help_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
register int i;
|
||||
char *pattern, *name;
|
||||
int plen, match_found, sflag, dflag, mflag;
|
||||
|
||||
dflag = sflag = mflag = 0;
|
||||
reset_internal_getopt ();
|
||||
while ((i = internal_getopt (list, "dms")) != -1)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 'd':
|
||||
dflag = 1;
|
||||
break;
|
||||
case 'm':
|
||||
mflag = 1;
|
||||
break;
|
||||
case 's':
|
||||
sflag = 1;
|
||||
break;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
if (list == 0)
|
||||
{
|
||||
show_shell_version (0);
|
||||
show_builtin_command_help ();
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
/* We should consider making `help bash' do something. */
|
||||
|
||||
if (glob_pattern_p (list->word->word))
|
||||
{
|
||||
printf (ngettext ("Shell commands matching keyword `", "Shell commands matching keywords `", (list->next ? 2 : 1)));
|
||||
print_word_list (list, ", ");
|
||||
printf ("'\n\n");
|
||||
}
|
||||
|
||||
for (match_found = 0, pattern = ""; list; list = list->next)
|
||||
{
|
||||
pattern = list->word->word;
|
||||
plen = strlen (pattern);
|
||||
|
||||
for (i = 0; name = shell_builtins[i].name; i++)
|
||||
{
|
||||
QUIT;
|
||||
if ((strcmp (pattern, name) == 0) ||
|
||||
(strmatch (pattern, name, FNMATCH_EXTFLAG) != FNM_NOMATCH))
|
||||
{
|
||||
match_found++;
|
||||
if (dflag)
|
||||
{
|
||||
show_desc (name, i);
|
||||
continue;
|
||||
}
|
||||
else if (mflag)
|
||||
{
|
||||
show_manpage (name, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf ("%s: %s\n", name, _(shell_builtins[i].short_doc));
|
||||
|
||||
if (sflag == 0)
|
||||
show_longdoc (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match_found == 0)
|
||||
{
|
||||
builtin_error (_("no help topics match `%s'. Try `help help' or `man -k %s' or `info %s'."), pattern, pattern, pattern);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
fflush (stdout);
|
||||
return (EXECUTION_SUCCESS);
|
||||
}
|
||||
|
||||
static int
|
||||
open_helpfile (name)
|
||||
char *name;
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open (name, O_RDONLY);
|
||||
if (fd == -1)
|
||||
{
|
||||
builtin_error (_("%s: cannot open: %s"), name, strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* By convention, enforced by mkbuiltins.c, if separate help files are being
|
||||
used, the long_doc array contains one string -- the full pathname of the
|
||||
help file for this builtin. */
|
||||
static void
|
||||
show_longdoc (i)
|
||||
int i;
|
||||
{
|
||||
register int j;
|
||||
char * const *doc;
|
||||
int fd;
|
||||
|
||||
doc = shell_builtins[i].long_doc;
|
||||
|
||||
if (doc && doc[0] && *doc[0] == '/' && doc[1] == (char *)NULL)
|
||||
{
|
||||
fd = open_helpfile (doc[0]);
|
||||
if (fd < 0)
|
||||
return;
|
||||
zcatfd (fd, 1, doc[0]);
|
||||
close (fd);
|
||||
}
|
||||
else
|
||||
for (j = 0; doc[j]; j++)
|
||||
printf ("%*s%s\n", BASE_INDENT, " ", _(doc[j]));
|
||||
}
|
||||
|
||||
static void
|
||||
show_desc (name, i)
|
||||
char *name;
|
||||
int i;
|
||||
{
|
||||
register int j;
|
||||
char **doc, *line;
|
||||
int fd, usefile;
|
||||
|
||||
doc = (char **)shell_builtins[i].long_doc;
|
||||
|
||||
usefile = (doc && doc[0] && *doc[0] == '/' && doc[1] == (char *)NULL);
|
||||
if (usefile)
|
||||
{
|
||||
fd = open_helpfile (doc[0]);
|
||||
if (fd < 0)
|
||||
return;
|
||||
zmapfd (fd, &line, doc[0]);
|
||||
close (fd);
|
||||
}
|
||||
else
|
||||
line = doc ? doc[0] : (char *)NULL;
|
||||
|
||||
printf ("%s - ", name);
|
||||
for (j = 0; line && line[j]; j++)
|
||||
{
|
||||
putchar (line[j]);
|
||||
if (line[j] == '\n')
|
||||
break;
|
||||
}
|
||||
|
||||
fflush (stdout);
|
||||
|
||||
if (usefile)
|
||||
free (line);
|
||||
}
|
||||
|
||||
/* Print builtin help in pseudo-manpage format. */
|
||||
static void
|
||||
show_manpage (name, i)
|
||||
char *name;
|
||||
int i;
|
||||
{
|
||||
register int j;
|
||||
char **doc, *line;
|
||||
int fd, usefile;
|
||||
|
||||
doc = (char **)shell_builtins[i].long_doc;
|
||||
|
||||
usefile = (doc && doc[0] && *doc[0] == '/' && doc[1] == (char *)NULL);
|
||||
if (usefile)
|
||||
{
|
||||
fd = open_helpfile (doc[0]);
|
||||
if (fd < 0)
|
||||
return;
|
||||
zmapfd (fd, &line, doc[0]);
|
||||
close (fd);
|
||||
}
|
||||
else
|
||||
line = doc ? _(doc[0]) : (char *)NULL;
|
||||
|
||||
/* NAME */
|
||||
printf ("NAME\n");
|
||||
printf ("%*s%s - ", BASE_INDENT, " ", name);
|
||||
for (j = 0; line && line[j]; j++)
|
||||
{
|
||||
putchar (line[j]);
|
||||
if (line[j] == '\n')
|
||||
break;
|
||||
}
|
||||
printf ("\n");
|
||||
|
||||
/* SYNOPSIS */
|
||||
printf ("SYNOPSIS\n");
|
||||
printf ("%*s%s\n\n", BASE_INDENT, " ", _(shell_builtins[i].short_doc));
|
||||
|
||||
/* DESCRIPTION */
|
||||
printf ("DESCRIPTION\n");
|
||||
if (usefile == 0)
|
||||
{
|
||||
for (j = 0; doc[j]; j++)
|
||||
printf ("%*s%s\n", BASE_INDENT, " ", _(doc[j]));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (j = 0; line && line[j]; j++)
|
||||
{
|
||||
putchar (line[j]);
|
||||
if (line[j] == '\n')
|
||||
printf ("%*s", BASE_INDENT, " ");
|
||||
}
|
||||
}
|
||||
putchar ('\n');
|
||||
|
||||
/* SEE ALSO */
|
||||
printf ("SEE ALSO\n");
|
||||
printf ("%*sbash(1)\n\n", BASE_INDENT, " ");
|
||||
|
||||
/* IMPLEMENTATION */
|
||||
printf ("IMPLEMENTATION\n");
|
||||
printf ("%*s", BASE_INDENT, " ");
|
||||
show_shell_version (0);
|
||||
printf ("%*s", BASE_INDENT, " ");
|
||||
printf ("%s\n", _(bash_copyright));
|
||||
printf ("%*s", BASE_INDENT, " ");
|
||||
printf ("%s\n", _(bash_license));
|
||||
|
||||
fflush (stdout);
|
||||
if (usefile)
|
||||
free (line);
|
||||
}
|
||||
|
||||
static void
|
||||
show_builtin_command_help ()
|
||||
{
|
||||
int i, j;
|
||||
int height, width;
|
||||
char *t, blurb[128];
|
||||
|
||||
printf (
|
||||
_("These shell commands are defined internally. Type `help' to see this list.\n\
|
||||
Type `help name' to find out more about the function `name'.\n\
|
||||
Use `info bash' to find out more about the shell in general.\n\
|
||||
Use `man -k' or `info' to find out more about commands not in this list.\n\
|
||||
\n\
|
||||
A star (*) next to a name means that the command is disabled.\n\
|
||||
\n"));
|
||||
|
||||
t = get_string_value ("COLUMNS");
|
||||
width = (t && *t) ? atoi (t) : 80;
|
||||
if (width <= 0)
|
||||
width = 80;
|
||||
|
||||
width /= 2;
|
||||
if (width > sizeof (blurb))
|
||||
width = sizeof (blurb);
|
||||
if (width <= 3)
|
||||
width = 40;
|
||||
height = (num_shell_builtins + 1) / 2; /* number of rows */
|
||||
|
||||
for (i = 0; i < height; i++)
|
||||
{
|
||||
QUIT;
|
||||
|
||||
/* first column */
|
||||
blurb[0] = (shell_builtins[i].flags & BUILTIN_ENABLED) ? ' ' : '*';
|
||||
strncpy (blurb + 1, _(shell_builtins[i].short_doc), width - 2);
|
||||
blurb[width - 2] = '>'; /* indicate truncation */
|
||||
blurb[width - 1] = '\0';
|
||||
printf ("%s", blurb);
|
||||
if (((i << 1) >= num_shell_builtins) || (i+height >= num_shell_builtins))
|
||||
{
|
||||
printf ("\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* two spaces */
|
||||
for (j = strlen (blurb); j < width; j++)
|
||||
putc (' ', stdout);
|
||||
|
||||
/* second column */
|
||||
blurb[0] = (shell_builtins[i+height].flags & BUILTIN_ENABLED) ? ' ' : '*';
|
||||
strncpy (blurb + 1, _(shell_builtins[i+height].short_doc), width - 3);
|
||||
blurb[width - 3] = '>'; /* indicate truncation */
|
||||
blurb[width - 2] = '\0';
|
||||
printf ("%s\n", blurb);
|
||||
}
|
||||
}
|
||||
#endif /* HELP_BUILTIN */
|
||||
@@ -1,7 +1,7 @@
|
||||
/* mkbuiltins.c - Create builtins.c, builtext.h, and builtdoc.c from
|
||||
a single source file called builtins.def. */
|
||||
|
||||
/* Copyright (C) 1987-2010 Free Software Foundation, Inc.
|
||||
/* Copyright (C) 1987-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
@@ -519,6 +519,7 @@ extract_info (filename, structfile, externfile)
|
||||
if (nr == 0)
|
||||
{
|
||||
fprintf (stderr, "mkbuiltins: %s: skipping zero-length file\n", filename);
|
||||
free (buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1407,7 +1408,7 @@ write_documentation (stream, documentation, indentation, flags)
|
||||
|
||||
base_indent = (string_array && single_longdoc_strings && filename_p == 0) ? BASE_INDENT : 0;
|
||||
|
||||
for (i = 0, texinfo = (flags & TEXINFO); line = documentation[i]; i++)
|
||||
for (i = 0, texinfo = (flags & TEXINFO); documentation && (line = documentation[i]); i++)
|
||||
{
|
||||
/* Allow #ifdef's to be written out verbatim, but don't put them into
|
||||
separate help files. */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -737,7 +737,7 @@ assign_vars:
|
||||
xfree (t1);
|
||||
}
|
||||
else
|
||||
var = bind_read_variable (varname, t);
|
||||
var = bind_read_variable (varname, t ? t : "");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -0,0 +1,999 @@
|
||||
This file is read.def, from which is created read.c.
|
||||
It implements the builtin "read" in Bash.
|
||||
|
||||
Copyright (C) 1987-2010 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$PRODUCES read.c
|
||||
|
||||
$BUILTIN read
|
||||
$FUNCTION read_builtin
|
||||
$SHORT_DOC read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]
|
||||
Read a line from the standard input and split it into fields.
|
||||
|
||||
Reads a single line from the standard input, or from file descriptor FD
|
||||
if the -u option is supplied. The line is split into fields as with word
|
||||
splitting, and the first word is assigned to the first NAME, the second
|
||||
word to the second NAME, and so on, with any leftover words assigned to
|
||||
the last NAME. Only the characters found in $IFS are recognized as word
|
||||
delimiters.
|
||||
|
||||
If no NAMEs are supplied, the line read is stored in the REPLY variable.
|
||||
|
||||
Options:
|
||||
-a array assign the words read to sequential indices of the array
|
||||
variable ARRAY, starting at zero
|
||||
-d delim continue until the first character of DELIM is read, rather
|
||||
than newline
|
||||
-e use Readline to obtain the line in an interactive shell
|
||||
-i text Use TEXT as the initial text for Readline
|
||||
-n nchars return after reading NCHARS characters rather than waiting
|
||||
for a newline, but honor a delimiter if fewer than NCHARS
|
||||
characters are read before the delimiter
|
||||
-N nchars return only after reading exactly NCHARS characters, unless
|
||||
EOF is encountered or read times out, ignoring any delimiter
|
||||
-p prompt output the string PROMPT without a trailing newline before
|
||||
attempting to read
|
||||
-r do not allow backslashes to escape any characters
|
||||
-s do not echo input coming from a terminal
|
||||
-t timeout time out and return failure if a complete line of input is
|
||||
not read withint TIMEOUT seconds. The value of the TMOUT
|
||||
variable is the default timeout. TIMEOUT may be a
|
||||
fractional number. If TIMEOUT is 0, read returns immediately,
|
||||
without trying to read any data, returning success only if
|
||||
input is available on the specified file descriptor. The
|
||||
exit status is greater than 128 if the timeout is exceeded
|
||||
-u fd read from file descriptor FD instead of the standard input
|
||||
|
||||
Exit Status:
|
||||
The return code is zero, unless end-of-file is encountered, read times out,
|
||||
or an invalid file descriptor is supplied as the argument to -u.
|
||||
$END
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "bashtypes.h"
|
||||
#include "posixstat.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "bashansi.h"
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
# include <fcntl.h>
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
#include "../bashintl.h"
|
||||
|
||||
#include "../shell.h"
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
#include <shtty.h>
|
||||
|
||||
#if defined (READLINE)
|
||||
#include "../bashline.h"
|
||||
#include <readline/readline.h>
|
||||
#endif
|
||||
|
||||
#if defined (BUFFERED_INPUT)
|
||||
# include "input.h"
|
||||
#endif
|
||||
|
||||
#if !defined(errno)
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
struct ttsave
|
||||
{
|
||||
int fd;
|
||||
TTYSTRUCT *attrs;
|
||||
};
|
||||
|
||||
#if defined (READLINE)
|
||||
static void reset_attempted_completion_function __P((char *));
|
||||
static int set_itext __P((void));
|
||||
static char *edit_line __P((char *, char *));
|
||||
static void set_eol_delim __P((int));
|
||||
static void reset_eol_delim __P((char *));
|
||||
#endif
|
||||
static SHELL_VAR *bind_read_variable __P((char *, char *));
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
static int read_mbchar __P((int, char *, int, int, int));
|
||||
#endif
|
||||
static void ttyrestore __P((struct ttsave *));
|
||||
|
||||
static sighandler sigalrm __P((int));
|
||||
static void reset_alarm __P((void));
|
||||
|
||||
static procenv_t alrmbuf;
|
||||
static SigHandler *old_alrm;
|
||||
static unsigned char delim;
|
||||
|
||||
static sighandler
|
||||
sigalrm (s)
|
||||
int s;
|
||||
{
|
||||
longjmp (alrmbuf, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
reset_alarm ()
|
||||
{
|
||||
set_signal_handler (SIGALRM, old_alrm);
|
||||
falarm (0, 0);
|
||||
}
|
||||
|
||||
/* Read the value of the shell variables whose names follow.
|
||||
The reading is done from the current input stream, whatever
|
||||
that may be. Successive words of the input line are assigned
|
||||
to the variables mentioned in LIST. The last variable in LIST
|
||||
gets the remainder of the words on the line. If no variables
|
||||
are mentioned in LIST, then the default variable is $REPLY. */
|
||||
int
|
||||
read_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
register char *varname;
|
||||
int size, i, nr, pass_next, saw_escape, eof, opt, retval, code, print_ps2;
|
||||
int input_is_tty, input_is_pipe, unbuffered_read, skip_ctlesc, skip_ctlnul;
|
||||
int raw, edit, nchars, silent, have_timeout, ignore_delim, fd;
|
||||
unsigned int tmsec, tmusec;
|
||||
long ival, uval;
|
||||
intmax_t intval;
|
||||
char c;
|
||||
char *input_string, *orig_input_string, *ifs_chars, *prompt, *arrayname;
|
||||
char *e, *t, *t1, *ps2, *tofree;
|
||||
struct stat tsb;
|
||||
SHELL_VAR *var;
|
||||
TTYSTRUCT ttattrs, ttset;
|
||||
struct ttsave termsave;
|
||||
#if defined (ARRAY_VARS)
|
||||
WORD_LIST *alist;
|
||||
#endif
|
||||
#if defined (READLINE)
|
||||
char *rlbuf, *itext;
|
||||
int rlind;
|
||||
#endif
|
||||
|
||||
USE_VAR(size);
|
||||
USE_VAR(i);
|
||||
USE_VAR(pass_next);
|
||||
USE_VAR(print_ps2);
|
||||
USE_VAR(saw_escape);
|
||||
USE_VAR(input_is_pipe);
|
||||
/* USE_VAR(raw); */
|
||||
USE_VAR(edit);
|
||||
USE_VAR(tmsec);
|
||||
USE_VAR(tmusec);
|
||||
USE_VAR(nchars);
|
||||
USE_VAR(silent);
|
||||
USE_VAR(ifs_chars);
|
||||
USE_VAR(prompt);
|
||||
USE_VAR(arrayname);
|
||||
#if defined (READLINE)
|
||||
USE_VAR(rlbuf);
|
||||
USE_VAR(rlind);
|
||||
USE_VAR(itext);
|
||||
#endif
|
||||
USE_VAR(list);
|
||||
USE_VAR(ps2);
|
||||
|
||||
i = 0; /* Index into the string that we are reading. */
|
||||
raw = edit = 0; /* Not reading raw input by default. */
|
||||
silent = 0;
|
||||
arrayname = prompt = (char *)NULL;
|
||||
fd = 0; /* file descriptor to read from */
|
||||
|
||||
#if defined (READLINE)
|
||||
rlbuf = itext = (char *)0;
|
||||
rlind = 0;
|
||||
#endif
|
||||
|
||||
tmsec = tmusec = 0; /* no timeout */
|
||||
nr = nchars = input_is_tty = input_is_pipe = unbuffered_read = have_timeout = 0;
|
||||
delim = '\n'; /* read until newline */
|
||||
ignore_delim = 0;
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "ersa:d:i:n:p:t:u:N:")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'r':
|
||||
raw = 1;
|
||||
break;
|
||||
case 'p':
|
||||
prompt = list_optarg;
|
||||
break;
|
||||
case 's':
|
||||
silent = 1;
|
||||
break;
|
||||
case 'e':
|
||||
#if defined (READLINE)
|
||||
edit = 1;
|
||||
#endif
|
||||
break;
|
||||
case 'i':
|
||||
#if defined (READLINE)
|
||||
itext = list_optarg;
|
||||
#endif
|
||||
break;
|
||||
#if defined (ARRAY_VARS)
|
||||
case 'a':
|
||||
arrayname = list_optarg;
|
||||
break;
|
||||
#endif
|
||||
case 't':
|
||||
code = uconvert (list_optarg, &ival, &uval);
|
||||
if (code == 0 || ival < 0 || uval < 0)
|
||||
{
|
||||
builtin_error (_("%s: invalid timeout specification"), list_optarg);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else
|
||||
{
|
||||
have_timeout = 1;
|
||||
tmsec = ival;
|
||||
tmusec = uval;
|
||||
}
|
||||
break;
|
||||
case 'N':
|
||||
ignore_delim = 1;
|
||||
delim = -1;
|
||||
case 'n':
|
||||
code = legal_number (list_optarg, &intval);
|
||||
if (code == 0 || intval < 0 || intval != (int)intval)
|
||||
{
|
||||
sh_invalidnum (list_optarg);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
else
|
||||
nchars = intval;
|
||||
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 'd':
|
||||
delim = *list_optarg;
|
||||
break;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
/* `read -t 0 var' tests whether input is available with select/FIONREAD,
|
||||
and fails if those are unavailable */
|
||||
if (have_timeout && tmsec == 0 && tmusec == 0)
|
||||
#if 0
|
||||
return (EXECUTION_FAILURE);
|
||||
#else
|
||||
return (input_avail (fd) ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
|
||||
#endif
|
||||
|
||||
/* If we're asked to ignore the delimiter, make sure we do. */
|
||||
if (ignore_delim)
|
||||
delim = -1;
|
||||
|
||||
/* IF IFS is unset, we use the default of " \t\n". */
|
||||
ifs_chars = getifs ();
|
||||
if (ifs_chars == 0) /* XXX - shouldn't happen */
|
||||
ifs_chars = "";
|
||||
/* If we want to read exactly NCHARS chars, don't split on IFS */
|
||||
if (ignore_delim)
|
||||
ifs_chars = "";
|
||||
for (skip_ctlesc = skip_ctlnul = 0, e = ifs_chars; *e; e++)
|
||||
skip_ctlesc |= *e == CTLESC, skip_ctlnul |= *e == CTLNUL;
|
||||
|
||||
input_string = (char *)xmalloc (size = 112); /* XXX was 128 */
|
||||
input_string[0] = '\0';
|
||||
|
||||
/* $TMOUT, if set, is the default timeout for read. */
|
||||
if (have_timeout == 0 && (e = get_string_value ("TMOUT")))
|
||||
{
|
||||
code = uconvert (e, &ival, &uval);
|
||||
if (code == 0 || ival < 0 || uval < 0)
|
||||
tmsec = tmusec = 0;
|
||||
else
|
||||
{
|
||||
tmsec = ival;
|
||||
tmusec = uval;
|
||||
}
|
||||
}
|
||||
|
||||
begin_unwind_frame ("read_builtin");
|
||||
|
||||
#if defined (BUFFERED_INPUT)
|
||||
if (interactive == 0 && default_buffered_input >= 0 && fd_is_bash_input (fd))
|
||||
sync_buffered_stream (default_buffered_input);
|
||||
#endif
|
||||
|
||||
input_is_tty = isatty (fd);
|
||||
if (input_is_tty == 0)
|
||||
#ifndef __CYGWIN__
|
||||
input_is_pipe = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE);
|
||||
#else
|
||||
input_is_pipe = 1;
|
||||
#endif
|
||||
|
||||
/* If the -p, -e or -s flags were given, but input is not coming from the
|
||||
terminal, turn them off. */
|
||||
if ((prompt || edit || silent) && input_is_tty == 0)
|
||||
{
|
||||
prompt = (char *)NULL;
|
||||
#if defined (READLINE)
|
||||
itext = (char *)NULL;
|
||||
#endif
|
||||
edit = silent = 0;
|
||||
}
|
||||
|
||||
#if defined (READLINE)
|
||||
if (edit)
|
||||
add_unwind_protect (xfree, rlbuf);
|
||||
#endif
|
||||
|
||||
pass_next = 0; /* Non-zero signifies last char was backslash. */
|
||||
saw_escape = 0; /* Non-zero signifies that we saw an escape char */
|
||||
|
||||
if (tmsec > 0 || tmusec > 0)
|
||||
{
|
||||
/* Turn off the timeout if stdin is a regular file (e.g. from
|
||||
input redirection). */
|
||||
if ((fstat (fd, &tsb) < 0) || S_ISREG (tsb.st_mode))
|
||||
tmsec = tmusec = 0;
|
||||
}
|
||||
|
||||
if (tmsec > 0 || tmusec > 0)
|
||||
{
|
||||
code = setjmp (alrmbuf);
|
||||
if (code)
|
||||
{
|
||||
/* Tricky. The top of the unwind-protect stack is the free of
|
||||
input_string. We want to run all the rest and use input_string,
|
||||
so we have to remove it from the stack. */
|
||||
remove_unwind_protect ();
|
||||
run_unwind_frame ("read_builtin");
|
||||
input_string[i] = '\0'; /* make sure it's terminated */
|
||||
retval = 128+SIGALRM;
|
||||
goto assign_vars;
|
||||
}
|
||||
old_alrm = set_signal_handler (SIGALRM, sigalrm);
|
||||
add_unwind_protect (reset_alarm, (char *)NULL);
|
||||
#if defined (READLINE)
|
||||
if (edit)
|
||||
add_unwind_protect (reset_attempted_completion_function, (char *)NULL);
|
||||
#endif
|
||||
falarm (tmsec, tmusec);
|
||||
}
|
||||
|
||||
/* If we've been asked to read only NCHARS chars, or we're using some
|
||||
character other than newline to terminate the line, do the right
|
||||
thing to readline or the tty. */
|
||||
if (nchars > 0 || delim != '\n')
|
||||
{
|
||||
#if defined (READLINE)
|
||||
if (edit)
|
||||
{
|
||||
if (nchars > 0)
|
||||
{
|
||||
unwind_protect_int (rl_num_chars_to_read);
|
||||
rl_num_chars_to_read = nchars;
|
||||
}
|
||||
if (delim != '\n')
|
||||
{
|
||||
set_eol_delim (delim);
|
||||
add_unwind_protect (reset_eol_delim, (char *)NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (input_is_tty)
|
||||
{
|
||||
/* ttsave() */
|
||||
termsave.fd = fd;
|
||||
ttgetattr (fd, &ttattrs);
|
||||
termsave.attrs = &ttattrs;
|
||||
|
||||
ttset = ttattrs;
|
||||
i = silent ? ttfd_cbreak (fd, &ttset) : ttfd_onechar (fd, &ttset);
|
||||
if (i < 0)
|
||||
sh_ttyerror (1);
|
||||
add_unwind_protect ((Function *)ttyrestore, (char *)&termsave);
|
||||
}
|
||||
}
|
||||
else if (silent) /* turn off echo but leave term in canonical mode */
|
||||
{
|
||||
/* ttsave (); */
|
||||
termsave.fd = fd;
|
||||
ttgetattr (fd, &ttattrs);
|
||||
termsave.attrs = &ttattrs;
|
||||
|
||||
ttset = ttattrs;
|
||||
i = ttfd_noecho (fd, &ttset); /* ttnoecho (); */
|
||||
if (i < 0)
|
||||
sh_ttyerror (1);
|
||||
|
||||
add_unwind_protect ((Function *)ttyrestore, (char *)&termsave);
|
||||
}
|
||||
|
||||
/* This *must* be the top unwind-protect on the stack, so the manipulation
|
||||
of the unwind-protect stack after the realloc() works right. */
|
||||
add_unwind_protect (xfree, input_string);
|
||||
interrupt_immediately++;
|
||||
|
||||
unbuffered_read = (nchars > 0) || (delim != '\n') || input_is_pipe;
|
||||
|
||||
if (prompt && edit == 0)
|
||||
{
|
||||
fprintf (stderr, "%s", prompt);
|
||||
fflush (stderr);
|
||||
}
|
||||
|
||||
#if defined (__CYGWIN__) && defined (O_TEXT)
|
||||
setmode (0, O_TEXT);
|
||||
#endif
|
||||
|
||||
ps2 = 0;
|
||||
for (print_ps2 = eof = retval = 0;;)
|
||||
{
|
||||
#if defined (READLINE)
|
||||
if (edit)
|
||||
{
|
||||
if (rlbuf && rlbuf[rlind] == '\0')
|
||||
{
|
||||
xfree (rlbuf);
|
||||
rlbuf = (char *)0;
|
||||
}
|
||||
if (rlbuf == 0)
|
||||
{
|
||||
rlbuf = edit_line (prompt ? prompt : "", itext);
|
||||
rlind = 0;
|
||||
}
|
||||
if (rlbuf == 0)
|
||||
{
|
||||
eof = 1;
|
||||
break;
|
||||
}
|
||||
c = rlbuf[rlind++];
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
|
||||
if (print_ps2)
|
||||
{
|
||||
if (ps2 == 0)
|
||||
ps2 = get_string_value ("PS2");
|
||||
fprintf (stderr, "%s", ps2 ? ps2 : "");
|
||||
fflush (stderr);
|
||||
print_ps2 = 0;
|
||||
}
|
||||
|
||||
if (unbuffered_read)
|
||||
retval = zread (fd, &c, 1);
|
||||
else
|
||||
retval = zreadc (fd, &c);
|
||||
|
||||
if (retval <= 0)
|
||||
{
|
||||
CHECK_TERMSIG;
|
||||
eof = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined (READLINE)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (i + 4 >= size) /* XXX was i + 2; use i + 4 for multibyte/read_mbchar */
|
||||
{
|
||||
input_string = (char *)xrealloc (input_string, size += 128);
|
||||
remove_unwind_protect ();
|
||||
add_unwind_protect (xfree, input_string);
|
||||
}
|
||||
|
||||
/* If the next character is to be accepted verbatim, a backslash
|
||||
newline pair still disappears from the input. */
|
||||
if (pass_next)
|
||||
{
|
||||
pass_next = 0;
|
||||
if (c == '\n')
|
||||
{
|
||||
i--; /* back up over the CTLESC */
|
||||
if (interactive && input_is_tty && raw == 0)
|
||||
print_ps2 = 1;
|
||||
}
|
||||
else
|
||||
goto add_char;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* This may cause problems if IFS contains CTLESC */
|
||||
if (c == '\\' && raw == 0)
|
||||
{
|
||||
pass_next++;
|
||||
if (skip_ctlesc == 0)
|
||||
{
|
||||
saw_escape++;
|
||||
input_string[i++] = CTLESC;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((unsigned char)c == delim)
|
||||
break;
|
||||
|
||||
if ((skip_ctlesc == 0 && c == CTLESC) || (skip_ctlnul == 0 && c == CTLNUL))
|
||||
{
|
||||
saw_escape++;
|
||||
input_string[i++] = CTLESC;
|
||||
}
|
||||
|
||||
add_char:
|
||||
input_string[i++] = c;
|
||||
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
if (nchars > 0 && MB_CUR_MAX > 1)
|
||||
{
|
||||
input_string[i] = '\0'; /* for simplicity and debugging */
|
||||
i += read_mbchar (fd, input_string, i, c, unbuffered_read);
|
||||
}
|
||||
#endif
|
||||
|
||||
nr++;
|
||||
|
||||
if (nchars > 0 && nr >= nchars)
|
||||
break;
|
||||
}
|
||||
input_string[i] = '\0';
|
||||
|
||||
#if 1
|
||||
if (retval < 0)
|
||||
{
|
||||
builtin_error (_("read error: %d: %s"), fd, strerror (errno));
|
||||
run_unwind_frame ("read_builtin");
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (tmsec > 0 || tmusec > 0)
|
||||
reset_alarm ();
|
||||
|
||||
if (nchars > 0 || delim != '\n')
|
||||
{
|
||||
#if defined (READLINE)
|
||||
if (edit)
|
||||
{
|
||||
if (nchars > 0)
|
||||
rl_num_chars_to_read = 0;
|
||||
if (delim != '\n')
|
||||
reset_eol_delim ((char *)NULL);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (input_is_tty)
|
||||
ttyrestore (&termsave);
|
||||
}
|
||||
else if (silent)
|
||||
ttyrestore (&termsave);
|
||||
|
||||
if (unbuffered_read == 0)
|
||||
zsyncfd (fd);
|
||||
|
||||
discard_unwind_frame ("read_builtin");
|
||||
|
||||
retval = eof ? EXECUTION_FAILURE : EXECUTION_SUCCESS;
|
||||
|
||||
assign_vars:
|
||||
|
||||
interrupt_immediately--;
|
||||
|
||||
#if defined (ARRAY_VARS)
|
||||
/* If -a was given, take the string read, break it into a list of words,
|
||||
an assign them to `arrayname' in turn. */
|
||||
if (arrayname)
|
||||
{
|
||||
if (legal_identifier (arrayname) == 0)
|
||||
{
|
||||
sh_invalidid (arrayname);
|
||||
xfree (input_string);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
var = find_or_make_array_variable (arrayname, 1);
|
||||
if (var == 0)
|
||||
{
|
||||
xfree (input_string);
|
||||
return EXECUTION_FAILURE; /* readonly or noassign */
|
||||
}
|
||||
if (assoc_p (var))
|
||||
{
|
||||
builtin_error (_("%s: cannot convert associative to indexed array"), arrayname);
|
||||
xfree (input_string);
|
||||
return EXECUTION_FAILURE; /* existing associative array */
|
||||
}
|
||||
array_flush (array_cell (var));
|
||||
|
||||
alist = list_string (input_string, ifs_chars, 0);
|
||||
if (alist)
|
||||
{
|
||||
if (saw_escape)
|
||||
dequote_list (alist);
|
||||
else
|
||||
word_list_remove_quoted_nulls (alist);
|
||||
assign_array_var_from_word_list (var, alist, 0);
|
||||
dispose_words (alist);
|
||||
}
|
||||
xfree (input_string);
|
||||
return (retval);
|
||||
}
|
||||
#endif /* ARRAY_VARS */
|
||||
|
||||
/* If there are no variables, save the text of the line read to the
|
||||
variable $REPLY. ksh93 strips leading and trailing IFS whitespace,
|
||||
so that `read x ; echo "$x"' and `read ; echo "$REPLY"' behave the
|
||||
same way, but I believe that the difference in behaviors is useful
|
||||
enough to not do it. Without the bash behavior, there is no way
|
||||
to read a line completely without interpretation or modification
|
||||
unless you mess with $IFS (e.g., setting it to the empty string).
|
||||
If you disagree, change the occurrences of `#if 0' to `#if 1' below. */
|
||||
if (list == 0)
|
||||
{
|
||||
#if 0
|
||||
orig_input_string = input_string;
|
||||
for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t); t++)
|
||||
;
|
||||
input_string = t;
|
||||
input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw_escape);
|
||||
#endif
|
||||
|
||||
if (saw_escape)
|
||||
{
|
||||
t = dequote_string (input_string);
|
||||
var = bind_variable ("REPLY", t, 0);
|
||||
free (t);
|
||||
}
|
||||
else
|
||||
var = bind_variable ("REPLY", input_string, 0);
|
||||
VUNSETATTR (var, att_invisible);
|
||||
|
||||
free (input_string);
|
||||
return (retval);
|
||||
}
|
||||
|
||||
/* This code implements the Posix.2 spec for splitting the words
|
||||
read and assigning them to variables. */
|
||||
orig_input_string = input_string;
|
||||
|
||||
/* Remove IFS white space at the beginning of the input string. If
|
||||
$IFS is null, no field splitting is performed. */
|
||||
for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t); t++)
|
||||
;
|
||||
input_string = t;
|
||||
for (; list->next; list = list->next)
|
||||
{
|
||||
varname = list->word->word;
|
||||
#if defined (ARRAY_VARS)
|
||||
if (legal_identifier (varname) == 0 && valid_array_reference (varname) == 0)
|
||||
#else
|
||||
if (legal_identifier (varname) == 0)
|
||||
#endif
|
||||
{
|
||||
sh_invalidid (varname);
|
||||
xfree (orig_input_string);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
/* If there are more variables than words read from the input,
|
||||
the remaining variables are set to the empty string. */
|
||||
if (*input_string)
|
||||
{
|
||||
/* This call updates INPUT_STRING. */
|
||||
t = get_word_from_string (&input_string, ifs_chars, &e);
|
||||
if (t)
|
||||
*e = '\0';
|
||||
/* Don't bother to remove the CTLESC unless we added one
|
||||
somewhere while reading the string. */
|
||||
if (t && saw_escape)
|
||||
{
|
||||
t1 = dequote_string (t);
|
||||
var = bind_read_variable (varname, t1);
|
||||
xfree (t1);
|
||||
}
|
||||
else
|
||||
var = bind_read_variable (varname, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
t = (char *)0;
|
||||
var = bind_read_variable (varname, "");
|
||||
}
|
||||
|
||||
FREE (t);
|
||||
if (var == 0)
|
||||
{
|
||||
xfree (orig_input_string);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
stupidly_hack_special_variables (varname);
|
||||
VUNSETATTR (var, att_invisible);
|
||||
}
|
||||
|
||||
/* Now assign the rest of the line to the last variable argument. */
|
||||
#if defined (ARRAY_VARS)
|
||||
if (legal_identifier (list->word->word) == 0 && valid_array_reference (list->word->word) == 0)
|
||||
#else
|
||||
if (legal_identifier (list->word->word) == 0)
|
||||
#endif
|
||||
{
|
||||
sh_invalidid (list->word->word);
|
||||
xfree (orig_input_string);
|
||||
return (EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* This has to be done this way rather than using string_list
|
||||
and list_string because Posix.2 says that the last variable gets the
|
||||
remaining words and their intervening separators. */
|
||||
input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw_escape);
|
||||
#else
|
||||
/* Check whether or not the number of fields is exactly the same as the
|
||||
number of variables. */
|
||||
tofree = NULL;
|
||||
if (*input_string)
|
||||
{
|
||||
t1 = input_string;
|
||||
t = get_word_from_string (&input_string, ifs_chars, &e);
|
||||
if (*input_string == 0)
|
||||
tofree = input_string = t;
|
||||
else
|
||||
{
|
||||
input_string = strip_trailing_ifs_whitespace (t1, ifs_chars, saw_escape);
|
||||
tofree = t;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (saw_escape)
|
||||
{
|
||||
t = dequote_string (input_string);
|
||||
var = bind_read_variable (list->word->word, t);
|
||||
xfree (t);
|
||||
}
|
||||
else
|
||||
var = bind_read_variable (list->word->word, input_string);
|
||||
|
||||
if (var)
|
||||
{
|
||||
stupidly_hack_special_variables (list->word->word);
|
||||
VUNSETATTR (var, att_invisible);
|
||||
}
|
||||
else
|
||||
retval = EXECUTION_FAILURE;
|
||||
|
||||
FREE (tofree);
|
||||
xfree (orig_input_string);
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
static SHELL_VAR *
|
||||
bind_read_variable (name, value)
|
||||
char *name, *value;
|
||||
{
|
||||
SHELL_VAR *v;
|
||||
#if defined (ARRAY_VARS)
|
||||
if (valid_array_reference (name) == 0)
|
||||
v = bind_variable (name, value, 0);
|
||||
else
|
||||
v = assign_array_element (name, value, 0);
|
||||
#else /* !ARRAY_VARS */
|
||||
v = bind_variable (name, value, 0);
|
||||
#endif /* !ARRAY_VARS */
|
||||
return (v == 0 ? v
|
||||
: ((readonly_p (v) || noassign_p (v)) ? (SHELL_VAR *)NULL : v));
|
||||
}
|
||||
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
static int
|
||||
read_mbchar (fd, string, ind, ch, unbuffered)
|
||||
int fd;
|
||||
char *string;
|
||||
int ind, ch, unbuffered;
|
||||
{
|
||||
char mbchar[MB_LEN_MAX + 1];
|
||||
int i, n, r;
|
||||
char c;
|
||||
size_t ret;
|
||||
mbstate_t ps, ps_back;
|
||||
wchar_t wc;
|
||||
|
||||
memset (&ps, '\0', sizeof (mbstate_t));
|
||||
memset (&ps_back, '\0', sizeof (mbstate_t));
|
||||
|
||||
mbchar[0] = ch;
|
||||
i = 1;
|
||||
for (n = 0; n <= MB_LEN_MAX; n++)
|
||||
{
|
||||
ps_back = ps;
|
||||
ret = mbrtowc (&wc, mbchar, i, &ps);
|
||||
if (ret == (size_t)-2)
|
||||
{
|
||||
ps = ps_back;
|
||||
if (unbuffered)
|
||||
r = zread (fd, &c, 1);
|
||||
else
|
||||
r = zreadc (fd, &c);
|
||||
if (r < 0)
|
||||
goto mbchar_return;
|
||||
mbchar[i++] = c;
|
||||
continue;
|
||||
}
|
||||
else if (ret == (size_t)-1 || ret == (size_t)0 || ret > (size_t)0)
|
||||
break;
|
||||
}
|
||||
|
||||
mbchar_return:
|
||||
if (i > 1) /* read a multibyte char */
|
||||
/* mbchar[0] is already string[ind-1] */
|
||||
for (r = 1; r < i; r++)
|
||||
string[ind+r-1] = mbchar[r];
|
||||
return i - 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
ttyrestore (ttp)
|
||||
struct ttsave *ttp;
|
||||
{
|
||||
ttsetattr (ttp->fd, ttp->attrs);
|
||||
}
|
||||
|
||||
#if defined (READLINE)
|
||||
static rl_completion_func_t *old_attempted_completion_function = 0;
|
||||
static rl_hook_func_t *old_startup_hook;
|
||||
static char *deftext;
|
||||
|
||||
static void
|
||||
reset_attempted_completion_function (cp)
|
||||
char *cp;
|
||||
{
|
||||
if (rl_attempted_completion_function == 0 && old_attempted_completion_function)
|
||||
rl_attempted_completion_function = old_attempted_completion_function;
|
||||
}
|
||||
|
||||
static int
|
||||
set_itext ()
|
||||
{
|
||||
int r1, r2;
|
||||
|
||||
r1 = r2 = 0;
|
||||
if (old_startup_hook)
|
||||
r1 = (*old_startup_hook) ();
|
||||
if (deftext)
|
||||
{
|
||||
r2 = rl_insert_text (deftext);
|
||||
deftext = (char *)NULL;
|
||||
rl_startup_hook = old_startup_hook;
|
||||
old_startup_hook = (rl_hook_func_t *)NULL;
|
||||
}
|
||||
return (r1 || r2);
|
||||
}
|
||||
|
||||
static char *
|
||||
edit_line (p, itext)
|
||||
char *p;
|
||||
char *itext;
|
||||
{
|
||||
char *ret;
|
||||
int len;
|
||||
|
||||
if (bash_readline_initialized == 0)
|
||||
initialize_readline ();
|
||||
|
||||
old_attempted_completion_function = rl_attempted_completion_function;
|
||||
rl_attempted_completion_function = (rl_completion_func_t *)NULL;
|
||||
if (itext)
|
||||
{
|
||||
old_startup_hook = rl_startup_hook;
|
||||
rl_startup_hook = set_itext;
|
||||
deftext = itext;
|
||||
}
|
||||
ret = readline (p);
|
||||
rl_attempted_completion_function = old_attempted_completion_function;
|
||||
old_attempted_completion_function = (rl_completion_func_t *)NULL;
|
||||
|
||||
if (ret == 0)
|
||||
return ret;
|
||||
len = strlen (ret);
|
||||
ret = (char *)xrealloc (ret, len + 2);
|
||||
ret[len++] = delim;
|
||||
ret[len] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int old_delim_ctype;
|
||||
static rl_command_func_t *old_delim_func;
|
||||
static int old_newline_ctype;
|
||||
static rl_command_func_t *old_newline_func;
|
||||
|
||||
static unsigned char delim_char;
|
||||
|
||||
static void
|
||||
set_eol_delim (c)
|
||||
int c;
|
||||
{
|
||||
Keymap cmap;
|
||||
|
||||
if (bash_readline_initialized == 0)
|
||||
initialize_readline ();
|
||||
cmap = rl_get_keymap ();
|
||||
|
||||
/* Change newline to self-insert */
|
||||
old_newline_ctype = cmap[RETURN].type;
|
||||
old_newline_func = cmap[RETURN].function;
|
||||
cmap[RETURN].type = ISFUNC;
|
||||
cmap[RETURN].function = rl_insert;
|
||||
|
||||
/* Bind the delimiter character to accept-line. */
|
||||
old_delim_ctype = cmap[c].type;
|
||||
old_delim_func = cmap[c].function;
|
||||
cmap[c].type = ISFUNC;
|
||||
cmap[c].function = rl_newline;
|
||||
|
||||
delim_char = c;
|
||||
}
|
||||
|
||||
static void
|
||||
reset_eol_delim (cp)
|
||||
char *cp;
|
||||
{
|
||||
Keymap cmap;
|
||||
|
||||
cmap = rl_get_keymap ();
|
||||
|
||||
cmap[RETURN].type = old_newline_ctype;
|
||||
cmap[RETURN].function = old_newline_func;
|
||||
|
||||
cmap[delim_char].type = old_delim_ctype;
|
||||
cmap[delim_char].function = old_delim_func;
|
||||
}
|
||||
#endif
|
||||
+5
-3
@@ -1,7 +1,7 @@
|
||||
This file is type.def, from which is created type.c.
|
||||
It implements the builtin "type" in Bash.
|
||||
|
||||
Copyright (C) 1987-2009 Free Software Foundation, Inc.
|
||||
Copyright (C) 1987-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
@@ -353,7 +353,7 @@ describe_command (command, dflags)
|
||||
user_command_matches (command, FS_EXEC_ONLY, found_file);
|
||||
/* XXX - should that be FS_EXEC_PREFERRED? */
|
||||
|
||||
if (!full_path)
|
||||
if (full_path == 0)
|
||||
break;
|
||||
|
||||
/* If we found the command as itself by looking through $PATH, it
|
||||
@@ -375,7 +375,9 @@ describe_command (command, dflags)
|
||||
else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY|CDESC_SHORTDESC))
|
||||
{
|
||||
f = MP_DOCWD | ((dflags & CDESC_ABSPATH) ? MP_RMDOT : 0);
|
||||
full_path = sh_makepath ((char *)NULL, full_path, f);
|
||||
x = sh_makepath ((char *)NULL, full_path, f);
|
||||
free (full_path);
|
||||
full_path = x;
|
||||
}
|
||||
}
|
||||
/* If we require a full path and don't have one, make one */
|
||||
|
||||
@@ -0,0 +1,403 @@
|
||||
This file is type.def, from which is created type.c.
|
||||
It implements the builtin "type" in Bash.
|
||||
|
||||
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/>.
|
||||
|
||||
$PRODUCES type.c
|
||||
|
||||
$BUILTIN type
|
||||
$FUNCTION type_builtin
|
||||
$SHORT_DOC type [-afptP] name [name ...]
|
||||
Display information about command type.
|
||||
|
||||
For each NAME, indicate how it would be interpreted if used as a
|
||||
command name.
|
||||
|
||||
Options:
|
||||
-a display all locations containing an executable named NAME;
|
||||
includes aliases, builtins, and functions, if and only if
|
||||
the `-p' option is not also used
|
||||
-f suppress shell function lookup
|
||||
-P force a PATH search for each NAME, even if it is an alias,
|
||||
builtin, or function, and returns the name of the disk file
|
||||
that would be executed
|
||||
-p returns either the name of the disk file that would be executed,
|
||||
or nothing if `type -t NAME' would not return `file'.
|
||||
-t output a single word which is one of `alias', `keyword',
|
||||
`function', `builtin', `file' or `', if NAME is an alias, shell
|
||||
reserved word, shell function, shell builtin, disk file, or not
|
||||
found, respectively
|
||||
|
||||
Arguments:
|
||||
NAME Command name to be interpreted.
|
||||
|
||||
Exit Status:
|
||||
Returns success if all of the NAMEs are found; fails if any are not found.
|
||||
$END
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "../bashtypes.h"
|
||||
#include "posixstat.h"
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../bashansi.h"
|
||||
#include "../bashintl.h"
|
||||
|
||||
#include "../shell.h"
|
||||
#include "../findcmd.h"
|
||||
#include "../hashcmd.h"
|
||||
|
||||
#if defined (ALIAS)
|
||||
#include "../alias.h"
|
||||
#endif /* ALIAS */
|
||||
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
extern int find_reserved_word __P((char *));
|
||||
|
||||
extern char *this_command_name;
|
||||
extern int expand_aliases, posixly_correct;
|
||||
|
||||
/* For each word in LIST, find out what the shell is going to do with
|
||||
it as a simple command. i.e., which file would this shell use to
|
||||
execve, or if it is a builtin command, or an alias. Possible flag
|
||||
arguments:
|
||||
-t Returns the "type" of the object, one of
|
||||
`alias', `keyword', `function', `builtin',
|
||||
or `file'.
|
||||
|
||||
-p Returns the pathname of the file if -type is
|
||||
a file.
|
||||
|
||||
-a Returns all occurrences of words, whether they
|
||||
be a filename in the path, alias, function,
|
||||
or builtin.
|
||||
|
||||
-f Suppress shell function lookup, like `command'.
|
||||
|
||||
-P Force a path search even in the presence of other
|
||||
definitions.
|
||||
|
||||
Order of evaluation:
|
||||
alias
|
||||
keyword
|
||||
function
|
||||
builtin
|
||||
file
|
||||
*/
|
||||
|
||||
int
|
||||
type_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int dflags, any_failed, opt;
|
||||
WORD_LIST *this;
|
||||
|
||||
if (list == 0)
|
||||
return (EXECUTION_SUCCESS);
|
||||
|
||||
dflags = CDESC_SHORTDESC; /* default */
|
||||
any_failed = 0;
|
||||
|
||||
/* Handle the obsolescent `-type', `-path', and `-all' by prescanning
|
||||
the arguments and converting those options to the form that
|
||||
internal_getopt recognizes. Converts `--type', `--path', and `--all'
|
||||
also. THIS SHOULD REALLY GO AWAY. */
|
||||
for (this = list; this && this->word->word[0] == '-'; this = this->next)
|
||||
{
|
||||
char *flag = &(this->word->word[1]);
|
||||
|
||||
if (STREQ (flag, "type") || STREQ (flag, "-type"))
|
||||
{
|
||||
this->word->word[1] = 't';
|
||||
this->word->word[2] = '\0';
|
||||
}
|
||||
else if (STREQ (flag, "path") || STREQ (flag, "-path"))
|
||||
{
|
||||
this->word->word[1] = 'p';
|
||||
this->word->word[2] = '\0';
|
||||
}
|
||||
else if (STREQ (flag, "all") || STREQ (flag, "-all"))
|
||||
{
|
||||
this->word->word[1] = 'a';
|
||||
this->word->word[2] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
reset_internal_getopt ();
|
||||
while ((opt = internal_getopt (list, "afptP")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'a':
|
||||
dflags |= CDESC_ALL;
|
||||
break;
|
||||
case 'f':
|
||||
dflags |= CDESC_NOFUNCS;
|
||||
break;
|
||||
case 'p':
|
||||
dflags |= CDESC_PATH_ONLY;
|
||||
dflags &= ~(CDESC_TYPE|CDESC_SHORTDESC);
|
||||
break;
|
||||
case 't':
|
||||
dflags |= CDESC_TYPE;
|
||||
dflags &= ~(CDESC_PATH_ONLY|CDESC_SHORTDESC);
|
||||
break;
|
||||
case 'P': /* shorthand for type -ap */
|
||||
dflags |= (CDESC_PATH_ONLY|CDESC_FORCE_PATH);
|
||||
dflags &= ~(CDESC_TYPE|CDESC_SHORTDESC);
|
||||
break;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
}
|
||||
list = loptend;
|
||||
|
||||
while (list)
|
||||
{
|
||||
int found;
|
||||
|
||||
found = describe_command (list->word->word, dflags);
|
||||
|
||||
if (!found && (dflags & (CDESC_PATH_ONLY|CDESC_TYPE)) == 0)
|
||||
sh_notfound (list->word->word);
|
||||
|
||||
any_failed += found == 0;
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
opt = (any_failed == 0) ? EXECUTION_SUCCESS : EXECUTION_FAILURE;
|
||||
return (sh_chkwrite (opt));
|
||||
}
|
||||
|
||||
/*
|
||||
* Describe COMMAND as required by the type and command builtins.
|
||||
*
|
||||
* Behavior is controlled by DFLAGS. Flag values are
|
||||
* CDESC_ALL print all descriptions of a command
|
||||
* CDESC_SHORTDESC print the description for type and command -V
|
||||
* CDESC_REUSABLE print in a format that may be reused as input
|
||||
* CDESC_TYPE print the type for type -t
|
||||
* CDESC_PATH_ONLY print the path for type -p
|
||||
* CDESC_FORCE_PATH force a path search for type -P
|
||||
* CDESC_NOFUNCS skip function lookup for type -f
|
||||
* CDESC_ABSPATH convert to absolute path, no ./ prefix
|
||||
*
|
||||
* CDESC_ALL says whether or not to look for all occurrences of COMMAND, or
|
||||
* return after finding it once.
|
||||
*/
|
||||
int
|
||||
describe_command (command, dflags)
|
||||
char *command;
|
||||
int dflags;
|
||||
{
|
||||
int found, i, found_file, f, all;
|
||||
char *full_path, *x;
|
||||
SHELL_VAR *func;
|
||||
#if defined (ALIAS)
|
||||
alias_t *alias;
|
||||
#endif
|
||||
|
||||
all = (dflags & CDESC_ALL) != 0;
|
||||
found = found_file = 0;
|
||||
full_path = (char *)NULL;
|
||||
|
||||
#if defined (ALIAS)
|
||||
/* Command is an alias? */
|
||||
if (((dflags & CDESC_FORCE_PATH) == 0) && expand_aliases && (alias = find_alias (command)))
|
||||
{
|
||||
if (dflags & CDESC_TYPE)
|
||||
puts ("alias");
|
||||
else if (dflags & CDESC_SHORTDESC)
|
||||
printf (_("%s is aliased to `%s'\n"), command, alias->value);
|
||||
else if (dflags & CDESC_REUSABLE)
|
||||
{
|
||||
x = sh_single_quote (alias->value);
|
||||
printf ("alias %s=%s\n", command, x);
|
||||
free (x);
|
||||
}
|
||||
|
||||
found = 1;
|
||||
|
||||
if (all == 0)
|
||||
return (1);
|
||||
}
|
||||
#endif /* ALIAS */
|
||||
|
||||
/* Command is a shell reserved word? */
|
||||
if (((dflags & CDESC_FORCE_PATH) == 0) && (i = find_reserved_word (command)) >= 0)
|
||||
{
|
||||
if (dflags & CDESC_TYPE)
|
||||
puts ("keyword");
|
||||
else if (dflags & CDESC_SHORTDESC)
|
||||
printf (_("%s is a shell keyword\n"), command);
|
||||
else if (dflags & CDESC_REUSABLE)
|
||||
printf ("%s\n", command);
|
||||
|
||||
found = 1;
|
||||
|
||||
if (all == 0)
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Command is a function? */
|
||||
if (((dflags & (CDESC_FORCE_PATH|CDESC_NOFUNCS)) == 0) && (func = find_function (command)))
|
||||
{
|
||||
if (dflags & CDESC_TYPE)
|
||||
puts ("function");
|
||||
else if (dflags & CDESC_SHORTDESC)
|
||||
{
|
||||
char *result;
|
||||
|
||||
printf (_("%s is a function\n"), command);
|
||||
|
||||
/* We're blowing away THE_PRINTED_COMMAND here... */
|
||||
|
||||
result = named_function_string (command, function_cell (func), FUNC_MULTILINE|FUNC_EXTERNAL);
|
||||
printf ("%s\n", result);
|
||||
}
|
||||
else if (dflags & CDESC_REUSABLE)
|
||||
printf ("%s\n", command);
|
||||
|
||||
found = 1;
|
||||
|
||||
if (all == 0)
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Command is a builtin? */
|
||||
if (((dflags & CDESC_FORCE_PATH) == 0) && find_shell_builtin (command))
|
||||
{
|
||||
if (dflags & CDESC_TYPE)
|
||||
puts ("builtin");
|
||||
else if (dflags & CDESC_SHORTDESC)
|
||||
printf (_("%s is a shell builtin\n"), command);
|
||||
else if (dflags & CDESC_REUSABLE)
|
||||
printf ("%s\n", command);
|
||||
|
||||
found = 1;
|
||||
|
||||
if (all == 0)
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Command is a disk file? */
|
||||
/* If the command name given is already an absolute command, just
|
||||
check to see if it is executable. */
|
||||
if (absolute_program (command))
|
||||
{
|
||||
f = file_status (command);
|
||||
if (f & FS_EXECABLE)
|
||||
{
|
||||
if (dflags & CDESC_TYPE)
|
||||
puts ("file");
|
||||
else if (dflags & CDESC_SHORTDESC)
|
||||
printf (_("%s is %s\n"), command, command);
|
||||
else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
|
||||
printf ("%s\n", command);
|
||||
|
||||
/* There's no use looking in the hash table or in $PATH,
|
||||
because they're not consulted when an absolute program
|
||||
name is supplied. */
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the user isn't doing "-a", then we might care about
|
||||
whether the file is present in our hash table. */
|
||||
if (all == 0 || (dflags & CDESC_FORCE_PATH))
|
||||
{
|
||||
if (full_path = phash_search (command))
|
||||
{
|
||||
if (dflags & CDESC_TYPE)
|
||||
puts ("file");
|
||||
else if (dflags & CDESC_SHORTDESC)
|
||||
printf (_("%s is hashed (%s)\n"), command, full_path);
|
||||
else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
|
||||
printf ("%s\n", full_path);
|
||||
|
||||
free (full_path);
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now search through $PATH. */
|
||||
while (1)
|
||||
{
|
||||
if (all == 0)
|
||||
full_path = find_user_command (command);
|
||||
else
|
||||
full_path =
|
||||
user_command_matches (command, FS_EXEC_ONLY, found_file);
|
||||
/* XXX - should that be FS_EXEC_PREFERRED? */
|
||||
|
||||
if (!full_path)
|
||||
break;
|
||||
|
||||
/* If we found the command as itself by looking through $PATH, it
|
||||
probably doesn't exist. Check whether or not the command is an
|
||||
executable file. If it's not, don't report a match. This is
|
||||
the default posix mode behavior */
|
||||
if (STREQ (full_path, command) || posixly_correct)
|
||||
{
|
||||
f = file_status (full_path);
|
||||
if ((f & FS_EXECABLE) == 0)
|
||||
{
|
||||
free (full_path);
|
||||
full_path = (char *)NULL;
|
||||
if (all == 0)
|
||||
break;
|
||||
}
|
||||
else if (ABSPATH (full_path))
|
||||
; /* placeholder; don't need to do anything yet */
|
||||
else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY|CDESC_SHORTDESC))
|
||||
{
|
||||
f = MP_DOCWD | ((dflags & CDESC_ABSPATH) ? MP_RMDOT : 0);
|
||||
full_path = sh_makepath ((char *)NULL, full_path, f);
|
||||
}
|
||||
}
|
||||
/* If we require a full path and don't have one, make one */
|
||||
else if ((dflags & CDESC_ABSPATH) && ABSPATH (full_path) == 0)
|
||||
full_path = sh_makepath ((char *)NULL, full_path, MP_DOCWD|MP_RMDOT);
|
||||
|
||||
found_file++;
|
||||
found = 1;
|
||||
|
||||
if (dflags & CDESC_TYPE)
|
||||
puts ("file");
|
||||
else if (dflags & CDESC_SHORTDESC)
|
||||
printf (_("%s is %s\n"), command, full_path);
|
||||
else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
|
||||
printf ("%s\n", full_path);
|
||||
|
||||
free (full_path);
|
||||
full_path = (char *)NULL;
|
||||
|
||||
if (all == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return (found);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eval.c -- reading and evaluating commands. */
|
||||
|
||||
/* Copyright (C) 1996-2009 Free Software Foundation, Inc.
|
||||
/* Copyright (C) 1996-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
@@ -190,12 +190,14 @@ alrm_catcher(i)
|
||||
static void
|
||||
send_pwd_to_eterm ()
|
||||
{
|
||||
char *pwd;
|
||||
char *pwd, *f;
|
||||
|
||||
f = 0;
|
||||
pwd = get_string_value ("PWD");
|
||||
if (pwd == 0)
|
||||
pwd = get_working_directory ("eterm");
|
||||
f = pwd = get_working_directory ("eterm");
|
||||
fprintf (stderr, "\032/%s\n", pwd);
|
||||
free (f);
|
||||
}
|
||||
|
||||
/* Call the YACC-generated parser and return the status of the parse.
|
||||
|
||||
@@ -0,0 +1,283 @@
|
||||
/* eval.c -- reading and evaluating commands. */
|
||||
|
||||
/* Copyright (C) 1996-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 (HAVE_UNISTD_H)
|
||||
# ifdef _MINIX
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "bashansi.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#include "bashintl.h"
|
||||
|
||||
#include "shell.h"
|
||||
#include "flags.h"
|
||||
#include "trap.h"
|
||||
|
||||
#include "builtins/common.h"
|
||||
|
||||
#include "input.h"
|
||||
#include "execute_cmd.h"
|
||||
|
||||
#if defined (HISTORY)
|
||||
# include "bashhist.h"
|
||||
#endif
|
||||
|
||||
extern int EOF_reached;
|
||||
extern int indirection_level;
|
||||
extern int posixly_correct;
|
||||
extern int subshell_environment, running_under_emacs;
|
||||
extern int last_command_exit_value, stdin_redir;
|
||||
extern int need_here_doc;
|
||||
extern int current_command_number, current_command_line_count, line_number;
|
||||
extern int expand_aliases;
|
||||
|
||||
static void send_pwd_to_eterm __P((void));
|
||||
static sighandler alrm_catcher __P((int));
|
||||
|
||||
/* Read and execute commands until EOF is reached. This assumes that
|
||||
the input source has already been initialized. */
|
||||
int
|
||||
reader_loop ()
|
||||
{
|
||||
int our_indirection_level;
|
||||
COMMAND * volatile current_command;
|
||||
|
||||
USE_VAR(current_command);
|
||||
|
||||
current_command = (COMMAND *)NULL;
|
||||
|
||||
our_indirection_level = ++indirection_level;
|
||||
|
||||
while (EOF_Reached == 0)
|
||||
{
|
||||
int code;
|
||||
|
||||
code = setjmp (top_level);
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
unlink_fifo_list ();
|
||||
#endif /* PROCESS_SUBSTITUTION */
|
||||
|
||||
if (interactive_shell && signal_is_ignored (SIGINT) == 0)
|
||||
set_signal_handler (SIGINT, sigint_sighandler);
|
||||
|
||||
if (code != NOT_JUMPED)
|
||||
{
|
||||
indirection_level = our_indirection_level;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
/* Some kind of throw to top_level has occured. */
|
||||
case FORCE_EOF:
|
||||
case ERREXIT:
|
||||
case EXITPROG:
|
||||
current_command = (COMMAND *)NULL;
|
||||
if (exit_immediately_on_error)
|
||||
variable_context = 0; /* not in a function */
|
||||
EOF_Reached = EOF;
|
||||
goto exec_done;
|
||||
|
||||
case DISCARD:
|
||||
/* Make sure the exit status is reset to a non-zero value, but
|
||||
leave existing non-zero values (e.g., > 128 on signal)
|
||||
alone. */
|
||||
if (last_command_exit_value == 0)
|
||||
last_command_exit_value = EXECUTION_FAILURE;
|
||||
if (subshell_environment)
|
||||
{
|
||||
current_command = (COMMAND *)NULL;
|
||||
EOF_Reached = EOF;
|
||||
goto exec_done;
|
||||
}
|
||||
/* Obstack free command elements, etc. */
|
||||
if (current_command)
|
||||
{
|
||||
dispose_command (current_command);
|
||||
current_command = (COMMAND *)NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
command_error ("reader_loop", CMDERR_BADJUMP, code, 0);
|
||||
}
|
||||
}
|
||||
|
||||
executing = 0;
|
||||
if (temporary_env)
|
||||
dispose_used_env_vars ();
|
||||
|
||||
#if (defined (ultrix) && defined (mips)) || defined (C_ALLOCA)
|
||||
/* Attempt to reclaim memory allocated with alloca (). */
|
||||
(void) alloca (0);
|
||||
#endif
|
||||
|
||||
if (read_command () == 0)
|
||||
{
|
||||
if (interactive_shell == 0 && read_but_dont_execute)
|
||||
{
|
||||
last_command_exit_value = EXECUTION_SUCCESS;
|
||||
dispose_command (global_command);
|
||||
global_command = (COMMAND *)NULL;
|
||||
}
|
||||
else if (current_command = global_command)
|
||||
{
|
||||
global_command = (COMMAND *)NULL;
|
||||
current_command_number++;
|
||||
|
||||
executing = 1;
|
||||
stdin_redir = 0;
|
||||
execute_command (current_command);
|
||||
|
||||
exec_done:
|
||||
QUIT;
|
||||
|
||||
if (current_command)
|
||||
{
|
||||
dispose_command (current_command);
|
||||
current_command = (COMMAND *)NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Parse error, maybe discard rest of stream if not interactive. */
|
||||
if (interactive == 0)
|
||||
EOF_Reached = EOF;
|
||||
}
|
||||
if (just_one_command)
|
||||
EOF_Reached = EOF;
|
||||
}
|
||||
indirection_level--;
|
||||
return (last_command_exit_value);
|
||||
}
|
||||
|
||||
static sighandler
|
||||
alrm_catcher(i)
|
||||
int i;
|
||||
{
|
||||
printf (_("\007timed out waiting for input: auto-logout\n"));
|
||||
fflush (stdout);
|
||||
bash_logout (); /* run ~/.bash_logout if this is a login shell */
|
||||
jump_to_top_level (EXITPROG);
|
||||
SIGRETURN (0);
|
||||
}
|
||||
|
||||
/* Send an escape sequence to emacs term mode to tell it the
|
||||
current working directory. */
|
||||
static void
|
||||
send_pwd_to_eterm ()
|
||||
{
|
||||
char *pwd, *f;
|
||||
|
||||
f = 0;
|
||||
pwd = get_string_value ("PWD");
|
||||
if (pwd == 0)
|
||||
f = pwd = get_working_directory ("eterm");
|
||||
fprintf (stderr, "\032/%s\n", pwd);
|
||||
free (f);
|
||||
}
|
||||
|
||||
/* Call the YACC-generated parser and return the status of the parse.
|
||||
Input is read from the current input stream (bash_input). yyparse
|
||||
leaves the parsed command in the global variable GLOBAL_COMMAND.
|
||||
This is where PROMPT_COMMAND is executed. */
|
||||
int
|
||||
parse_command ()
|
||||
{
|
||||
int r;
|
||||
char *command_to_execute;
|
||||
|
||||
need_here_doc = 0;
|
||||
run_pending_traps ();
|
||||
|
||||
/* Allow the execution of a random command just before the printing
|
||||
of each primary prompt. If the shell variable PROMPT_COMMAND
|
||||
is set then the value of it is the command to execute. */
|
||||
if (interactive && bash_input.type != st_string)
|
||||
{
|
||||
command_to_execute = get_string_value ("PROMPT_COMMAND");
|
||||
if (command_to_execute)
|
||||
execute_variable_command (command_to_execute, "PROMPT_COMMAND");
|
||||
|
||||
if (running_under_emacs == 2)
|
||||
send_pwd_to_eterm (); /* Yuck */
|
||||
}
|
||||
|
||||
current_command_line_count = 0;
|
||||
r = yyparse ();
|
||||
|
||||
if (need_here_doc)
|
||||
gather_here_documents ();
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
/* Read and parse a command, returning the status of the parse. The command
|
||||
is left in the globval variable GLOBAL_COMMAND for use by reader_loop.
|
||||
This is where the shell timeout code is executed. */
|
||||
int
|
||||
read_command ()
|
||||
{
|
||||
SHELL_VAR *tmout_var;
|
||||
int tmout_len, result;
|
||||
SigHandler *old_alrm;
|
||||
|
||||
set_current_prompt_level (1);
|
||||
global_command = (COMMAND *)NULL;
|
||||
|
||||
/* Only do timeouts if interactive. */
|
||||
tmout_var = (SHELL_VAR *)NULL;
|
||||
tmout_len = 0;
|
||||
old_alrm = (SigHandler *)NULL;
|
||||
|
||||
if (interactive)
|
||||
{
|
||||
tmout_var = find_variable ("TMOUT");
|
||||
|
||||
if (tmout_var && var_isset (tmout_var))
|
||||
{
|
||||
tmout_len = atoi (value_cell (tmout_var));
|
||||
if (tmout_len > 0)
|
||||
{
|
||||
old_alrm = set_signal_handler (SIGALRM, alrm_catcher);
|
||||
alarm (tmout_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QUIT;
|
||||
|
||||
current_command_line_count = 0;
|
||||
result = parse_command ();
|
||||
|
||||
if (interactive && tmout_var && (tmout_len > 0))
|
||||
{
|
||||
alarm(0);
|
||||
set_signal_handler (SIGALRM, old_alrm);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
+47
-25
@@ -1252,7 +1252,7 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
posix_time = (command->flags & CMD_TIME_POSIX);
|
||||
posix_time = command && (command->flags & CMD_TIME_POSIX);
|
||||
|
||||
nullcmd = (command == 0) || (command->type == cm_simple && command->value.Simple->words == 0 && command->value.Simple->redirects == 0);
|
||||
if (posixly_correct && nullcmd)
|
||||
@@ -1672,32 +1672,45 @@ cpl_delete (pid)
|
||||
static void
|
||||
cpl_reap ()
|
||||
{
|
||||
struct cpelement *prev, *p;
|
||||
struct cpelement *p, *next, *nh, *nt;
|
||||
|
||||
for (prev = p = coproc_list.head; p; prev = p, p = p->next)
|
||||
if (p->coproc->c_flags & COPROC_DEAD)
|
||||
{
|
||||
prev->next = p->next; /* remove from list */
|
||||
|
||||
/* Housekeeping in the border cases. */
|
||||
if (p == coproc_list.head)
|
||||
coproc_list.head = coproc_list.head->next;
|
||||
else if (p == coproc_list.tail)
|
||||
coproc_list.tail = prev;
|
||||
|
||||
coproc_list.ncoproc--;
|
||||
if (coproc_list.ncoproc == 0)
|
||||
coproc_list.head = coproc_list.tail = 0;
|
||||
else if (coproc_list.ncoproc == 1)
|
||||
coproc_list.tail = coproc_list.head; /* just to make sure */
|
||||
/* Build a new list by removing dead coprocs and fix up the coproc_list
|
||||
pointers when done. */
|
||||
nh = nt = next = (struct cpelement *)0;
|
||||
for (p = coproc_list.head; p; p = next)
|
||||
{
|
||||
next = p->next;
|
||||
if (p->coproc->c_flags & COPROC_DEAD)
|
||||
{
|
||||
coproc_list.ncoproc--; /* keep running count, fix up pointers later */
|
||||
|
||||
#if defined (DEBUG)
|
||||
itrace("cpl_reap: deleting %d", p->coproc->c_pid);
|
||||
itrace("cpl_reap: deleting %d", p->coproc->c_pid);
|
||||
#endif
|
||||
|
||||
coproc_dispose (p->coproc);
|
||||
cpe_dispose (p);
|
||||
}
|
||||
coproc_dispose (p->coproc);
|
||||
cpe_dispose (p);
|
||||
}
|
||||
else if (nh == 0)
|
||||
nh = nt = p;
|
||||
else
|
||||
{
|
||||
nt->next = p;
|
||||
nt = nt->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (coproc_list.ncoproc == 0)
|
||||
coproc_list.head = coproc_list.tail = 0;
|
||||
else
|
||||
{
|
||||
if (nt)
|
||||
nt->next = 0;
|
||||
coproc_list.head = nh;
|
||||
coproc_list.tail = nt;
|
||||
if (coproc_list.ncoproc == 1)
|
||||
coproc_list.tail = coproc_list.head; /* just to make sure */
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear out the list of saved statuses */
|
||||
@@ -2321,7 +2334,8 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
|
||||
lastpipe_jid = stop_pipeline (0, (COMMAND *)NULL); /* XXX */
|
||||
add_unwind_protect (lastpipe_cleanup, lastpipe_jid);
|
||||
}
|
||||
cmd->flags |= CMD_LASTPIPE;
|
||||
if (cmd)
|
||||
cmd->flags |= CMD_LASTPIPE;
|
||||
}
|
||||
if (prev >= 0)
|
||||
add_unwind_protect (close, prev);
|
||||
@@ -2850,6 +2864,8 @@ print_index_and_element (len, ind, list)
|
||||
return (0);
|
||||
for (i = ind, l = list; l && --i; l = l->next)
|
||||
;
|
||||
if (l == 0) /* don't think this can happen */
|
||||
return (0);
|
||||
fprintf (stderr, "%*d%s%s", len, ind, RP_SPACE, l->word->word);
|
||||
return (displen (l->word->word));
|
||||
}
|
||||
@@ -2990,7 +3006,7 @@ select_query (list, list_len, prompt, print_menu)
|
||||
|
||||
for (l = list; l && --reply; l = l->next)
|
||||
;
|
||||
return (l->word->word);
|
||||
return (l->word->word); /* XXX - can't be null? */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3431,6 +3447,7 @@ execute_arith_command (arith_command)
|
||||
|
||||
static char * const nullstr = "";
|
||||
|
||||
/* XXX - can COND ever be NULL when this is called? */
|
||||
static int
|
||||
execute_cond_node (cond)
|
||||
COND_COM *cond;
|
||||
@@ -3745,8 +3762,12 @@ is_dirname (pathname)
|
||||
char *pathname;
|
||||
{
|
||||
char *temp;
|
||||
int ret;
|
||||
|
||||
temp = search_for_command (pathname);
|
||||
return (temp ? file_isdir (temp) : file_isdir (pathname));
|
||||
ret = (temp ? file_isdir (temp) : file_isdir (pathname));
|
||||
free (temp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The meaty part of all the executions. We have to start hacking the
|
||||
@@ -5092,6 +5113,7 @@ shell_execve (command, args, env)
|
||||
run it for some reason. See why. */
|
||||
#if defined (HAVE_HASH_BANG_EXEC)
|
||||
READ_SAMPLE_BUF (command, sample, sample_len);
|
||||
sample[sample_len - 1] = '\0';
|
||||
if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
|
||||
{
|
||||
char *interp;
|
||||
|
||||
+76
-25
@@ -530,6 +530,10 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
|
||||
REDIRECT *my_undo_list, *exec_undo_list;
|
||||
volatile int last_pid;
|
||||
volatile int save_line_number;
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
volatile int ofifo, nfifo, osize, saved_fifo;
|
||||
volatile char *ofifo_list;
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
if (command == 0 || breaking || continuing || read_but_dont_execute)
|
||||
@@ -670,6 +674,17 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
|
||||
if (shell_control_structure (command->type) && command->redirects)
|
||||
stdin_redir = stdin_redirects (command->redirects);
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
if (variable_context != 0)
|
||||
{
|
||||
ofifo = num_fifos ();
|
||||
ofifo_list = copy_fifo_list (&osize);
|
||||
saved_fifo = 1;
|
||||
}
|
||||
else
|
||||
saved_fifo = 0;
|
||||
#endif
|
||||
|
||||
/* Handle WHILE FOR CASE etc. with redirections. (Also '&' input
|
||||
redirection.) */
|
||||
if (do_redirections (command->redirects, RX_ACTIVE|RX_UNDOABLE) != 0)
|
||||
@@ -677,6 +692,9 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
|
||||
cleanup_redirects (redirection_undo_list);
|
||||
redirection_undo_list = (REDIRECT *)NULL;
|
||||
dispose_exec_redirects ();
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
free (ofifo_list);
|
||||
#endif
|
||||
return (last_command_exit_value = EXECUTION_FAILURE);
|
||||
}
|
||||
|
||||
@@ -971,6 +989,16 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
|
||||
if (my_undo_list || exec_undo_list)
|
||||
discard_unwind_frame ("loop_redirections");
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
if (saved_fifo)
|
||||
{
|
||||
nfifo = num_fifos ();
|
||||
if (nfifo > ofifo)
|
||||
close_new_fifos (ofifo_list, osize);
|
||||
free (ofifo_list);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Invert the return value if we have to */
|
||||
if (invert)
|
||||
exec_result = (exec_result == EXECUTION_SUCCESS)
|
||||
@@ -1001,6 +1029,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out,
|
||||
if (running_trap == 0)
|
||||
#endif
|
||||
currently_executing_command = (COMMAND *)NULL;
|
||||
|
||||
return (last_command_exit_value);
|
||||
}
|
||||
|
||||
@@ -1223,7 +1252,7 @@ time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
posix_time = (command->flags & CMD_TIME_POSIX);
|
||||
posix_time = command && (command->flags & CMD_TIME_POSIX);
|
||||
|
||||
nullcmd = (command == 0) || (command->type == cm_simple && command->value.Simple->words == 0 && command->value.Simple->redirects == 0);
|
||||
if (posixly_correct && nullcmd)
|
||||
@@ -1643,32 +1672,45 @@ cpl_delete (pid)
|
||||
static void
|
||||
cpl_reap ()
|
||||
{
|
||||
struct cpelement *prev, *p;
|
||||
struct cpelement *p, *next, *nh, *nt;
|
||||
|
||||
for (prev = p = coproc_list.head; p; prev = p, p = p->next)
|
||||
if (p->coproc->c_flags & COPROC_DEAD)
|
||||
{
|
||||
prev->next = p->next; /* remove from list */
|
||||
|
||||
/* Housekeeping in the border cases. */
|
||||
if (p == coproc_list.head)
|
||||
coproc_list.head = coproc_list.head->next;
|
||||
else if (p == coproc_list.tail)
|
||||
coproc_list.tail = prev;
|
||||
|
||||
coproc_list.ncoproc--;
|
||||
if (coproc_list.ncoproc == 0)
|
||||
coproc_list.head = coproc_list.tail = 0;
|
||||
else if (coproc_list.ncoproc == 1)
|
||||
coproc_list.tail = coproc_list.head; /* just to make sure */
|
||||
/* Build a new list by removing dead coprocs and fix up the coproc_list
|
||||
pointers when done. */
|
||||
nh = nt = next = (struct cpelement *)0;
|
||||
for (p = coproc_list.head; p; p = next)
|
||||
{
|
||||
next = p->next;
|
||||
if (p->coproc->c_flags & COPROC_DEAD)
|
||||
{
|
||||
coproc_list.ncoproc--; /* keep running count, fix up pointers later */
|
||||
|
||||
#if defined (DEBUG)
|
||||
itrace("cpl_reap: deleting %d", p->coproc->c_pid);
|
||||
itrace("cpl_reap: deleting %d", p->coproc->c_pid);
|
||||
#endif
|
||||
|
||||
coproc_dispose (p->coproc);
|
||||
cpe_dispose (p);
|
||||
}
|
||||
coproc_dispose (p->coproc);
|
||||
cpe_dispose (p);
|
||||
}
|
||||
else if (nh == 0)
|
||||
nh = nt = p;
|
||||
else
|
||||
{
|
||||
nt->next = p;
|
||||
nt = nt->next;
|
||||
}
|
||||
}
|
||||
|
||||
nt->next = 0;
|
||||
|
||||
if (coproc_list.ncoproc == 0)
|
||||
coproc_list.head = coproc_list.tail = 0;
|
||||
else
|
||||
{
|
||||
coproc_list.head = nh;
|
||||
coproc_list.tail = nt;
|
||||
if (coproc_list.ncoproc == 1)
|
||||
coproc_list.tail = coproc_list.head; /* just to make sure */
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear out the list of saved statuses */
|
||||
@@ -2292,7 +2334,8 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
|
||||
lastpipe_jid = stop_pipeline (0, (COMMAND *)NULL); /* XXX */
|
||||
add_unwind_protect (lastpipe_cleanup, lastpipe_jid);
|
||||
}
|
||||
cmd->flags |= CMD_LASTPIPE;
|
||||
if (cmd)
|
||||
cmd->flags |= CMD_LASTPIPE;
|
||||
}
|
||||
if (prev >= 0)
|
||||
add_unwind_protect (close, prev);
|
||||
@@ -2821,6 +2864,8 @@ print_index_and_element (len, ind, list)
|
||||
return (0);
|
||||
for (i = ind, l = list; l && --i; l = l->next)
|
||||
;
|
||||
if (l == 0) /* don't think this can happen */
|
||||
return (0);
|
||||
fprintf (stderr, "%*d%s%s", len, ind, RP_SPACE, l->word->word);
|
||||
return (displen (l->word->word));
|
||||
}
|
||||
@@ -2961,7 +3006,7 @@ select_query (list, list_len, prompt, print_menu)
|
||||
|
||||
for (l = list; l && --reply; l = l->next)
|
||||
;
|
||||
return (l->word->word);
|
||||
return (l->word->word); /* XXX - can't be null? */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3402,6 +3447,7 @@ execute_arith_command (arith_command)
|
||||
|
||||
static char * const nullstr = "";
|
||||
|
||||
/* XXX - can COND ever be NULL when this is called? */
|
||||
static int
|
||||
execute_cond_node (cond)
|
||||
COND_COM *cond;
|
||||
@@ -3716,8 +3762,12 @@ is_dirname (pathname)
|
||||
char *pathname;
|
||||
{
|
||||
char *temp;
|
||||
int ret;
|
||||
|
||||
temp = search_for_command (pathname);
|
||||
return (temp ? file_isdir (temp) : file_isdir (pathname));
|
||||
ret = (temp ? file_isdir (temp) : file_isdir (pathname));
|
||||
free (temp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The meaty part of all the executions. We have to start hacking the
|
||||
@@ -5063,6 +5113,7 @@ shell_execve (command, args, env)
|
||||
run it for some reason. See why. */
|
||||
#if defined (HAVE_HASH_BANG_EXEC)
|
||||
READ_SAMPLE_BUF (command, sample, sample_len);
|
||||
sample[sample_len - 1] = '\0';
|
||||
if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
|
||||
{
|
||||
char *interp;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* expr.c -- arithmetic expression evaluation. */
|
||||
|
||||
/* Copyright (C) 1990-2010 Free Software Foundation, Inc.
|
||||
/* Copyright (C) 1990-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
|
||||
@@ -494,10 +494,16 @@ expassign ()
|
||||
lvalue *= value;
|
||||
break;
|
||||
case DIV:
|
||||
lvalue /= value;
|
||||
if (lvalue == INTMAX_MIN && value == -1)
|
||||
value = 1;
|
||||
else
|
||||
lvalue /= value;
|
||||
break;
|
||||
case MOD:
|
||||
lvalue %= value;
|
||||
if (lvalue == INTMAX_MIN && value == -1)
|
||||
lvalue = 0;
|
||||
else
|
||||
lvalue %= value;
|
||||
break;
|
||||
case PLUS:
|
||||
lvalue += value;
|
||||
@@ -550,7 +556,6 @@ expcond ()
|
||||
{
|
||||
intmax_t cval, val1, val2, rval;
|
||||
int set_noeval;
|
||||
EXPR_CONTEXT ec;
|
||||
|
||||
set_noeval = 0;
|
||||
rval = cval = explor ();
|
||||
@@ -812,6 +817,7 @@ exp2 ()
|
||||
|
||||
val2 = exppower ();
|
||||
|
||||
/* Handle division by 0 and twos-complement arithmetic overflow */
|
||||
if (((op == DIV) || (op == MOD)) && (val2 == 0))
|
||||
{
|
||||
if (noeval == 0)
|
||||
@@ -819,6 +825,13 @@ exp2 ()
|
||||
else
|
||||
val2 = 1;
|
||||
}
|
||||
else if (op == MOD && val1 == INTMAX_MIN && val2 == -1)
|
||||
{
|
||||
val1 = 0;
|
||||
continue;
|
||||
}
|
||||
else if (op == DIV && val1 == INTMAX_MIN && val2 == -1)
|
||||
val2 = 1;
|
||||
|
||||
if (op == MUL)
|
||||
val1 *= val2;
|
||||
@@ -830,6 +843,23 @@ exp2 ()
|
||||
return (val1);
|
||||
}
|
||||
|
||||
static intmax_t
|
||||
ipow (base, exp)
|
||||
intmax_t base, exp;
|
||||
{
|
||||
intmax_t result;
|
||||
|
||||
result = 1;
|
||||
while (exp)
|
||||
{
|
||||
if (exp & 1)
|
||||
result *= base;
|
||||
exp >>= 1;
|
||||
base *= base;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static intmax_t
|
||||
exppower ()
|
||||
{
|
||||
@@ -844,9 +874,7 @@ exppower ()
|
||||
return (1);
|
||||
if (val2 < 0)
|
||||
evalerror (_("exponent less than 0"));
|
||||
for (c = 1; val2--; c *= val1)
|
||||
;
|
||||
val1 = c;
|
||||
val1 = ipow (val1, val2);
|
||||
}
|
||||
return (val1);
|
||||
}
|
||||
|
||||
@@ -398,7 +398,8 @@ user_command_matches (name, flags, state)
|
||||
name_len = strlen (name);
|
||||
file_to_lose_on = (char *)NULL;
|
||||
dot_found_in_search = 0;
|
||||
stat (".", &dotinfo);
|
||||
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;
|
||||
}
|
||||
@@ -569,7 +570,8 @@ find_user_command_in_path (name, path_list, flags)
|
||||
|
||||
file_to_lose_on = (char *)NULL;
|
||||
name_len = strlen (name);
|
||||
stat (".", &dotinfo);
|
||||
if (stat (".", &dotinfo) < 0)
|
||||
dotinfo.st_dev = dotinfo.st_ino = 0;
|
||||
path_index = 0;
|
||||
|
||||
while (path_list[path_index])
|
||||
|
||||
+618
@@ -0,0 +1,618 @@
|
||||
/* findcmd.c -- Functions to search for commands by name. */
|
||||
|
||||
/* Copyright (C) 1997-2009 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "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;
|
||||
|
||||
/* 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_internal ("PATH", 1)) /* 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. Returns a newly-allocated string. */
|
||||
char *
|
||||
search_for_command (pathname)
|
||||
const char *pathname;
|
||||
{
|
||||
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_internal ("PATH", 1);
|
||||
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)
|
||||
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);
|
||||
stat (".", &dotinfo);
|
||||
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,6 +1,6 @@
|
||||
/* general.c -- Stuff that is used by all files. */
|
||||
|
||||
/* Copyright (C) 1987-2009 Free Software Foundation, Inc.
|
||||
/* Copyright (C) 1987-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
@@ -171,6 +171,9 @@ legal_number (string, result)
|
||||
if (result)
|
||||
*result = 0;
|
||||
|
||||
if (string == 0)
|
||||
return 0;
|
||||
|
||||
errno = 0;
|
||||
value = strtoimax (string, &ep, 10);
|
||||
if (errno || ep == string)
|
||||
@@ -182,7 +185,7 @@ legal_number (string, result)
|
||||
|
||||
/* If *string is not '\0' but *ep is '\0' on return, the entire string
|
||||
is valid. */
|
||||
if (string && *string && *ep == '\0')
|
||||
if (*string && *ep == '\0')
|
||||
{
|
||||
if (result)
|
||||
*result = value;
|
||||
@@ -398,7 +401,8 @@ check_dev_tty ()
|
||||
return;
|
||||
tty_fd = open (tty, O_RDWR|O_NONBLOCK);
|
||||
}
|
||||
close (tty_fd);
|
||||
if (tty_fd >= 0)
|
||||
close (tty_fd);
|
||||
}
|
||||
|
||||
/* Return 1 if PATH1 and PATH2 are the same file. This is kind of
|
||||
|
||||
+1163
File diff suppressed because it is too large
Load Diff
@@ -3669,7 +3669,8 @@ initialize_job_control (force)
|
||||
if (shell_tty == -1)
|
||||
shell_tty = dup (fileno (stderr)); /* fd 2 */
|
||||
|
||||
shell_tty = move_to_high_fd (shell_tty, 1, -1);
|
||||
if (shell_tty != -1)
|
||||
shell_tty = move_to_high_fd (shell_tty, 1, -1);
|
||||
|
||||
/* Compensate for a bug in systems that compiled the BSD
|
||||
rlogind with DEBUG defined, like NeXT and Alliant. */
|
||||
|
||||
@@ -45,7 +45,9 @@
|
||||
|
||||
#include "filecntl.h"
|
||||
#include <sys/ioctl.h>
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#if defined (BUFFERED_INPUT)
|
||||
# include "input.h"
|
||||
|
||||
+30
-6
@@ -404,7 +404,6 @@ finddirs (pat, sdir, flags, ep, np)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Return a vector of names of files in directory DIR
|
||||
whose names match glob pattern PAT.
|
||||
@@ -475,7 +474,7 @@ glob_vector (pat, dir, flags)
|
||||
skip = 1;
|
||||
}
|
||||
|
||||
patlen = strlen (pat);
|
||||
patlen = (pat && *pat) ? strlen (pat) : 0;
|
||||
|
||||
/* If the filename pattern (PAT) does not contain any globbing characters,
|
||||
we can dispense with reading the directory, and just see if there is
|
||||
@@ -493,7 +492,11 @@ glob_vector (pat, dir, flags)
|
||||
nextname = (char *)malloc (dirlen + patlen + 2);
|
||||
npat = (char *)malloc (patlen + 1);
|
||||
if (nextname == 0 || npat == 0)
|
||||
lose = 1;
|
||||
{
|
||||
FREE (nextname);
|
||||
FREE (npat);
|
||||
lose = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy (npat, pat);
|
||||
@@ -515,7 +518,10 @@ glob_vector (pat, dir, flags)
|
||||
count = 1;
|
||||
}
|
||||
else
|
||||
lose = 1;
|
||||
{
|
||||
free (npat);
|
||||
lose = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -632,6 +638,8 @@ glob_vector (pat, dir, flags)
|
||||
nextname = (char *) malloc (sdlen + 1);
|
||||
if (nextlink == 0 || nextname == 0)
|
||||
{
|
||||
FREE (nextlink);
|
||||
FREE (nextname);
|
||||
free (subdir);
|
||||
lose = 1;
|
||||
break;
|
||||
@@ -644,6 +652,8 @@ glob_vector (pat, dir, flags)
|
||||
++count;
|
||||
continue;
|
||||
}
|
||||
else if (flags & GX_MATCHDIRS)
|
||||
free (subdir);
|
||||
|
||||
convfn = fnx_fromfs (dp->d_name, D_NAMLEN (dp));
|
||||
if (strmatch (pat, convfn, mflags) != FNM_NOMATCH)
|
||||
@@ -663,6 +673,8 @@ glob_vector (pat, dir, flags)
|
||||
nextname = (char *) malloc (D_NAMLEN (dp) + 1);
|
||||
if (nextlink == 0 || nextname == 0)
|
||||
{
|
||||
FREE (nextlink);
|
||||
FREE (nextname);
|
||||
lose = 1;
|
||||
break;
|
||||
}
|
||||
@@ -686,7 +698,11 @@ glob_vector (pat, dir, flags)
|
||||
nextname = (char *)malloc (sdlen + 1);
|
||||
nextlink = (struct globval *) malloc (sizeof (struct globval));
|
||||
if (nextlink == 0 || nextname == 0)
|
||||
lose = 1;
|
||||
{
|
||||
FREE (nextlink);
|
||||
FREE (nextname);
|
||||
lose = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextlink->name = nextname;
|
||||
@@ -812,7 +828,13 @@ glob_dir_to_array (dir, array, flags)
|
||||
result[i] = (char *) malloc (l + strlen (array[i]) + 3);
|
||||
|
||||
if (result[i] == NULL)
|
||||
return (NULL);
|
||||
{
|
||||
int ind;
|
||||
for (ind = 0; ind < i; ind++)
|
||||
free (result[ind]);
|
||||
free (result);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
strcpy (result[i], dir);
|
||||
if (add_slash)
|
||||
@@ -983,6 +1005,8 @@ glob_filename (pathname, flags)
|
||||
/* Note that the elements of ARRAY are not freed. */
|
||||
if (array != temp_results)
|
||||
free ((char *) array);
|
||||
else if ((dflags & GX_ALLDIRS) && filename[0] == '*' && filename[1] == '*' && filename[2] == '\0')
|
||||
free (temp_results); /* expanding ** case above */
|
||||
}
|
||||
}
|
||||
/* Free the directories. */
|
||||
|
||||
+1129
File diff suppressed because it is too large
Load Diff
+12
-1
@@ -271,6 +271,8 @@ xdupmbstowcs (destp, indicesp, src)
|
||||
{
|
||||
if (destp)
|
||||
*destp = NULL;
|
||||
if (indicesp)
|
||||
*indicesp = NULL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
@@ -286,6 +288,8 @@ xdupmbstowcs (destp, indicesp, src)
|
||||
if (wsbuf == NULL)
|
||||
{
|
||||
*destp = NULL;
|
||||
if (indicesp)
|
||||
*indicesp = NULL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
@@ -297,6 +301,7 @@ xdupmbstowcs (destp, indicesp, src)
|
||||
{
|
||||
free (wsbuf);
|
||||
*destp = NULL;
|
||||
*indicesp = NULL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
}
|
||||
@@ -331,6 +336,8 @@ xdupmbstowcs (destp, indicesp, src)
|
||||
free (wsbuf);
|
||||
FREE (indices);
|
||||
*destp = NULL;
|
||||
if (indicesp)
|
||||
*indicesp = NULL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
@@ -350,18 +357,22 @@ xdupmbstowcs (destp, indicesp, src)
|
||||
free (wsbuf);
|
||||
FREE (indices);
|
||||
*destp = NULL;
|
||||
if (indicesp)
|
||||
*indicesp = NULL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
wsbuf = wstmp;
|
||||
|
||||
if (indicesp)
|
||||
{
|
||||
idxtmp = (char **) realloc (indices, wsbuf_size * sizeof (char **));
|
||||
idxtmp = (char **) realloc (indices, wsbuf_size * sizeof (char *));
|
||||
if (idxtmp == NULL)
|
||||
{
|
||||
free (wsbuf);
|
||||
free (indices);
|
||||
*destp = NULL;
|
||||
if (indicesp)
|
||||
*indicesp = NULL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
indices = idxtmp;
|
||||
|
||||
@@ -0,0 +1,397 @@
|
||||
/* xmbsrtowcs.c -- replacement function for mbsrtowcs */
|
||||
|
||||
/* Copyright (C) 2002-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/>.
|
||||
*/
|
||||
|
||||
/* Ask for GNU extensions to get extern declaration for mbsnrtowcs if
|
||||
available via glibc. */
|
||||
#ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE 1
|
||||
#endif
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <bashansi.h>
|
||||
|
||||
/* <wchar.h>, <wctype.h> and <stdlib.h> are included in "shmbutil.h".
|
||||
If <wchar.h>, <wctype.h>, mbsrtowcs(), exist, HANDLE_MULTIBYTE
|
||||
is defined as 1. */
|
||||
#include <shmbutil.h>
|
||||
|
||||
#if HANDLE_MULTIBYTE
|
||||
|
||||
#define WSBUF_INC 32
|
||||
|
||||
#ifndef FREE
|
||||
# define FREE(x) do { if (x) free (x); } while (0)
|
||||
#endif
|
||||
|
||||
#if ! HAVE_STRCHRNUL
|
||||
extern char *strchrnul __P((const char *, int));
|
||||
#endif
|
||||
|
||||
/* On some locales (ex. ja_JP.sjis), mbsrtowc doesn't convert 0x5c to U<0x5c>.
|
||||
So, this function is made for converting 0x5c to U<0x5c>. */
|
||||
|
||||
static mbstate_t local_state;
|
||||
static int local_state_use = 0;
|
||||
|
||||
size_t
|
||||
xmbsrtowcs (dest, src, len, pstate)
|
||||
wchar_t *dest;
|
||||
const char **src;
|
||||
size_t len;
|
||||
mbstate_t *pstate;
|
||||
{
|
||||
mbstate_t *ps;
|
||||
size_t mblength, wclength, n;
|
||||
|
||||
ps = pstate;
|
||||
if (pstate == NULL)
|
||||
{
|
||||
if (!local_state_use)
|
||||
{
|
||||
memset (&local_state, '\0', sizeof(mbstate_t));
|
||||
local_state_use = 1;
|
||||
}
|
||||
ps = &local_state;
|
||||
}
|
||||
|
||||
n = strlen (*src);
|
||||
|
||||
if (dest == NULL)
|
||||
{
|
||||
wchar_t *wsbuf;
|
||||
const char *mbs;
|
||||
mbstate_t psbuf;
|
||||
|
||||
/* It doesn't matter if malloc fails here, since mbsrtowcs should do
|
||||
the right thing with a NULL first argument. */
|
||||
wsbuf = (wchar_t *) malloc ((n + 1) * sizeof(wchar_t));
|
||||
mbs = *src;
|
||||
psbuf = *ps;
|
||||
|
||||
wclength = mbsrtowcs (wsbuf, &mbs, n, &psbuf);
|
||||
|
||||
if (wsbuf)
|
||||
free (wsbuf);
|
||||
return wclength;
|
||||
}
|
||||
|
||||
for (wclength = 0; wclength < len; wclength++, dest++)
|
||||
{
|
||||
if (mbsinit(ps))
|
||||
{
|
||||
if (**src == '\0')
|
||||
{
|
||||
*dest = L'\0';
|
||||
*src = NULL;
|
||||
return (wclength);
|
||||
}
|
||||
else if (**src == '\\')
|
||||
{
|
||||
*dest = L'\\';
|
||||
mblength = 1;
|
||||
}
|
||||
else
|
||||
mblength = mbrtowc(dest, *src, n, ps);
|
||||
}
|
||||
else
|
||||
mblength = mbrtowc(dest, *src, n, ps);
|
||||
|
||||
/* Cannot convert multibyte character to wide character. */
|
||||
if (mblength == (size_t)-1 || mblength == (size_t)-2)
|
||||
return (size_t)-1;
|
||||
|
||||
*src += mblength;
|
||||
n -= mblength;
|
||||
|
||||
/* The multibyte string has been completely converted,
|
||||
including the terminating '\0'. */
|
||||
if (*dest == L'\0')
|
||||
{
|
||||
*src = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (wclength);
|
||||
}
|
||||
|
||||
#if HAVE_MBSNRTOWCS
|
||||
/* Convert a multibyte string to a wide character string. Memory for the
|
||||
new wide character string is obtained with malloc.
|
||||
|
||||
Fast multiple-character version of xdupmbstowcs used when the indices are
|
||||
not required and mbsnrtowcs is available. */
|
||||
|
||||
static size_t
|
||||
xdupmbstowcs2 (destp, src)
|
||||
wchar_t **destp; /* Store the pointer to the wide character string */
|
||||
const char *src; /* Multibyte character string */
|
||||
{
|
||||
const char *p; /* Conversion start position of src */
|
||||
wchar_t *wsbuf; /* Buffer for wide characters. */
|
||||
size_t wsbuf_size; /* Size of WSBUF */
|
||||
size_t wcnum; /* Number of wide characters in WSBUF */
|
||||
mbstate_t state; /* Conversion State */
|
||||
size_t n, wcslength; /* Number of wide characters produced by the conversion. */
|
||||
const char *end_or_backslash;
|
||||
size_t nms; /* Number of multibyte characters to convert at one time. */
|
||||
mbstate_t tmp_state;
|
||||
const char *tmp_p;
|
||||
|
||||
memset (&state, '\0', sizeof(mbstate_t));
|
||||
|
||||
wsbuf_size = 0;
|
||||
wsbuf = NULL;
|
||||
|
||||
p = src;
|
||||
wcnum = 0;
|
||||
do
|
||||
{
|
||||
end_or_backslash = strchrnul(p, '\\');
|
||||
nms = end_or_backslash - p;
|
||||
if (*end_or_backslash == '\0')
|
||||
nms++;
|
||||
|
||||
/* Compute the number of produced wide-characters. */
|
||||
tmp_p = p;
|
||||
tmp_state = state;
|
||||
|
||||
if (nms == 0 && *p == '\\') /* special initial case */
|
||||
nms = wcslength = 1;
|
||||
else
|
||||
wcslength = mbsnrtowcs (NULL, &tmp_p, nms, 0, &tmp_state);
|
||||
|
||||
if (wcslength == 0)
|
||||
{
|
||||
tmp_p = p; /* will need below */
|
||||
tmp_state = state;
|
||||
wcslength = 1; /* take a single byte */
|
||||
}
|
||||
|
||||
/* Conversion failed. */
|
||||
if (wcslength == (size_t)-1)
|
||||
{
|
||||
free (wsbuf);
|
||||
*destp = NULL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
/* Resize the buffer if it is not large enough. */
|
||||
if (wsbuf_size < wcnum+wcslength+1) /* 1 for the L'\0' or the potential L'\\' */
|
||||
{
|
||||
wchar_t *wstmp;
|
||||
|
||||
while (wsbuf_size < wcnum+wcslength+1) /* 1 for the L'\0' or the potential L'\\' */
|
||||
wsbuf_size += WSBUF_INC;
|
||||
|
||||
wstmp = (wchar_t *) realloc (wsbuf, wsbuf_size * sizeof (wchar_t));
|
||||
if (wstmp == NULL)
|
||||
{
|
||||
free (wsbuf);
|
||||
*destp = NULL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
wsbuf = wstmp;
|
||||
}
|
||||
|
||||
/* Perform the conversion. This is assumed to return 'wcslength'.
|
||||
It may set 'p' to NULL. */
|
||||
n = mbsnrtowcs(wsbuf+wcnum, &p, nms, wsbuf_size-wcnum, &state);
|
||||
|
||||
/* Compensate for taking single byte on wcs conversion failure above. */
|
||||
if (wcslength == 1 && (n == 0 || n == (size_t)-1))
|
||||
{
|
||||
state = tmp_state;
|
||||
p = tmp_p;
|
||||
wsbuf[wcnum++] = *p++;
|
||||
}
|
||||
else
|
||||
wcnum += wcslength;
|
||||
|
||||
if (mbsinit (&state) && (p != NULL) && (*p == '\\'))
|
||||
{
|
||||
wsbuf[wcnum++] = L'\\';
|
||||
p++;
|
||||
}
|
||||
}
|
||||
while (p != NULL);
|
||||
|
||||
*destp = wsbuf;
|
||||
|
||||
/* Return the length of the wide character string, not including `\0'. */
|
||||
return wcnum;
|
||||
}
|
||||
#endif /* HAVE_MBSNRTOWCS */
|
||||
|
||||
/* Convert a multibyte string to a wide character string. Memory for the
|
||||
new wide character string is obtained with malloc.
|
||||
|
||||
The return value is the length of the wide character string. Returns a
|
||||
pointer to the wide character string in DESTP. If INDICESP is not NULL,
|
||||
INDICESP stores the pointer to the pointer array. Each pointer is to
|
||||
the first byte of each multibyte character. Memory for the pointer array
|
||||
is obtained with malloc, too.
|
||||
If conversion is failed, the return value is (size_t)-1 and the values
|
||||
of DESTP and INDICESP are NULL. */
|
||||
|
||||
size_t
|
||||
xdupmbstowcs (destp, indicesp, src)
|
||||
wchar_t **destp; /* Store the pointer to the wide character string */
|
||||
char ***indicesp; /* Store the pointer to the pointer array. */
|
||||
const char *src; /* Multibyte character string */
|
||||
{
|
||||
const char *p; /* Conversion start position of src */
|
||||
wchar_t wc; /* Created wide character by conversion */
|
||||
wchar_t *wsbuf; /* Buffer for wide characters. */
|
||||
char **indices; /* Buffer for indices. */
|
||||
size_t wsbuf_size; /* Size of WSBUF */
|
||||
size_t wcnum; /* Number of wide characters in WSBUF */
|
||||
mbstate_t state; /* Conversion State */
|
||||
|
||||
/* In case SRC or DESP is NULL, conversion doesn't take place. */
|
||||
if (src == NULL || destp == NULL)
|
||||
{
|
||||
if (destp)
|
||||
*destp = NULL;
|
||||
if (indicesp)
|
||||
*indicesp = NULL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
#if HAVE_MBSNRTOWCS
|
||||
if (indicesp == NULL)
|
||||
return (xdupmbstowcs2 (destp, src));
|
||||
#endif
|
||||
|
||||
memset (&state, '\0', sizeof(mbstate_t));
|
||||
wsbuf_size = WSBUF_INC;
|
||||
|
||||
wsbuf = (wchar_t *) malloc (wsbuf_size * sizeof(wchar_t));
|
||||
if (wsbuf == NULL)
|
||||
{
|
||||
*destp = NULL;
|
||||
if (indicesp)
|
||||
*indicesp = NULL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
indices = NULL;
|
||||
if (indicesp)
|
||||
{
|
||||
indices = (char **) malloc (wsbuf_size * sizeof(char *));
|
||||
if (indices == NULL)
|
||||
{
|
||||
free (wsbuf);
|
||||
*destp = NULL;
|
||||
*indicesp = NULL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
}
|
||||
|
||||
p = src;
|
||||
wcnum = 0;
|
||||
do
|
||||
{
|
||||
size_t mblength; /* Byte length of one multibyte character. */
|
||||
|
||||
if (mbsinit (&state))
|
||||
{
|
||||
if (*p == '\0')
|
||||
{
|
||||
wc = L'\0';
|
||||
mblength = 1;
|
||||
}
|
||||
else if (*p == '\\')
|
||||
{
|
||||
wc = L'\\';
|
||||
mblength = 1;
|
||||
}
|
||||
else
|
||||
mblength = mbrtowc(&wc, p, MB_LEN_MAX, &state);
|
||||
}
|
||||
else
|
||||
mblength = mbrtowc(&wc, p, MB_LEN_MAX, &state);
|
||||
|
||||
/* Conversion failed. */
|
||||
if (MB_INVALIDCH (mblength))
|
||||
{
|
||||
free (wsbuf);
|
||||
FREE (indices);
|
||||
*destp = NULL;
|
||||
if (indicesp)
|
||||
*indicesp = NULL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
++wcnum;
|
||||
|
||||
/* Resize buffers when they are not large enough. */
|
||||
if (wsbuf_size < wcnum)
|
||||
{
|
||||
wchar_t *wstmp;
|
||||
char **idxtmp;
|
||||
|
||||
wsbuf_size += WSBUF_INC;
|
||||
|
||||
wstmp = (wchar_t *) realloc (wsbuf, wsbuf_size * sizeof (wchar_t));
|
||||
if (wstmp == NULL)
|
||||
{
|
||||
free (wsbuf);
|
||||
FREE (indices);
|
||||
*destp = NULL;
|
||||
if (indicesp)
|
||||
*indicesp = NULL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
wsbuf = wstmp;
|
||||
|
||||
if (indicesp)
|
||||
{
|
||||
idxtmp = (char **) realloc (indices, wsbuf_size * sizeof (char **));
|
||||
if (idxtmp == NULL)
|
||||
{
|
||||
free (wsbuf);
|
||||
free (indices);
|
||||
*destp = NULL;
|
||||
if (indicesp)
|
||||
*indicesp = NULL;
|
||||
return (size_t)-1;
|
||||
}
|
||||
indices = idxtmp;
|
||||
}
|
||||
}
|
||||
|
||||
wsbuf[wcnum - 1] = wc;
|
||||
if (indices)
|
||||
indices[wcnum - 1] = (char *)p;
|
||||
p += mblength;
|
||||
}
|
||||
while (MB_NULLWCH (wc) == 0);
|
||||
|
||||
/* Return the length of the wide character string, not including `\0'. */
|
||||
*destp = wsbuf;
|
||||
if (indicesp != NULL)
|
||||
*indicesp = indices;
|
||||
|
||||
return (wcnum - 1);
|
||||
}
|
||||
|
||||
#endif /* HANDLE_MULTIBYTE */
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
/* fmtulong.c -- Convert unsigned long int to string. */
|
||||
|
||||
/* Copyright (C) 1998-2002 Free Software Foundation, Inc.
|
||||
/* Copyright (C) 1998-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
@@ -98,8 +98,9 @@ fmtulong (ui, base, buf, len, flags)
|
||||
if (base < 2 || base > 64)
|
||||
{
|
||||
#if 1
|
||||
/* XXX - truncation possible with long translation */
|
||||
strncpy (buf, _("invalid base"), len - 1);
|
||||
buf[len] = '\0';
|
||||
buf[len-1] = '\0';
|
||||
errno = EINVAL;
|
||||
return (p = buf);
|
||||
#else
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
/* fmtulong.c -- Convert unsigned long int to string. */
|
||||
|
||||
/* Copyright (C) 1998-2002 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_LIMITS_H)
|
||||
# include <limits.h>
|
||||
#endif
|
||||
|
||||
#include <bashansi.h>
|
||||
#ifdef HAVE_STDDEF_H
|
||||
# include <stddef.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
# include <inttypes.h>
|
||||
#endif
|
||||
#include <chartypes.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <bashintl.h>
|
||||
|
||||
#include "stdc.h"
|
||||
|
||||
#include <typemax.h>
|
||||
|
||||
#ifndef errno
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
#define x_digs "0123456789abcdef"
|
||||
#define X_digs "0123456789ABCDEF"
|
||||
|
||||
/* XXX -- assumes uppercase letters, lowercase letters, and digits are
|
||||
contiguous */
|
||||
#define FMTCHAR(x) \
|
||||
((x) < 10) ? (x) + '0' \
|
||||
: (((x) < 36) ? (x) - 10 + 'a' \
|
||||
: (((x) < 62) ? (x) - 36 + 'A' \
|
||||
: (((x) == 62) ? '@' : '_')))
|
||||
|
||||
#ifndef FL_PREFIX
|
||||
# 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 */
|
||||
#endif
|
||||
|
||||
#ifndef LONG
|
||||
# define LONG long
|
||||
# define UNSIGNED_LONG unsigned long
|
||||
#endif
|
||||
|
||||
/* `unsigned long' (or unsigned long long) to string conversion for a given
|
||||
base. The caller passes the output buffer and the size. This should
|
||||
check for buffer underflow, but currently does not. */
|
||||
char *
|
||||
fmtulong (ui, base, buf, len, flags)
|
||||
UNSIGNED_LONG ui;
|
||||
int base;
|
||||
char *buf;
|
||||
size_t len;
|
||||
int flags;
|
||||
{
|
||||
char *p;
|
||||
int sign;
|
||||
LONG si;
|
||||
|
||||
if (base == 0)
|
||||
base = 10;
|
||||
|
||||
if (base < 2 || base > 64)
|
||||
{
|
||||
#if 1
|
||||
/* XXX - truncation possible with long translation */
|
||||
strncpy (buf, _("invalid base"), len - 1);
|
||||
buf[len-1] = '\0';
|
||||
errno = EINVAL;
|
||||
return (p = buf);
|
||||
#else
|
||||
base = 10;
|
||||
#endif
|
||||
}
|
||||
|
||||
sign = 0;
|
||||
if ((flags & FL_UNSIGNED) == 0 && (LONG)ui < 0)
|
||||
{
|
||||
ui = -ui;
|
||||
sign = '-';
|
||||
}
|
||||
|
||||
p = buf + len - 2;
|
||||
p[1] = '\0';
|
||||
|
||||
/* handle common cases explicitly */
|
||||
switch (base)
|
||||
{
|
||||
case 10:
|
||||
if (ui < 10)
|
||||
{
|
||||
*p-- = TOCHAR (ui);
|
||||
break;
|
||||
}
|
||||
/* Favor signed arithmetic over unsigned arithmetic; it is faster on
|
||||
many machines. */
|
||||
if ((LONG)ui < 0)
|
||||
{
|
||||
*p-- = TOCHAR (ui % 10);
|
||||
si = ui / 10;
|
||||
}
|
||||
else
|
||||
si = ui;
|
||||
do
|
||||
*p-- = TOCHAR (si % 10);
|
||||
while (si /= 10);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
do
|
||||
*p-- = TOCHAR (ui & 7);
|
||||
while (ui >>= 3);
|
||||
break;
|
||||
|
||||
case 16:
|
||||
do
|
||||
*p-- = (flags & FL_HEXUPPER) ? X_digs[ui & 15] : x_digs[ui & 15];
|
||||
while (ui >>= 4);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
do
|
||||
*p-- = TOCHAR (ui & 1);
|
||||
while (ui >>= 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
do
|
||||
*p-- = FMTCHAR (ui % base);
|
||||
while (ui /= base);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((flags & FL_PREFIX) && (base == 8 || base == 16))
|
||||
{
|
||||
if (base == 16)
|
||||
{
|
||||
*p-- = (flags & FL_HEXUPPER) ? 'X' : 'x';
|
||||
*p-- = '0';
|
||||
}
|
||||
else if (p[1] != '0')
|
||||
*p-- = '0';
|
||||
}
|
||||
else if ((flags & FL_ADDBASE) && base != 10)
|
||||
{
|
||||
*p-- = '#';
|
||||
*p-- = TOCHAR (base % 10);
|
||||
if (base > 10)
|
||||
*p-- = TOCHAR (base / 10);
|
||||
}
|
||||
|
||||
if (sign)
|
||||
*p-- = '-';
|
||||
|
||||
return (p + 1);
|
||||
}
|
||||
+1
-2
@@ -66,11 +66,10 @@ zmapfd (fd, ostr, fn)
|
||||
}
|
||||
else if (nr < 0)
|
||||
{
|
||||
rval = -1;
|
||||
free (result);
|
||||
if (ostr)
|
||||
*ostr = (char *)NULL;
|
||||
break;
|
||||
return -1;
|
||||
}
|
||||
|
||||
RESIZE_MALLOCED_BUFFER (result, rind, nr, rsize, 128);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/* zmapfd - read contents of file descriptor into a newly-allocated buffer */
|
||||
|
||||
/* Copyright (C) 2006 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 "bashansi.h"
|
||||
#include "command.h"
|
||||
#include "general.h"
|
||||
|
||||
#if !defined (errno)
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
extern ssize_t zread __P((int, char *, size_t));
|
||||
|
||||
/* Dump contents of file descriptor FD to *OSTR. FN is the filename for
|
||||
error messages (not used right now). */
|
||||
int
|
||||
zmapfd (fd, ostr, fn)
|
||||
int fd;
|
||||
char **ostr;
|
||||
char *fn;
|
||||
{
|
||||
ssize_t nr;
|
||||
int rval;
|
||||
char lbuf[128];
|
||||
char *result;
|
||||
int rsize, rind;
|
||||
|
||||
rval = 0;
|
||||
result = (char *)xmalloc (rsize = 64);
|
||||
rind = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
nr = zread (fd, lbuf, sizeof (lbuf));
|
||||
if (nr == 0)
|
||||
{
|
||||
rval = rind;
|
||||
break;
|
||||
}
|
||||
else if (nr < 0)
|
||||
{
|
||||
rval = -1;
|
||||
free (result);
|
||||
if (ostr)
|
||||
*ostr = (char *)NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
RESIZE_MALLOCED_BUFFER (result, rind, nr, rsize, 128);
|
||||
memcpy (result+rind, lbuf, nr);
|
||||
rind += nr;
|
||||
}
|
||||
|
||||
RESIZE_MALLOCED_BUFFER (result, rind, 1, rsize, 128);
|
||||
result[rind] = '\0';
|
||||
|
||||
if (ostr)
|
||||
*ostr = result;
|
||||
else
|
||||
free (result);
|
||||
|
||||
return rval;
|
||||
}
|
||||
@@ -324,6 +324,9 @@ make_arith_for_command (exprs, action, lineno)
|
||||
else
|
||||
parser_error (lineno, _("syntax error: `;' unexpected"));
|
||||
parser_error (lineno, _("syntax error: `((%s))'"), exprs->word->word);
|
||||
free (init);
|
||||
free (test);
|
||||
free (step);
|
||||
last_command_exit_value = 2;
|
||||
return ((COMMAND *)NULL);
|
||||
}
|
||||
|
||||
+1
-1
@@ -292,7 +292,7 @@ make_arith_for_command (exprs, action, lineno)
|
||||
s++;
|
||||
start = s;
|
||||
/* skip to the semicolon or EOS */
|
||||
i = skip_to_delim (s, 0, ";", SD_NOJMP);
|
||||
i = skip_to_delim (start, 0, ";", SD_NOJMP);
|
||||
s = start + i;
|
||||
|
||||
t = (i > 0) ? substring (start, 0, i) : (char *)NULL;
|
||||
|
||||
+12
-3
@@ -1,6 +1,6 @@
|
||||
/* pcomplete.c - functions to generate lists of matches for programmable completion. */
|
||||
|
||||
/* Copyright (C) 1999-2009 Free Software Foundation, Inc.
|
||||
/* Copyright (C) 1999-2011 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
@@ -372,6 +372,7 @@ it_init_aliases (itp)
|
||||
#else
|
||||
itp->slist = (STRINGLIST *)NULL;
|
||||
#endif
|
||||
free (alias_list);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1191,7 +1192,8 @@ gen_command_matches (cs, text, line, ind, lwords, nw, cw)
|
||||
|
||||
tw = command_substitute (cscmd, 0);
|
||||
csbuf = tw ? tw->word : (char *)NULL;
|
||||
dispose_word_desc (tw);
|
||||
if (tw)
|
||||
dispose_word_desc (tw);
|
||||
|
||||
/* Now clean up and destroy everything. */
|
||||
dispose_words (cmdlist);
|
||||
@@ -1431,6 +1433,7 @@ gen_compspec_completions (cs, cmd, word, start, end, foundp)
|
||||
{
|
||||
tcs = compspec_create ();
|
||||
tcs->actions = CA_DIRECTORY;
|
||||
FREE (ret);
|
||||
ret = gen_action_completions (tcs, word);
|
||||
compspec_dispose (tcs);
|
||||
}
|
||||
@@ -1491,7 +1494,13 @@ gen_progcomp_completions (ocmd, cmd, word, start, end, foundp, retryp, lastcs)
|
||||
cs = progcomp_search (ocmd);
|
||||
|
||||
if (cs == 0 || cs == *lastcs)
|
||||
return (NULL);
|
||||
{
|
||||
#if 0
|
||||
if (foundp)
|
||||
*foundp = 0;
|
||||
#endif
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (*lastcs)
|
||||
compspec_dispose (*lastcs);
|
||||
|
||||
+11
-2
@@ -83,6 +83,7 @@ extern char *strpbrk __P((char *, char *));
|
||||
extern int array_needs_making;
|
||||
extern STRING_INT_ALIST word_token_alist[];
|
||||
extern char *signal_names[];
|
||||
extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
|
||||
|
||||
#if defined (DEBUG)
|
||||
#if defined (PREFER_STDARG)
|
||||
@@ -1190,7 +1191,8 @@ gen_command_matches (cs, text, line, ind, lwords, nw, cw)
|
||||
|
||||
tw = command_substitute (cscmd, 0);
|
||||
csbuf = tw ? tw->word : (char *)NULL;
|
||||
dispose_word_desc (tw);
|
||||
if (tw)
|
||||
dispose_word_desc (tw);
|
||||
|
||||
/* Now clean up and destroy everything. */
|
||||
dispose_words (cmdlist);
|
||||
@@ -1430,6 +1432,7 @@ gen_compspec_completions (cs, cmd, word, start, end, foundp)
|
||||
{
|
||||
tcs = compspec_create ();
|
||||
tcs->actions = CA_DIRECTORY;
|
||||
FREE (ret);
|
||||
ret = gen_action_completions (tcs, word);
|
||||
compspec_dispose (tcs);
|
||||
}
|
||||
@@ -1490,7 +1493,13 @@ gen_progcomp_completions (ocmd, cmd, word, start, end, foundp, retryp, lastcs)
|
||||
cs = progcomp_search (ocmd);
|
||||
|
||||
if (cs == 0 || cs == *lastcs)
|
||||
return (NULL);
|
||||
{
|
||||
#if 0
|
||||
if (foundp)
|
||||
*foundp = 0;
|
||||
#endif
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (*lastcs)
|
||||
compspec_dispose (*lastcs);
|
||||
|
||||
+3
-1
@@ -972,7 +972,7 @@ print_deferred_heredocs (cstring)
|
||||
cprintf (" ");
|
||||
print_heredoc_header (hdtail);
|
||||
}
|
||||
if (cstring[0] && (cstring[0] != ';' || cstring[1]))
|
||||
if (cstring && cstring[0] && (cstring[0] != ';' || cstring[1]))
|
||||
cprintf ("%s", cstring);
|
||||
if (deferred_heredocs)
|
||||
cprintf ("\n");
|
||||
@@ -1507,6 +1507,8 @@ cprintf (control, va_alist)
|
||||
}
|
||||
}
|
||||
|
||||
va_end (args);
|
||||
|
||||
the_printed_command[command_string_index] = '\0';
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -431,7 +431,7 @@ indirection_level_string ()
|
||||
|
||||
old = change_flag ('x', FLAG_OFF);
|
||||
ps4 = decode_prompt_string (ps4);
|
||||
if (old == FLAG_ON)
|
||||
if (old)
|
||||
change_flag ('x', FLAG_ON);
|
||||
|
||||
if (ps4 == 0 || *ps4 == '\0')
|
||||
@@ -972,7 +972,7 @@ print_deferred_heredocs (cstring)
|
||||
cprintf (" ");
|
||||
print_heredoc_header (hdtail);
|
||||
}
|
||||
if (cstring[0] && (cstring[0] != ';' || cstring[1]))
|
||||
if (cstring && cstring[0] && (cstring[0] != ';' || cstring[1]))
|
||||
cprintf ("%s", cstring);
|
||||
if (deferred_heredocs)
|
||||
cprintf ("\n");
|
||||
|
||||
@@ -115,7 +115,7 @@ redirection_error (temp, error)
|
||||
|
||||
allocname = 0;
|
||||
if (temp->rflags & REDIR_VARASSIGN)
|
||||
filename = savestring (temp->redirector.filename->word);
|
||||
filename = allocname = savestring (temp->redirector.filename->word);
|
||||
else if (temp->redirector.dest < 0)
|
||||
/* This can happen when read_token_word encounters overflow, like in
|
||||
exec 4294967297>x */
|
||||
|
||||
+1
-1
@@ -158,7 +158,7 @@ strsub (string, pat, rep, global)
|
||||
if (replen)
|
||||
RESIZE_MALLOCED_BUFFER (temp, templen, replen, tempsize, (replen * 2));
|
||||
|
||||
for (r = rep; *r; )
|
||||
for (r = rep; *r; ) /* can rep == "" */
|
||||
temp[templen++] = *r++;
|
||||
|
||||
i += patlen ? patlen : 1; /* avoid infinite recursion */
|
||||
|
||||
+287
@@ -0,0 +1,287 @@
|
||||
/* stringlib.c - Miscellaneous string functions. */
|
||||
|
||||
/* Copyright (C) 1996-2009 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Bash, the Bourne Again SHell.
|
||||
|
||||
Bash is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Bash is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "bashtypes.h"
|
||||
|
||||
#if defined (HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "bashansi.h"
|
||||
#include <stdio.h>
|
||||
#include "chartypes.h"
|
||||
|
||||
#include "shell.h"
|
||||
#include "pathexp.h"
|
||||
|
||||
#include <glob/glob.h>
|
||||
|
||||
#if defined (EXTENDED_GLOB)
|
||||
# include <glob/strmatch.h>
|
||||
#endif
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Functions to manage arrays of strings */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* Find STRING in ALIST, a list of string key/int value pairs. If FLAGS
|
||||
is 1, STRING is treated as a pattern and matched using strmatch. */
|
||||
int
|
||||
find_string_in_alist (string, alist, flags)
|
||||
char *string;
|
||||
STRING_INT_ALIST *alist;
|
||||
int flags;
|
||||
{
|
||||
register int i;
|
||||
int r;
|
||||
|
||||
for (i = r = 0; alist[i].word; i++)
|
||||
{
|
||||
#if defined (EXTENDED_GLOB)
|
||||
if (flags)
|
||||
r = strmatch (alist[i].word, string, FNM_EXTMATCH) != FNM_NOMATCH;
|
||||
else
|
||||
#endif
|
||||
r = STREQ (string, alist[i].word);
|
||||
|
||||
if (r)
|
||||
return (alist[i].token);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find TOKEN in ALIST, a list of string/int value pairs. Return the
|
||||
corresponding string. Allocates memory for the returned
|
||||
string. FLAGS is currently ignored, but reserved. */
|
||||
char *
|
||||
find_token_in_alist (token, alist, flags)
|
||||
int token;
|
||||
STRING_INT_ALIST *alist;
|
||||
int flags;
|
||||
{
|
||||
register int i;
|
||||
|
||||
for (i = 0; alist[i].word; i++)
|
||||
{
|
||||
if (alist[i].token == token)
|
||||
return (savestring (alist[i].word));
|
||||
}
|
||||
return ((char *)NULL);
|
||||
}
|
||||
|
||||
int
|
||||
find_index_in_alist (string, alist, flags)
|
||||
char *string;
|
||||
STRING_INT_ALIST *alist;
|
||||
int flags;
|
||||
{
|
||||
register int i;
|
||||
int r;
|
||||
|
||||
for (i = r = 0; alist[i].word; i++)
|
||||
{
|
||||
#if defined (EXTENDED_GLOB)
|
||||
if (flags)
|
||||
r = strmatch (alist[i].word, string, FNM_EXTMATCH) != FNM_NOMATCH;
|
||||
else
|
||||
#endif
|
||||
r = STREQ (string, alist[i].word);
|
||||
|
||||
if (r)
|
||||
return (i);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* String Management Functions */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* Cons a new string from STRING starting at START and ending at END,
|
||||
not including END. */
|
||||
char *
|
||||
substring (string, start, end)
|
||||
const char *string;
|
||||
int start, end;
|
||||
{
|
||||
register int len;
|
||||
register char *result;
|
||||
|
||||
len = end - start;
|
||||
result = (char *)xmalloc (len + 1);
|
||||
strncpy (result, string + start, len);
|
||||
result[len] = '\0';
|
||||
return (result);
|
||||
}
|
||||
|
||||
/* Replace occurrences of PAT with REP in STRING. If GLOBAL is non-zero,
|
||||
replace all occurrences, otherwise replace only the first.
|
||||
This returns a new string; the caller should free it. */
|
||||
char *
|
||||
strsub (string, pat, rep, global)
|
||||
char *string, *pat, *rep;
|
||||
int global;
|
||||
{
|
||||
int patlen, replen, templen, tempsize, repl, i;
|
||||
char *temp, *r;
|
||||
|
||||
patlen = strlen (pat);
|
||||
replen = strlen (rep);
|
||||
for (temp = (char *)NULL, i = templen = tempsize = 0, repl = 1; string[i]; )
|
||||
{
|
||||
if (repl && STREQN (string + i, pat, patlen))
|
||||
{
|
||||
if (replen)
|
||||
RESIZE_MALLOCED_BUFFER (temp, templen, replen, tempsize, (replen * 2));
|
||||
|
||||
for (r = rep; *r; )
|
||||
temp[templen++] = *r++;
|
||||
|
||||
i += patlen ? patlen : 1; /* avoid infinite recursion */
|
||||
repl = global != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
RESIZE_MALLOCED_BUFFER (temp, templen, 1, tempsize, 16);
|
||||
temp[templen++] = string[i++];
|
||||
}
|
||||
}
|
||||
if (temp)
|
||||
temp[templen] = 0;
|
||||
else
|
||||
temp = savestring (string);
|
||||
return (temp);
|
||||
}
|
||||
|
||||
/* Replace all instances of C in STRING with TEXT. TEXT may be empty or
|
||||
NULL. If DO_GLOB is non-zero, we quote the replacement text for
|
||||
globbing. Backslash may be used to quote C. */
|
||||
char *
|
||||
strcreplace (string, c, text, do_glob)
|
||||
char *string;
|
||||
int c;
|
||||
char *text;
|
||||
int do_glob;
|
||||
{
|
||||
char *ret, *p, *r, *t;
|
||||
int len, rlen, ind, tlen;
|
||||
|
||||
len = STRLEN (text);
|
||||
rlen = len + strlen (string) + 2;
|
||||
ret = (char *)xmalloc (rlen);
|
||||
|
||||
for (p = string, r = ret; p && *p; )
|
||||
{
|
||||
if (*p == c)
|
||||
{
|
||||
if (len)
|
||||
{
|
||||
ind = r - ret;
|
||||
if (do_glob && (glob_pattern_p (text) || strchr (text, '\\')))
|
||||
{
|
||||
t = quote_globbing_chars (text);
|
||||
tlen = strlen (t);
|
||||
RESIZE_MALLOCED_BUFFER (ret, ind, tlen, rlen, rlen);
|
||||
r = ret + ind; /* in case reallocated */
|
||||
strcpy (r, t);
|
||||
r += tlen;
|
||||
free (t);
|
||||
}
|
||||
else
|
||||
{
|
||||
RESIZE_MALLOCED_BUFFER (ret, ind, len, rlen, rlen);
|
||||
r = ret + ind; /* in case reallocated */
|
||||
strcpy (r, text);
|
||||
r += len;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*p == '\\' && p[1] == c)
|
||||
p++;
|
||||
|
||||
ind = r - ret;
|
||||
RESIZE_MALLOCED_BUFFER (ret, ind, 2, rlen, rlen);
|
||||
r = ret + ind; /* in case reallocated */
|
||||
*r++ = *p++;
|
||||
}
|
||||
*r = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_UNUSED
|
||||
/* Remove all leading whitespace from STRING. This includes
|
||||
newlines. STRING should be terminated with a zero. */
|
||||
void
|
||||
strip_leading (string)
|
||||
char *string;
|
||||
{
|
||||
char *start = string;
|
||||
|
||||
while (*string && (whitespace (*string) || *string == '\n'))
|
||||
string++;
|
||||
|
||||
if (string != start)
|
||||
{
|
||||
int len = strlen (string);
|
||||
FASTCOPY (string, start, len);
|
||||
start[len] = '\0';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Remove all trailing whitespace from STRING. This includes
|
||||
newlines. If NEWLINES_ONLY is non-zero, only trailing newlines
|
||||
are removed. STRING should be terminated with a zero. */
|
||||
void
|
||||
strip_trailing (string, len, newlines_only)
|
||||
char *string;
|
||||
int len;
|
||||
int newlines_only;
|
||||
{
|
||||
while (len >= 0)
|
||||
{
|
||||
if ((newlines_only && string[len] == '\n') ||
|
||||
(!newlines_only && whitespace (string[len])))
|
||||
len--;
|
||||
else
|
||||
break;
|
||||
}
|
||||
string[len + 1] = '\0';
|
||||
}
|
||||
|
||||
/* A wrapper for bcopy that can be prototyped in general.h */
|
||||
void
|
||||
xbcopy (s, d, n)
|
||||
char *s, *d;
|
||||
int n;
|
||||
{
|
||||
FASTCOPY (s, d, n);
|
||||
}
|
||||
@@ -1772,6 +1772,7 @@ skip_to_delim (string, start, delims, flags)
|
||||
if (string[si] == '\0')
|
||||
CQ_RETURN(si);
|
||||
temp = extract_process_subst (string, (c == '<') ? "<(" : ">(", &si);
|
||||
free (temp); /* no SX_ALLOC here */
|
||||
i = si;
|
||||
if (string[i] == '\0')
|
||||
break;
|
||||
@@ -2070,6 +2071,8 @@ split_at_delims (string, slen, delims, sentinel, flags, nwp, cwp)
|
||||
if (cwp)
|
||||
*cwp = cw;
|
||||
|
||||
FREE (d2);
|
||||
|
||||
return (REVERSE_LIST (ret, WORD_LIST *));
|
||||
}
|
||||
#endif /* READLINE */
|
||||
@@ -5524,9 +5527,11 @@ array_length_reference (s)
|
||||
if (akey == 0 || *akey == 0)
|
||||
{
|
||||
err_badarraysub (t);
|
||||
FREE (akey);
|
||||
return (-1);
|
||||
}
|
||||
t = assoc_reference (assoc_cell (var), akey);
|
||||
free (akey);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -5676,6 +5681,7 @@ parameter_brace_expand_word (name, var_is_special, quoted, pflags, indp)
|
||||
#if defined (ARRAY_VARS)
|
||||
else if (valid_array_reference (name))
|
||||
{
|
||||
/* XXX - does this leak if name[@] or name[*]? */
|
||||
temp = array_value (name, quoted, 0, &atype, &ind);
|
||||
if (atype == 0 && temp)
|
||||
{
|
||||
@@ -6327,7 +6333,11 @@ parameter_brace_substring (varname, value, ind, substr, quoted, flags)
|
||||
r = verify_substring_values (v, val, substr, vtype, &e1, &e2);
|
||||
this_command_name = oname;
|
||||
if (r <= 0)
|
||||
return ((r == 0) ? &expand_param_error : (char *)NULL);
|
||||
{
|
||||
if (vtype == VT_VARIABLE)
|
||||
FREE (val);
|
||||
return ((r == 0) ? &expand_param_error : (char *)NULL);
|
||||
}
|
||||
|
||||
switch (vtype)
|
||||
{
|
||||
@@ -6956,9 +6966,8 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
|
||||
(sindex == t_index - 1 && string[sindex] == '!' && VALID_INDIR_PARAM (string[t_index])))
|
||||
{
|
||||
t_index++;
|
||||
free (name);
|
||||
temp1 = string_extract (string, &t_index, "#%:-=?+/}", 0);
|
||||
name = (char *)xmalloc (3 + (strlen (temp1)));
|
||||
name = (char *)xrealloc (name, 3 + (strlen (temp1)));
|
||||
*name = string[sindex];
|
||||
if (string[sindex] == '!')
|
||||
{
|
||||
@@ -7106,6 +7115,8 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
|
||||
free (temp1);
|
||||
*indexp = sindex;
|
||||
|
||||
free (name);
|
||||
|
||||
ret = alloc_word_desc ();
|
||||
ret->word = temp;
|
||||
return ret;
|
||||
@@ -7236,8 +7247,6 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta
|
||||
else if (temp1 == &expand_param_fatal)
|
||||
return (&expand_wdesc_fatal);
|
||||
|
||||
ret = alloc_word_desc ();
|
||||
ret->word = temp1;
|
||||
ret = alloc_word_desc ();
|
||||
ret->word = temp1;
|
||||
if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
|
||||
|
||||
@@ -1058,7 +1058,7 @@ restore_original_signals ()
|
||||
}
|
||||
|
||||
/* If a trap handler exists for signal SIG, then call it; otherwise just
|
||||
return failure. */
|
||||
return failure. Returns 1 if it called the trap handler. */
|
||||
int
|
||||
maybe_call_trap_handler (sig)
|
||||
int sig;
|
||||
|
||||
+1
-1
@@ -4071,7 +4071,7 @@ push_dollar_vars ()
|
||||
{
|
||||
dollar_arg_stack = (WORD_LIST **)
|
||||
xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
|
||||
* sizeof (WORD_LIST **));
|
||||
* sizeof (WORD_LIST *));
|
||||
}
|
||||
dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
|
||||
dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
|
||||
|
||||
@@ -4650,7 +4650,6 @@ void
|
||||
sv_strict_posix (name)
|
||||
char *name;
|
||||
{
|
||||
itrace("sv_strict_posix: entry");
|
||||
SET_INT_VAR (name, posixly_correct);
|
||||
posix_initialize (posixly_correct);
|
||||
#if defined (READLINE)
|
||||
@@ -4658,7 +4657,6 @@ itrace("sv_strict_posix: entry");
|
||||
posix_readline_initialize (posixly_correct);
|
||||
#endif /* READLINE */
|
||||
set_shellopts (); /* make sure `posix' is/is not in $SHELLOPTS */
|
||||
itrace("sv_strict_posix: return");
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user