]>
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 | ||
ac50fbac | 4 | Copyright (C) 1987-2013 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 | |
37 | PATTERN | |
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> | |
61 | ||
b80f6443 JA |
62 | #include "../bashintl.h" |
63 | ||
726f6388 JA |
64 | #include "../shell.h" |
65 | #include "../builtins.h" | |
cce855bc | 66 | #include "../pathexp.h" |
f73dda09 | 67 | #include "common.h" |
ccc6cda3 JA |
68 | #include "bashgetopt.h" |
69 | ||
f73dda09 | 70 | #include <glob/strmatch.h> |
ccc6cda3 | 71 | #include <glob/glob.h> |
726f6388 | 72 | |
7117c2d2 JA |
73 | #ifndef errno |
74 | extern int errno; | |
75 | #endif | |
76 | ||
3185942a JA |
77 | extern const char * const bash_copyright; |
78 | extern const char * const bash_license; | |
79 | ||
7117c2d2 | 80 | static void show_builtin_command_help __P((void)); |
3185942a JA |
81 | static int open_helpfile __P((char *)); |
82 | static void show_desc __P((char *, int)); | |
83 | static void show_manpage __P((char *, int)); | |
7117c2d2 | 84 | static void show_longdoc __P((int)); |
726f6388 JA |
85 | |
86 | /* Print out a list of the known functions in the shell, and what they do. | |
87 | If LIST is supplied, print out the list which matches for each pattern | |
88 | specified. */ | |
ccc6cda3 | 89 | int |
726f6388 JA |
90 | help_builtin (list) |
91 | WORD_LIST *list; | |
92 | { | |
7117c2d2 | 93 | register int i; |
ccc6cda3 | 94 | char *pattern, *name; |
ac50fbac | 95 | int plen, match_found, sflag, dflag, mflag, m, pass, this_found; |
726f6388 | 96 | |
3185942a | 97 | dflag = sflag = mflag = 0; |
ccc6cda3 | 98 | reset_internal_getopt (); |
3185942a | 99 | while ((i = internal_getopt (list, "dms")) != -1) |
ccc6cda3 JA |
100 | { |
101 | switch (i) | |
726f6388 | 102 | { |
3185942a JA |
103 | case 'd': |
104 | dflag = 1; | |
105 | break; | |
106 | case 'm': | |
107 | mflag = 1; | |
108 | break; | |
bb70624e JA |
109 | case 's': |
110 | sflag = 1; | |
111 | break; | |
ccc6cda3 JA |
112 | default: |
113 | builtin_usage (); | |
114 | return (EX_USAGE); | |
726f6388 | 115 | } |
726f6388 | 116 | } |
ccc6cda3 | 117 | list = loptend; |
726f6388 | 118 | |
d166f048 JA |
119 | if (list == 0) |
120 | { | |
121 | show_shell_version (0); | |
122 | show_builtin_command_help (); | |
123 | return (EXECUTION_SUCCESS); | |
124 | } | |
125 | ||
ccc6cda3 | 126 | /* We should consider making `help bash' do something. */ |
726f6388 | 127 | |
ccc6cda3 JA |
128 | if (glob_pattern_p (list->word->word)) |
129 | { | |
3185942a | 130 | printf (ngettext ("Shell commands matching keyword `", "Shell commands matching keywords `", (list->next ? 2 : 1))); |
ccc6cda3 JA |
131 | print_word_list (list, ", "); |
132 | printf ("'\n\n"); | |
133 | } | |
726f6388 | 134 | |
ccc6cda3 JA |
135 | for (match_found = 0, pattern = ""; list; list = list->next) |
136 | { | |
137 | pattern = list->word->word; | |
138 | plen = strlen (pattern); | |
726f6388 | 139 | |
ac50fbac | 140 | for (pass = 1, this_found = 0; pass < 3; pass++) |
ccc6cda3 | 141 | { |
ac50fbac | 142 | for (i = 0; name = shell_builtins[i].name; i++) |
726f6388 | 143 | { |
ac50fbac CR |
144 | QUIT; |
145 | ||
146 | /* First pass: look for exact string or pattern matches. | |
147 | Second pass: look for prefix matches like bash-4.2 */ | |
148 | if (pass == 1) | |
149 | m = (strcmp (pattern, name) == 0) || | |
150 | (strmatch (pattern, name, FNMATCH_EXTFLAG) != FNM_NOMATCH); | |
151 | else | |
152 | m = strncmp (pattern, name, plen) == 0; | |
153 | ||
154 | if (m) | |
155 | { | |
156 | this_found = 1; | |
157 | match_found++; | |
158 | if (dflag) | |
159 | { | |
160 | show_desc (name, i); | |
161 | continue; | |
162 | } | |
163 | else if (mflag) | |
164 | { | |
165 | show_manpage (name, i); | |
166 | continue; | |
167 | } | |
168 | ||
169 | printf ("%s: %s\n", name, _(shell_builtins[i].short_doc)); | |
170 | ||
171 | if (sflag == 0) | |
172 | show_longdoc (i); | |
173 | } | |
726f6388 | 174 | } |
ac50fbac CR |
175 | if (pass == 1 && this_found == 1) |
176 | break; | |
726f6388 | 177 | } |
ccc6cda3 | 178 | } |
726f6388 | 179 | |
ccc6cda3 JA |
180 | if (match_found == 0) |
181 | { | |
b80f6443 | 182 | builtin_error (_("no help topics match `%s'. Try `help help' or `man -k %s' or `info %s'."), pattern, pattern, pattern); |
ccc6cda3 | 183 | return (EXECUTION_FAILURE); |
726f6388 | 184 | } |
ccc6cda3 | 185 | |
726f6388 JA |
186 | fflush (stdout); |
187 | return (EXECUTION_SUCCESS); | |
188 | } | |
ccc6cda3 | 189 | |
3185942a JA |
190 | static int |
191 | open_helpfile (name) | |
192 | char *name; | |
193 | { | |
194 | int fd; | |
195 | ||
196 | fd = open (name, O_RDONLY); | |
197 | if (fd == -1) | |
198 | { | |
199 | builtin_error (_("%s: cannot open: %s"), name, strerror (errno)); | |
200 | return -1; | |
201 | } | |
202 | return fd; | |
203 | } | |
204 | ||
7117c2d2 JA |
205 | /* By convention, enforced by mkbuiltins.c, if separate help files are being |
206 | used, the long_doc array contains one string -- the full pathname of the | |
207 | help file for this builtin. */ | |
208 | static void | |
209 | show_longdoc (i) | |
210 | int i; | |
211 | { | |
212 | register int j; | |
213 | char * const *doc; | |
214 | int fd; | |
215 | ||
216 | doc = shell_builtins[i].long_doc; | |
217 | ||
218 | if (doc && doc[0] && *doc[0] == '/' && doc[1] == (char *)NULL) | |
219 | { | |
3185942a JA |
220 | fd = open_helpfile (doc[0]); |
221 | if (fd < 0) | |
222 | return; | |
7117c2d2 JA |
223 | zcatfd (fd, 1, doc[0]); |
224 | close (fd); | |
225 | } | |
ac50fbac | 226 | else if (doc) |
7117c2d2 | 227 | for (j = 0; doc[j]; j++) |
95732b49 | 228 | printf ("%*s%s\n", BASE_INDENT, " ", _(doc[j])); |
7117c2d2 JA |
229 | } |
230 | ||
3185942a JA |
231 | static void |
232 | show_desc (name, i) | |
233 | char *name; | |
234 | int i; | |
235 | { | |
236 | register int j; | |
237 | char **doc, *line; | |
238 | int fd, usefile; | |
239 | ||
240 | doc = (char **)shell_builtins[i].long_doc; | |
241 | ||
242 | usefile = (doc && doc[0] && *doc[0] == '/' && doc[1] == (char *)NULL); | |
243 | if (usefile) | |
244 | { | |
245 | fd = open_helpfile (doc[0]); | |
246 | if (fd < 0) | |
247 | return; | |
248 | zmapfd (fd, &line, doc[0]); | |
249 | close (fd); | |
250 | } | |
251 | else | |
252 | line = doc ? doc[0] : (char *)NULL; | |
253 | ||
254 | printf ("%s - ", name); | |
255 | for (j = 0; line && line[j]; j++) | |
256 | { | |
257 | putchar (line[j]); | |
258 | if (line[j] == '\n') | |
259 | break; | |
260 | } | |
261 | ||
262 | fflush (stdout); | |
263 | ||
264 | if (usefile) | |
265 | free (line); | |
266 | } | |
267 | ||
268 | /* Print builtin help in pseudo-manpage format. */ | |
269 | static void | |
270 | show_manpage (name, i) | |
271 | char *name; | |
272 | int i; | |
273 | { | |
274 | register int j; | |
275 | char **doc, *line; | |
276 | int fd, usefile; | |
277 | ||
278 | doc = (char **)shell_builtins[i].long_doc; | |
279 | ||
280 | usefile = (doc && doc[0] && *doc[0] == '/' && doc[1] == (char *)NULL); | |
281 | if (usefile) | |
282 | { | |
283 | fd = open_helpfile (doc[0]); | |
284 | if (fd < 0) | |
285 | return; | |
286 | zmapfd (fd, &line, doc[0]); | |
287 | close (fd); | |
288 | } | |
289 | else | |
290 | line = doc ? _(doc[0]) : (char *)NULL; | |
291 | ||
292 | /* NAME */ | |
293 | printf ("NAME\n"); | |
294 | printf ("%*s%s - ", BASE_INDENT, " ", name); | |
295 | for (j = 0; line && line[j]; j++) | |
296 | { | |
297 | putchar (line[j]); | |
298 | if (line[j] == '\n') | |
299 | break; | |
300 | } | |
301 | printf ("\n"); | |
302 | ||
303 | /* SYNOPSIS */ | |
304 | printf ("SYNOPSIS\n"); | |
495aee44 | 305 | printf ("%*s%s\n\n", BASE_INDENT, " ", _(shell_builtins[i].short_doc)); |
3185942a JA |
306 | |
307 | /* DESCRIPTION */ | |
308 | printf ("DESCRIPTION\n"); | |
309 | if (usefile == 0) | |
310 | { | |
311 | for (j = 0; doc[j]; j++) | |
312 | printf ("%*s%s\n", BASE_INDENT, " ", _(doc[j])); | |
313 | } | |
314 | else | |
315 | { | |
316 | for (j = 0; line && line[j]; j++) | |
317 | { | |
318 | putchar (line[j]); | |
319 | if (line[j] == '\n') | |
320 | printf ("%*s", BASE_INDENT, " "); | |
321 | } | |
322 | } | |
323 | putchar ('\n'); | |
324 | ||
325 | /* SEE ALSO */ | |
326 | printf ("SEE ALSO\n"); | |
327 | printf ("%*sbash(1)\n\n", BASE_INDENT, " "); | |
328 | ||
329 | /* IMPLEMENTATION */ | |
330 | printf ("IMPLEMENTATION\n"); | |
331 | printf ("%*s", BASE_INDENT, " "); | |
332 | show_shell_version (0); | |
333 | printf ("%*s", BASE_INDENT, " "); | |
334 | printf ("%s\n", _(bash_copyright)); | |
335 | printf ("%*s", BASE_INDENT, " "); | |
336 | printf ("%s\n", _(bash_license)); | |
337 | ||
338 | fflush (stdout); | |
339 | if (usefile) | |
340 | free (line); | |
341 | } | |
342 | ||
ac50fbac CR |
343 | static void |
344 | dispcolumn (i, buf, bufsize, width, height) | |
345 | int i; | |
346 | char *buf; | |
347 | size_t bufsize; | |
348 | int width, height; | |
349 | { | |
350 | int j; | |
351 | int displen; | |
352 | char *helpdoc; | |
353 | ||
354 | /* first column */ | |
355 | helpdoc = _(shell_builtins[i].short_doc); | |
356 | ||
357 | buf[0] = (shell_builtins[i].flags & BUILTIN_ENABLED) ? ' ' : '*'; | |
358 | strncpy (buf + 1, helpdoc, width - 2); | |
359 | buf[width - 2] = '>'; /* indicate truncation */ | |
360 | buf[width - 1] = '\0'; | |
361 | printf ("%s", buf); | |
362 | if (((i << 1) >= num_shell_builtins) || (i+height >= num_shell_builtins)) | |
363 | { | |
364 | printf ("\n"); | |
365 | return; | |
366 | } | |
367 | ||
368 | displen = strlen (buf); | |
369 | /* two spaces */ | |
370 | for (j = displen; j < width; j++) | |
371 | putc (' ', stdout); | |
372 | ||
373 | /* second column */ | |
374 | helpdoc = _(shell_builtins[i+height].short_doc); | |
375 | ||
376 | buf[0] = (shell_builtins[i+height].flags & BUILTIN_ENABLED) ? ' ' : '*'; | |
377 | strncpy (buf + 1, helpdoc, width - 3); | |
378 | buf[width - 3] = '>'; /* indicate truncation */ | |
379 | buf[width - 2] = '\0'; | |
380 | ||
381 | printf ("%s\n", buf); | |
382 | } | |
383 | ||
384 | #if defined (HANDLE_MULTIBYTE) | |
385 | static void | |
386 | wdispcolumn (i, buf, bufsize, width, height) | |
387 | int i; | |
388 | char *buf; | |
389 | size_t bufsize; | |
390 | int width, height; | |
391 | { | |
392 | int j; | |
393 | int displen; | |
394 | char *helpdoc; | |
395 | wchar_t *wcstr; | |
396 | size_t slen, n; | |
397 | int wclen; | |
398 | ||
399 | /* first column */ | |
400 | helpdoc = _(shell_builtins[i].short_doc); | |
401 | ||
402 | wcstr = 0; | |
403 | slen = mbstowcs ((wchar_t *)0, helpdoc, 0); | |
404 | if (slen == -1) | |
405 | { | |
406 | dispcolumn (i, buf, bufsize, width, height); | |
407 | return; | |
408 | } | |
409 | ||
410 | /* No bigger than the passed max width */ | |
411 | if (slen >= width) | |
412 | slen = width - 2; | |
413 | wcstr = (wchar_t *)xmalloc (sizeof (wchar_t) * (width + 2)); | |
414 | n = mbstowcs (wcstr+1, helpdoc, slen + 1); | |
415 | wcstr[n+1] = L'\0'; | |
416 | ||
417 | /* Turn tabs and newlines into spaces for column display, since wcwidth | |
418 | returns -1 for them */ | |
419 | for (j = 1; j < n; j++) | |
420 | if (wcstr[j] == L'\n' || wcstr[j] == L'\t') | |
421 | wcstr[j] = L' '; | |
422 | ||
423 | displen = wcsnwidth (wcstr+1, slen, width - 2) + 1; /* +1 for ' ' or '*' */ | |
424 | ||
425 | wcstr[0] = (shell_builtins[i].flags & BUILTIN_ENABLED) ? L' ' : L'*'; | |
426 | ||
427 | /* This assumes each wide char takes up one column position when displayed */ | |
428 | wcstr[width - 2] = L'>'; /* indicate truncation */ | |
429 | wcstr[width - 1] = L'\0'; | |
430 | ||
431 | printf ("%ls", wcstr); | |
432 | if (((i << 1) >= num_shell_builtins) || (i+height >= num_shell_builtins)) | |
433 | { | |
434 | printf ("\n"); | |
435 | return; | |
436 | } | |
437 | ||
438 | /* at least one space */ | |
439 | for (j = displen; j < width; j++) | |
440 | putc (' ', stdout); | |
441 | ||
442 | /* second column */ | |
443 | helpdoc = _(shell_builtins[i+height].short_doc); | |
444 | slen = mbstowcs ((wchar_t *)0, helpdoc, 0); | |
445 | if (slen == -1) | |
446 | { | |
447 | /* for now */ | |
448 | printf ("%c%s\n", (shell_builtins[i+height].flags & BUILTIN_ENABLED) ? ' ' : '*', helpdoc); | |
449 | return; | |
450 | } | |
451 | ||
452 | /* Reuse wcstr since it is already width wide chars long */ | |
453 | if (slen >= width) | |
454 | slen = width - 2; | |
455 | n = mbstowcs (wcstr+1, helpdoc, slen + 1); | |
456 | wcstr[n+1] = L'\0'; /* make sure null-terminated */ | |
457 | ||
458 | /* Turn tabs and newlines into spaces for column display */ | |
459 | for (j = 1; j < n; j++) | |
460 | if (wcstr[j] == L'\n' || wcstr[j] == L'\t') | |
461 | wcstr[j] = L' '; | |
462 | ||
463 | displen = wcsnwidth (wcstr+1, slen, width - 2); | |
464 | ||
465 | wcstr[0] = (shell_builtins[i+height].flags & BUILTIN_ENABLED) ? L' ' : L'*'; | |
466 | ||
467 | /* This assumes each wide char takes up one column position when displayed */ | |
468 | wcstr[width - 3] = L'>'; /* indicate truncation */ | |
469 | wcstr[width - 2] = L'\0'; | |
470 | ||
471 | printf ("%ls\n", wcstr); | |
472 | ||
473 | free (wcstr); | |
474 | } | |
475 | #endif /* HANDLE_MULTIBYTE */ | |
476 | ||
ccc6cda3 JA |
477 | static void |
478 | show_builtin_command_help () | |
479 | { | |
480 | int i, j; | |
3185942a JA |
481 | int height, width; |
482 | char *t, blurb[128]; | |
ccc6cda3 JA |
483 | |
484 | printf ( | |
b80f6443 | 485 | _("These shell commands are defined internally. Type `help' to see this list.\n\ |
ccc6cda3 JA |
486 | Type `help name' to find out more about the function `name'.\n\ |
487 | Use `info bash' to find out more about the shell in general.\n\ | |
7117c2d2 | 488 | Use `man -k' or `info' to find out more about commands not in this list.\n\ |
ccc6cda3 JA |
489 | \n\ |
490 | A star (*) next to a name means that the command is disabled.\n\ | |
b80f6443 | 491 | \n")); |
ccc6cda3 | 492 | |
3185942a JA |
493 | t = get_string_value ("COLUMNS"); |
494 | width = (t && *t) ? atoi (t) : 80; | |
495 | if (width <= 0) | |
496 | width = 80; | |
497 | ||
498 | width /= 2; | |
499 | if (width > sizeof (blurb)) | |
500 | width = sizeof (blurb); | |
0001803f CR |
501 | if (width <= 3) |
502 | width = 40; | |
3185942a JA |
503 | height = (num_shell_builtins + 1) / 2; /* number of rows */ |
504 | ||
505 | for (i = 0; i < height; i++) | |
ccc6cda3 JA |
506 | { |
507 | QUIT; | |
3185942a | 508 | |
ac50fbac CR |
509 | #if defined (HANDLE_MULTIBYTE) |
510 | if (MB_CUR_MAX > 1) | |
511 | wdispcolumn (i, blurb, sizeof (blurb), width, height); | |
512 | else | |
513 | #endif | |
514 | dispcolumn (i, blurb, sizeof (blurb), width, height); | |
ccc6cda3 | 515 | } |
ccc6cda3 JA |
516 | } |
517 | #endif /* HELP_BUILTIN */ |