862 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			862 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
 | |
| 
 | |
|    This file is part of GNU Bash, the Bourne Again SHell.
 | |
| 
 | |
|    Bash is free software; you can redistribute it and/or modify it under
 | |
|    the terms of the GNU General Public License as published by the Free
 | |
|    Software Foundation; either version 1, or (at your option) any later
 | |
|    version.
 | |
| 
 | |
|    Bash is distributed in the hope that it will be useful, but WITHOUT ANY
 | |
|    WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | |
|    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 | |
|    for more details.
 | |
|    
 | |
|    You should have received a copy of the GNU General Public License along
 | |
|    with Bash; see the file COPYING.  If not, write to the Free Software
 | |
|    Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
 | |
| 
 | |
| #include <config.h>
 | |
| 
 | |
| #if defined (HAVE_UNISTD_H)
 | |
| #  ifdef _MINIX
 | |
| #    include <sys/types.h>
 | |
| #  endif
 | |
| #  include <unistd.h>
 | |
| #endif
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include "../bashtypes.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
 | |
| #endif
 | |
| 
 | |
| #include "../bashansi.h"
 | |
| 
 | |
| #include "../shell.h"
 | |
| #include "../maxpath.h"
 | |
| #include "../flags.h"
 | |
| #include "../jobs.h"
 | |
| #include "../builtins.h"
 | |
| #include "../input.h"
 | |
| #include "../execute_cmd.h"
 | |
| #include "../trap.h"
 | |
| #include "bashgetopt.h"
 | |
| #include "common.h"
 | |
| #include "builtext.h"
 | |
| #include <tilde/tilde.h>
 | |
| 
 | |
| #if defined (HISTORY)
 | |
| #  include "../bashhist.h"
 | |
| #endif
 | |
| 
 | |
| #if !defined (errno)
 | |
| extern int errno;   
 | |
| #endif /* !errno */
 | |
| 
 | |
| extern int no_symbolic_links, interactive, interactive_shell;
 | |
| extern int indirection_level, startup_state, 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;
 | |
| 
 | |
| /* **************************************************************** */
 | |
| /*								    */
 | |
| /*	     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 (USE_VARARGS)
 | |
| void
 | |
| #if defined (PREFER_STDARG)
 | |
| builtin_error (const char *format, ...)
 | |
| #else
 | |
| builtin_error (format, va_alist)
 | |
|      const char *format;
 | |
|      va_dcl
 | |
| #endif
 | |
| {
 | |
|   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);
 | |
| #endif
 | |
| 
 | |
|   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
 | |
| 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
 | |
|    only discard the ones that are to be replaced. */
 | |
| void
 | |
| remember_args (list, destructive)
 | |
|      WORD_LIST *list;
 | |
|      int destructive;
 | |
| {
 | |
|   register int i;
 | |
| 
 | |
|   for (i = 1; i < 10; i++)
 | |
|     {
 | |
|       if ((destructive || list) && dollar_vars[i])
 | |
| 	{
 | |
| 	  free (dollar_vars[i]);
 | |
| 	  dollar_vars[i] = (char *)NULL;
 | |
| 	}
 | |
| 
 | |
|       if (list)
 | |
| 	{
 | |
| 	  dollar_vars[i] = savestring (list->word->word);
 | |
| 	  list = list->next;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /* If arguments remain, assign them to REST_OF_ARGS.
 | |
|      Note that copy_word_list (NULL) returns NULL, and
 | |
|      that dispose_words (NULL) does nothing. */
 | |
|   if (destructive || list)
 | |
|     {
 | |
|       dispose_words (rest_of_args);
 | |
|       rest_of_args = copy_word_list (list);
 | |
|     }
 | |
| 
 | |
|   if (destructive)
 | |
|     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
 | |
|    checked? */
 | |
| int
 | |
| dollar_vars_changed ()
 | |
| {
 | |
|   return (changed_dollar_vars);
 | |
| }
 | |
| 
 | |
| void
 | |
| set_dollar_vars_unchanged ()
 | |
| {
 | |
|   changed_dollar_vars = 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| set_dollar_vars_changed ()
 | |
| {
 | |
|   changed_dollar_vars = 1;
 | |
| }
 | |
| 
 | |
| /* **************************************************************** */
 | |
| /*								    */
 | |
| /*	        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.  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
 | |
| get_numeric_arg (list, fatal)
 | |
|      WORD_LIST *list;
 | |
|      int fatal;
 | |
| {
 | |
|   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);
 | |
| 	  if (fatal)
 | |
| 	    throw_to_top_level ();
 | |
| 	  else
 | |
| 	    jump_to_top_level (DISCARD);
 | |
| 	}
 | |
|       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);
 | |
| }
 | |
| 
 | |
| /* **************************************************************** */
 | |
| /*								    */
 | |
| /*	     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;
 | |
| 
 | |
| char *
 | |
| get_working_directory (for_whom)
 | |
|      char *for_whom;
 | |
| {
 | |
|   char *directory;
 | |
| 
 | |
|   if (no_symbolic_links)
 | |
|     {
 | |
|       if (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)
 | |
| 	{
 | |
| 	  fprintf (stderr, "%s: could not get 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,
 | |
| 		   strerror (errno));
 | |
| 
 | |
| 	  free (the_current_working_directory);
 | |
| 	  the_current_working_directory = (char *)NULL;
 | |
| 	  return (char *)NULL;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return (savestring (the_current_working_directory));
 | |
| }
 | |
| 
 | |
| /* Make NAME our internal idea of the current working directory. */
 | |
| void
 | |
| set_working_directory (name)
 | |
|      char *name;
 | |
| {
 | |
|   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, substring;
 | |
| 
 | |
|   if (list == 0)
 | |
|     return (current_job);
 | |
| 
 | |
|   word = list->word->word;
 | |
| 
 | |
|   if (*word == '\0')
 | |
|     return (current_job);
 | |
| 
 | |
|   if (*word == '%')
 | |
|     word++;
 | |
| 
 | |
|   if (digit (*word) && all_digits (word))
 | |
|     {
 | |
|       job = atoi (word);
 | |
|       return (job - 1);
 | |
|     }
 | |
| 
 | |
|   substring = 0;
 | |
|   switch (*word)
 | |
|     {
 | |
|     case 0:
 | |
|     case '%':
 | |
|     case '+':
 | |
|       return (current_job);
 | |
| 
 | |
|     case '-':
 | |
|       return (previous_job);
 | |
| 
 | |
|     case '?':			/* Substring search requested. */
 | |
|       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);
 | |
|       }
 | |
|     }
 | |
| }
 | |
| #endif /* JOB_CONTROL */
 | |
| 
 | |
| int
 | |
| display_signal_list (list, forcecols)
 | |
|      WORD_LIST *list;
 | |
|      int forcecols;
 | |
| {
 | |
|   register int i, column;
 | |
|   char *name;
 | |
|   int result;
 | |
|   long signum;
 | |
| 
 | |
|   result = EXECUTION_SUCCESS;
 | |
|   if (!list)
 | |
|     {
 | |
|       for (i = 1, column = 0; i < NSIG; i++)
 | |
| 	{
 | |
| 	  name = signal_name (i);
 | |
| 	  if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7))
 | |
| 	    continue;
 | |
| 
 | |
| 	  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;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       if ((posixly_correct && !forcecols) || column != 0)
 | |
| 	printf ("\n");
 | |
|       return result;
 | |
|     }
 | |
| 
 | |
|   /* List individual signal names or numbers. */
 | |
|   while (list)
 | |
|     {
 | |
|       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;
 | |
| 	    }
 | |
| #if defined (JOB_CONTROL)
 | |
| 	  /* POSIX.2 says that `kill -l signum' prints the signal name without
 | |
| 	     the `SIG' prefix. */
 | |
| 	  printf ("%s\n", (this_shell_builtin == kill_builtin) ? name + 3 : name);
 | |
| #else
 | |
| 	  printf ("%s\n", name);
 | |
| #endif
 | |
| 	}
 | |
|       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;
 | |
|     }
 | |
|   return (result);
 | |
| }
 | |
| 
 | |
| /* **************************************************************** */
 | |
| /*								    */
 | |
| /*	    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. */
 | |
| struct builtin *
 | |
| builtin_address_internal (name, disabled_okay)
 | |
|      char *name;
 | |
|      int disabled_okay;
 | |
| {
 | |
|   int hi, lo, mid, j;
 | |
| 
 | |
|   hi = num_shell_builtins - 1;
 | |
|   lo = 0;
 | |
| 
 | |
|   while (lo <= hi)
 | |
|     {
 | |
|       mid = (lo + hi) / 2;
 | |
| 
 | |
|       j = shell_builtins[mid].name[0] - name[0];
 | |
| 
 | |
|       if (j == 0)
 | |
| 	j = strcmp (shell_builtins[mid].name, name);
 | |
| 
 | |
|       if (j == 0)
 | |
| 	{
 | |
| 	  /* It must have a function pointer.  It must be enabled, or we
 | |
| 	     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]);
 | |
| 	  else
 | |
| 	    return ((struct builtin *)NULL);
 | |
| 	}
 | |
|       if (j > 0)
 | |
| 	hi = mid - 1;
 | |
|       else
 | |
| 	lo = mid + 1;
 | |
|     }
 | |
|   return ((struct builtin *)NULL);
 | |
| }
 | |
| 
 | |
| /* Return the pointer to the function implementing builtin command NAME. */
 | |
| Function *
 | |
| find_shell_builtin (name)
 | |
|      char *name;
 | |
| {
 | |
|   current_builtin = builtin_address_internal (name, 0);
 | |
|   return (current_builtin ? current_builtin->function : (Function *)NULL);
 | |
| }
 | |
| 
 | |
| /* Return the address of builtin with NAME, whether it is enabled or not. */
 | |
| Function *
 | |
| builtin_address (name)
 | |
|      char *name;
 | |
| {
 | |
|   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;
 | |
| {
 | |
|   int result;
 | |
| 
 | |
|   if ((result = sbp1->name[0] - sbp2->name[0]) == 0)
 | |
|     result = strcmp (sbp1->name, sbp2->name);
 | |
| 
 | |
|   return (result);
 | |
| }
 | |
| 
 | |
| /* Sort the table of shell builtins so that the binary search will work
 | |
|    in find_shell_builtin. */
 | |
| void
 | |
| initialize_shell_builtins ()
 | |
| {
 | |
| #ifdef _MINIX
 | |
|   qsort (shell_builtins, num_shell_builtins, sizeof (struct builtin),
 | |
|     (int (*)(const void *, const void *))shell_builtin_compare);
 | |
| #else
 | |
|   qsort (shell_builtins, num_shell_builtins, sizeof (struct builtin),
 | |
|     shell_builtin_compare);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /* **************************************************************** */
 | |
| /*								    */
 | |
| /*	 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;
 | |
| #if 0
 | |
| 	case '~':				/* tilde expansion */
 | |
| 	  if (s == string || s[-1] == '=' || s[-1] == ':')
 | |
| 	    *r++ = '\\';
 | |
| 	  *r++ = c;
 | |
| 	  break;
 | |
| #endif
 | |
| 	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 '~':				/* tilde expansion */
 | |
| 	  if (s == string || s[-1] == '=' || s[-1] == ':')
 | |
| 	    return (1);
 | |
| 	case '#':
 | |
| 	  if (s == string)			/* comment char */
 | |
| 	    return (1);
 | |
| 	  /* FALLTHROUGH */
 | |
| 	default:
 | |
| 	  break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return (0);
 | |
| }
 | 
