diff options
Diffstat (limited to 'builtins/common.c')
-rw-r--r-- | builtins/common.c | 936 |
1 files changed, 502 insertions, 434 deletions
diff --git a/builtins/common.c b/builtins/common.c index ff940b56..2936b88a 100644 --- a/builtins/common.c +++ b/builtins/common.c @@ -16,27 +16,37 @@ with Bash; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + #include <stdio.h> #include <sys/types.h> #include "../posixstat.h" -#if defined (HAVE_VFPRINTF) -#include <varargs.h> -#endif /* VFPRINTF */ +#include <signal.h> + +#if defined (PREFER_STDARG) +# include <stdarg.h> +#else +# if defined (PREFER_VARARGS) +# include <varargs.h> +# endif +#endif -#if defined (HAVE_STRING_H) -# include <string.h> -#else /* !HAVE_STRING_H */ -# include <strings.h> -#endif /* !HAVE_STRING_H */ +#include "../bashansi.h" #include "../shell.h" -#include "../unwind_prot.h" #include "../maxpath.h" +#include "../flags.h" #include "../jobs.h" #include "../builtins.h" #include "../input.h" #include "../execute_cmd.h" +#include "../trap.h" #include "hashcom.h" +#include "bashgetopt.h" #include "common.h" #include <tilde/tilde.h> @@ -45,91 +55,57 @@ #endif extern int no_symbolic_links, interactive, interactive_shell; -extern int indirection_level, startup_state; +extern int indirection_level, startup_state, subshell_environment; +extern int line_number; extern int last_command_exit_value; -extern int hashing_disabled; +extern int running_trap; +extern int hashing_enabled; extern int variable_context; +extern int posixly_correct; extern char *this_command_name, *shell_name; extern COMMAND *global_command; extern HASH_TABLE *hashed_filenames; +extern char *bash_getcwd_errstr; -/* Read a numeric arg for this_command_name, the name of the shell builtin - that wants it. LIST is the word list that the arg is to come from. */ -int -get_numeric_arg (list) - WORD_LIST *list; -{ - int count = 1; - - if (list) - { - register char *arg; - int sign = 1; - - arg = list->word->word; - if (!arg) - goto bad_number; - - /* Skip optional leading white space. */ - while (whitespace (*arg)) - arg++; - - if (!*arg) - goto bad_number; - - /* We allow leading `-' or `+'. */ - if (*arg == '-' || *arg == '+') - { - if (!digit (arg[1])) - goto bad_number; - - if (*arg == '-') - sign = -1; - - arg++; - } - - for (count = 0; digit (*arg); arg++) - count = (count * 10) + digit_value (*arg); - - /* Skip trailing whitespace, if any. */ - while (whitespace (*arg)) - arg++; - - if (!*arg) - count = count * sign; - else - { - bad_number: - builtin_error ("bad non-numeric arg `%s'", list->word->word); - throw_to_top_level (); - } - no_args (list->next); - } - return (count); -} +/* **************************************************************** */ +/* */ +/* Error reporting, usage, and option processing */ +/* */ +/* **************************************************************** */ /* 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 (HAVE_VFPRINTF) +#if defined (USE_VARARGS) void -builtin_error (va_alist) +#if defined (PREFER_STDARG) +builtin_error (const char *format, ...) +#else +builtin_error (format, va_alist) + const char *format; va_dcl +#endif { - char *format; va_list args; + char *name; + + name = get_name_for_error (); + fprintf (stderr, "%s: ", name); 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); - format = va_arg (args, char *); +#endif + vfprintf (stderr, format, args); va_end (args); fprintf (stderr, "\n"); } -#else /* !HAVE_VFPRINTF */ +#else /* !USE_VARARGS */ void builtin_error (format, arg1, arg2, arg3, arg4, arg5) char *format, *arg1, *arg2, *arg3, *arg4, *arg5; @@ -141,7 +117,75 @@ builtin_error (format, arg1, arg2, arg3, arg4, arg5) fprintf (stderr, "\n"); fflush (stderr); } -#endif /* !HAVE_VFPRINTF */ +#endif /* !USE_VARARGS */ + +/* Print a usage summary for the currently-executing builtin command. */ +void +builtin_usage () +{ + if (this_command_name && *this_command_name) + fprintf (stderr, "%s: usage: ", this_command_name); + fprintf (stderr, "%s\n", current_builtin->short_doc); + fflush (stderr); +} + +/* Return if LIST is NULL else barf and jump to top_level. Used by some + builtins that do not accept arguments. */ +void +no_args (list) + WORD_LIST *list; +{ + if (list) + { + builtin_error ("too many arguments"); + 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 +no_options (list) + WORD_LIST *list; +{ + reset_internal_getopt (); + if (internal_getopt (list, "") != -1) + { + builtin_usage (); + return (1); + } + return (0); +} + +/* **************************************************************** */ +/* */ +/* Shell positional parameter manipulation */ +/* */ +/* **************************************************************** */ + +/* Convert a WORD_LIST into a C-style argv. Return the number of elements + in the list in *IP, if IP is non-null. A convenience function for + loadable builtins; also used by `test'. */ +char ** +make_builtin_argv (list, ip) + WORD_LIST *list; + int *ip; +{ + char **argv; + + argv = word_list_to_argv (list, 0, 1, ip); + argv[0] = this_command_name; + return argv; +} /* Remember LIST in $0 ... $9, and REST_OF_ARGS. If DESTRUCTIVE is non-zero, then discard whatever the existing arguments are, else @@ -155,7 +199,7 @@ remember_args (list, destructive) for (i = 1; i < 10; i++) { - if (destructive && dollar_vars[i]) + if ((destructive || list) && dollar_vars[i]) { free (dollar_vars[i]); dollar_vars[i] = (char *)NULL; @@ -163,9 +207,6 @@ remember_args (list, destructive) if (list) { - if (!destructive && dollar_vars[i]) - free (dollar_vars[i]); - dollar_vars[i] = savestring (list->word->word); list = list->next; } @@ -184,142 +225,15 @@ remember_args (list, destructive) set_dollar_vars_changed (); } -/* Return if LIST is NULL else barf and jump to top_level. */ -void -no_args (list) - WORD_LIST *list; -{ - if (list) - { - builtin_error ("extra arguments"); - longjmp (top_level, DISCARD); - } -} - -/* Return the octal number parsed from STRING, or -1 to indicate - that the string contained a bad number. */ -int -read_octal (string) - char *string; -{ - int result = 0; - int digits = 0; - - while (*string && *string >= '0' && *string < '8') - { - digits++; - result = (result * 8) + *string++ - '0'; - } - - if (!digits || result > 0777 || *string) - result = -1; - - return (result); -} - -/* Temporary static. */ -static char *dotted_filename = (char *)NULL; - -/* Return the full pathname that FILENAME hashes to. If FILENAME - is hashed, but data->check_dot is non-zero, check ./FILENAME - and return that if it is executable. */ -char * -find_hashed_filename (filename) - char *filename; -{ - register BUCKET_CONTENTS *item; - - if (hashing_disabled) - return ((char *)NULL); - - item = find_hash_item (filename, hashed_filenames); - - if (item) - { - /* If this filename is hashed, but `.' comes before it in the path, - then see if `./filename' is an executable. */ - if (pathdata(item)->check_dot) - { - if (dotted_filename) - free (dotted_filename); - - dotted_filename = (char *)xmalloc (3 + strlen (filename)); - strcpy (dotted_filename, "./"); - strcat (dotted_filename, filename); - - if (executable_file (dotted_filename)) - return (dotted_filename); - - /* Watch out. If this file was hashed to "./filename", and - "./filename" is not executable, then return NULL. */ - - /* Since we already know "./filename" is not executable, what - we're really interested in is whether or not the `path' - portion of the hashed filename is equivalent to the current - directory, but only if it starts with a `.'. (This catches - ./. and so on.) same_file () is in execute_cmd.c; it tests - general Unix file equivalence -- same device and inode. */ - { - char *path = pathdata (item)->path; - - if (*path == '.') - { - int same = 0; - char *tail; - - tail = (char *) strrchr (path, '/'); - - if (tail) - { - *tail = '\0'; - same = same_file - (".", path, (struct stat *)NULL, (struct stat *)NULL); - *tail = '/'; - } - if (same) - return ((char *)NULL); - } - } - } - return (pathdata (item)->path); - } - else - return ((char *)NULL); -} - -/* Remove FILENAME from the table of hashed commands. */ -void -remove_hashed_filename (filename) - char *filename; -{ - register BUCKET_CONTENTS *item; - - if (hashing_disabled) - return; - - item = remove_hash_item (filename, hashed_filenames); - if (item) - { - if (item->data) - { - free (pathdata(item)->path); - free (item->data); - } - if (item->key) - free (item->key); - free (item); - } -} - /* **************************************************************** */ /* */ -/* Pushing and Popping a Context */ +/* Pushing and Popping variable contexts */ /* */ /* **************************************************************** */ static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL; -static int dollar_arg_stack_slots = 0; -static int dollar_arg_stack_index = 0; +static int dollar_arg_stack_slots; +static int dollar_arg_stack_index; void push_context () @@ -346,15 +260,15 @@ push_dollar_vars () 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; + 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) + if (!dollar_arg_stack || dollar_arg_stack_index == 0) return; remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1); @@ -365,17 +279,18 @@ pop_dollar_vars () void dispose_saved_dollar_vars () { - if (!dollar_arg_stack || !dollar_arg_stack_index) + 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 = 0; +static int changed_dollar_vars; /* Have the dollar variables been reset to new values since we last checked? */ +int dollar_vars_changed () { return (changed_dollar_vars); @@ -390,18 +305,141 @@ set_dollar_vars_unchanged () void set_dollar_vars_changed () { - changed_dollar_vars = 1; + changed_dollar_vars = 1; } -/* Function called when one of the builtin commands detects a bad - option. */ -void -bad_option (s) - char *s; +/* **************************************************************** */ +/* */ +/* Validating numeric input and arguments */ +/* */ +/* **************************************************************** */ + +/* Read a numeric arg for this_command_name, the name of the shell builtin + that wants it. LIST is the word list that the arg is to come from. + Accept only the numeric argument; report an error if other arguments + follow. */ +int +get_numeric_arg (list) + WORD_LIST *list; { - builtin_error ("unknown option: %s", s); + long count = 1; + + if (list) + { + register char *arg; + + arg = list->word->word; + if (!arg || (legal_number (arg, &count) == 0)) + { + builtin_error ("bad non-numeric arg `%s'", list->word->word); + throw_to_top_level (); + } + no_args (list->next); + } + return (count); +} + +/* Return the octal number parsed from STRING, or -1 to indicate + that the string contained a bad number. */ +int +read_octal (string) + char *string; +{ + int result, digits; + + result = digits = 0; + while (*string && *string >= '0' && *string < '8') + { + digits++; + result = (result * 8) + *string++ - '0'; + } + + if (!digits || result > 0777 || *string) + result = -1; + + return (result); +} + +/* **************************************************************** */ +/* */ +/* Command name hashing */ +/* */ +/* **************************************************************** */ + +/* Return the full pathname that FILENAME hashes to. If FILENAME + is hashed, but (data->flags & HASH_CHKDOT) is non-zero, check + ./FILENAME and return that if it is executable. */ +char * +find_hashed_filename (filename) + char *filename; +{ + register BUCKET_CONTENTS *item; + char *path, *dotted_filename, *tail; + int same; + + if (hashing_enabled == 0) + return ((char *)NULL); + + item = find_hash_item (filename, hashed_filenames); + + if (item == NULL) + return ((char *)NULL); + + /* If this filename is hashed, but `.' comes before it in the path, + see if ./filename is executable. If the hashed value is not an + absolute pathname, see if ./`hashed-value' exists. */ + path = pathdata(item)->path; + if (pathdata(item)->flags & (HASH_CHKDOT|HASH_RELPATH)) + { + tail = (pathdata(item)->flags & HASH_RELPATH) ? path : filename; + dotted_filename = xmalloc (3 + strlen (tail)); + dotted_filename[0] = '.'; dotted_filename[1] = '/'; + strcpy (dotted_filename + 2, tail); + + if (executable_file (dotted_filename)) + return (dotted_filename); + + free (dotted_filename); + +#if 0 + if (pathdata(item)->flags & HASH_RELPATH) + return ((char *)NULL); +#endif + + /* Watch out. If this file was hashed to "./filename", and + "./filename" is not executable, then return NULL. */ + + /* Since we already know "./filename" is not executable, what + we're really interested in is whether or not the `path' + portion of the hashed filename is equivalent to the current + directory, but only if it starts with a `.'. (This catches + ./. and so on.) same_file () tests general Unix file + equivalence -- same device and inode. */ + if (*path == '.') + { + same = 0; + tail = (char *)strrchr (path, '/'); + + if (tail) + { + *tail = '\0'; + same = same_file (".", path, (struct stat *)NULL, (struct stat *)NULL); + *tail = '/'; + } + + return same ? (char *)NULL : path; + } + } + + return (path); } +/* **************************************************************** */ +/* */ +/* Manipulating the current working directory */ +/* */ +/* **************************************************************** */ + /* Return a consed string which is the current working directory. FOR_WHOM is the name of the caller for error printing. */ char *the_current_working_directory = (char *)NULL; @@ -410,6 +448,8 @@ char * get_working_directory (for_whom) char *for_whom; { + char *directory; + if (no_symbolic_links) { if (the_current_working_directory) @@ -418,21 +458,18 @@ get_working_directory (for_whom) the_current_working_directory = (char *)NULL; } - if (!the_current_working_directory) + if (the_current_working_directory == 0) { - char *directory; - - the_current_working_directory = xmalloc (MAXPATHLEN); - directory = getwd (the_current_working_directory); - if (!directory) + 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 (for_whom && *for_whom) - fprintf (stderr, "%s: ", for_whom); - else - fprintf (stderr, "%s: ", get_name_for_error ()); - - fprintf (stderr, "could not get current directory: %s\n", - the_current_working_directory); + fprintf (stderr, "%s: could not get current directory: %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; @@ -448,35 +485,43 @@ void set_working_directory (name) char *name; { - if (the_current_working_directory) - free (the_current_working_directory); - + FREE (the_current_working_directory); the_current_working_directory = savestring (name); } +/* **************************************************************** */ +/* */ +/* Job control support functions */ +/* */ +/* **************************************************************** */ + #if defined (JOB_CONTROL) /* Return the job spec found in LIST. */ +int get_job_spec (list) WORD_LIST *list; { register char *word; - int job = NO_JOB; - int substring = 0; + int job, substring; - if (!list) + if (list == 0) return (current_job); word = list->word->word; - if (!*word) + if (*word == '\0') return (current_job); if (*word == '%') word++; - if (digit (*word) && (sscanf (word, "%d", &job) == 1)) - return (job - 1); + if (digit (*word) && all_digits (word)) + { + job = atoi (word); + return (job - 1); + } + substring = 0; switch (*word) { case 0: @@ -490,21 +535,24 @@ get_job_spec (list) case '?': /* Substring search requested. */ substring++; word++; - goto find_string; + /* FALLTHROUGH */ default: - find_string: { - register int i, wl = strlen (word); + register int i, wl; + + job = NO_JOB; + wl = strlen (word); for (i = 0; i < job_slots; i++) { if (jobs[i]) { - register PROCESS *p = jobs[i]->pipe; + register PROCESS *p; + p = jobs[i]->pipe; do { if ((substring && strindex (p->command, word)) || - (strncmp (p->command, word, wl) == 0)) + (STREQN (p->command, word, wl))) if (job != NO_JOB) { builtin_error ("ambigious job spec: %s", word); @@ -524,171 +572,100 @@ get_job_spec (list) } #endif /* JOB_CONTROL */ -int parse_and_execute_level = 0; - -/* How to force parse_and_execute () to clean up after itself. */ -void -parse_and_execute_cleanup () -{ - run_unwind_frame ("parse_and_execute_top"); -} - -/* Parse and execute the commands in STRING. Returns whatever - execute_command () returns. This frees STRING. INTERACT is - the new value for `interactive' while the commands are being - executed. A value of -1 means don't change it. */ int -parse_and_execute (string, from_file, interact) - char *string; - char *from_file; - int interact; +display_signal_list (list, forcecols) + WORD_LIST *list; + int forcecols; { - int last_result = EXECUTION_SUCCESS; - int code = 0, jump_to_top_level = 0; - char *orig_string = string; - - /* Unwind protect this invocation of parse_and_execute (). */ - begin_unwind_frame ("parse_and_execute_top"); - unwind_protect_int (parse_and_execute_level); - unwind_protect_jmp_buf (top_level); - unwind_protect_int (indirection_level); - if (interact != -1 && interactive != interact) - unwind_protect_int (interactive); + register int i, column; + char *name; + int result; + long signum; -#if defined (HISTORY) - if (interactive_shell) + result = EXECUTION_SUCCESS; + if (!list) { - unwind_protect_int (remember_on_history); -# if defined (BANG_HISTORY) - unwind_protect_int (history_expansion_inhibited); -# endif /* BANG_HISTORY */ - } -#endif /* HISTORY */ - - add_unwind_protect (pop_stream, (char *)NULL); - if (orig_string) - add_unwind_protect (xfree, orig_string); - end_unwind_frame (); - - parse_and_execute_level++; - push_stream (); - indirection_level++; - if (interact != -1) - interactive = interact; - -#if defined (HISTORY) - /* We don't remember text read by the shell this way on - the history list, and we don't use !$ in shell scripts. */ - remember_on_history = 0; -# if defined (BANG_HISTORY) - history_expansion_inhibited = 1; -# endif /* BANG_HISTORY */ -#endif /* HISTORY */ - - with_input_from_string (string, from_file); - { - COMMAND *command; - - while (*(bash_input.location.string)) - { - if (interrupt_state) - { - last_result = EXECUTION_FAILURE; - break; - } - - /* Provide a location for functions which `longjmp (top_level)' to - jump to. This prevents errors in substitution from restarting - the reader loop directly, for example. */ - code = setjmp (top_level); - - if (code) - { - jump_to_top_level = 0; - switch (code) - { - case FORCE_EOF: - case EXITPROG: - run_unwind_frame ("pe_dispose"); - /* Remember to call longjmp (top_level) after the old - value for it is restored. */ - jump_to_top_level = 1; - goto out; - - case DISCARD: - dispose_command (command); - run_unwind_frame ("pe_dispose"); - last_command_exit_value = 1; - continue; - - default: - programming_error ("bad jump to top_level: %d", code); - break; - } - } - - if (parse_command () == 0) - { - if ((command = global_command) != (COMMAND *)NULL) - { - struct fd_bitmap *bitmap; - - bitmap = new_fd_bitmap (FD_BITMAP_SIZE); - begin_unwind_frame ("pe_dispose"); - add_unwind_protect (dispose_fd_bitmap, bitmap); - - global_command = (COMMAND *)NULL; - -#if defined (ONESHOT) - if (startup_state == 2 && *bash_input.location.string == '\0' && - command->type == cm_simple && !command->redirects && - !command->value.Simple->redirects) - { - command->flags |= CMD_NO_FORK; - command->value.Simple->flags |= CMD_NO_FORK; - } -#endif /* ONESHOT */ - - last_result = execute_command_internal - (command, 0, NO_PIPE, NO_PIPE, bitmap); - - dispose_command (command); - run_unwind_frame ("pe_dispose"); - } - } - else - { - last_result = EXECUTION_FAILURE; - - /* Since we are shell compatible, syntax errors in a script - abort the execution of the script. Right? */ - break; - } - } - } + for (i = 1, column = 0; i < NSIG; i++) + { + name = signal_name (i); + if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7)) + continue; - out: + if (posixly_correct && !forcecols) + printf ("%s%s", name, (i == NSIG - 1) ? "" : " "); + else + { + printf ("%2d) %s", i, name); + + if (++column < 4) + printf ("\t"); + else + { + printf ("\n"); + column = 0; + } + } + } - run_unwind_frame ("parse_and_execute_top"); + if ((posixly_correct && !forcecols) || column != 0) + printf ("\n"); + return result; + } - if (interrupt_state && parse_and_execute_level == 0) + /* List individual signal names or numbers. */ + while (list) { - /* An interrupt during non-interactive execution in an - interactive shell (e.g. via $PROMPT_COMMAND) should - not cause the shell to exit. */ - interactive = interactive_shell; - throw_to_top_level (); + if (legal_number (list->word->word, &signum)) + { + /* 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) + { + builtin_error ("bad signal number: %s", list->word->word); + result = EXECUTION_FAILURE; + list = list->next; + continue; + } + + name = signal_name (signum); + if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7)) + { + list = list->next; + continue; + } + printf ("%s\n", name); + } + else + { + signum = decode_signal (list->word->word); + if (signum == NO_SIG) + { + builtin_error ("%s: not a signal specification", list->word->word); + result = EXECUTION_FAILURE; + list = list->next; + continue; + } + printf ("%ld\n", signum); + } + list = list->next; } - - if (jump_to_top_level) - longjmp (top_level, code); - - return (last_result); + return (result); } -/* Return the address of the builtin named NAME. +/* **************************************************************** */ +/* */ +/* Finding builtin commands and their functions */ +/* */ +/* **************************************************************** */ + +/* Perform a binary search and return the address of the builtin function + whose name is NAME. If the function couldn't be found, or the builtin + is disabled or has no function associated with it, return NULL. + Return the address of the builtin. DISABLED_OKAY means find it even if the builtin is disabled. */ -static Function * +struct builtin * builtin_address_internal (name, disabled_okay) char *name; int disabled_okay; @@ -710,40 +687,53 @@ builtin_address_internal (name, disabled_okay) if (j == 0) { /* It must have a function pointer. It must be enabled, or we - must have explicitly allowed disabled functions to be found. */ + must have explicitly allowed disabled functions to be found, + and it must not have been deleted. */ if (shell_builtins[mid].function && + ((shell_builtins[mid].flags & BUILTIN_DELETED) == 0) && ((shell_builtins[mid].flags & BUILTIN_ENABLED) || disabled_okay)) - return (shell_builtins[mid].function); + return (&shell_builtins[mid]); else - return ((Function *)NULL); + return ((struct builtin *)NULL); } if (j > 0) hi = mid - 1; else lo = mid + 1; } - return ((Function *)NULL); + return ((struct builtin *)NULL); } -/* Perform a binary search and return the address of the builtin function - whose name is NAME. If the function couldn't be found, or the builtin - is disabled or has no function associated with it, return NULL. */ +/* Return the pointer to the function implementing builtin command NAME. */ Function * find_shell_builtin (name) - char *name; + char *name; { - return (builtin_address_internal (name, 0)); + current_builtin = builtin_address_internal (name, 0); + return (current_builtin ? current_builtin->function : (Function *)NULL); } -/* Return the address of builtin with NAME, irregardless of its state of - enableness. */ +/* Return the address of builtin with NAME, whether it is enabled or not. */ Function * builtin_address (name) char *name; { - return (builtin_address_internal (name, 1)); + current_builtin = builtin_address_internal (name, 1); + return (current_builtin ? current_builtin->function : (Function *)NULL); } +/* Return the function implementing the builtin NAME, but only if it is a + POSIX.2 special builtin. */ +Function * +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); +} + static int shell_builtin_compare (sbp1, sbp2) struct builtin *sbp1, *sbp2; @@ -765,49 +755,56 @@ initialize_shell_builtins () shell_builtin_compare); } -/* Return a new string which is the quoted version of STRING. This is used - by alias and trap. */ +/* **************************************************************** */ +/* */ +/* 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 i, j, c; - char *result; - - result = (char *)xmalloc (3 + (3 * strlen (string))); + register int c; + char *result, *r, *s; - result[0] = '\''; + result = (char *)xmalloc (3 + (4 * strlen (string))); + r = result; + *r++ = '\''; - for (i = 0, j = 1; string && (c = string[i]); i++) + for (s = string; s && (c = *s); s++) { - result[j++] = c; + *r++ = c; if (c == '\'') { - result[j++] = '\\'; /* insert escaped single quote */ - result[j++] = '\''; - result[j++] = '\''; /* start new quoted string */ + *r++ = '\\'; /* insert escaped single quote */ + *r++ = '\''; + *r++ = '\''; /* start new quoted string */ } } - result[j++] = '\''; - result[j] = '\0'; + *r++ = '\''; + *r = '\0'; return (result); } +/* Quote STRING using double quotes. Return a new string. */ char * double_quote (string) char *string; { - register int i, j, c; - char *result; - - result = (char *)xmalloc (3 + (3 * strlen (string))); + register int c; + char *result, *r, *s; - result[0] = '"'; + result = (char *)xmalloc (3 + (2 * strlen (string))); + r = result; + *r++ = '"'; - for (i = 0, j = 1; string && (c = string[i]); i++) + for (s = string; s && (c = *s); s++) { switch (c) { @@ -815,15 +812,86 @@ double_quote (string) case '$': case '`': case '\\': - result[j++] = '\\'; + *r++ = '\\'; default: - result[j++] = c; + *r++ = c; break; } } - result[j++] = '"'; - result[j] = '\0'; + *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 (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); +} |