-/* Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2007 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
Bash is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 1, or (at your option) any later
+ Software Foundation; either version 2, or (at your option) any later
version.
Bash is distributed in the hope that it will be useful, but WITHOUT ANY
You should have received a copy of the GNU General Public License along
with Bash; see the file COPYING. If not, write to the Free Software
- Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+ Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
#include <config.h>
#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
# include <unistd.h>
#endif
#include <stdio.h>
+#include <chartypes.h>
#include "../bashtypes.h"
-#include "../posixstat.h"
+#include "posixstat.h"
#include <signal.h>
+#include <errno.h>
+
#if defined (PREFER_STDARG)
# include <stdarg.h>
#else
-# if defined (PREFER_VARARGS)
-# include <varargs.h>
-# endif
+# include <varargs.h>
#endif
#include "../bashansi.h"
+#include "../bashintl.h"
#include "../shell.h"
-#include "../maxpath.h"
+#include "maxpath.h"
#include "../flags.h"
#include "../jobs.h"
#include "../builtins.h"
# include "../bashhist.h"
#endif
-extern int no_symbolic_links, interactive, interactive_shell;
-extern int indirection_level, startup_state, subshell_environment;
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+extern int indirection_level, subshell_environment;
extern int line_number;
extern int last_command_exit_value;
extern int running_trap;
-extern int variable_context;
extern int posixly_correct;
extern char *this_command_name, *shell_name;
-extern COMMAND *global_command;
extern char *bash_getcwd_errstr;
/* Used by some builtins and the mainline code. */
-Function *last_shell_builtin = (Function *)NULL;
-Function *this_shell_builtin = (Function *)NULL;
+sh_builtin_func_t *last_shell_builtin = (sh_builtin_func_t *)NULL;
+sh_builtin_func_t *this_shell_builtin = (sh_builtin_func_t *)NULL;
/* **************************************************************** */
/* */
/* This is a lot like report_error (), but it is for shell builtins
instead of shell control structures, and it won't ever exit the
shell. */
-#if defined (USE_VARARGS)
void
#if defined (PREFER_STDARG)
builtin_error (const char *format, ...)
name = get_name_for_error ();
fprintf (stderr, "%s: ", name);
+ if (interactive_shell == 0)
+ fprintf (stderr, "line %d: ", executing_line_number ());
+
if (this_command_name && *this_command_name)
fprintf (stderr, "%s: ", this_command_name);
-#if defined (PREFER_STDARG)
- va_start (args, format);
-#else
- va_start (args);
-#endif
+ SH_VA_START (args, format);
vfprintf (stderr, format, args);
va_end (args);
fprintf (stderr, "\n");
}
-#else /* !USE_VARARGS */
-void
-builtin_error (format, arg1, arg2, arg3, arg4, arg5)
- char *format, *arg1, *arg2, *arg3, *arg4, *arg5;
-{
- if (this_command_name && *this_command_name)
- fprintf (stderr, "%s: ", this_command_name);
-
- fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
- fprintf (stderr, "\n");
- fflush (stderr);
-}
-#endif /* !USE_VARARGS */
/* Print a usage summary for the currently-executing builtin command. */
void
{
if (list)
{
- builtin_error ("too many arguments");
+ builtin_error (_("too many arguments"));
+ top_level_cleanup ();
jump_to_top_level (DISCARD);
}
}
-/* Function called when one of the builtin commands detects a bad
- option. */
-void
-bad_option (s)
- char *s;
-{
- builtin_error ("unknown option: %s", s);
-}
-
/* Check that no options were given to the currently-executing builtin,
and return 0 if there were options. */
int
return (0);
}
+void
+sh_needarg (s)
+ char *s;
+{
+ builtin_error (_("%s: option requires an argument"), s);
+}
+
+void
+sh_neednumarg (s)
+ char *s;
+{
+ builtin_error (_("%s: numeric argument required"), s);
+}
+
+void
+sh_notfound (s)
+ char *s;
+{
+ builtin_error (_("%s: not found"), s);
+}
+
+/* Function called when one of the builtin commands detects an invalid
+ option. */
+void
+sh_invalidopt (s)
+ char *s;
+{
+ builtin_error (_("%s: invalid option"), s);
+}
+
+void
+sh_invalidoptname (s)
+ char *s;
+{
+ builtin_error (_("%s: invalid option name"), s);
+}
+
+void
+sh_invalidid (s)
+ char *s;
+{
+ builtin_error (_("`%s': not a valid identifier"), s);
+}
+
+void
+sh_invalidnum (s)
+ char *s;
+{
+ builtin_error (_("%s: invalid number"), s);
+}
+
+void
+sh_invalidsig (s)
+ char *s;
+{
+ builtin_error (_("%s: invalid signal specification"), s);
+}
+
+void
+sh_badpid (s)
+ char *s;
+{
+ builtin_error (_("`%s': not a pid or valid job spec"), s);
+}
+
+void
+sh_readonly (s)
+ const char *s;
+{
+ builtin_error (_("%s: readonly variable"), s);
+}
+
+void
+sh_erange (s, desc)
+ char *s, *desc;
+{
+ if (s)
+ builtin_error (_("%s: %s out of range"), s, desc ? desc : _("argument"));
+ else
+ builtin_error (_("%s out of range"), desc ? desc : _("argument"));
+}
+
+#if defined (JOB_CONTROL)
+void
+sh_badjob (s)
+ char *s;
+{
+ builtin_error (_("%s: no such job"), s);
+}
+
+void
+sh_nojobs (s)
+ char *s;
+{
+ if (s)
+ builtin_error (_("%s: no job control"), s);
+ else
+ builtin_error (_("no job control"));
+}
+#endif
+
+#if defined (RESTRICTED_SHELL)
+void
+sh_restricted (s)
+ char *s;
+{
+ if (s)
+ builtin_error (_("%s: restricted"), s);
+ else
+ builtin_error (_("restricted"));
+}
+#endif
+
+void
+sh_notbuiltin (s)
+ char *s;
+{
+ builtin_error (_("%s: not a shell builtin"), s);
+}
+
+void
+sh_wrerror ()
+{
+ builtin_error (_("write error: %s"), strerror (errno));
+}
+
/* **************************************************************** */
/* */
/* Shell positional parameter manipulation */
{
char **argv;
- argv = word_list_to_argv (list, 0, 1, ip);
+ argv = strvec_from_word_list (list, 0, 1, ip);
argv[0] = this_command_name;
return argv;
}
set_dollar_vars_changed ();
}
-/* **************************************************************** */
-/* */
-/* Pushing and Popping variable contexts */
-/* */
-/* **************************************************************** */
-
-static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
-static int dollar_arg_stack_slots;
-static int dollar_arg_stack_index;
-
-void
-push_context ()
-{
- push_dollar_vars ();
- variable_context++;
-}
-
-void
-pop_context ()
-{
- pop_dollar_vars ();
- kill_all_local_variables ();
- variable_context--;
-}
-
-/* Save the existing positional parameters on a stack. */
-void
-push_dollar_vars ()
-{
- if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
- {
- dollar_arg_stack = (WORD_LIST **)
- xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
- * sizeof (WORD_LIST **));
- }
- dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
-}
-
-/* Restore the positional parameters from our stack. */
-void
-pop_dollar_vars ()
-{
- if (!dollar_arg_stack || dollar_arg_stack_index == 0)
- return;
-
- remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
- dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
-}
-
-void
-dispose_saved_dollar_vars ()
-{
- if (!dollar_arg_stack || dollar_arg_stack_index == 0)
- return;
-
- dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
-}
-
static int changed_dollar_vars;
/* Have the dollar variables been reset to new values since we last
void
set_dollar_vars_changed ()
{
- changed_dollar_vars = 1;
+ if (variable_context)
+ changed_dollar_vars |= ARGS_FUNC;
+ else if (this_shell_builtin == set_builtin)
+ changed_dollar_vars |= ARGS_SETBLTIN;
+ else
+ changed_dollar_vars |= ARGS_INVOC;
}
/* **************************************************************** */
/* */
-/* Validating numeric input and arguments */
+/* Validating numeric input and arguments */
/* */
/* **************************************************************** */
follow. If FATAL is true, call throw_to_top_level, which exits the
shell; if not, call jump_to_top_level (DISCARD), which aborts the
current command. */
-int
+intmax_t
get_numeric_arg (list, fatal)
WORD_LIST *list;
int fatal;
{
- long count = 1;
+ intmax_t count = 1;
+
+ if (list && list->word && ISOPTION (list->word->word, '-'))
+ list = list->next;
if (list)
{
register char *arg;
arg = list->word->word;
- if (!arg || (legal_number (arg, &count) == 0))
+ if (arg == 0 || (legal_number (arg, &count) == 0))
{
- builtin_error ("bad non-numeric arg `%s'", list->word->word);
+ sh_neednumarg (list->word->word);
if (fatal)
throw_to_top_level ();
else
- jump_to_top_level (DISCARD);
+ {
+ top_level_cleanup ();
+ jump_to_top_level (DISCARD);
+ }
}
no_args (list->next);
}
+
return (count);
}
+/* Get an eight-bit status value from LIST */
+int
+get_exitstat (list)
+ WORD_LIST *list;
+{
+ int status;
+ intmax_t sval;
+ char *arg;
+
+ if (list && list->word && ISOPTION (list->word->word, '-'))
+ list = list->next;
+
+ if (list == 0)
+ return (last_command_exit_value);
+
+ arg = list->word->word;
+ if (arg == 0 || legal_number (arg, &sval) == 0)
+ {
+ sh_neednumarg (list->word->word ? list->word->word : "`'");
+ return 255;
+ }
+ no_args (list->next);
+
+ status = sval & 255;
+ return status;
+}
+
/* Return the octal number parsed from STRING, or -1 to indicate
that the string contained a bad number. */
int
int result, digits;
result = digits = 0;
- while (*string && *string >= '0' && *string < '8')
+ while (*string && ISOCTAL (*string))
{
digits++;
- result = (result * 8) + *string++ - '0';
+ result = (result * 8) + (*string++ - '0');
+ if (result > 0777)
+ return -1;
}
- if (!digits || result > 0777 || *string)
+ if (digits == 0 || *string)
result = -1;
return (result);
get_working_directory (for_whom)
char *for_whom;
{
- char *directory;
-
if (no_symbolic_links)
{
- if (the_current_working_directory)
- free (the_current_working_directory);
-
+ FREE (the_current_working_directory);
the_current_working_directory = (char *)NULL;
}
if (the_current_working_directory == 0)
{
- the_current_working_directory = xmalloc (PATH_MAX);
- the_current_working_directory[0] = '\0';
- directory = getcwd (the_current_working_directory, PATH_MAX);
- if (directory == 0)
+#if defined (GETCWD_BROKEN)
+ the_current_working_directory = getcwd (0, PATH_MAX);
+#else
+ the_current_working_directory = getcwd (0, 0);
+#endif
+ if (the_current_working_directory == 0)
{
- fprintf (stderr, "%s: could not get current directory: %s\n",
+ fprintf (stderr, _("%s: error retrieving current directory: %s: %s\n"),
(for_whom && *for_whom) ? for_whom : get_name_for_error (),
- the_current_working_directory[0]
- ? the_current_working_directory
- : bash_getcwd_errstr);
-
- free (the_current_working_directory);
- the_current_working_directory = (char *)NULL;
+ _(bash_getcwd_errstr), strerror (errno));
return (char *)NULL;
}
}
/* **************************************************************** */
#if defined (JOB_CONTROL)
+int
+get_job_by_name (name, flags)
+ const char *name;
+ int flags;
+{
+ register int i, wl, cl, match, job;
+ register PROCESS *p;
+ register JOB *j;
+
+ job = NO_JOB;
+ wl = strlen (name);
+ for (i = js.j_jobslots - 1; i >= 0; i--)
+ {
+ j = get_job_by_jid (i);
+ if (j == 0 || ((flags & JM_STOPPED) && J_JOBSTATE(j) != JSTOPPED))
+ continue;
+
+ p = j->pipe;
+ do
+ {
+ if (flags & JM_EXACT)
+ {
+ cl = strlen (p->command);
+ match = STREQN (p->command, name, cl);
+ }
+ else if (flags & JM_SUBSTRING)
+ match = strindex (p->command, name) != (char *)0;
+ else
+ match = STREQN (p->command, name, wl);
+
+ if (match == 0)
+ {
+ p = p->next;
+ continue;
+ }
+ else if (flags & JM_FIRSTMATCH)
+ return i; /* return first match */
+ else if (job != NO_JOB)
+ {
+ if (this_shell_builtin)
+ builtin_error (_("%s: ambiguous job spec"), name);
+ else
+ report_error (_("%s: ambiguous job spec"), name);
+ return (DUP_JOB);
+ }
+ else
+ job = i;
+ }
+ while (p != j->pipe);
+ }
+
+ return (job);
+}
+
/* Return the job spec found in LIST. */
int
get_job_spec (list)
WORD_LIST *list;
{
register char *word;
- int job, substring;
+ int job, jflags;
if (list == 0)
- return (current_job);
+ return (js.j_current);
word = list->word->word;
if (*word == '\0')
- return (current_job);
+ return (NO_JOB);
if (*word == '%')
word++;
- if (digit (*word) && all_digits (word))
+ if (DIGIT (*word) && all_digits (word))
{
job = atoi (word);
- return (job - 1);
+ return (job > js.j_jobslots ? NO_JOB : job - 1);
}
- substring = 0;
+ jflags = 0;
switch (*word)
{
case 0:
case '%':
case '+':
- return (current_job);
+ return (js.j_current);
case '-':
- return (previous_job);
+ return (js.j_previous);
case '?': /* Substring search requested. */
- substring++;
+ jflags |= JM_SUBSTRING;
word++;
/* FALLTHROUGH */
default:
- {
- register int i, wl;
-
- job = NO_JOB;
- wl = strlen (word);
- for (i = 0; i < job_slots; i++)
- {
- if (jobs[i])
- {
- register PROCESS *p;
- p = jobs[i]->pipe;
- do
- {
- if ((substring && strindex (p->command, word)) ||
- (STREQN (p->command, word, wl)))
- if (job != NO_JOB)
- {
- builtin_error ("ambigious job spec: %s", word);
- return (DUP_JOB);
- }
- else
- job = i;
-
- p = p->next;
- }
- while (p != jobs[i]->pipe);
- }
- }
- return (job);
- }
+ return get_job_by_name (word, jflags);
}
}
#endif /* JOB_CONTROL */
+/*
+ * NOTE: `kill' calls this function with forcecols == 0
+ */
int
display_signal_list (list, forcecols)
WORD_LIST *list;
{
register int i, column;
char *name;
- int result;
- long signum;
+ int result, signum, dflags;
+ intmax_t lsignum;
result = EXECUTION_SUCCESS;
if (!list)
continue;
if (posixly_correct && !forcecols)
- printf ("%s%s", name, (i == NSIG - 1) ? "" : " ");
+ {
+ /* This is for the kill builtin. POSIX.2 says the signal names
+ are displayed without the `SIG' prefix. */
+ if (STREQN (name, "SIG", 3))
+ name += 3;
+ printf ("%s%s", name, (i == NSIG - 1) ? "" : " ");
+ }
else
{
printf ("%2d) %s", i, name);
/* List individual signal names or numbers. */
while (list)
{
- if (legal_number (list->word->word, &signum))
+ if (legal_number (list->word->word, &lsignum))
{
/* This is specified by Posix.2 so that exit statuses can be
mapped into signal numbers. */
- if (signum > 128)
- signum -= 128;
- if (signum < 0 || signum >= NSIG)
+ if (lsignum > 128)
+ lsignum -= 128;
+ if (lsignum < 0 || lsignum >= NSIG)
{
- builtin_error ("bad signal number: %s", list->word->word);
+ sh_invalidsig (list->word->word);
result = EXECUTION_FAILURE;
list = list->next;
continue;
}
+ signum = lsignum;
name = signal_name (signum);
if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7))
{
}
else
{
- signum = decode_signal (list->word->word);
+ dflags = DSIG_NOCASE;
+ if (posixly_correct == 0 || this_shell_builtin != kill_builtin)
+ dflags |= DSIG_SIGPREFIX;
+ signum = decode_signal (list->word->word, dflags);
if (signum == NO_SIG)
{
- builtin_error ("%s: not a signal specification", list->word->word);
+ sh_invalidsig (list->word->word);
result = EXECUTION_FAILURE;
list = list->next;
continue;
}
- printf ("%ld\n", signum);
+ printf ("%d\n", signum);
}
list = list->next;
}
}
/* Return the pointer to the function implementing builtin command NAME. */
-Function *
+sh_builtin_func_t *
find_shell_builtin (name)
char *name;
{
current_builtin = builtin_address_internal (name, 0);
- return (current_builtin ? current_builtin->function : (Function *)NULL);
+ return (current_builtin ? current_builtin->function : (sh_builtin_func_t *)NULL);
}
/* Return the address of builtin with NAME, whether it is enabled or not. */
-Function *
+sh_builtin_func_t *
builtin_address (name)
char *name;
{
current_builtin = builtin_address_internal (name, 1);
- return (current_builtin ? current_builtin->function : (Function *)NULL);
+ return (current_builtin ? current_builtin->function : (sh_builtin_func_t *)NULL);
}
/* Return the function implementing the builtin NAME, but only if it is a
POSIX.2 special builtin. */
-Function *
+sh_builtin_func_t *
find_special_builtin (name)
char *name;
{
current_builtin = builtin_address_internal (name, 0);
return ((current_builtin && (current_builtin->flags & SPECIAL_BUILTIN)) ?
current_builtin->function :
- (Function *)NULL);
+ (sh_builtin_func_t *)NULL);
}
static int
initialize_shell_builtins ()
{
qsort (shell_builtins, num_shell_builtins, sizeof (struct builtin),
- shell_builtin_compare);
-}
-
-/* **************************************************************** */
-/* */
-/* Functions for quoting strings to be re-read as input */
-/* */
-/* **************************************************************** */
-
-/* Return a new string which is the single-quoted version of STRING.
- Used by alias and trap, among others. */
-char *
-single_quote (string)
- char *string;
-{
- register int c;
- char *result, *r, *s;
-
- result = (char *)xmalloc (3 + (4 * strlen (string)));
- r = result;
- *r++ = '\'';
-
- for (s = string; s && (c = *s); s++)
- {
- *r++ = c;
-
- if (c == '\'')
- {
- *r++ = '\\'; /* insert escaped single quote */
- *r++ = '\'';
- *r++ = '\''; /* start new quoted string */
- }
- }
-
- *r++ = '\'';
- *r = '\0';
-
- return (result);
-}
-
-/* Quote STRING using double quotes. Return a new string. */
-char *
-double_quote (string)
- char *string;
-{
- register int c;
- char *result, *r, *s;
-
- result = (char *)xmalloc (3 + (2 * strlen (string)));
- r = result;
- *r++ = '"';
-
- for (s = string; s && (c = *s); s++)
- {
- switch (c)
- {
- case '"':
- case '$':
- case '`':
- case '\\':
- *r++ = '\\';
- default:
- *r++ = c;
- break;
- }
- }
-
- *r++ = '"';
- *r = '\0';
-
- return (result);
-}
-
-/* Quote special characters in STRING using backslashes. Return a new
- string. */
-char *
-backslash_quote (string)
- char *string;
-{
- int c;
- char *result, *r, *s;
-
- result = xmalloc (2 * strlen (string) + 1);
-
- for (r = result, s = string; s && (c = *s); s++)
- {
- switch (c)
- {
- case ' ': case '\t': case '\n': /* IFS white space */
- case '\'': case '"': case '\\': /* quoting chars */
- case '|': case '&': case ';': /* shell metacharacters */
- case '(': case ')': case '<': case '>':
- case '!': case '{': case '}': /* reserved words */
- case '*': case '[': case '?': case ']': /* globbing chars */
- case '^':
- case '$': case '`': /* expansion chars */
- *r++ = '\\';
- *r++ = c;
- break;
- case '#': /* comment char */
-#if 0
- case '~': /* tilde expansion */
-#endif
- if (s == string)
- *r++ = '\\';
- /* FALLTHROUGH */
- default:
- *r++ = c;
- break;
- }
- }
-
- *r = '\0';
- return (result);
-}
-
-int
-contains_shell_metas (string)
- char *string;
-{
- char *s;
-
- for (s = string; s && *s; s++)
- {
- switch (*s)
- {
- case ' ': case '\t': case '\n': /* IFS white space */
- case '\'': case '"': case '\\': /* quoting chars */
- case '|': case '&': case ';': /* shell metacharacters */
- case '(': case ')': case '<': case '>':
- case '!': case '{': case '}': /* reserved words */
- case '*': case '[': case '?': case ']': /* globbing chars */
- case '^':
- case '$': case '`': /* expansion chars */
- return (1);
- case '#':
- if (s == string) /* comment char */
- return (1);
- /* FALLTHROUGH */
- default:
- break;
- }
- }
-
- return (0);
+ (QSFUNC *)shell_builtin_compare);
}