]>
Commit | Line | Data |
---|---|---|
726f6388 JA |
1 | This file is enable.def, from which is created enable.c. |
2 | It implements the builtin "enable" in Bash. | |
3 | ||
4 | Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. | |
5 | ||
6 | This file is part of GNU Bash, the Bourne Again SHell. | |
7 | ||
8 | Bash is free software; you can redistribute it and/or modify it under | |
9 | the terms of the GNU General Public License as published by the Free | |
bb70624e | 10 | Software Foundation; either version 2, or (at your option) any later |
726f6388 JA |
11 | version. |
12 | ||
13 | Bash is distributed in the hope that it will be useful, but WITHOUT ANY | |
14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License along | |
19 | with Bash; see the file COPYING. If not, write to the Free Software | |
bb70624e | 20 | Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. |
726f6388 JA |
21 | |
22 | $PRODUCES enable.c | |
23 | ||
24 | $BUILTIN enable | |
25 | $FUNCTION enable_builtin | |
ccc6cda3 | 26 | $SHORT_DOC enable [-pnds] [-a] [-f filename] [name ...] |
726f6388 JA |
27 | Enable and disable builtin shell commands. This allows |
28 | you to use a disk command which has the same name as a shell | |
b72432fd JA |
29 | builtin without specifying a full pathname. If -n is used, the |
30 | NAMEs become disabled; otherwise NAMEs are enabled. For example, | |
31 | to use the `test' found in $PATH instead of the shell builtin | |
32 | version, type `enable -n test'. On systems supporting dynamic | |
33 | loading, the -f option may be used to load new builtins from the | |
34 | shared object FILENAME. The -d option will delete a builtin | |
35 | previously loaded with -f. If no non-option names are given, or | |
36 | the -p option is supplied, a list of builtins is printed. The | |
37 | -a option means to print every builtin with an indication of whether | |
38 | or not it is enabled. The -s option restricts the output to the POSIX.2 | |
39 | `special' builtins. The -n option displays a list of all disabled builtins. | |
726f6388 JA |
40 | $END |
41 | ||
ccc6cda3 JA |
42 | #include <config.h> |
43 | ||
44 | #if defined (HAVE_UNISTD_H) | |
cce855bc JA |
45 | # ifdef _MINIX |
46 | # include <sys/types.h> | |
47 | # endif | |
ccc6cda3 JA |
48 | # include <unistd.h> |
49 | #endif | |
50 | ||
51 | #include <stdio.h> | |
52 | #include "../bashansi.h" | |
726f6388 JA |
53 | #include "../shell.h" |
54 | #include "../builtins.h" | |
ccc6cda3 | 55 | #include "../flags.h" |
726f6388 | 56 | #include "common.h" |
ccc6cda3 JA |
57 | #include "bashgetopt.h" |
58 | ||
bb70624e JA |
59 | #if defined (PROGRAMMABLE_COMPLETION) |
60 | # include "../pcomplete.h" | |
61 | #endif | |
62 | ||
ccc6cda3 JA |
63 | #if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM) |
64 | static int dyn_load_builtin (); | |
65 | #endif | |
66 | ||
67 | #if defined (HAVE_DLCLOSE) | |
68 | static int dyn_unload_builtin (); | |
69 | #endif | |
726f6388 JA |
70 | |
71 | #define ENABLED 1 | |
72 | #define DISABLED 2 | |
ccc6cda3 JA |
73 | #define SPECIAL 4 |
74 | ||
75 | #define AFLAG 0x01 | |
76 | #define DFLAG 0x02 | |
77 | #define FFLAG 0x04 | |
78 | #define NFLAG 0x08 | |
79 | #define PFLAG 0x10 | |
80 | #define SFLAG 0x20 | |
726f6388 JA |
81 | |
82 | static int enable_shell_command (); | |
83 | static void list_some_builtins (); | |
84 | ||
85 | /* Enable/disable shell commands present in LIST. If list is not specified, | |
86 | then print out a list of shell commands showing which are enabled and | |
87 | which are disabled. */ | |
ccc6cda3 | 88 | int |
726f6388 JA |
89 | enable_builtin (list) |
90 | WORD_LIST *list; | |
91 | { | |
ccc6cda3 JA |
92 | int result, flags; |
93 | int opt, filter; | |
94 | #if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM) | |
95 | char *filename; | |
96 | #endif | |
726f6388 | 97 | |
ccc6cda3 JA |
98 | result = EXECUTION_SUCCESS; |
99 | flags = 0; | |
726f6388 | 100 | |
ccc6cda3 JA |
101 | reset_internal_getopt (); |
102 | while ((opt = internal_getopt (list, "adnpsf:")) != -1) | |
726f6388 | 103 | { |
ccc6cda3 | 104 | switch (opt) |
726f6388 | 105 | { |
ccc6cda3 JA |
106 | case 'a': |
107 | flags |= AFLAG; | |
108 | break; | |
109 | case 'n': | |
110 | flags |= NFLAG; | |
111 | break; | |
112 | case 'p': | |
113 | flags |= PFLAG; | |
114 | break; | |
115 | case 's': | |
116 | flags |= SFLAG; | |
117 | break; | |
118 | case 'f': | |
119 | #if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM) | |
120 | flags |= FFLAG; | |
121 | filename = list_optarg; | |
122 | break; | |
123 | #else | |
124 | builtin_error ("dynamic loading not available"); | |
125 | return (EX_USAGE); | |
126 | #endif | |
127 | #if defined (HAVE_DLCLOSE) | |
128 | case 'd': | |
129 | flags |= DFLAG; | |
130 | break; | |
131 | #else | |
132 | builtin_error ("dynamic loading not available"); | |
133 | return (EX_USAGE); | |
134 | #endif /* HAVE_DLCLOSE */ | |
135 | default: | |
136 | builtin_usage (); | |
137 | return (EX_USAGE); | |
726f6388 JA |
138 | } |
139 | } | |
140 | ||
ccc6cda3 JA |
141 | list = loptend; |
142 | ||
143 | #if defined (RESTRICTED_SHELL) | |
144 | /* Restricted shells cannot load new builtins. */ | |
145 | if (restricted && (flags & (FFLAG|DFLAG))) | |
726f6388 | 146 | { |
ccc6cda3 JA |
147 | builtin_error ("restricted"); |
148 | return (EXECUTION_FAILURE); | |
149 | } | |
150 | #endif | |
726f6388 | 151 | |
ccc6cda3 JA |
152 | if (list == 0 || (flags & PFLAG)) |
153 | { | |
154 | filter = (flags & AFLAG) ? (ENABLED | DISABLED) | |
155 | : (flags & NFLAG) ? DISABLED : ENABLED; | |
156 | ||
157 | if (flags & SFLAG) | |
158 | filter |= SPECIAL; | |
726f6388 JA |
159 | |
160 | list_some_builtins (filter); | |
161 | } | |
ccc6cda3 JA |
162 | #if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM) |
163 | else if (flags & FFLAG) | |
164 | { | |
165 | filter = (flags & NFLAG) ? DISABLED : ENABLED; | |
166 | if (flags & SFLAG) | |
28ef6c31 | 167 | filter |= SPECIAL; |
ccc6cda3 JA |
168 | |
169 | result = dyn_load_builtin (list, filter, filename); | |
bb70624e JA |
170 | #if defined (PROGRAMMABLE_COMPLETION) |
171 | set_itemlist_dirty (&it_builtins); | |
172 | #endif | |
ccc6cda3 JA |
173 | } |
174 | #endif | |
175 | #if defined (HAVE_DLCLOSE) | |
176 | else if (flags & DFLAG) | |
177 | { | |
178 | while (list) | |
179 | { | |
180 | opt = dyn_unload_builtin (list->word->word); | |
181 | if (opt == EXECUTION_FAILURE) | |
182 | result = EXECUTION_FAILURE; | |
183 | list = list->next; | |
184 | } | |
bb70624e JA |
185 | #if defined (PROGRAMMABLE_COMPLETION) |
186 | set_itemlist_dirty (&it_builtins); | |
187 | #endif | |
ccc6cda3 JA |
188 | } |
189 | #endif | |
726f6388 JA |
190 | else |
191 | { | |
192 | while (list) | |
193 | { | |
ccc6cda3 | 194 | opt = enable_shell_command (list->word->word, flags & NFLAG); |
726f6388 | 195 | |
ccc6cda3 | 196 | if (opt == EXECUTION_FAILURE) |
726f6388 JA |
197 | { |
198 | builtin_error ("%s: not a shell builtin", list->word->word); | |
ccc6cda3 | 199 | result = EXECUTION_FAILURE; |
726f6388 JA |
200 | } |
201 | list = list->next; | |
202 | } | |
203 | } | |
ccc6cda3 | 204 | return (result); |
726f6388 JA |
205 | } |
206 | ||
207 | /* List some builtins. | |
208 | FILTER is a mask with two slots: ENABLED and DISABLED. */ | |
209 | static void | |
210 | list_some_builtins (filter) | |
211 | int filter; | |
212 | { | |
213 | register int i; | |
214 | ||
215 | for (i = 0; i < num_shell_builtins; i++) | |
216 | { | |
ccc6cda3 | 217 | if (shell_builtins[i].function == 0 || (shell_builtins[i].flags & BUILTIN_DELETED)) |
726f6388 JA |
218 | continue; |
219 | ||
ccc6cda3 JA |
220 | if ((filter & SPECIAL) && |
221 | (shell_builtins[i].flags & SPECIAL_BUILTIN) == 0) | |
222 | continue; | |
223 | ||
224 | if ((filter & ENABLED) && (shell_builtins[i].flags & BUILTIN_ENABLED)) | |
225 | printf ("enable %s\n", shell_builtins[i].name); | |
726f6388 JA |
226 | else if ((filter & DISABLED) && |
227 | ((shell_builtins[i].flags & BUILTIN_ENABLED) == 0)) | |
ccc6cda3 | 228 | printf ("enable -n %s\n", shell_builtins[i].name); |
726f6388 JA |
229 | } |
230 | } | |
231 | ||
232 | /* Enable the shell command NAME. If DISABLE_P is non-zero, then | |
233 | disable NAME instead. */ | |
234 | static int | |
235 | enable_shell_command (name, disable_p) | |
236 | char *name; | |
237 | int disable_p; | |
238 | { | |
ccc6cda3 | 239 | struct builtin *b; |
726f6388 | 240 | |
ccc6cda3 JA |
241 | b = builtin_address_internal (name, 1); |
242 | if (b == 0) | |
243 | return (EXECUTION_FAILURE); | |
244 | ||
245 | if (disable_p) | |
246 | b->flags &= ~BUILTIN_ENABLED; | |
247 | else | |
248 | b->flags |= BUILTIN_ENABLED; | |
249 | ||
bb70624e JA |
250 | #if defined (PROGRAMMABLE_COMPLETION) |
251 | set_itemlist_dirty (&it_enabled); | |
252 | set_itemlist_dirty (&it_disabled); | |
253 | #endif | |
254 | ||
ccc6cda3 JA |
255 | return (EXECUTION_SUCCESS); |
256 | } | |
257 | ||
258 | #if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM) | |
d166f048 JA |
259 | |
260 | #if defined (HAVE_DLFCN_H) | |
261 | # include <dlfcn.h> | |
262 | #endif | |
ccc6cda3 JA |
263 | |
264 | static int | |
265 | dyn_load_builtin (list, flags, filename) | |
266 | WORD_LIST *list; | |
267 | int flags; | |
268 | char *filename; | |
269 | { | |
270 | WORD_LIST *l; | |
271 | void *handle; | |
272 | ||
273 | int total, size, new, replaced; | |
274 | char *struct_name, *name; | |
275 | struct builtin **new_builtins, *b, *new_shell_builtins, *old_builtin; | |
276 | ||
277 | if (list == 0) | |
278 | return (EXECUTION_FAILURE); | |
279 | ||
280 | #ifndef RTLD_LAZY | |
281 | #define RTLD_LAZY 1 | |
282 | #endif | |
283 | ||
284 | #if defined (_AIX) | |
285 | handle = dlopen (filename, RTLD_NOW|RTLD_GLOBAL); | |
286 | #else | |
287 | handle = dlopen (filename, RTLD_LAZY); | |
288 | #endif /* !_AIX */ | |
289 | ||
290 | if (handle == 0) | |
726f6388 | 291 | { |
ccc6cda3 JA |
292 | builtin_error ("cannot open shared object %s: %s", filename, dlerror ()); |
293 | return (EXECUTION_FAILURE); | |
294 | } | |
726f6388 | 295 | |
ccc6cda3 JA |
296 | for (new = 0, l = list; l; l = l->next, new++) |
297 | ; | |
298 | new_builtins = (struct builtin **)xmalloc (new * sizeof (struct builtin *)); | |
726f6388 | 299 | |
ccc6cda3 JA |
300 | /* For each new builtin in the shared object, find it and its describing |
301 | structure. If this is overwriting an existing builtin, do so, otherwise | |
302 | save the loaded struct for creating the new list of builtins. */ | |
303 | for (replaced = new = 0; list; list = list->next) | |
304 | { | |
305 | name = list->word->word; | |
306 | ||
307 | size = strlen (name); | |
308 | struct_name = xmalloc (size + 8); | |
309 | strcpy (struct_name, name); | |
310 | strcpy (struct_name + size, "_struct"); | |
311 | ||
312 | b = (struct builtin *)dlsym (handle, struct_name); | |
313 | if (b == 0) | |
314 | { | |
315 | builtin_error ("cannot find %s in shared object %s: %s", struct_name, | |
316 | filename, dlerror ()); | |
317 | free (struct_name); | |
318 | continue; | |
726f6388 | 319 | } |
ccc6cda3 JA |
320 | |
321 | free (struct_name); | |
322 | ||
323 | b->flags &= ~STATIC_BUILTIN; | |
324 | if (flags & SPECIAL) | |
325 | b->flags |= SPECIAL_BUILTIN; | |
326 | b->handle = handle; | |
327 | ||
328 | if (old_builtin = builtin_address_internal (name, 1)) | |
28ef6c31 JA |
329 | { |
330 | replaced++; | |
ccc6cda3 | 331 | FASTCOPY ((char *)b, (char *)old_builtin, sizeof (struct builtin)); |
28ef6c31 | 332 | } |
ccc6cda3 JA |
333 | else |
334 | new_builtins[new++] = b; | |
335 | } | |
336 | ||
337 | if (replaced == 0 && new == 0) | |
338 | { | |
339 | free (new_builtins); | |
340 | dlclose (handle); | |
341 | return (EXECUTION_FAILURE); | |
726f6388 | 342 | } |
ccc6cda3 JA |
343 | |
344 | if (new) | |
345 | { | |
346 | total = num_shell_builtins + new; | |
347 | size = (total + 1) * sizeof (struct builtin); | |
348 | ||
349 | new_shell_builtins = (struct builtin *)xmalloc (size); | |
350 | FASTCOPY ((char *)shell_builtins, (char *)new_shell_builtins, | |
351 | num_shell_builtins * sizeof (struct builtin)); | |
352 | for (replaced = 0; replaced < new; replaced++) | |
353 | FASTCOPY ((char *)new_builtins[replaced], | |
354 | (char *)&new_shell_builtins[num_shell_builtins + replaced], | |
355 | sizeof (struct builtin)); | |
356 | ||
357 | new_shell_builtins[total].name = (char *)0; | |
358 | new_shell_builtins[total].function = (Function *)0; | |
359 | new_shell_builtins[total].flags = 0; | |
360 | ||
361 | if (shell_builtins != static_shell_builtins) | |
362 | free (shell_builtins); | |
363 | ||
364 | shell_builtins = new_shell_builtins; | |
365 | num_shell_builtins = total; | |
366 | initialize_shell_builtins (); | |
367 | } | |
368 | ||
369 | free (new_builtins); | |
370 | return (EXECUTION_SUCCESS); | |
371 | } | |
372 | #endif | |
373 | ||
374 | #if defined (HAVE_DLCLOSE) | |
375 | static void | |
376 | delete_builtin (b) | |
377 | struct builtin *b; | |
378 | { | |
379 | int ind, size; | |
380 | struct builtin *new_shell_builtins; | |
381 | ||
382 | /* XXX - funky pointer arithmetic - XXX */ | |
d166f048 JA |
383 | #ifdef __STDC__ |
384 | ind = b - shell_builtins; | |
385 | #else | |
ccc6cda3 | 386 | ind = ((int)b - (int)shell_builtins) / sizeof (struct builtin); |
d166f048 | 387 | #endif |
ccc6cda3 JA |
388 | size = num_shell_builtins * sizeof (struct builtin); |
389 | new_shell_builtins = (struct builtin *)xmalloc (size); | |
390 | ||
391 | /* Copy shell_builtins[0]...shell_builtins[ind - 1] to new_shell_builtins */ | |
392 | if (ind) | |
393 | FASTCOPY ((char *)shell_builtins, (char *)new_shell_builtins, | |
394 | ind * sizeof (struct builtin)); | |
395 | /* Copy shell_builtins[ind+1]...shell_builtins[num_shell_builtins to | |
396 | new_shell_builtins, starting at ind. */ | |
397 | FASTCOPY ((char *)(&shell_builtins[ind+1]), | |
398 | (char *)(&new_shell_builtins[ind]), | |
399 | (num_shell_builtins - ind) * sizeof (struct builtin)); | |
400 | ||
401 | if (shell_builtins != static_shell_builtins) | |
402 | free (shell_builtins); | |
403 | ||
404 | /* The result is still sorted. */ | |
405 | num_shell_builtins--; | |
406 | shell_builtins = new_shell_builtins; | |
407 | } | |
408 | ||
b72432fd JA |
409 | /* Tenon's MachTen has a dlclose that doesn't return a value, so we |
410 | finesse it with a local wrapper. */ | |
411 | static int | |
412 | local_dlclose (handle) | |
413 | void *handle; | |
414 | { | |
415 | #if !defined (__MACHTEN__) | |
416 | return (dlclose (handle)); | |
417 | #else /* __MACHTEN__ */ | |
418 | dlclose (handle); | |
419 | return ((dlerror () != NULL) ? -1 : 0); | |
420 | #endif /* __MACHTEN__ */ | |
421 | } | |
422 | ||
ccc6cda3 JA |
423 | static int |
424 | dyn_unload_builtin (name) | |
425 | char *name; | |
426 | { | |
427 | struct builtin *b; | |
428 | void *handle; | |
429 | int ref, i; | |
b72432fd | 430 | char *uerror; |
ccc6cda3 JA |
431 | |
432 | b = builtin_address_internal (name, 1); | |
433 | if (b == 0) | |
434 | { | |
435 | builtin_error ("%s: not a shell builtin", name); | |
436 | return (EXECUTION_FAILURE); | |
437 | } | |
438 | if (b->flags & STATIC_BUILTIN) | |
439 | { | |
440 | builtin_error ("%s: not dynamically loaded", name); | |
441 | return (EXECUTION_FAILURE); | |
442 | } | |
443 | ||
444 | handle = (void *)b->handle; | |
445 | for (ref = i = 0; i < num_shell_builtins; i++) | |
446 | { | |
447 | if (shell_builtins[i].handle == b->handle) | |
448 | ref++; | |
449 | } | |
450 | ||
451 | /* Don't remove the shared object unless the reference count of builtins | |
452 | using it drops to zero. */ | |
b72432fd | 453 | if (ref == 1 && local_dlclose (handle) != 0) |
ccc6cda3 JA |
454 | { |
455 | builtin_error ("cannot delete %s: %s", name, dlerror ()); | |
456 | return (EXECUTION_FAILURE); | |
457 | } | |
458 | ||
459 | /* Now remove this entry from the builtin table and reinitialize. */ | |
460 | delete_builtin (b); | |
461 | ||
462 | return (EXECUTION_SUCCESS); | |
726f6388 | 463 | } |
ccc6cda3 | 464 | #endif |