source: trunk/essentials/sys-apps/texinfo/info/infodoc.c

Last change on this file was 2617, checked in by bird, 19 years ago

GNU Texinfo 4.8

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