]>
Commit | Line | Data |
---|---|---|
726f6388 JA |
1 | This file is help.def, from which is created help.c. |
2 | It implements the builtin "help" in Bash. | |
3 | ||
a0c0a00f | 4 | Copyright (C) 1987-2015 Free Software Foundation, Inc. |
726f6388 JA |
5 | |
6 | This file is part of GNU Bash, the Bourne Again SHell. | |
7 | ||
3185942a JA |
8 | Bash is free software: you can redistribute it and/or modify |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation, either version 3 of the License, or | |
11 | (at your option) any later version. | |
726f6388 | 12 | |
3185942a JA |
13 | Bash is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
726f6388 | 17 | |
3185942a JA |
18 | You should have received a copy of the GNU General Public License |
19 | along with Bash. If not, see <http://www.gnu.org/licenses/>. | |
726f6388 JA |
20 | |
21 | $PRODUCES help.c | |
22 | ||
23 | $BUILTIN help | |
24 | $FUNCTION help_builtin | |
ccc6cda3 | 25 | $DEPENDS_ON HELP_BUILTIN |
0001803f | 26 | $SHORT_DOC help [-dms] [pattern ...] |
3185942a JA |
27 | Display information about builtin commands. |
28 | ||
29 | Displays brief summaries of builtin commands. If PATTERN is | |
726f6388 | 30 | specified, gives detailed help on all commands matching PATTERN, |
3185942a JA |
31 | otherwise the list of help topics is printed. |
32 | ||
33 | Options: | |
34 | -d output short description for each topic | |
35 | -m display usage in pseudo-manpage format | |
36 | -s output only a short usage synopsis for each topic matching | |
a0c0a00f | 37 | PATTERN |
3185942a JA |
38 | |
39 | Arguments: | |
40 | PATTERN Pattern specifiying a help topic | |
41 | ||
42 | Exit Status: | |
43 | Returns success unless PATTERN is not found or an invalid option is given. | |
726f6388 JA |
44 | $END |
45 | ||
ccc6cda3 JA |
46 | #include <config.h> |
47 | ||
48 | #if defined (HELP_BUILTIN) | |
726f6388 | 49 | #include <stdio.h> |
ccc6cda3 JA |
50 | |
51 | #if defined (HAVE_UNISTD_H) | |
cce855bc JA |
52 | # ifdef _MINIX |
53 | # include <sys/types.h> | |
54 | # endif | |
ccc6cda3 JA |
55 | # include <unistd.h> |
56 | #endif | |
57 | ||
7117c2d2 JA |
58 | #include <errno.h> |
59 | ||
60 | #include <filecntl.h> | |
a0c0a00f | 61 | #include <stddef.h> |
7117c2d2 | 62 | |
b80f6443 JA |
63 | #include "../bashintl.h" |
64 | ||
726f6388 JA |
65 | #include "../shell.h" |
66 | #include "../builtins.h" | |
cce855bc | 67 | #include "../pathexp.h" |
f73dda09 | 68 | #include "common.h" |
ccc6cda3 JA |
69 | #include "bashgetopt.h" |
70 | ||
f73dda09 | 71 | #include <glob/strmatch.h> |
ccc6cda3 | 72 | #include <glob/glob.h> |
726f6388 | 73 | |
7117c2d2 JA |
74 | #ifndef errno |
75 | extern int errno; | |
76 | #endif | |
77 | ||
3185942a JA |
78 | extern const char * const bash_copyright; |
79 | extern const char * const bash_license; | |
80 | ||
a0c0a00f CR |
81 | extern char *this_command_name; |
82 | extern struct builtin *current_builtin; | |
83 | ||
7117c2d2 | 84 | static void show_builtin_command_help __P((void)); |
3185942a JA |
85 | static int open_helpfile __P((char *)); |
86 | static void show_desc __P((char *, int)); | |
87 | static void show_manpage __P((char *, int)); | |
7117c2d2 | 88 | static void show_longdoc __P((int)); |
726f6388 JA |
89 | |
90 | /* Print out a list of the known functions in the shell, and what they do. | |
91 | If LIST is supplied, print out the list which matches for each pattern | |
92 | specified. */ | |
ccc6cda3 | 93 | int |
726f6388 JA |
94 | help_builtin (list) |
95 | WORD_LIST *list; | |
96 | { | |
7117c2d2 | 97 | register int i; |
ccc6cda3 | 98 | char *pattern, *name; |
ac50fbac | 99 | int plen, match_found, sflag, dflag, mflag, m, pass, this_found; |
726f6388 | 100 | |
3185942a | 101 | dflag = sflag = mflag = 0; |
ccc6cda3 | 102 | reset_internal_getopt (); |
3185942a | 103 | while ((i = internal_getopt (list, "dms")) != -1) |
ccc6cda3 JA |
104 | { |
105 | switch (i) | |
726f6388 | 106 | { |
3185942a JA |
107 | case 'd': |
108 | dflag = 1; | |
109 | break; | |
110 | case 'm': | |
111 | mflag = 1; | |
112 | break; | |
bb70624e JA |
113 | case 's': |
114 | sflag = 1; | |
115 | break; | |
a0c0a00f | 116 | CASE_HELPOPT; |
ccc6cda3 JA |
117 | default: |
118 | builtin_usage (); | |
119 | return (EX_USAGE); | |
726f6388 | 120 | } |
726f6388 | 121 | } |
ccc6cda3 | 122 | list = loptend; |
726f6388 | 123 | |
d166f048 JA |
124 | if (list == 0) |
125 | { | |
126 | show_shell_version (0); | |
127 | show_builtin_command_help (); | |
128 | return (EXECUTION_SUCCESS); | |
129 | } | |
130 | ||
ccc6cda3 | 131 | /* We should consider making `help bash' do something. */ |
726f6388 | 132 | |
ccc6cda3 JA |
133 | if (glob_pattern_p (list->word->word)) |
134 | { | |
3185942a | 135 | printf (ngettext ("Shell commands matching keyword `", "Shell commands matching keywords `", (list->next ? 2 : 1))); |
ccc6cda3 JA |
136 | print_word_list (list, ", "); |
137 | printf ("'\n\n"); | |
138 | } | |
726f6388 | 139 | |
ccc6cda3 JA |
140 | for (match_found = 0, pattern = ""; list; list = list->next) |
141 | { | |
142 | pattern = list->word->word; | |
143 | plen = strlen (pattern); | |
726f6388 | 144 | |
ac50fbac | 145 | for (pass = 1, this_found = 0; pass < 3; pass++) |
ccc6cda3 | 146 | { |
ac50fbac | 147 | for (i = 0; name = shell_builtins[i].name; i++) |
726f6388 | 148 | { |
ac50fbac CR |
149 | QUIT; |
150 | ||
151 | /* First pass: look for exact string or pattern matches. | |
152 | Second pass: look for prefix matches like bash-4.2 */ | |
153 | if (pass == 1) | |
154 | m = (strcmp (pattern, name) == 0) || | |
155 | (strmatch (pattern, name, FNMATCH_EXTFLAG) != FNM_NOMATCH); | |
156 | else | |
157 | m = strncmp (pattern, name, plen) == 0; | |
158 | ||
159 | if (m) | |
160 | { | |
161 | this_found = 1; | |
162 | match_found++; | |
163 | if (dflag) | |
164 | { | |
165 | show_desc (name, i); | |
166 | continue; | |
167 | } | |
168 | else if (mflag) | |
169 | { | |
170 | show_manpage (name, i); | |
171 | continue; | |
172 | } | |
173 | ||
174 | printf ("%s: %s\n", name, _(shell_builtins[i].short_doc)); | |
175 | ||
176 | if (sflag == 0) | |
177 | show_longdoc (i); | |
178 | } | |
726f6388 | 179 | } |
ac50fbac CR |
180 | if (pass == 1 && this_found == 1) |
181 | break; | |
726f6388 | 182 | } |
ccc6cda3 | 183 | } |
726f6388 | 184 | |
ccc6cda3 JA |
185 | if (match_found == 0) |
186 | { | |
b80f6443 | 187 | builtin_error (_("no help topics match `%s'. Try `help help' or `man -k %s' or `info %s'."), pattern, pattern, pattern); |
ccc6cda3 | 188 | return (EXECUTION_FAILURE); |
726f6388 | 189 | } |
ccc6cda3 | 190 | |
726f6388 JA |
191 | fflush (stdout); |
192 | return (EXECUTION_SUCCESS); | |
193 | } | |
ccc6cda3 | 194 | |
a0c0a00f CR |
195 | void |
196 | builtin_help () | |
197 | { | |
198 | int ind; | |
199 | ptrdiff_t d; | |
200 | ||
201 | current_builtin = builtin_address_internal (this_command_name, 0); | |
202 | if (current_builtin == 0) | |
203 | return; | |
204 | ||
205 | d = current_builtin - shell_builtins; | |
206 | ||
207 | #if defined (__STDC__) | |
208 | ind = (int)d; | |
209 | #else | |
210 | ind = (int)d / sizeof (struct builtin); | |
211 | #endif | |
212 | ||
213 | printf ("%s: %s\n", this_command_name, _(shell_builtins[ind].short_doc)); | |
214 | show_longdoc (ind); | |
215 | } | |
216 | ||
3185942a JA |
217 | static int |
218 | open_helpfile (name) | |
219 | char *name; | |
220 | { | |
221 | int fd; | |
222 | ||
223 | fd = open (name, O_RDONLY); | |
224 | if (fd == -1) | |
225 | { | |
226 | builtin_error (_("%s: cannot open: %s"), name, strerror (errno)); | |
227 | return -1; | |
228 | } | |
229 | return fd; | |
230 | } | |
231 | ||
7117c2d2 JA |
232 | /* By convention, enforced by mkbuiltins.c, if separate help files are being |
233 | used, the long_doc array contains one string -- the full pathname of the | |
234 | help file for this builtin. */ | |
235 | static void | |
236 | show_longdoc (i) | |
237 | int i; | |
238 | { | |
239 | register int j; | |
240 | char * const *doc; | |
241 | int fd; | |
242 | ||
243 | doc = shell_builtins[i].long_doc; | |
244 | ||
245 | if (doc && doc[0] && *doc[0] == '/' && doc[1] == (char *)NULL) | |
246 | { | |
3185942a JA |
247 | fd = open_helpfile (doc[0]); |
248 | if (fd < 0) | |
249 | return; | |
7117c2d2 JA |
250 | zcatfd (fd, 1, doc[0]); |
251 | close (fd); | |
252 | } | |
ac50fbac | 253 | else if (doc) |
7117c2d2 | 254 | for (j = 0; doc[j]; j++) |
95732b49 | 255 | printf ("%*s%s\n", BASE_INDENT, " ", _(doc[j])); |
7117c2d2 JA |
256 | } |
257 | ||
3185942a JA |
258 | static void |
259 | show_desc (name, i) | |
260 | char *name; | |
261 | int i; | |
262 | { | |
263 | register int j; | |
264 | char **doc, *line; | |
265 | int fd, usefile; | |
266 | ||
267 | doc = (char **)shell_builtins[i].long_doc; | |
268 | ||
269 | usefile = (doc && doc[0] && *doc[0] == '/' && doc[1] == (char *)NULL); | |
270 | if (usefile) | |
271 | { | |
272 | fd = open_helpfile (doc[0]); | |
273 | if (fd < 0) | |
274 | return; | |
275 | zmapfd (fd, &line, doc[0]); | |
276 | close (fd); | |
277 | } | |
278 | else | |
279 | line = doc ? doc[0] : (char *)NULL; | |
280 | ||
281 | printf ("%s - ", name); | |
282 | for (j = 0; line && line[j]; j++) | |
283 | { | |
284 | putchar (line[j]); | |
285 | if (line[j] == '\n') | |
286 | break; | |
287 | } | |
288 | ||
289 | fflush (stdout); | |
290 | ||
291 | if (usefile) | |
292 | free (line); | |
293 | } | |
294 | ||
295 | /* Print builtin help in pseudo-manpage format. */ | |
296 | static void | |
297 | show_manpage (name, i) | |
298 | char *name; | |
299 | int i; | |
300 | { | |
301 | register int j; | |
302 | char **doc, *line; | |
303 | int fd, usefile; | |
304 | ||
305 | doc = (char **)shell_builtins[i].long_doc; | |
306 | ||
307 | usefile = (doc && doc[0] && *doc[0] == '/' && doc[1] == (char *)NULL); | |
308 | if (usefile) | |
309 | { | |
310 | fd = open_helpfile (doc[0]); | |
311 | if (fd < 0) | |
312 | return; | |
313 | zmapfd (fd, &line, doc[0]); | |
314 | close (fd); | |
315 | } | |
316 | else | |
317 | line = doc ? _(doc[0]) : (char *)NULL; | |
318 | ||
319 | /* NAME */ | |
320 | printf ("NAME\n"); | |
321 | printf ("%*s%s - ", BASE_INDENT, " ", name); | |
322 | for (j = 0; line && line[j]; j++) | |
323 | { | |
324 | putchar (line[j]); | |
325 | if (line[j] == '\n') | |
326 | break; | |
327 | } | |
328 | printf ("\n"); | |
329 | ||
330 | /* SYNOPSIS */ | |
331 | printf ("SYNOPSIS\n"); | |
495aee44 | 332 | printf ("%*s%s\n\n", BASE_INDENT, " ", _(shell_builtins[i].short_doc)); |
3185942a JA |
333 | |
334 | /* DESCRIPTION */ | |
335 | printf ("DESCRIPTION\n"); | |
336 | if (usefile == 0) | |
337 | { | |
338 | for (j = 0; doc[j]; j++) | |
339 | printf ("%*s%s\n", BASE_INDENT, " ", _(doc[j])); | |
340 | } | |
341 | else | |
342 | { | |
343 | for (j = 0; line && line[j]; j++) | |
344 | { | |
345 | putchar (line[j]); | |
346 | if (line[j] == '\n') | |
347 | printf ("%*s", BASE_INDENT, " "); | |
348 | } | |
349 | } | |
350 | putchar ('\n'); | |
351 | ||
352 | /* SEE ALSO */ | |
353 | printf ("SEE ALSO\n"); | |
354 | printf ("%*sbash(1)\n\n", BASE_INDENT, " "); | |
355 | ||
356 | /* IMPLEMENTATION */ | |
357 | printf ("IMPLEMENTATION\n"); | |
358 | printf ("%*s", BASE_INDENT, " "); | |
359 | show_shell_version (0); | |
360 | printf ("%*s", BASE_INDENT, " "); | |
361 | printf ("%s\n", _(bash_copyright)); | |
362 | printf ("%*s", BASE_INDENT, " "); | |
363 | printf ("%s\n", _(bash_license)); | |
364 | ||
365 | fflush (stdout); | |
366 | if (usefile) | |
367 | free (line); | |
368 | } | |
369 | ||
ac50fbac CR |
370 | static void |
371 | dispcolumn (i, buf, bufsize, width, height) | |
372 | int i; | |
373 | char *buf; | |
374 | size_t bufsize; | |
375 | int width, height; | |
376 | { | |
377 | int j; | |
a0c0a00f | 378 | int dispcols; |
ac50fbac CR |
379 | char *helpdoc; |
380 | ||
381 | /* first column */ | |
382 | helpdoc = _(shell_builtins[i].short_doc); | |
383 | ||
384 | buf[0] = (shell_builtins[i].flags & BUILTIN_ENABLED) ? ' ' : '*'; | |
385 | strncpy (buf + 1, helpdoc, width - 2); | |
386 | buf[width - 2] = '>'; /* indicate truncation */ | |
387 | buf[width - 1] = '\0'; | |
388 | printf ("%s", buf); | |
389 | if (((i << 1) >= num_shell_builtins) || (i+height >= num_shell_builtins)) | |
390 | { | |
391 | printf ("\n"); | |
392 | return; | |
393 | } | |
394 | ||
a0c0a00f | 395 | dispcols = strlen (buf); |
ac50fbac | 396 | /* two spaces */ |
a0c0a00f | 397 | for (j = dispcols; j < width; j++) |
ac50fbac CR |
398 | putc (' ', stdout); |
399 | ||
400 | /* second column */ | |
401 | helpdoc = _(shell_builtins[i+height].short_doc); | |
402 | ||
403 | buf[0] = (shell_builtins[i+height].flags & BUILTIN_ENABLED) ? ' ' : '*'; | |
404 | strncpy (buf + 1, helpdoc, width - 3); | |
405 | buf[width - 3] = '>'; /* indicate truncation */ | |
406 | buf[width - 2] = '\0'; | |
407 | ||
408 | printf ("%s\n", buf); | |
409 | } | |
410 | ||
411 | #if defined (HANDLE_MULTIBYTE) | |
412 | static void | |
413 | wdispcolumn (i, buf, bufsize, width, height) | |
414 | int i; | |
415 | char *buf; | |
416 | size_t bufsize; | |
417 | int width, height; | |
418 | { | |
419 | int j; | |
a0c0a00f | 420 | int dispcols, dispchars; |
ac50fbac CR |
421 | char *helpdoc; |
422 | wchar_t *wcstr; | |
423 | size_t slen, n; | |
424 | int wclen; | |
425 | ||
426 | /* first column */ | |
427 | helpdoc = _(shell_builtins[i].short_doc); | |
428 | ||
429 | wcstr = 0; | |
430 | slen = mbstowcs ((wchar_t *)0, helpdoc, 0); | |
431 | if (slen == -1) | |
432 | { | |
433 | dispcolumn (i, buf, bufsize, width, height); | |
434 | return; | |
435 | } | |
436 | ||
437 | /* No bigger than the passed max width */ | |
438 | if (slen >= width) | |
439 | slen = width - 2; | |
440 | wcstr = (wchar_t *)xmalloc (sizeof (wchar_t) * (width + 2)); | |
441 | n = mbstowcs (wcstr+1, helpdoc, slen + 1); | |
442 | wcstr[n+1] = L'\0'; | |
443 | ||
444 | /* Turn tabs and newlines into spaces for column display, since wcwidth | |
445 | returns -1 for them */ | |
446 | for (j = 1; j < n; j++) | |
447 | if (wcstr[j] == L'\n' || wcstr[j] == L'\t') | |
448 | wcstr[j] = L' '; | |
449 | ||
a0c0a00f CR |
450 | /* dispchars == number of characters that will be displayed */ |
451 | dispchars = wcsnwidth (wcstr+1, slen, width - 2); | |
452 | /* dispcols == number of columns required to display DISPCHARS */ | |
453 | dispcols = wcswidth (wcstr+1, dispchars) + 1; /* +1 for ' ' or '*' */ | |
454 | ||
ac50fbac CR |
455 | wcstr[0] = (shell_builtins[i].flags & BUILTIN_ENABLED) ? L' ' : L'*'; |
456 | ||
a0c0a00f CR |
457 | if (dispcols >= width-2) |
458 | { | |
459 | wcstr[dispchars] = L'>'; /* indicate truncation */ | |
460 | wcstr[dispchars+1] = L'\0'; | |
461 | } | |
ac50fbac CR |
462 | |
463 | printf ("%ls", wcstr); | |
464 | if (((i << 1) >= num_shell_builtins) || (i+height >= num_shell_builtins)) | |
465 | { | |
466 | printf ("\n"); | |
a0c0a00f | 467 | free (wcstr); |
ac50fbac CR |
468 | return; |
469 | } | |
470 | ||
471 | /* at least one space */ | |
a0c0a00f | 472 | for (j = dispcols; j < width; j++) |
ac50fbac CR |
473 | putc (' ', stdout); |
474 | ||
475 | /* second column */ | |
476 | helpdoc = _(shell_builtins[i+height].short_doc); | |
477 | slen = mbstowcs ((wchar_t *)0, helpdoc, 0); | |
478 | if (slen == -1) | |
479 | { | |
480 | /* for now */ | |
481 | printf ("%c%s\n", (shell_builtins[i+height].flags & BUILTIN_ENABLED) ? ' ' : '*', helpdoc); | |
a0c0a00f | 482 | free (wcstr); |
ac50fbac CR |
483 | return; |
484 | } | |
485 | ||
486 | /* Reuse wcstr since it is already width wide chars long */ | |
487 | if (slen >= width) | |
488 | slen = width - 2; | |
489 | n = mbstowcs (wcstr+1, helpdoc, slen + 1); | |
490 | wcstr[n+1] = L'\0'; /* make sure null-terminated */ | |
491 | ||
492 | /* Turn tabs and newlines into spaces for column display */ | |
493 | for (j = 1; j < n; j++) | |
494 | if (wcstr[j] == L'\n' || wcstr[j] == L'\t') | |
495 | wcstr[j] = L' '; | |
496 | ||
a0c0a00f CR |
497 | /* dispchars == number of characters that will be displayed */ |
498 | dispchars = wcsnwidth (wcstr+1, slen, width - 2); | |
499 | dispcols = wcswidth (wcstr+1, dispchars) + 1; /* +1 for ' ' or '*' */ | |
ac50fbac CR |
500 | |
501 | wcstr[0] = (shell_builtins[i+height].flags & BUILTIN_ENABLED) ? L' ' : L'*'; | |
502 | ||
a0c0a00f CR |
503 | /* The dispchars-1 is there for terminals that behave strangely when you |
504 | have \n in the nth column for terminal width n; this is what bash-4.3 | |
505 | did. */ | |
506 | if (dispcols >= width - 2) | |
507 | { | |
508 | wcstr[dispchars-1] = L'>'; /* indicate truncation */ | |
509 | wcstr[dispchars] = L'\0'; | |
510 | } | |
ac50fbac CR |
511 | |
512 | printf ("%ls\n", wcstr); | |
513 | ||
514 | free (wcstr); | |
515 | } | |
516 | #endif /* HANDLE_MULTIBYTE */ | |
517 | ||
ccc6cda3 JA |
518 | static void |
519 | show_builtin_command_help () | |
520 | { | |
521 | int i, j; | |
3185942a JA |
522 | int height, width; |
523 | char *t, blurb[128]; | |
ccc6cda3 JA |
524 | |
525 | printf ( | |
b80f6443 | 526 | _("These shell commands are defined internally. Type `help' to see this list.\n\ |
ccc6cda3 JA |
527 | Type `help name' to find out more about the function `name'.\n\ |
528 | Use `info bash' to find out more about the shell in general.\n\ | |
7117c2d2 | 529 | Use `man -k' or `info' to find out more about commands not in this list.\n\ |
ccc6cda3 JA |
530 | \n\ |
531 | A star (*) next to a name means that the command is disabled.\n\ | |
b80f6443 | 532 | \n")); |
ccc6cda3 | 533 | |
3185942a JA |
534 | t = get_string_value ("COLUMNS"); |
535 | width = (t && *t) ? atoi (t) : 80; | |
536 | if (width <= 0) | |
537 | width = 80; | |
538 | ||
539 | width /= 2; | |
540 | if (width > sizeof (blurb)) | |
541 | width = sizeof (blurb); | |
0001803f CR |
542 | if (width <= 3) |
543 | width = 40; | |
3185942a JA |
544 | height = (num_shell_builtins + 1) / 2; /* number of rows */ |
545 | ||
546 | for (i = 0; i < height; i++) | |
ccc6cda3 JA |
547 | { |
548 | QUIT; | |
3185942a | 549 | |
ac50fbac CR |
550 | #if defined (HANDLE_MULTIBYTE) |
551 | if (MB_CUR_MAX > 1) | |
552 | wdispcolumn (i, blurb, sizeof (blurb), width, height); | |
553 | else | |
554 | #endif | |
555 | dispcolumn (i, blurb, sizeof (blurb), width, height); | |
ccc6cda3 | 556 | } |
ccc6cda3 JA |
557 | } |
558 | #endif /* HELP_BUILTIN */ |