infodoc.c revision 1.1 1 /* $NetBSD: infodoc.c,v 1.1 2016/01/14 00:11:29 christos Exp $ */
2
3 /* infodoc.c -- functions which build documentation nodes.
4 Id: infodoc.c,v 1.8 2004/04/11 17:56:45 karl Exp
5
6 Copyright (C) 1993, 1997, 1998, 1999, 2001, 2002, 2003, 2004 Free Software
7 Foundation, Inc.
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23 Written by Brian Fox (bfox (at) ai.mit.edu). */
24
25 #include "info.h"
26 #include "funs.h"
27
28 /* HELP_NODE_GETS_REGENERATED is always defined now that keys may get
29 rebound, or other changes in the help text may occur. */
30 #define HELP_NODE_GETS_REGENERATED 1
31
32 /* The name of the node used in the help window. */
33 static char *info_help_nodename = "*Info Help*";
34
35 /* A node containing printed key bindings and their documentation. */
36 static NODE *internal_info_help_node = (NODE *)NULL;
37
38 /* A pointer to the contents of the help node. */
39 static char *internal_info_help_node_contents = (char *)NULL;
40
41 /* The (more or less) static text which appears in the internal info
42 help node. The actual key bindings are inserted. Keep the
43 underlines (****, etc.) in the same N_ call as the text lines they
44 refer to, so translations can make the number of *'s or -'s match. */
45 #if defined(INFOKEY)
46
47 static char *info_internal_help_text[] = {
48 N_("Basic Commands in Info Windows\n\
49 ******************************\n"),
50 "\n",
51 N_("\\%-10[quit-help] Quit this help.\n"),
52 N_("\\%-10[quit] Quit Info altogether.\n"),
53 N_("\\%-10[get-info-help-node] Invoke the Info tutorial.\n"),
54 "\n",
55 N_("Selecting other nodes:\n\
56 ----------------------\n"),
57 N_("\\%-10[next-node] Move to the \"next\" node of this node.\n"),
58 N_("\\%-10[prev-node] Move to the \"previous\" node of this node.\n"),
59 N_("\\%-10[up-node] Move \"up\" from this node.\n"),
60 N_("\\%-10[menu-item] Pick menu item specified by name.\n\
61 Picking a menu item causes another node to be selected.\n"),
62 N_("\\%-10[xref-item] Follow a cross reference. Reads name of reference.\n"),
63 N_("\\%-10[history-node] Move to the last node seen in this window.\n"),
64 N_("\\%-10[move-to-next-xref] Skip to next hypertext link within this node.\n"),
65 N_("\\%-10[move-to-prev-xref] Skip to previous hypertext link within this node.\n"),
66 N_("\\%-10[select-reference-this-line] Follow the hypertext link under cursor.\n"),
67 N_("\\%-10[dir-node] Move to the `directory' node. Equivalent to `\\[goto-node] (DIR)'.\n"),
68 N_("\\%-10[top-node] Move to the Top node. Equivalent to `\\[goto-node] Top'.\n"),
69 "\n",
70 N_("Moving within a node:\n\
71 ---------------------\n"),
72 N_("\\%-10[beginning-of-node] Go to the beginning of this node.\n"),
73 N_("\\%-10[end-of-node] Go to the end of this node.\n"),
74 N_("\\%-10[next-line] Scroll forward 1 line.\n"),
75 N_("\\%-10[prev-line] Scroll backward 1 line.\n"),
76 N_("\\%-10[scroll-forward] Scroll forward a page.\n"),
77 N_("\\%-10[scroll-backward] Scroll backward a page.\n"),
78 "\n",
79 N_("Other commands:\n\
80 ---------------\n"),
81 N_("\\%-10[menu-digit] Pick first ... ninth item in node's menu.\n"),
82 N_("\\%-10[last-menu-item] Pick last item in node's menu.\n"),
83 N_("\\%-10[index-search] Search for a specified string in the index entries of this Info\n\
84 file, and select the node referenced by the first entry found.\n"),
85 N_("\\%-10[goto-node] Move to node specified by name.\n\
86 You may include a filename as well, as in (FILENAME)NODENAME.\n"),
87 N_("\\%-10[search] Search forward for a specified string\n\
88 and select the node in which the next occurrence is found.\n"),
89 N_("\\%-10[search-backward] Search backward for a specified string\n\
90 and select the node in which the previous occurrence is found.\n"),
91 NULL
92 };
93
94 #else /* !INFOKEY */
95
96 static char *info_internal_help_text[] = {
97 N_("Basic Commands in Info Windows\n\
98 ******************************\n"),
99 "\n",
100 N_(" %-10s Quit this help.\n"),
101 N_(" %-10s Quit Info altogether.\n"),
102 N_(" %-10s Invoke the Info tutorial.\n"),
103 "\n",
104 N_("Selecting other nodes:\n\
105 ----------------------\n",
106 N_(" %-10s Move to the `next' node of this node.\n"),
107 N_(" %-10s Move to the `previous' node of this node.\n"),
108 N_(" %-10s Move `up' from this node.\n"),
109 N_(" %-10s Pick menu item specified by name.\n"),
110 N_(" Picking a menu item causes another node to be selected.\n"),
111 N_(" %-10s Follow a cross reference. Reads name of reference.\n"),
112 N_(" %-10s Move to the last node seen in this window.\n"),
113 N_(" %-10s Skip to next hypertext link within this node.\n"),
114 N_(" %-10s Follow the hypertext link under cursor.\n"),
115 N_(" %-10s Move to the `directory' node. Equivalent to `g (DIR)'.\n"),
116 N_(" %-10s Move to the Top node. Equivalent to `g Top'.\n"),
117 "\n",
118 N_("Moving within a node:\n\
119 ---------------------\n"),
120 N_(" %-10s Scroll forward a page.\n"),
121 N_(" %-10s Scroll backward a page.\n"),
122 N_(" %-10s Go to the beginning of this node.\n"),
123 N_(" %-10s Go to the end of this node.\n"),
124 N_(" %-10s Scroll forward 1 line.\n"),
125 N_(" %-10s Scroll backward 1 line.\n"),
126 "\n",
127 N_("Other commands:\n\
128 ---------------\n"),
129 N_(" %-10s Pick first ... ninth item in node's menu.\n"),
130 N_(" %-10s Pick last item in node's menu.\n"),
131 N_(" %-10s Search for a specified string in the index entries of this Info\n"),
132 N_(" file, and select the node referenced by the first entry found.\n"),
133 N_(" %-10s Move to node specified by name.\n"),
134 N_(" You may include a filename as well, as in (FILENAME)NODENAME.\n"),
135 N_(" %-10s Search forward for a specified string,\n"),
136 N_(" and select the node in which the next occurrence is found.\n"),
137 N_(" %-10s Search backward for a specified string\n"),
138 N_(" and select the node in which the next occurrence is found.\n"),
139 NULL
140 };
141
142 static char *info_help_keys_text[][2] = {
143 { "", "" },
144 { "", "" },
145 { "", "" },
146 { "CTRL-x 0", "CTRL-x 0" },
147 { "q", "q" },
148 { "h", "ESC h" },
149 { "", "" },
150 { "", "" },
151 { "", "" },
152 { "SPC", "SPC" },
153 { "DEL", "b" },
154 { "b", "ESC b" },
155 { "e", "ESC e" },
156 { "ESC 1 SPC", "RET" },
157 { "ESC 1 DEL", "y" },
158 { "", "" },
159 { "", "" },
160 { "", "" },
161 { "n", "CTRL-x n" },
162 { "p", "CTRL-x p" },
163 { "u", "CTRL-x u" },
164 { "m", "ESC m" },
165 { "", "" },
166 { "f", "ESC f" },
167 { "l", "l" },
168 { "TAB", "TAB" },
169 { "RET", "CTRL-x RET" },
170 { "d", "ESC d" },
171 { "t", "ESC t" },
172 { "", "" },
173 { "", "" },
174 { "", "" },
175 { "1-9", "ESC 1-9" },
176 { "0", "ESC 0" },
177 { "i", "CTRL-x i" },
178 { "", "" },
179 { "g", "CTRL-x g" },
180 { "", "" },
181 { "s", "/" },
182 { "", "" },
183 { "ESC - s", "?" },
184 { "", "" },
185 NULL
186 };
187
188 #endif /* !INFOKEY */
189
190 static char *where_is_internal (Keymap map, InfoCommand *cmd);
191
192 void
193 dump_map_to_message_buffer (char *prefix, Keymap map)
194 {
195 register int i;
196 unsigned prefix_len = strlen (prefix);
197 char *new_prefix = (char *)xmalloc (prefix_len + 2);
198
199 strncpy (new_prefix, prefix, prefix_len);
200 new_prefix[prefix_len + 1] = '\0';
201
202 for (i = 0; i < 256; i++)
203 {
204 new_prefix[prefix_len] = i;
205 if (map[i].type == ISKMAP)
206 {
207 dump_map_to_message_buffer (new_prefix, (Keymap)map[i].function);
208 }
209 else if (map[i].function)
210 {
211 register int last;
212 char *doc, *name;
213
214 doc = function_documentation (map[i].function);
215 name = function_name (map[i].function);
216
217 if (!*doc)
218 continue;
219
220 /* Find out if there is a series of identical functions, as in
221 ea_insert (). */
222 for (last = i + 1; last < 256; last++)
223 if ((map[last].type != ISFUNC) ||
224 (map[last].function != map[i].function))
225 break;
226
227 if (last - 1 != i)
228 {
229 printf_to_message_buffer ("%s .. ", pretty_keyseq (new_prefix),
230 NULL, NULL);
231 new_prefix[prefix_len] = last - 1;
232 printf_to_message_buffer ("%s\t", pretty_keyseq (new_prefix),
233 NULL, NULL);
234 i = last - 1;
235 }
236 else
237 printf_to_message_buffer ("%s\t", pretty_keyseq (new_prefix),
238 NULL, NULL);
239
240 #if defined (NAMED_FUNCTIONS)
241 /* Print the name of the function, and some padding before the
242 documentation string is printed. */
243 {
244 int length_so_far;
245 int desired_doc_start = 40; /* Must be multiple of 8. */
246
247 printf_to_message_buffer ("(%s)", name, NULL, NULL);
248 length_so_far = message_buffer_length_this_line ();
249
250 if ((desired_doc_start + strlen (doc))
251 >= (unsigned int) the_screen->width)
252 printf_to_message_buffer ("\n ", NULL, NULL, NULL);
253 else
254 {
255 while (length_so_far < desired_doc_start)
256 {
257 printf_to_message_buffer ("\t", NULL, NULL, NULL);
258 length_so_far += character_width ('\t', length_so_far);
259 }
260 }
261 }
262 #endif /* NAMED_FUNCTIONS */
263 printf_to_message_buffer ("%s\n", doc, NULL, NULL);
264 }
265 }
266 free (new_prefix);
267 }
268
269 /* How to create internal_info_help_node. HELP_IS_ONLY_WINDOW_P says
270 whether we're going to end up in a second (or more) window of our
271 own, or whether there's only one window and we're going to usurp it.
272 This determines how to quit the help window. Maybe we should just
273 make q do the right thing in both cases. */
274
275 static void
276 create_internal_info_help_node (int help_is_only_window_p)
277 {
278 register int i;
279 NODE *node;
280 char *contents = NULL;
281 char *exec_keys;
282
283 #ifndef HELP_NODE_GETS_REGENERATED
284 if (internal_info_help_node_contents)
285 contents = internal_info_help_node_contents;
286 #endif /* !HELP_NODE_GETS_REGENERATED */
287
288 if (!contents)
289 {
290 int printed_one_mx = 0;
291
292 initialize_message_buffer ();
293
294 for (i = 0; info_internal_help_text[i]; i++)
295 {
296 #ifdef INFOKEY
297 printf_to_message_buffer (replace_in_documentation
298 ((char *) _(info_internal_help_text[i]), help_is_only_window_p),
299 NULL, NULL, NULL);
300 #else
301 /* Don't translate blank lines, gettext outputs the po file
302 header in that case. We want a blank line. */
303 char *msg = *(info_internal_help_text[i])
304 ? _(info_internal_help_text[i])
305 : info_internal_help_text[i];
306 char *key = info_help_keys_text[i][vi_keys_p];
307
308 /* If we have only one window (because the window size was too
309 small to split it), CTRL-x 0 doesn't work to `quit' help. */
310 if (STREQ (key, "CTRL-x 0") && help_is_only_window_p)
311 key = "l";
312
313 printf_to_message_buffer (msg, key, NULL, NULL);
314 #endif /* !INFOKEY */
315 }
316
317 printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL);
318 printf_to_message_buffer ((char *) _("The current search path is:\n"),
319 NULL, NULL, NULL);
320 printf_to_message_buffer (" %s\n", infopath, NULL, NULL);
321 printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL);
322 printf_to_message_buffer ((char *) _("Commands available in Info windows:\n\n"),
323 NULL, NULL, NULL);
324 dump_map_to_message_buffer ("", info_keymap);
325 printf_to_message_buffer ("---------------------\n\n", NULL, NULL, NULL);
326 printf_to_message_buffer ((char *) _("Commands available in the echo area:\n\n"),
327 NULL, NULL, NULL);
328 dump_map_to_message_buffer ("", echo_area_keymap);
329
330 #if defined (NAMED_FUNCTIONS)
331 /* Get a list of commands which have no keystroke equivs. */
332 exec_keys = where_is (info_keymap, InfoCmd(info_execute_command));
333 if (exec_keys)
334 exec_keys = xstrdup (exec_keys);
335 for (i = 0; function_doc_array[i].func; i++)
336 {
337 InfoCommand *cmd = DocInfoCmd(&function_doc_array[i]);
338
339 if (InfoFunction(cmd) != (VFunction *) info_do_lowercase_version
340 && !where_is_internal (info_keymap, cmd)
341 && !where_is_internal (echo_area_keymap, cmd))
342 {
343 if (!printed_one_mx)
344 {
345 printf_to_message_buffer ("---------------------\n\n",
346 NULL, NULL, NULL);
347 if (exec_keys && exec_keys[0])
348 printf_to_message_buffer
349 ((char *) _("The following commands can only be invoked via %s:\n\n"),
350 exec_keys, NULL, NULL);
351 else
352 printf_to_message_buffer
353 ((char *) _("The following commands cannot be invoked at all:\n\n"),
354 NULL, NULL, NULL);
355 printed_one_mx = 1;
356 }
357
358 printf_to_message_buffer
359 ("%s %s\n %s\n",
360 exec_keys,
361 function_doc_array[i].func_name,
362 replace_in_documentation (strlen (function_doc_array[i].doc)
363 ? (char *) _(function_doc_array[i].doc) : "", 0)
364 );
365
366 }
367 }
368
369 if (printed_one_mx)
370 printf_to_message_buffer ("\n", NULL, NULL, NULL);
371
372 maybe_free (exec_keys);
373 #endif /* NAMED_FUNCTIONS */
374
375 printf_to_message_buffer
376 ("%s", replace_in_documentation
377 ((char *) _("--- Use `\\[history-node]' or `\\[kill-node]' to exit ---\n"), 0),
378 NULL, NULL);
379 node = message_buffer_to_node ();
380 internal_info_help_node_contents = node->contents;
381 }
382 else
383 {
384 /* We already had the right contents, so simply use them. */
385 node = build_message_node ("", 0, 0);
386 free (node->contents);
387 node->contents = contents;
388 node->nodelen = 1 + strlen (contents);
389 }
390
391 internal_info_help_node = node;
392
393 /* Do not GC this node's contents. It never changes, and we never need
394 to delete it once it is made. If you change some things (such as
395 placing information about dynamic variables in the help text) then
396 you will need to allow the contents to be gc'd, and you will have to
397 arrange to always regenerate the help node. */
398 #if defined (HELP_NODE_GETS_REGENERATED)
399 add_gcable_pointer (internal_info_help_node->contents);
400 #endif
401
402 name_internal_node (internal_info_help_node, info_help_nodename);
403
404 /* Even though this is an internal node, we don't want the window
405 system to treat it specially. So we turn off the internalness
406 of it here. */
407 internal_info_help_node->flags &= ~N_IsInternal;
408 }
409
410 /* Return a window which is the window showing help in this Info. */
411
412 /* If the eligible window's height is >= this, split it to make the help
413 window. Otherwise display the help window in the current window. */
414 #define HELP_SPLIT_SIZE 24
415
416 static WINDOW *
417 info_find_or_create_help_window (void)
418 {
419 int help_is_only_window_p;
420 WINDOW *eligible = NULL;
421 WINDOW *help_window = get_window_of_node (internal_info_help_node);
422
423 /* If we couldn't find the help window, then make it. */
424 if (!help_window)
425 {
426 WINDOW *window;
427 int max = 0;
428
429 for (window = windows; window; window = window->next)
430 {
431 if (window->height > max)
432 {
433 max = window->height;
434 eligible = window;
435 }
436 }
437
438 if (!eligible)
439 return NULL;
440 }
441 #ifndef HELP_NODE_GETS_REGENERATED
442 else
443 /* help window is static, just return it. */
444 return help_window;
445 #endif /* not HELP_NODE_GETS_REGENERATED */
446
447 /* Make sure that we have a node containing the help text. The
448 argument is false if help will be the only window (so l must be used
449 to quit help), true if help will be one of several visible windows
450 (so CTRL-x 0 must be used to quit help). */
451 help_is_only_window_p = ((help_window && !windows->next)
452 || (!help_window && eligible->height < HELP_SPLIT_SIZE));
453 create_internal_info_help_node (help_is_only_window_p);
454
455 /* Either use the existing window to display the help node, or create
456 a new window if there was no existing help window. */
457 if (!help_window)
458 { /* Split the largest window into 2 windows, and show the help text
459 in that window. */
460 if (eligible->height >= HELP_SPLIT_SIZE)
461 {
462 active_window = eligible;
463 help_window = window_make_window (internal_info_help_node);
464 }
465 else
466 {
467 set_remembered_pagetop_and_point (active_window);
468 window_set_node_of_window (active_window, internal_info_help_node);
469 help_window = active_window;
470 }
471 }
472 else
473 { /* Case where help node always gets regenerated, and we have an
474 existing window in which to place the node. */
475 if (active_window != help_window)
476 {
477 set_remembered_pagetop_and_point (active_window);
478 active_window = help_window;
479 }
480 window_set_node_of_window (active_window, internal_info_help_node);
481 }
482 remember_window_and_node (help_window, help_window->node);
483 return help_window;
484 }
485
486 /* Create or move to the help window. */
487 DECLARE_INFO_COMMAND (info_get_help_window, _("Display help message"))
488 {
489 WINDOW *help_window;
490
491 help_window = info_find_or_create_help_window ();
492 if (help_window)
493 {
494 active_window = help_window;
495 active_window->flags |= W_UpdateWindow;
496 }
497 else
498 {
499 info_error ((char *) msg_cant_make_help, NULL, NULL);
500 }
501 }
502
503 /* Show the Info help node. This means that the "info" file is installed
504 where it can easily be found on your system. */
505 DECLARE_INFO_COMMAND (info_get_info_help_node, _("Visit Info node `(info)Help'"))
506 {
507 NODE *node;
508 char *nodename;
509
510 /* If there is a window on the screen showing the node "(info)Help" or
511 the node "(info)Help-Small-Screen", simply select that window. */
512 {
513 WINDOW *win;
514
515 for (win = windows; win; win = win->next)
516 {
517 if (win->node && win->node->filename &&
518 (strcasecmp
519 (filename_non_directory (win->node->filename), "info") == 0) &&
520 ((strcmp (win->node->nodename, "Help") == 0) ||
521 (strcmp (win->node->nodename, "Help-Small-Screen") == 0)))
522 {
523 active_window = win;
524 return;
525 }
526 }
527 }
528
529 /* If the current window is small, show the small screen help. */
530 if (active_window->height < 24)
531 nodename = "Help-Small-Screen";
532 else
533 nodename = "Help";
534
535 /* Try to get the info file for Info. */
536 node = info_get_node ("Info", nodename);
537
538 if (!node)
539 {
540 if (info_recent_file_error)
541 info_error (info_recent_file_error, NULL, NULL);
542 else
543 info_error ((char *) msg_cant_file_node, "Info", nodename);
544 }
545 else
546 {
547 /* If the current window is very large (greater than 45 lines),
548 then split it and show the help node in another window.
549 Otherwise, use the current window. */
550
551 if (active_window->height > 45)
552 active_window = window_make_window (node);
553 else
554 {
555 set_remembered_pagetop_and_point (active_window);
556 window_set_node_of_window (active_window, node);
557 }
558
559 remember_window_and_node (active_window, node);
560 }
561 }
562
563 /* **************************************************************** */
565 /* */
566 /* Groveling Info Keymaps and Docs */
567 /* */
568 /* **************************************************************** */
569
570 /* Return the documentation associated with the Info command FUNCTION. */
571 char *
572 function_documentation (InfoCommand *cmd)
573 {
574 char *doc;
575
576 #if defined (INFOKEY)
577
578 doc = cmd->doc;
579
580 #else /* !INFOKEY */
581
582 register int i;
583
584 for (i = 0; function_doc_array[i].func; i++)
585 if (InfoFunction(cmd) == function_doc_array[i].func)
586 break;
587
588 doc = function_doc_array[i].func ? function_doc_array[i].doc : "";
589
590 #endif /* !INFOKEY */
591
592 return replace_in_documentation ((strlen (doc) == 0) ? doc : (char *) _(doc), 0);
593 }
594
595 #if defined (NAMED_FUNCTIONS)
596 /* Return the user-visible name of the function associated with the
597 Info command FUNCTION. */
598 char *
599 function_name (InfoCommand *cmd)
600 {
601 #if defined (INFOKEY)
602
603 return cmd->func_name;
604
605 #else /* !INFOKEY */
606
607 register int i;
608
609 for (i = 0; function_doc_array[i].func; i++)
610 if (InfoFunction(cmd) == function_doc_array[i].func)
611 break;
612
613 return (function_doc_array[i].func_name);
614
615 #endif /* !INFOKEY */
616 }
617
618 /* Return a pointer to the info command for function NAME. */
619 InfoCommand *
620 named_function (char *name)
621 {
622 register int i;
623
624 for (i = 0; function_doc_array[i].func; i++)
625 if (strcmp (function_doc_array[i].func_name, name) == 0)
626 break;
627
628 return (DocInfoCmd(&function_doc_array[i]));
629 }
630 #endif /* NAMED_FUNCTIONS */
631
632 /* Return the documentation associated with KEY in MAP. */
633 char *
634 key_documentation (char key, Keymap map)
635 {
636 InfoCommand *function = map[key].function;
637
638 if (function)
639 return (function_documentation (function));
640 else
641 return ((char *)NULL);
642 }
643
644 DECLARE_INFO_COMMAND (describe_key, _("Print documentation for KEY"))
645 {
646 char keys[50];
647 unsigned char keystroke;
648 char *k = keys;
649 Keymap map;
650
651 *k = '\0';
652 map = window->keymap;
653
654 for (;;)
655 {
656 message_in_echo_area ((char *) _("Describe key: %s"),
657 pretty_keyseq (keys), NULL);
658 keystroke = info_get_input_char ();
659 unmessage_in_echo_area ();
660
661 #if !defined (INFOKEY)
662 if (Meta_p (keystroke))
663 {
664 if (map[ESC].type != ISKMAP)
665 {
666 window_message_in_echo_area
667 (_("ESC %s is undefined."), pretty_keyname (UnMeta (keystroke)));
668 return;
669 }
670
671 *k++ = '\e';
672 keystroke = UnMeta (keystroke);
673 map = (Keymap)map[ESC].function;
674 }
675 #endif /* !INFOKEY */
676
677 /* Add the KEYSTROKE to our list. */
678 *k++ = keystroke;
679 *k = '\0';
680
681 if (map[keystroke].function == (InfoCommand *)NULL)
682 {
683 message_in_echo_area ((char *) _("%s is undefined."),
684 pretty_keyseq (keys), NULL);
685 return;
686 }
687 else if (map[keystroke].type == ISKMAP)
688 {
689 map = (Keymap)map[keystroke].function;
690 continue;
691 }
692 else
693 {
694 char *keyname, *message, *fundoc, *funname = "";
695
696 #if defined (INFOKEY)
697 /* If the key is bound to do-lowercase-version, but its
698 lower-case variant is undefined, say that this key is
699 also undefined. This is especially important for unbound
700 edit keys that emit an escape sequence: it's terribly
701 confusing to see a message "Home (do-lowercase-version)"
702 or some such when Home is unbound. */
703 if (InfoFunction(map[keystroke].function)
704 == (VFunction *) info_do_lowercase_version)
705 {
706 unsigned char lowerkey = Meta_p(keystroke)
707 ? Meta (tolower (UnMeta (keystroke)))
708 : tolower (keystroke);
709
710 if (map[lowerkey].function == (InfoCommand *)NULL)
711 {
712 message_in_echo_area ((char *) _("%s is undefined."),
713 pretty_keyseq (keys), NULL);
714 return;
715 }
716 }
717 #endif
718
719 keyname = pretty_keyseq (keys);
720
721 #if defined (NAMED_FUNCTIONS)
722 funname = function_name (map[keystroke].function);
723 #endif /* NAMED_FUNCTIONS */
724
725 fundoc = function_documentation (map[keystroke].function);
726
727 message = (char *)xmalloc
728 (10 + strlen (keyname) + strlen (fundoc) + strlen (funname));
729
730 #if defined (NAMED_FUNCTIONS)
731 sprintf (message, "%s (%s): %s.", keyname, funname, fundoc);
732 #else
733 sprintf (message, _("%s is defined to %s."), keyname, fundoc);
734 #endif /* !NAMED_FUNCTIONS */
735
736 window_message_in_echo_area ("%s", message, NULL);
737 free (message);
738 break;
739 }
740 }
741 }
742
743 /* Return the pretty printable name of a single character. */
744 char *
745 pretty_keyname (unsigned char key)
746 {
747 static char rep_buffer[30];
748 char *rep;
749
750 if (Meta_p (key))
751 {
752 char temp[20];
753
754 rep = pretty_keyname (UnMeta (key));
755
756 #if defined (INFOKEY)
757 sprintf (temp, "M-%s", rep);
758 #else /* !INFOKEY */
759 sprintf (temp, "ESC %s", rep);
760 #endif /* !INFOKEY */
761 strcpy (rep_buffer, temp);
762 rep = rep_buffer;
763 }
764 else if (Control_p (key))
765 {
766 switch (key)
767 {
768 case '\n': rep = "LFD"; break;
769 case '\t': rep = "TAB"; break;
770 case '\r': rep = "RET"; break;
771 case ESC: rep = "ESC"; break;
772
773 default:
774 sprintf (rep_buffer, "C-%c", UnControl (key));
775 rep = rep_buffer;
776 }
777 }
778 else
779 {
780 switch (key)
781 {
782 case ' ': rep = "SPC"; break;
783 case DEL: rep = "DEL"; break;
784 default:
785 rep_buffer[0] = key;
786 rep_buffer[1] = '\0';
787 rep = rep_buffer;
788 }
789 }
790 return (rep);
791 }
792
793 /* Return the pretty printable string which represents KEYSEQ. */
794
795 static void pretty_keyseq_internal (char *keyseq, char *rep);
796
797 char *
798 pretty_keyseq (char *keyseq)
799 {
800 static char keyseq_rep[200];
801
802 keyseq_rep[0] = '\0';
803 if (*keyseq)
804 pretty_keyseq_internal (keyseq, keyseq_rep);
805 return (keyseq_rep);
806 }
807
808 static void
809 pretty_keyseq_internal (char *keyseq, char *rep)
810 {
811 if (term_kP && strncmp(keyseq, term_kP, strlen(term_kP)) == 0)
812 {
813 strcpy(rep, "PgUp");
814 keyseq += strlen(term_kP);
815 }
816 else if (term_kN && strncmp(keyseq, term_kN, strlen(term_kN)) == 0)
817 {
818 strcpy(rep, "PgDn");
819 keyseq += strlen(term_kN);
820 }
821 #if defined(INFOKEY)
822 else if (term_kh && strncmp(keyseq, term_kh, strlen(term_kh)) == 0)
823 {
824 strcpy(rep, "Home");
825 keyseq += strlen(term_kh);
826 }
827 else if (term_ke && strncmp(keyseq, term_ke, strlen(term_ke)) == 0)
828 {
829 strcpy(rep, "End");
830 keyseq += strlen(term_ke);
831 }
832 else if (term_ki && strncmp(keyseq, term_ki, strlen(term_ki)) == 0)
833 {
834 strcpy(rep, "INS");
835 keyseq += strlen(term_ki);
836 }
837 else if (term_kx && strncmp(keyseq, term_kx, strlen(term_kx)) == 0)
838 {
839 strcpy(rep, "DEL");
840 keyseq += strlen(term_kx);
841 }
842 #endif /* INFOKEY */
843 else if (term_ku && strncmp(keyseq, term_ku, strlen(term_ku)) == 0)
844 {
845 strcpy(rep, "Up");
846 keyseq += strlen(term_ku);
847 }
848 else if (term_kd && strncmp(keyseq, term_kd, strlen(term_kd)) == 0)
849 {
850 strcpy(rep, "Down");
851 keyseq += strlen(term_kd);
852 }
853 else if (term_kl && strncmp(keyseq, term_kl, strlen(term_kl)) == 0)
854 {
855 strcpy(rep, "Left");
856 keyseq += strlen(term_kl);
857 }
858 else if (term_kr && strncmp(keyseq, term_kr, strlen(term_kr)) == 0)
859 {
860 strcpy(rep, "Right");
861 keyseq += strlen(term_kr);
862 }
863 else
864 {
865 strcpy (rep, pretty_keyname (keyseq[0]));
866 keyseq++;
867 }
868 if (*keyseq)
869 {
870 strcat (rep, " ");
871 pretty_keyseq_internal (keyseq, rep + strlen(rep));
872 }
873 }
874
875 /* Return a pointer to the last character in s that is found in f. */
876 static char *
877 strrpbrk (const char *s, const char *f)
878 {
879 register const char *e = s + strlen(s);
880 register const char *t;
881
882 while (e-- != s)
883 {
884 for (t = f; *t; t++)
885 if (*e == *t)
886 return (char *)e;
887 }
888 return NULL;
889 }
890
891 /* Replace the names of functions with the key that invokes them. */
892 char *
893 replace_in_documentation (char *string, int help_is_only_window_p)
894 {
895 unsigned reslen = strlen (string);
896 register int i, start, next;
897 static char *result = (char *)NULL;
898
899 maybe_free (result);
900 result = (char *)xmalloc (1 + reslen);
901
902 i = next = start = 0;
903
904 /* Skip to the beginning of a replaceable function. */
905 for (i = start; string[i]; i++)
906 {
907 int j = i + 1;
908
909 /* Is this the start of a replaceable function name? */
910 if (string[i] == '\\')
911 {
912 char *fmt = NULL;
913 unsigned min = 0;
914 unsigned max = 0;
915
916 if(string[j] == '%')
917 {
918 if (string[++j] == '-')
919 j++;
920 if (isdigit(string[j]))
921 {
922 min = atoi(string + j);
923 while (isdigit(string[j]))
924 j++;
925 if (string[j] == '.' && isdigit(string[j + 1]))
926 {
927 j += 1;
928 max = atoi(string + j);
929 while (isdigit(string[j]))
930 j++;
931 }
932 fmt = (char *)xmalloc (j - i + 2);
933 strncpy (fmt, string + i + 1, j - i);
934 fmt[j - i - 1] = 's';
935 fmt[j - i] = '\0';
936 }
937 else
938 j = i + 1;
939 }
940 if (string[j] == '[')
941 {
942 unsigned arg = 0;
943 char *argstr = NULL;
944 char *rep_name, *fun_name, *rep;
945 InfoCommand *command;
946 char *repstr = NULL;
947 unsigned replen;
948
949 /* Copy in the old text. */
950 strncpy (result + next, string + start, i - start);
951 next += (i - start);
952 start = j + 1;
953
954 /* Look for an optional numeric arg. */
955 i = start;
956 if (isdigit(string[i])
957 || (string[i] == '-' && isdigit(string[i + 1])) )
958 {
959 arg = atoi(string + i);
960 if (string[i] == '-')
961 i++;
962 while (isdigit(string[i]))
963 i++;
964 }
965 start = i;
966
967 /* Move to the end of the function name. */
968 for (i = start; string[i] && (string[i] != ']'); i++);
969
970 rep_name = (char *)xmalloc (1 + i - start);
971 strncpy (rep_name, string + start, i - start);
972 rep_name[i - start] = '\0';
973
974 /* If we have only one window (because the window size was too
975 small to split it), we have to quit help by going back one
976 noew in the history list, not deleting the window. */
977 if (strcmp (rep_name, "quit-help") == 0)
978 fun_name = help_is_only_window_p ? "history-node"
979 : "delete-window";
980 else
981 fun_name = rep_name;
982
983 /* Find a key which invokes this function in the info_keymap. */
984 command = named_function (fun_name);
985
986 free (rep_name);
987
988 /* If the internal documentation string fails, there is a
989 serious problem with the associated command's documentation.
990 We croak so that it can be fixed immediately. */
991 if (!command)
992 abort ();
993
994 if (arg)
995 {
996 char *argrep, *p;
997
998 argrep = where_is (info_keymap, InfoCmd(info_add_digit_to_numeric_arg));
999 p = argrep ? strrpbrk (argrep, "0123456789-") : NULL;
1000 if (p)
1001 {
1002 argstr = (char *)xmalloc (p - argrep + 21);
1003 strncpy (argstr, argrep, p - argrep);
1004 sprintf (argstr + (p - argrep), "%d", arg);
1005 }
1006 else
1007 command = NULL;
1008 }
1009 rep = command ? where_is (info_keymap, command) : NULL;
1010 if (!rep)
1011 rep = "N/A";
1012 replen = (argstr ? strlen (argstr) : 0) + strlen (rep) + 1;
1013 repstr = (char *)xmalloc (replen);
1014 repstr[0] = '\0';
1015 if (argstr)
1016 {
1017 strcat(repstr, argstr);
1018 strcat(repstr, " ");
1019 free (argstr);
1020 }
1021 strcat(repstr, rep);
1022
1023 if (fmt)
1024 {
1025 if (replen > max)
1026 replen = max;
1027 if (replen < min)
1028 replen = min;
1029 }
1030 if (next + replen > reslen)
1031 {
1032 reslen = next + replen + 1;
1033 result = (char *)xrealloc (result, reslen + 1);
1034 }
1035
1036 if (fmt)
1037 sprintf (result + next, fmt, repstr);
1038 else
1039 strcpy (result + next, repstr);
1040
1041 next = strlen (result);
1042 free (repstr);
1043
1044 start = i;
1045 if (string[i])
1046 start++;
1047 }
1048
1049 maybe_free (fmt);
1050 }
1051 }
1052 strcpy (result + next, string + start);
1053 return (result);
1054 }
1055
1056 /* Return a string of characters which could be typed from the keymap
1057 MAP to invoke FUNCTION. */
1058 static char *where_is_rep = (char *)NULL;
1059 static int where_is_rep_index = 0;
1060 static int where_is_rep_size = 0;
1061
1062 char *
1063 where_is (Keymap map, InfoCommand *cmd)
1064 {
1065 char *rep;
1066
1067 if (!where_is_rep_size)
1068 where_is_rep = (char *)xmalloc (where_is_rep_size = 100);
1069 where_is_rep_index = 0;
1070
1071 rep = where_is_internal (map, cmd);
1072
1073 /* If it couldn't be found, return "M-x Foo" (or equivalent). */
1074 if (!rep)
1075 {
1076 char *name;
1077
1078 name = function_name (cmd);
1079 if (!name)
1080 return NULL; /* no such function */
1081
1082 rep = where_is_internal (map, InfoCmd(info_execute_command));
1083 if (!rep)
1084 return ""; /* function exists but can't be got to by user */
1085
1086 sprintf (where_is_rep, "%s %s", rep, name);
1087
1088 rep = where_is_rep;
1089 }
1090 return (rep);
1091 }
1092
1093 /* Return the printed rep of the keystrokes that invoke FUNCTION,
1094 as found in MAP, or NULL. */
1095 static char *
1096 where_is_internal (Keymap map, InfoCommand *cmd)
1097 {
1098 #if defined(INFOKEY)
1099
1100 register FUNCTION_KEYSEQ *k;
1101
1102 for (k = cmd->keys; k; k = k->next)
1103 if (k->map == map)
1104 return pretty_keyseq (k->keyseq);
1105
1106 return NULL;
1107
1108 #else /* !INFOKEY */
1109 /* There is a bug in that create_internal_info_help_node calls
1110 where_is_internal without setting where_is_rep_index to zero. This
1111 was found by Mandrake and reported by Thierry Vignaud
1112 <tvignaud (at) mandrakesoft.com> around April 24, 2002.
1113
1114 I think the best fix is to make where_is_rep_index another
1115 parameter to this recursively-called function, instead of a static
1116 variable. But this [!INFOKEY] branch of the code is not enabled
1117 any more, so let's just skip the whole thing. --karl, 28sep02. */
1118 register int i;
1119
1120 /* If the function is directly invokable in MAP, return the representation
1121 of that keystroke. */
1122 for (i = 0; i < 256; i++)
1123 if ((map[i].type == ISFUNC) && map[i].function == cmd)
1124 {
1125 sprintf (where_is_rep + where_is_rep_index, "%s", pretty_keyname (i));
1126 return (where_is_rep);
1127 }
1128
1129 /* Okay, search subsequent maps for this function. */
1130 for (i = 0; i < 256; i++)
1131 {
1132 if (map[i].type == ISKMAP)
1133 {
1134 int saved_index = where_is_rep_index;
1135 char *rep;
1136
1137 sprintf (where_is_rep + where_is_rep_index, "%s ",
1138 pretty_keyname (i));
1139
1140 where_is_rep_index = strlen (where_is_rep);
1141 rep = where_is_internal ((Keymap)map[i].function, cmd);
1142
1143 if (rep)
1144 return (where_is_rep);
1145
1146 where_is_rep_index = saved_index;
1147 }
1148 }
1149
1150 return NULL;
1151
1152 #endif /* INFOKEY */
1153 }
1154
1155 DECLARE_INFO_COMMAND (info_where_is,
1156 _("Show what to type to execute a given command"))
1157 {
1158 char *command_name;
1159
1160 command_name = read_function_name ((char *) _("Where is command: "), window);
1161
1162 if (!command_name)
1163 {
1164 info_abort_key (active_window, count, key);
1165 return;
1166 }
1167
1168 if (*command_name)
1169 {
1170 InfoCommand *command;
1171
1172 command = named_function (command_name);
1173
1174 if (command)
1175 {
1176 char *location;
1177
1178 location = where_is (active_window->keymap, command);
1179
1180 if (!location || !location[0])
1181 {
1182 info_error ((char *) _("`%s' is not on any keys"),
1183 command_name, NULL);
1184 }
1185 else
1186 {
1187 if (strstr (location, function_name (command)))
1188 window_message_in_echo_area
1189 ((char *) _("%s can only be invoked via %s."),
1190 command_name, location);
1191 else
1192 window_message_in_echo_area
1193 ((char *) _("%s can be invoked via %s."),
1194 command_name, location);
1195 }
1196 }
1197 else
1198 info_error ((char *) _("There is no function named `%s'"),
1199 command_name, NULL);
1200 }
1201
1202 free (command_name);
1203 }
1204