483 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Modula-2
		
	
	
	
	
	
			
		
		
	
	
			483 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Modula-2
		
	
	
	
	
	
| This file is enable.def, from which is created enable.c.
 | |
| It implements the builtin "enable" 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 enable.c
 | |
| 
 | |
| $BUILTIN enable
 | |
| $FUNCTION enable_builtin
 | |
| $SHORT_DOC enable [-a] [-dnps] [-f filename] [name ...]
 | |
| Enable and disable shell builtins.
 | |
| 
 | |
| Enables and disables builtin shell commands.  Disabling allows you to
 | |
| execute a disk command which has the same name as a shell builtin
 | |
| without using a full pathname.
 | |
| 
 | |
| Options:
 | |
|   -a	print a list of builtins showing whether or not each is enabled
 | |
|   -n	disable each NAME or display a list of disabled builtins
 | |
|   -p	print the list of builtins in a reusable format
 | |
|   -s	print only the names of Posix `special' builtins
 | |
| 
 | |
| Options controlling dynamic loading:
 | |
|   -f	Load builtin NAME from shared object FILENAME
 | |
|   -d	Remove a builtin loaded with -f
 | |
| 
 | |
| Without options, each NAME is enabled.
 | |
| 
 | |
| To use the `test' found in $PATH instead of the shell builtin
 | |
| version, type `enable -n test'.
 | |
| 
 | |
| Exit Status:
 | |
| Returns success unless NAME is not a shell builtin or an error occurs.
 | |
| $END
 | |
| 
 | |
| #include <config.h>
 | |
| 
 | |
| #if defined (HAVE_UNISTD_H)
 | |
| #  ifdef _MINIX
 | |
| #    include <sys/types.h>
 | |
| #  endif
 | |
| #  include <unistd.h>
 | |
| #endif
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include "../bashansi.h"
 | |
| #include "../bashintl.h"
 | |
| 
 | |
| #include "../shell.h"
 | |
| #include "../builtins.h"
 | |
| #include "../flags.h"
 | |
| #include "common.h"
 | |
| #include "bashgetopt.h"
 | |
| 
 | |
| #if defined (PROGRAMMABLE_COMPLETION)
 | |
| #  include "../pcomplete.h"
 | |
| #endif
 | |
| 
 | |
| #define ENABLED  1
 | |
| #define DISABLED 2
 | |
| #define SPECIAL  4
 | |
| 
 | |
| #define AFLAG	0x01
 | |
| #define DFLAG	0x02
 | |
| #define FFLAG	0x04
 | |
| #define NFLAG	0x08
 | |
| #define PFLAG	0x10
 | |
| #define SFLAG	0x20
 | |
| 
 | |
| #if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM)
 | |
| static int dyn_load_builtin __P((WORD_LIST *, int, char *));
 | |
| #endif
 | |
| 
 | |
| #if defined (HAVE_DLCLOSE)
 | |
| static int dyn_unload_builtin __P((char *));
 | |
| static void delete_builtin __P((struct builtin *));
 | |
| static int local_dlclose __P((void *));
 | |
| #endif
 | |
| 
 | |
| static void list_some_builtins __P((int));
 | |
| static int enable_shell_command __P((char *, int));
 | |
| 
 | |
| /* Enable/disable shell commands present in LIST.  If list is not specified,
 | |
|    then print out a list of shell commands showing which are enabled and
 | |
|    which are disabled. */
 | |
| int
 | |
| enable_builtin (list)
 | |
|      WORD_LIST *list;
 | |
| {
 | |
|   int result, flags;
 | |
|   int opt, filter;
 | |
| #if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM)
 | |
|   char *filename;
 | |
| #endif
 | |
| 
 | |
|   result = EXECUTION_SUCCESS;
 | |
|   flags = 0;
 | |
| 
 | |
|   reset_internal_getopt ();
 | |
|   while ((opt = internal_getopt (list, "adnpsf:")) != -1)
 | |
|     {
 | |
|       switch (opt)
 | |
| 	{
 | |
| 	case 'a':
 | |
| 	  flags |= AFLAG;
 | |
| 	  break;
 | |
| 	case 'n':
 | |
| 	  flags |= NFLAG;
 | |
| 	  break;
 | |
| 	case 'p':
 | |
| 	  flags |= PFLAG;
 | |
| 	  break;
 | |
| 	case 's':
 | |
| 	  flags |= SFLAG;
 | |
| 	  break;
 | |
| 	case 'f':
 | |
| #if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM)
 | |
| 	  flags |= FFLAG;
 | |
| 	  filename = list_optarg;
 | |
| 	  break;
 | |
| #else
 | |
| 	  builtin_error (_("dynamic loading not available"));
 | |
| 	  return (EX_USAGE);
 | |
| #endif
 | |
| #if defined (HAVE_DLCLOSE)
 | |
| 	case 'd':
 | |
| 	  flags |= DFLAG;
 | |
| 	  break;
 | |
| #else
 | |
| 	  builtin_error (_("dynamic loading not available"));
 | |
| 	  return (EX_USAGE);
 | |
| #endif /* HAVE_DLCLOSE */
 | |
| 	default:
 | |
| 	  builtin_usage ();
 | |
| 	  return (EX_USAGE);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   list = loptend;
 | |
| 
 | |
| #if defined (RESTRICTED_SHELL)
 | |
|   /* Restricted shells cannot load new builtins. */
 | |
|   if (restricted && (flags & (FFLAG|DFLAG)))
 | |
|     {
 | |
|       sh_restricted ((char *)NULL);
 | |
|       return (EXECUTION_FAILURE);
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   if (list == 0 || (flags & PFLAG))
 | |
|     {
 | |
|       filter = (flags & AFLAG) ? (ENABLED | DISABLED)
 | |
| 			       : (flags & NFLAG) ? DISABLED : ENABLED;
 | |
| 
 | |
|       if (flags & SFLAG)
 | |
| 	filter |= SPECIAL;
 | |
| 
 | |
|       list_some_builtins (filter);
 | |
|     }
 | |
| #if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM)
 | |
|   else if (flags & FFLAG)
 | |
|     {
 | |
|       filter = (flags & NFLAG) ? DISABLED : ENABLED;
 | |
|       if (flags & SFLAG)
 | |
| 	filter |= SPECIAL;
 | |
| 
 | |
|       result = dyn_load_builtin (list, filter, filename);
 | |
| #if defined (PROGRAMMABLE_COMPLETION)
 | |
|       set_itemlist_dirty (&it_builtins);
 | |
| #endif
 | |
|     }
 | |
| #endif
 | |
| #if defined (HAVE_DLCLOSE)
 | |
|   else if (flags & DFLAG)
 | |
|     {
 | |
|       while (list)
 | |
| 	{
 | |
| 	  opt = dyn_unload_builtin (list->word->word);
 | |
| 	  if (opt == EXECUTION_FAILURE)
 | |
| 	    result = EXECUTION_FAILURE;
 | |
| 	  list = list->next;
 | |
| 	}
 | |
| #if defined (PROGRAMMABLE_COMPLETION)
 | |
|       set_itemlist_dirty (&it_builtins);
 | |
| #endif
 | |
|     }
 | |
| #endif
 | |
|   else
 | |
|     {
 | |
|       while (list)
 | |
| 	{
 | |
| 	  opt = enable_shell_command (list->word->word, flags & NFLAG);
 | |
| 
 | |
| 	  if (opt == EXECUTION_FAILURE)
 | |
| 	    {
 | |
| 	      sh_notbuiltin (list->word->word);
 | |
| 	      result = EXECUTION_FAILURE;
 | |
| 	    }
 | |
| 	  list = list->next;
 | |
| 	}
 | |
|     }
 | |
|   return (result);
 | |
| }
 | |
| 
 | |
| /* List some builtins.
 | |
|    FILTER is a mask with two slots: ENABLED and DISABLED. */
 | |
| static void
 | |
| list_some_builtins (filter)
 | |
|      int filter;
 | |
| {
 | |
|   register int i;
 | |
| 
 | |
|   for (i = 0; i < num_shell_builtins; i++)
 | |
|     {
 | |
|       if (shell_builtins[i].function == 0 || (shell_builtins[i].flags & BUILTIN_DELETED))
 | |
| 	continue;
 | |
| 
 | |
|       if ((filter & SPECIAL) &&
 | |
| 	  (shell_builtins[i].flags & SPECIAL_BUILTIN) == 0)
 | |
| 	continue;
 | |
| 
 | |
|       if ((filter & ENABLED) && (shell_builtins[i].flags & BUILTIN_ENABLED))
 | |
| 	printf ("enable %s\n", shell_builtins[i].name);
 | |
|       else if ((filter & DISABLED) &&
 | |
| 	       ((shell_builtins[i].flags & BUILTIN_ENABLED) == 0))
 | |
| 	printf ("enable -n %s\n", shell_builtins[i].name);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Enable the shell command NAME.  If DISABLE_P is non-zero, then
 | |
|    disable NAME instead. */
 | |
| static int
 | |
| enable_shell_command (name, disable_p)
 | |
|      char *name;
 | |
|      int disable_p;
 | |
| {
 | |
|   struct builtin *b;
 | |
| 
 | |
|   b = builtin_address_internal (name, 1);
 | |
|   if (b == 0)
 | |
|     return (EXECUTION_FAILURE);
 | |
| 
 | |
|   if (disable_p)
 | |
|     b->flags &= ~BUILTIN_ENABLED;
 | |
| #if defined (RESTRICTED_SHELL)
 | |
|   else if (restricted && ((b->flags & BUILTIN_ENABLED) == 0))
 | |
|     {
 | |
|       sh_restricted ((char *)NULL);
 | |
|       return (EXECUTION_FAILURE);
 | |
|     }
 | |
| #endif
 | |
|   else
 | |
|     b->flags |= BUILTIN_ENABLED;
 | |
| 
 | |
| #if defined (PROGRAMMABLE_COMPLETION)
 | |
|   set_itemlist_dirty (&it_enabled);
 | |
|   set_itemlist_dirty (&it_disabled);
 | |
| #endif
 | |
| 
 | |
|   return (EXECUTION_SUCCESS);
 | |
| }
 | |
| 
 | |
| #if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM)
 | |
| 
 | |
| #if defined (HAVE_DLFCN_H)
 | |
| #  include <dlfcn.h>
 | |
| #endif
 | |
| 
 | |
| static int
 | |
| dyn_load_builtin (list, flags, filename)
 | |
|      WORD_LIST *list;
 | |
|      int flags;
 | |
|      char *filename;
 | |
| {
 | |
|   WORD_LIST *l;
 | |
|   void *handle;
 | |
|   
 | |
|   int total, size, new, replaced;
 | |
|   char *struct_name, *name;
 | |
|   struct builtin **new_builtins, *b, *new_shell_builtins, *old_builtin;
 | |
| 
 | |
|   if (list == 0)
 | |
|     return (EXECUTION_FAILURE);
 | |
| 
 | |
| #ifndef RTLD_LAZY
 | |
| #define RTLD_LAZY 1
 | |
| #endif
 | |
| 
 | |
| #if defined (_AIX)
 | |
|   handle = dlopen (filename, RTLD_NOW|RTLD_GLOBAL);
 | |
| #else
 | |
|   handle = dlopen (filename, RTLD_LAZY);
 | |
| #endif /* !_AIX */
 | |
| 
 | |
|   if (handle == 0)
 | |
|     {
 | |
|       builtin_error (_("cannot open shared object %s: %s"), filename, dlerror ());
 | |
|       return (EXECUTION_FAILURE);
 | |
|     }
 | |
| 
 | |
|   for (new = 0, l = list; l; l = l->next, new++)
 | |
|     ;
 | |
|   new_builtins = (struct builtin **)xmalloc (new * sizeof (struct builtin *));
 | |
| 
 | |
|   /* For each new builtin in the shared object, find it and its describing
 | |
|      structure.  If this is overwriting an existing builtin, do so, otherwise
 | |
|      save the loaded struct for creating the new list of builtins. */
 | |
|   for (replaced = new = 0; list; list = list->next)
 | |
|     {
 | |
|       name = list->word->word;
 | |
| 
 | |
|       size = strlen (name);
 | |
|       struct_name = (char *)xmalloc (size + 8);
 | |
|       strcpy (struct_name, name);
 | |
|       strcpy (struct_name + size, "_struct");
 | |
| 
 | |
|       b = (struct builtin *)dlsym (handle, struct_name);
 | |
|       if (b == 0)
 | |
| 	{
 | |
| 	  builtin_error (_("cannot find %s in shared object %s: %s"),
 | |
| 			  struct_name, filename, dlerror ());
 | |
| 	  free (struct_name);
 | |
| 	  continue;
 | |
| 	}
 | |
| 
 | |
|       free (struct_name);
 | |
| 
 | |
|       b->flags &= ~STATIC_BUILTIN;
 | |
|       if (flags & SPECIAL)
 | |
| 	b->flags |= SPECIAL_BUILTIN;
 | |
|       b->handle = handle;
 | |
| 
 | |
|       if (old_builtin = builtin_address_internal (name, 1))
 | |
| 	{
 | |
| 	  replaced++;
 | |
| 	  FASTCOPY ((char *)b, (char *)old_builtin, sizeof (struct builtin));
 | |
| 	}
 | |
|       else
 | |
| 	  new_builtins[new++] = b;
 | |
|     }
 | |
| 
 | |
|   if (replaced == 0 && new == 0)
 | |
|     {
 | |
|       free (new_builtins);
 | |
|       dlclose (handle);
 | |
|       return (EXECUTION_FAILURE);
 | |
|     }
 | |
| 
 | |
|   if (new)
 | |
|     {
 | |
|       total = num_shell_builtins + new;
 | |
|       size = (total + 1) * sizeof (struct builtin);
 | |
| 
 | |
|       new_shell_builtins = (struct builtin *)xmalloc (size);
 | |
|       FASTCOPY ((char *)shell_builtins, (char *)new_shell_builtins,
 | |
| 		num_shell_builtins * sizeof (struct builtin));
 | |
|       for (replaced = 0; replaced < new; replaced++)
 | |
| 	FASTCOPY ((char *)new_builtins[replaced],
 | |
| 		  (char *)&new_shell_builtins[num_shell_builtins + replaced],
 | |
| 		  sizeof (struct builtin));
 | |
| 
 | |
|       new_shell_builtins[total].name = (char *)0;
 | |
|       new_shell_builtins[total].function = (sh_builtin_func_t *)0;
 | |
|       new_shell_builtins[total].flags = 0;
 | |
| 
 | |
|       if (shell_builtins != static_shell_builtins)
 | |
| 	free (shell_builtins);
 | |
| 
 | |
|       shell_builtins = new_shell_builtins;
 | |
|       num_shell_builtins = total;
 | |
|       initialize_shell_builtins ();
 | |
|     }
 | |
| 
 | |
|   free (new_builtins);
 | |
|   return (EXECUTION_SUCCESS);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #if defined (HAVE_DLCLOSE)
 | |
| static void
 | |
| delete_builtin (b)
 | |
|      struct builtin *b;
 | |
| {
 | |
|   int ind, size;
 | |
|   struct builtin *new_shell_builtins;
 | |
| 
 | |
|   /* XXX - funky pointer arithmetic - XXX */
 | |
| #ifdef __STDC__
 | |
|   ind = b - shell_builtins;
 | |
| #else
 | |
|   ind = ((int)b - (int)shell_builtins) / sizeof (struct builtin);
 | |
| #endif
 | |
|   size = num_shell_builtins * sizeof (struct builtin);
 | |
|   new_shell_builtins = (struct builtin *)xmalloc (size);
 | |
| 
 | |
|   /* Copy shell_builtins[0]...shell_builtins[ind - 1] to new_shell_builtins */
 | |
|   if (ind)
 | |
|     FASTCOPY ((char *)shell_builtins, (char *)new_shell_builtins,
 | |
| 	      ind * sizeof (struct builtin));
 | |
|   /* Copy shell_builtins[ind+1]...shell_builtins[num_shell_builtins to
 | |
|      new_shell_builtins, starting at ind. */
 | |
|   FASTCOPY ((char *)(&shell_builtins[ind+1]),
 | |
|   	    (char *)(&new_shell_builtins[ind]),
 | |
|   	    (num_shell_builtins - ind) * sizeof (struct builtin));
 | |
| 
 | |
|   if (shell_builtins != static_shell_builtins)
 | |
|     free (shell_builtins);
 | |
| 
 | |
|   /* The result is still sorted. */
 | |
|   num_shell_builtins--;
 | |
|   shell_builtins = new_shell_builtins;
 | |
| }
 | |
| 
 | |
| /* Tenon's MachTen has a dlclose that doesn't return a value, so we
 | |
|    finesse it with a local wrapper. */
 | |
| static int
 | |
| local_dlclose (handle)
 | |
|      void *handle;
 | |
| {
 | |
| #if !defined (__MACHTEN__)
 | |
|   return (dlclose (handle));
 | |
| #else /* __MACHTEN__ */
 | |
|   dlclose (handle);
 | |
|   return ((dlerror () != NULL) ? -1 : 0);    
 | |
| #endif /* __MACHTEN__ */
 | |
| }
 | |
| 
 | |
| static int
 | |
| dyn_unload_builtin (name)
 | |
|      char *name;
 | |
| {
 | |
|   struct builtin *b;
 | |
|   void *handle;
 | |
|   int ref, i;
 | |
| 
 | |
|   b = builtin_address_internal (name, 1);
 | |
|   if (b == 0)
 | |
|     {
 | |
|       sh_notbuiltin (name);
 | |
|       return (EXECUTION_FAILURE);
 | |
|     }
 | |
|   if (b->flags & STATIC_BUILTIN)
 | |
|     {
 | |
|       builtin_error (_("%s: not dynamically loaded"), name);
 | |
|       return (EXECUTION_FAILURE);
 | |
|     }
 | |
| 
 | |
|   handle = (void *)b->handle;
 | |
|   for (ref = i = 0; i < num_shell_builtins; i++)
 | |
|     {
 | |
|       if (shell_builtins[i].handle == b->handle)
 | |
| 	ref++;
 | |
|     }
 | |
| 
 | |
|   /* Don't remove the shared object unless the reference count of builtins
 | |
|      using it drops to zero. */
 | |
|   if (ref == 1 && local_dlclose (handle) != 0)
 | |
|     {
 | |
|       builtin_error (_("%s: cannot delete: %s"), name, dlerror ());
 | |
|       return (EXECUTION_FAILURE);
 | |
|     }
 | |
| 
 | |
|   /* Now remove this entry from the builtin table and reinitialize. */
 | |
|   delete_builtin (b);
 | |
| 
 | |
|   return (EXECUTION_SUCCESS);
 | |
| }
 | |
| #endif
 | 
