]>
Commit | Line | Data |
---|---|---|
3185942a JA |
1 | /* common.c - utility functions for all builtins */ |
2 | ||
a0c0a00f | 3 | /* Copyright (C) 1987-2016 Free Software Foundation, Inc. |
726f6388 JA |
4 | |
5 | This file is part of GNU Bash, the Bourne Again SHell. | |
6 | ||
3185942a JA |
7 | Bash is free software: you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation, either version 3 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | Bash is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
726f6388 | 16 | |
3185942a JA |
17 | You should have received a copy of the GNU General Public License |
18 | along with Bash. If not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
726f6388 | 20 | |
ccc6cda3 JA |
21 | #include <config.h> |
22 | ||
23 | #if defined (HAVE_UNISTD_H) | |
cce855bc JA |
24 | # ifdef _MINIX |
25 | # include <sys/types.h> | |
26 | # endif | |
ccc6cda3 JA |
27 | # include <unistd.h> |
28 | #endif | |
29 | ||
726f6388 | 30 | #include <stdio.h> |
f73dda09 | 31 | #include <chartypes.h> |
d166f048 | 32 | #include "../bashtypes.h" |
bb70624e | 33 | #include "posixstat.h" |
ccc6cda3 JA |
34 | #include <signal.h> |
35 | ||
cce855bc JA |
36 | #include <errno.h> |
37 | ||
ccc6cda3 JA |
38 | #if defined (PREFER_STDARG) |
39 | # include <stdarg.h> | |
40 | #else | |
7117c2d2 | 41 | # include <varargs.h> |
ccc6cda3 | 42 | #endif |
726f6388 | 43 | |
ccc6cda3 | 44 | #include "../bashansi.h" |
b80f6443 | 45 | #include "../bashintl.h" |
726f6388 | 46 | |
3185942a JA |
47 | #define NEED_FPURGE_DECL |
48 | ||
726f6388 | 49 | #include "../shell.h" |
bb70624e | 50 | #include "maxpath.h" |
ccc6cda3 | 51 | #include "../flags.h" |
726f6388 JA |
52 | #include "../jobs.h" |
53 | #include "../builtins.h" | |
54 | #include "../input.h" | |
55 | #include "../execute_cmd.h" | |
ccc6cda3 | 56 | #include "../trap.h" |
ccc6cda3 | 57 | #include "bashgetopt.h" |
726f6388 | 58 | #include "common.h" |
d166f048 | 59 | #include "builtext.h" |
726f6388 JA |
60 | #include <tilde/tilde.h> |
61 | ||
62 | #if defined (HISTORY) | |
63 | # include "../bashhist.h" | |
64 | #endif | |
65 | ||
cce855bc JA |
66 | #if !defined (errno) |
67 | extern int errno; | |
68 | #endif /* !errno */ | |
69 | ||
7117c2d2 | 70 | extern int indirection_level, subshell_environment; |
ccc6cda3 | 71 | extern int line_number; |
726f6388 | 72 | extern int last_command_exit_value; |
a0c0a00f | 73 | extern int trap_saved_exit_value; |
ccc6cda3 | 74 | extern int running_trap; |
ccc6cda3 | 75 | extern int posixly_correct; |
726f6388 | 76 | extern char *this_command_name, *shell_name; |
3185942a | 77 | extern const char * const bash_getcwd_errstr; |
726f6388 | 78 | |
d166f048 | 79 | /* Used by some builtins and the mainline code. */ |
f73dda09 JA |
80 | sh_builtin_func_t *last_shell_builtin = (sh_builtin_func_t *)NULL; |
81 | sh_builtin_func_t *this_shell_builtin = (sh_builtin_func_t *)NULL; | |
d166f048 | 82 | |
ccc6cda3 JA |
83 | /* **************************************************************** */ |
84 | /* */ | |
85 | /* Error reporting, usage, and option processing */ | |
86 | /* */ | |
87 | /* **************************************************************** */ | |
726f6388 JA |
88 | |
89 | /* This is a lot like report_error (), but it is for shell builtins | |
90 | instead of shell control structures, and it won't ever exit the | |
91 | shell. */ | |
3185942a JA |
92 | |
93 | static void | |
94 | builtin_error_prolog () | |
95 | { | |
96 | char *name; | |
97 | ||
98 | name = get_name_for_error (); | |
99 | fprintf (stderr, "%s: ", name); | |
100 | ||
101 | if (interactive_shell == 0) | |
102 | fprintf (stderr, _("line %d: "), executing_line_number ()); | |
103 | ||
104 | if (this_command_name && *this_command_name) | |
105 | fprintf (stderr, "%s: ", this_command_name); | |
106 | } | |
107 | ||
726f6388 | 108 | void |
ccc6cda3 JA |
109 | #if defined (PREFER_STDARG) |
110 | builtin_error (const char *format, ...) | |
111 | #else | |
112 | builtin_error (format, va_alist) | |
113 | const char *format; | |
726f6388 | 114 | va_dcl |
ccc6cda3 | 115 | #endif |
726f6388 | 116 | { |
726f6388 | 117 | va_list args; |
ccc6cda3 | 118 | |
3185942a | 119 | builtin_error_prolog (); |
726f6388 | 120 | |
3185942a | 121 | SH_VA_START (args, format); |
7117c2d2 | 122 | |
3185942a JA |
123 | vfprintf (stderr, format, args); |
124 | va_end (args); | |
125 | fprintf (stderr, "\n"); | |
126 | } | |
127 | ||
128 | void | |
129 | #if defined (PREFER_STDARG) | |
130 | builtin_warning (const char *format, ...) | |
131 | #else | |
132 | builtin_warning (format, va_alist) | |
133 | const char *format; | |
134 | va_dcl | |
135 | #endif | |
136 | { | |
137 | va_list args; | |
138 | ||
139 | builtin_error_prolog (); | |
140 | fprintf (stderr, _("warning: ")); | |
726f6388 | 141 | |
7117c2d2 | 142 | SH_VA_START (args, format); |
ccc6cda3 | 143 | |
726f6388 JA |
144 | vfprintf (stderr, format, args); |
145 | va_end (args); | |
146 | fprintf (stderr, "\n"); | |
147 | } | |
ccc6cda3 JA |
148 | |
149 | /* Print a usage summary for the currently-executing builtin command. */ | |
150 | void | |
151 | builtin_usage () | |
152 | { | |
153 | if (this_command_name && *this_command_name) | |
3185942a | 154 | fprintf (stderr, _("%s: usage: "), this_command_name); |
495aee44 | 155 | fprintf (stderr, "%s\n", _(current_builtin->short_doc)); |
ccc6cda3 JA |
156 | fflush (stderr); |
157 | } | |
158 | ||
159 | /* Return if LIST is NULL else barf and jump to top_level. Used by some | |
160 | builtins that do not accept arguments. */ | |
161 | void | |
162 | no_args (list) | |
163 | WORD_LIST *list; | |
164 | { | |
165 | if (list) | |
166 | { | |
b80f6443 | 167 | builtin_error (_("too many arguments")); |
f1be666c | 168 | top_level_cleanup (); |
ccc6cda3 JA |
169 | jump_to_top_level (DISCARD); |
170 | } | |
171 | } | |
172 | ||
ccc6cda3 JA |
173 | /* Check that no options were given to the currently-executing builtin, |
174 | and return 0 if there were options. */ | |
175 | int | |
176 | no_options (list) | |
177 | WORD_LIST *list; | |
178 | { | |
a0c0a00f CR |
179 | int opt; |
180 | ||
ccc6cda3 | 181 | reset_internal_getopt (); |
a0c0a00f | 182 | if ((opt = internal_getopt (list, "")) != -1) |
ccc6cda3 | 183 | { |
a0c0a00f CR |
184 | if (opt == GETOPT_HELP) |
185 | { | |
186 | builtin_help (); | |
187 | return (2); | |
188 | } | |
ccc6cda3 JA |
189 | builtin_usage (); |
190 | return (1); | |
191 | } | |
192 | return (0); | |
193 | } | |
194 | ||
7117c2d2 JA |
195 | void |
196 | sh_needarg (s) | |
197 | char *s; | |
198 | { | |
b80f6443 | 199 | builtin_error (_("%s: option requires an argument"), s); |
7117c2d2 JA |
200 | } |
201 | ||
202 | void | |
203 | sh_neednumarg (s) | |
204 | char *s; | |
205 | { | |
b80f6443 | 206 | builtin_error (_("%s: numeric argument required"), s); |
7117c2d2 JA |
207 | } |
208 | ||
209 | void | |
210 | sh_notfound (s) | |
211 | char *s; | |
212 | { | |
b80f6443 | 213 | builtin_error (_("%s: not found"), s); |
7117c2d2 JA |
214 | } |
215 | ||
216 | /* Function called when one of the builtin commands detects an invalid | |
217 | option. */ | |
218 | void | |
219 | sh_invalidopt (s) | |
220 | char *s; | |
221 | { | |
b80f6443 | 222 | builtin_error (_("%s: invalid option"), s); |
7117c2d2 JA |
223 | } |
224 | ||
225 | void | |
226 | sh_invalidoptname (s) | |
227 | char *s; | |
228 | { | |
b80f6443 | 229 | builtin_error (_("%s: invalid option name"), s); |
7117c2d2 JA |
230 | } |
231 | ||
232 | void | |
233 | sh_invalidid (s) | |
234 | char *s; | |
235 | { | |
b80f6443 | 236 | builtin_error (_("`%s': not a valid identifier"), s); |
7117c2d2 JA |
237 | } |
238 | ||
239 | void | |
240 | sh_invalidnum (s) | |
241 | char *s; | |
242 | { | |
3185942a JA |
243 | char *msg; |
244 | ||
a0c0a00f | 245 | if (*s == '0' && isdigit ((unsigned char)s[1])) |
3185942a JA |
246 | msg = _("invalid octal number"); |
247 | else if (*s == '0' && s[1] == 'x') | |
248 | msg = _("invalid hex number"); | |
249 | else | |
250 | msg = _("invalid number"); | |
251 | builtin_error ("%s: %s", s, msg); | |
7117c2d2 JA |
252 | } |
253 | ||
254 | void | |
255 | sh_invalidsig (s) | |
256 | char *s; | |
257 | { | |
b80f6443 | 258 | builtin_error (_("%s: invalid signal specification"), s); |
7117c2d2 JA |
259 | } |
260 | ||
261 | void | |
262 | sh_badpid (s) | |
263 | char *s; | |
264 | { | |
b80f6443 | 265 | builtin_error (_("`%s': not a pid or valid job spec"), s); |
7117c2d2 JA |
266 | } |
267 | ||
268 | void | |
269 | sh_readonly (s) | |
270 | const char *s; | |
271 | { | |
b80f6443 | 272 | builtin_error (_("%s: readonly variable"), s); |
7117c2d2 JA |
273 | } |
274 | ||
275 | void | |
276 | sh_erange (s, desc) | |
277 | char *s, *desc; | |
278 | { | |
279 | if (s) | |
b80f6443 | 280 | builtin_error (_("%s: %s out of range"), s, desc ? desc : _("argument")); |
7117c2d2 | 281 | else |
b80f6443 | 282 | builtin_error (_("%s out of range"), desc ? desc : _("argument")); |
7117c2d2 JA |
283 | } |
284 | ||
285 | #if defined (JOB_CONTROL) | |
286 | void | |
287 | sh_badjob (s) | |
288 | char *s; | |
289 | { | |
b80f6443 | 290 | builtin_error (_("%s: no such job"), s); |
7117c2d2 JA |
291 | } |
292 | ||
293 | void | |
294 | sh_nojobs (s) | |
295 | char *s; | |
296 | { | |
297 | if (s) | |
b80f6443 | 298 | builtin_error (_("%s: no job control"), s); |
7117c2d2 | 299 | else |
b80f6443 | 300 | builtin_error (_("no job control")); |
7117c2d2 JA |
301 | } |
302 | #endif | |
303 | ||
304 | #if defined (RESTRICTED_SHELL) | |
305 | void | |
306 | sh_restricted (s) | |
307 | char *s; | |
308 | { | |
309 | if (s) | |
b80f6443 | 310 | builtin_error (_("%s: restricted"), s); |
7117c2d2 | 311 | else |
b80f6443 | 312 | builtin_error (_("restricted")); |
7117c2d2 JA |
313 | } |
314 | #endif | |
315 | ||
b80f6443 JA |
316 | void |
317 | sh_notbuiltin (s) | |
318 | char *s; | |
319 | { | |
320 | builtin_error (_("%s: not a shell builtin"), s); | |
321 | } | |
322 | ||
95732b49 JA |
323 | void |
324 | sh_wrerror () | |
325 | { | |
3185942a JA |
326 | #if defined (DONT_REPORT_BROKEN_PIPE_WRITE_ERRORS) && defined (EPIPE) |
327 | if (errno != EPIPE) | |
328 | #endif /* DONT_REPORT_BROKEN_PIPE_WRITE_ERRORS && EPIPE */ | |
95732b49 JA |
329 | builtin_error (_("write error: %s"), strerror (errno)); |
330 | } | |
331 | ||
17345e5a JA |
332 | void |
333 | sh_ttyerror (set) | |
334 | int set; | |
335 | { | |
336 | if (set) | |
337 | builtin_error (_("error setting terminal attributes: %s"), strerror (errno)); | |
338 | else | |
339 | builtin_error (_("error getting terminal attributes: %s"), strerror (errno)); | |
340 | } | |
341 | ||
3185942a JA |
342 | int |
343 | sh_chkwrite (s) | |
344 | int s; | |
345 | { | |
a0c0a00f | 346 | QUIT; |
3185942a | 347 | fflush (stdout); |
a0c0a00f | 348 | QUIT; |
3185942a JA |
349 | if (ferror (stdout)) |
350 | { | |
351 | sh_wrerror (); | |
352 | fpurge (stdout); | |
353 | clearerr (stdout); | |
354 | return (EXECUTION_FAILURE); | |
355 | } | |
356 | return (s); | |
357 | } | |
358 | ||
ccc6cda3 JA |
359 | /* **************************************************************** */ |
360 | /* */ | |
361 | /* Shell positional parameter manipulation */ | |
362 | /* */ | |
363 | /* **************************************************************** */ | |
364 | ||
365 | /* Convert a WORD_LIST into a C-style argv. Return the number of elements | |
366 | in the list in *IP, if IP is non-null. A convenience function for | |
367 | loadable builtins; also used by `test'. */ | |
368 | char ** | |
369 | make_builtin_argv (list, ip) | |
370 | WORD_LIST *list; | |
371 | int *ip; | |
372 | { | |
373 | char **argv; | |
374 | ||
7117c2d2 | 375 | argv = strvec_from_word_list (list, 0, 1, ip); |
ccc6cda3 JA |
376 | argv[0] = this_command_name; |
377 | return argv; | |
378 | } | |
726f6388 | 379 | |
495aee44 | 380 | /* Remember LIST in $1 ... $9, and REST_OF_ARGS. If DESTRUCTIVE is |
726f6388 JA |
381 | non-zero, then discard whatever the existing arguments are, else |
382 | only discard the ones that are to be replaced. */ | |
383 | void | |
384 | remember_args (list, destructive) | |
385 | WORD_LIST *list; | |
386 | int destructive; | |
387 | { | |
388 | register int i; | |
389 | ||
390 | for (i = 1; i < 10; i++) | |
391 | { | |
ccc6cda3 | 392 | if ((destructive || list) && dollar_vars[i]) |
726f6388 JA |
393 | { |
394 | free (dollar_vars[i]); | |
395 | dollar_vars[i] = (char *)NULL; | |
396 | } | |
397 | ||
398 | if (list) | |
399 | { | |
726f6388 JA |
400 | dollar_vars[i] = savestring (list->word->word); |
401 | list = list->next; | |
402 | } | |
403 | } | |
404 | ||
405 | /* If arguments remain, assign them to REST_OF_ARGS. | |
406 | Note that copy_word_list (NULL) returns NULL, and | |
407 | that dispose_words (NULL) does nothing. */ | |
408 | if (destructive || list) | |
409 | { | |
410 | dispose_words (rest_of_args); | |
411 | rest_of_args = copy_word_list (list); | |
412 | } | |
413 | ||
414 | if (destructive) | |
415 | set_dollar_vars_changed (); | |
a0c0a00f CR |
416 | |
417 | invalidate_cached_quoted_dollar_at (); | |
726f6388 JA |
418 | } |
419 | ||
ccc6cda3 | 420 | static int changed_dollar_vars; |
726f6388 JA |
421 | |
422 | /* Have the dollar variables been reset to new values since we last | |
423 | checked? */ | |
ccc6cda3 | 424 | int |
726f6388 JA |
425 | dollar_vars_changed () |
426 | { | |
427 | return (changed_dollar_vars); | |
428 | } | |
429 | ||
430 | void | |
431 | set_dollar_vars_unchanged () | |
432 | { | |
433 | changed_dollar_vars = 0; | |
434 | } | |
435 | ||
436 | void | |
437 | set_dollar_vars_changed () | |
438 | { | |
7117c2d2 JA |
439 | if (variable_context) |
440 | changed_dollar_vars |= ARGS_FUNC; | |
441 | else if (this_shell_builtin == set_builtin) | |
442 | changed_dollar_vars |= ARGS_SETBLTIN; | |
443 | else | |
444 | changed_dollar_vars |= ARGS_INVOC; | |
726f6388 JA |
445 | } |
446 | ||
ccc6cda3 JA |
447 | /* **************************************************************** */ |
448 | /* */ | |
28ef6c31 | 449 | /* Validating numeric input and arguments */ |
ccc6cda3 JA |
450 | /* */ |
451 | /* **************************************************************** */ | |
452 | ||
453 | /* Read a numeric arg for this_command_name, the name of the shell builtin | |
454 | that wants it. LIST is the word list that the arg is to come from. | |
455 | Accept only the numeric argument; report an error if other arguments | |
3185942a JA |
456 | follow. If FATAL is 1, call throw_to_top_level, which exits the |
457 | shell; if it's 2, call jump_to_top_level (DISCARD), which aborts the | |
458 | current command; if FATAL is 0, return an indication of an invalid | |
459 | number by setting *NUMOK == 0 and return -1. */ | |
460 | int | |
461 | get_numeric_arg (list, fatal, count) | |
ccc6cda3 | 462 | WORD_LIST *list; |
d166f048 | 463 | int fatal; |
3185942a | 464 | intmax_t *count; |
726f6388 | 465 | { |
3185942a JA |
466 | char *arg; |
467 | ||
468 | if (count) | |
469 | *count = 1; | |
7117c2d2 JA |
470 | |
471 | if (list && list->word && ISOPTION (list->word->word, '-')) | |
472 | list = list->next; | |
ccc6cda3 JA |
473 | |
474 | if (list) | |
475 | { | |
ccc6cda3 | 476 | arg = list->word->word; |
3185942a | 477 | if (arg == 0 || (legal_number (arg, count) == 0)) |
ccc6cda3 | 478 | { |
3185942a JA |
479 | sh_neednumarg (list->word->word ? list->word->word : "`'"); |
480 | if (fatal == 0) | |
481 | return 0; | |
482 | else if (fatal == 1) /* fatal == 1; abort */ | |
d166f048 | 483 | throw_to_top_level (); |
3185942a | 484 | else /* fatal == 2; discard current command */ |
f1be666c JA |
485 | { |
486 | top_level_cleanup (); | |
487 | jump_to_top_level (DISCARD); | |
488 | } | |
ccc6cda3 JA |
489 | } |
490 | no_args (list->next); | |
491 | } | |
f73dda09 | 492 | |
3185942a | 493 | return (1); |
ccc6cda3 JA |
494 | } |
495 | ||
f73dda09 JA |
496 | /* Get an eight-bit status value from LIST */ |
497 | int | |
498 | get_exitstat (list) | |
499 | WORD_LIST *list; | |
500 | { | |
501 | int status; | |
7117c2d2 | 502 | intmax_t sval; |
f73dda09 JA |
503 | char *arg; |
504 | ||
7117c2d2 JA |
505 | if (list && list->word && ISOPTION (list->word->word, '-')) |
506 | list = list->next; | |
507 | ||
508 | if (list == 0) | |
a0c0a00f CR |
509 | { |
510 | /* If we're not running the DEBUG trap, the return builtin, when not | |
511 | given any arguments, uses the value of $? before the trap ran. If | |
512 | given an argument, return uses it. This means that the trap can't | |
513 | change $?. The DEBUG trap gets to change $?, though, since that is | |
514 | part of its reason for existing, and because the extended debug mode | |
515 | does things with the return value. */ | |
516 | if (this_shell_builtin == return_builtin && running_trap > 0 && running_trap != DEBUG_TRAP+1) | |
517 | return (trap_saved_exit_value); | |
518 | return (last_command_exit_value); | |
519 | } | |
7117c2d2 | 520 | |
f73dda09 JA |
521 | arg = list->word->word; |
522 | if (arg == 0 || legal_number (arg, &sval) == 0) | |
523 | { | |
7117c2d2 | 524 | sh_neednumarg (list->word->word ? list->word->word : "`'"); |
ac50fbac | 525 | return EX_BADUSAGE; |
f73dda09 JA |
526 | } |
527 | no_args (list->next); | |
528 | ||
529 | status = sval & 255; | |
530 | return status; | |
531 | } | |
532 | ||
ccc6cda3 JA |
533 | /* Return the octal number parsed from STRING, or -1 to indicate |
534 | that the string contained a bad number. */ | |
535 | int | |
536 | read_octal (string) | |
537 | char *string; | |
538 | { | |
539 | int result, digits; | |
540 | ||
541 | result = digits = 0; | |
28ef6c31 | 542 | while (*string && ISOCTAL (*string)) |
ccc6cda3 JA |
543 | { |
544 | digits++; | |
28ef6c31 | 545 | result = (result * 8) + (*string++ - '0'); |
f73dda09 JA |
546 | if (result > 0777) |
547 | return -1; | |
ccc6cda3 JA |
548 | } |
549 | ||
f73dda09 | 550 | if (digits == 0 || *string) |
ccc6cda3 JA |
551 | result = -1; |
552 | ||
553 | return (result); | |
554 | } | |
555 | ||
ccc6cda3 JA |
556 | /* **************************************************************** */ |
557 | /* */ | |
558 | /* Manipulating the current working directory */ | |
559 | /* */ | |
560 | /* **************************************************************** */ | |
561 | ||
726f6388 JA |
562 | /* Return a consed string which is the current working directory. |
563 | FOR_WHOM is the name of the caller for error printing. */ | |
564 | char *the_current_working_directory = (char *)NULL; | |
565 | ||
566 | char * | |
567 | get_working_directory (for_whom) | |
568 | char *for_whom; | |
569 | { | |
570 | if (no_symbolic_links) | |
571 | { | |
b80f6443 | 572 | FREE (the_current_working_directory); |
726f6388 JA |
573 | the_current_working_directory = (char *)NULL; |
574 | } | |
575 | ||
ccc6cda3 | 576 | if (the_current_working_directory == 0) |
726f6388 | 577 | { |
f1be666c JA |
578 | #if defined (GETCWD_BROKEN) |
579 | the_current_working_directory = getcwd (0, PATH_MAX); | |
580 | #else | |
b80f6443 | 581 | the_current_working_directory = getcwd (0, 0); |
f1be666c | 582 | #endif |
b80f6443 | 583 | if (the_current_working_directory == 0) |
726f6388 | 584 | { |
b80f6443 | 585 | fprintf (stderr, _("%s: error retrieving current directory: %s: %s\n"), |
ccc6cda3 | 586 | (for_whom && *for_whom) ? for_whom : get_name_for_error (), |
b80f6443 | 587 | _(bash_getcwd_errstr), strerror (errno)); |
726f6388 JA |
588 | return (char *)NULL; |
589 | } | |
590 | } | |
591 | ||
592 | return (savestring (the_current_working_directory)); | |
593 | } | |
594 | ||
595 | /* Make NAME our internal idea of the current working directory. */ | |
596 | void | |
597 | set_working_directory (name) | |
598 | char *name; | |
599 | { | |
ccc6cda3 | 600 | FREE (the_current_working_directory); |
726f6388 JA |
601 | the_current_working_directory = savestring (name); |
602 | } | |
603 | ||
ccc6cda3 JA |
604 | /* **************************************************************** */ |
605 | /* */ | |
606 | /* Job control support functions */ | |
607 | /* */ | |
608 | /* **************************************************************** */ | |
609 | ||
726f6388 | 610 | #if defined (JOB_CONTROL) |
7117c2d2 JA |
611 | int |
612 | get_job_by_name (name, flags) | |
613 | const char *name; | |
614 | int flags; | |
615 | { | |
616 | register int i, wl, cl, match, job; | |
617 | register PROCESS *p; | |
95732b49 | 618 | register JOB *j; |
7117c2d2 JA |
619 | |
620 | job = NO_JOB; | |
621 | wl = strlen (name); | |
95732b49 | 622 | for (i = js.j_jobslots - 1; i >= 0; i--) |
7117c2d2 | 623 | { |
95732b49 JA |
624 | j = get_job_by_jid (i); |
625 | if (j == 0 || ((flags & JM_STOPPED) && J_JOBSTATE(j) != JSTOPPED)) | |
7117c2d2 JA |
626 | continue; |
627 | ||
95732b49 | 628 | p = j->pipe; |
7117c2d2 JA |
629 | do |
630 | { | |
631 | if (flags & JM_EXACT) | |
632 | { | |
633 | cl = strlen (p->command); | |
634 | match = STREQN (p->command, name, cl); | |
635 | } | |
636 | else if (flags & JM_SUBSTRING) | |
0001803f | 637 | match = strcasestr (p->command, name) != (char *)0; |
7117c2d2 JA |
638 | else |
639 | match = STREQN (p->command, name, wl); | |
640 | ||
641 | if (match == 0) | |
642 | { | |
643 | p = p->next; | |
644 | continue; | |
645 | } | |
646 | else if (flags & JM_FIRSTMATCH) | |
647 | return i; /* return first match */ | |
648 | else if (job != NO_JOB) | |
649 | { | |
650 | if (this_shell_builtin) | |
b80f6443 | 651 | builtin_error (_("%s: ambiguous job spec"), name); |
7117c2d2 | 652 | else |
ac50fbac | 653 | internal_error (_("%s: ambiguous job spec"), name); |
7117c2d2 JA |
654 | return (DUP_JOB); |
655 | } | |
656 | else | |
657 | job = i; | |
658 | } | |
95732b49 | 659 | while (p != j->pipe); |
7117c2d2 JA |
660 | } |
661 | ||
662 | return (job); | |
663 | } | |
664 | ||
726f6388 | 665 | /* Return the job spec found in LIST. */ |
ccc6cda3 | 666 | int |
726f6388 JA |
667 | get_job_spec (list) |
668 | WORD_LIST *list; | |
669 | { | |
670 | register char *word; | |
7117c2d2 | 671 | int job, jflags; |
726f6388 | 672 | |
ccc6cda3 | 673 | if (list == 0) |
95732b49 | 674 | return (js.j_current); |
726f6388 JA |
675 | |
676 | word = list->word->word; | |
677 | ||
ccc6cda3 | 678 | if (*word == '\0') |
b80f6443 | 679 | return (NO_JOB); |
726f6388 JA |
680 | |
681 | if (*word == '%') | |
682 | word++; | |
683 | ||
f73dda09 | 684 | if (DIGIT (*word) && all_digits (word)) |
ccc6cda3 JA |
685 | { |
686 | job = atoi (word); | |
95732b49 | 687 | return (job > js.j_jobslots ? NO_JOB : job - 1); |
ccc6cda3 | 688 | } |
726f6388 | 689 | |
7117c2d2 | 690 | jflags = 0; |
726f6388 JA |
691 | switch (*word) |
692 | { | |
693 | case 0: | |
694 | case '%': | |
695 | case '+': | |
95732b49 | 696 | return (js.j_current); |
726f6388 JA |
697 | |
698 | case '-': | |
95732b49 | 699 | return (js.j_previous); |
726f6388 JA |
700 | |
701 | case '?': /* Substring search requested. */ | |
7117c2d2 | 702 | jflags |= JM_SUBSTRING; |
726f6388 | 703 | word++; |
ccc6cda3 | 704 | /* FALLTHROUGH */ |
726f6388 JA |
705 | |
706 | default: | |
7117c2d2 | 707 | return get_job_by_name (word, jflags); |
726f6388 JA |
708 | } |
709 | } | |
710 | #endif /* JOB_CONTROL */ | |
711 | ||
b80f6443 JA |
712 | /* |
713 | * NOTE: `kill' calls this function with forcecols == 0 | |
714 | */ | |
726f6388 | 715 | int |
ccc6cda3 JA |
716 | display_signal_list (list, forcecols) |
717 | WORD_LIST *list; | |
718 | int forcecols; | |
726f6388 | 719 | { |
ccc6cda3 JA |
720 | register int i, column; |
721 | char *name; | |
b80f6443 | 722 | int result, signum, dflags; |
7117c2d2 | 723 | intmax_t lsignum; |
726f6388 | 724 | |
ccc6cda3 JA |
725 | result = EXECUTION_SUCCESS; |
726 | if (!list) | |
726f6388 | 727 | { |
ccc6cda3 JA |
728 | for (i = 1, column = 0; i < NSIG; i++) |
729 | { | |
730 | name = signal_name (i); | |
731 | if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7)) | |
732 | continue; | |
726f6388 | 733 | |
ccc6cda3 | 734 | if (posixly_correct && !forcecols) |
b80f6443 JA |
735 | { |
736 | /* This is for the kill builtin. POSIX.2 says the signal names | |
737 | are displayed without the `SIG' prefix. */ | |
738 | if (STREQN (name, "SIG", 3)) | |
739 | name += 3; | |
740 | printf ("%s%s", name, (i == NSIG - 1) ? "" : " "); | |
741 | } | |
ccc6cda3 JA |
742 | else |
743 | { | |
744 | printf ("%2d) %s", i, name); | |
745 | ||
3185942a | 746 | if (++column < 5) |
ccc6cda3 JA |
747 | printf ("\t"); |
748 | else | |
749 | { | |
750 | printf ("\n"); | |
751 | column = 0; | |
752 | } | |
753 | } | |
754 | } | |
726f6388 | 755 | |
ccc6cda3 JA |
756 | if ((posixly_correct && !forcecols) || column != 0) |
757 | printf ("\n"); | |
758 | return result; | |
759 | } | |
726f6388 | 760 | |
ccc6cda3 JA |
761 | /* List individual signal names or numbers. */ |
762 | while (list) | |
726f6388 | 763 | { |
7117c2d2 | 764 | if (legal_number (list->word->word, &lsignum)) |
ccc6cda3 JA |
765 | { |
766 | /* This is specified by Posix.2 so that exit statuses can be | |
767 | mapped into signal numbers. */ | |
7117c2d2 JA |
768 | if (lsignum > 128) |
769 | lsignum -= 128; | |
770 | if (lsignum < 0 || lsignum >= NSIG) | |
ccc6cda3 | 771 | { |
7117c2d2 | 772 | sh_invalidsig (list->word->word); |
ccc6cda3 JA |
773 | result = EXECUTION_FAILURE; |
774 | list = list->next; | |
775 | continue; | |
776 | } | |
777 | ||
7117c2d2 | 778 | signum = lsignum; |
ccc6cda3 JA |
779 | name = signal_name (signum); |
780 | if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7)) | |
781 | { | |
782 | list = list->next; | |
783 | continue; | |
784 | } | |
d166f048 JA |
785 | #if defined (JOB_CONTROL) |
786 | /* POSIX.2 says that `kill -l signum' prints the signal name without | |
787 | the `SIG' prefix. */ | |
788 | printf ("%s\n", (this_shell_builtin == kill_builtin) ? name + 3 : name); | |
789 | #else | |
ccc6cda3 | 790 | printf ("%s\n", name); |
d166f048 | 791 | #endif |
ccc6cda3 JA |
792 | } |
793 | else | |
794 | { | |
b80f6443 JA |
795 | dflags = DSIG_NOCASE; |
796 | if (posixly_correct == 0 || this_shell_builtin != kill_builtin) | |
797 | dflags |= DSIG_SIGPREFIX; | |
798 | signum = decode_signal (list->word->word, dflags); | |
ccc6cda3 JA |
799 | if (signum == NO_SIG) |
800 | { | |
7117c2d2 | 801 | sh_invalidsig (list->word->word); |
ccc6cda3 JA |
802 | result = EXECUTION_FAILURE; |
803 | list = list->next; | |
804 | continue; | |
805 | } | |
7117c2d2 | 806 | printf ("%d\n", signum); |
ccc6cda3 JA |
807 | } |
808 | list = list->next; | |
726f6388 | 809 | } |
ccc6cda3 | 810 | return (result); |
726f6388 JA |
811 | } |
812 | ||
ccc6cda3 JA |
813 | /* **************************************************************** */ |
814 | /* */ | |
815 | /* Finding builtin commands and their functions */ | |
816 | /* */ | |
817 | /* **************************************************************** */ | |
818 | ||
819 | /* Perform a binary search and return the address of the builtin function | |
820 | whose name is NAME. If the function couldn't be found, or the builtin | |
821 | is disabled or has no function associated with it, return NULL. | |
822 | Return the address of the builtin. | |
726f6388 | 823 | DISABLED_OKAY means find it even if the builtin is disabled. */ |
ccc6cda3 | 824 | struct builtin * |
726f6388 JA |
825 | builtin_address_internal (name, disabled_okay) |
826 | char *name; | |
827 | int disabled_okay; | |
828 | { | |
829 | int hi, lo, mid, j; | |
830 | ||
831 | hi = num_shell_builtins - 1; | |
832 | lo = 0; | |
833 | ||
834 | while (lo <= hi) | |
835 | { | |
836 | mid = (lo + hi) / 2; | |
837 | ||
838 | j = shell_builtins[mid].name[0] - name[0]; | |
839 | ||
840 | if (j == 0) | |
841 | j = strcmp (shell_builtins[mid].name, name); | |
842 | ||
843 | if (j == 0) | |
844 | { | |
845 | /* It must have a function pointer. It must be enabled, or we | |
ccc6cda3 JA |
846 | must have explicitly allowed disabled functions to be found, |
847 | and it must not have been deleted. */ | |
726f6388 | 848 | if (shell_builtins[mid].function && |
ccc6cda3 | 849 | ((shell_builtins[mid].flags & BUILTIN_DELETED) == 0) && |
726f6388 | 850 | ((shell_builtins[mid].flags & BUILTIN_ENABLED) || disabled_okay)) |
ccc6cda3 | 851 | return (&shell_builtins[mid]); |
726f6388 | 852 | else |
ccc6cda3 | 853 | return ((struct builtin *)NULL); |
726f6388 JA |
854 | } |
855 | if (j > 0) | |
856 | hi = mid - 1; | |
857 | else | |
858 | lo = mid + 1; | |
859 | } | |
ccc6cda3 | 860 | return ((struct builtin *)NULL); |
726f6388 JA |
861 | } |
862 | ||
ccc6cda3 | 863 | /* Return the pointer to the function implementing builtin command NAME. */ |
f73dda09 | 864 | sh_builtin_func_t * |
726f6388 | 865 | find_shell_builtin (name) |
ccc6cda3 | 866 | char *name; |
726f6388 | 867 | { |
ccc6cda3 | 868 | current_builtin = builtin_address_internal (name, 0); |
f73dda09 | 869 | return (current_builtin ? current_builtin->function : (sh_builtin_func_t *)NULL); |
726f6388 JA |
870 | } |
871 | ||
ccc6cda3 | 872 | /* Return the address of builtin with NAME, whether it is enabled or not. */ |
f73dda09 | 873 | sh_builtin_func_t * |
726f6388 JA |
874 | builtin_address (name) |
875 | char *name; | |
876 | { | |
ccc6cda3 | 877 | current_builtin = builtin_address_internal (name, 1); |
f73dda09 | 878 | return (current_builtin ? current_builtin->function : (sh_builtin_func_t *)NULL); |
726f6388 JA |
879 | } |
880 | ||
ccc6cda3 JA |
881 | /* Return the function implementing the builtin NAME, but only if it is a |
882 | POSIX.2 special builtin. */ | |
f73dda09 | 883 | sh_builtin_func_t * |
ccc6cda3 JA |
884 | find_special_builtin (name) |
885 | char *name; | |
886 | { | |
887 | current_builtin = builtin_address_internal (name, 0); | |
888 | return ((current_builtin && (current_builtin->flags & SPECIAL_BUILTIN)) ? | |
889 | current_builtin->function : | |
f73dda09 | 890 | (sh_builtin_func_t *)NULL); |
ccc6cda3 JA |
891 | } |
892 | ||
726f6388 JA |
893 | static int |
894 | shell_builtin_compare (sbp1, sbp2) | |
895 | struct builtin *sbp1, *sbp2; | |
896 | { | |
897 | int result; | |
898 | ||
899 | if ((result = sbp1->name[0] - sbp2->name[0]) == 0) | |
900 | result = strcmp (sbp1->name, sbp2->name); | |
901 | ||
902 | return (result); | |
903 | } | |
904 | ||
905 | /* Sort the table of shell builtins so that the binary search will work | |
906 | in find_shell_builtin. */ | |
907 | void | |
908 | initialize_shell_builtins () | |
909 | { | |
910 | qsort (shell_builtins, num_shell_builtins, sizeof (struct builtin), | |
bb70624e | 911 | (QSFUNC *)shell_builtin_compare); |
726f6388 | 912 | } |
a0c0a00f CR |
913 | |
914 | #if !defined (HELP_BUILTIN) | |
915 | void | |
916 | builtin_help () | |
917 | { | |
918 | printf ("%s: %s\n", this_command_name, _("help not available in this version")); | |
919 | } | |
920 | #endif |