| 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 |