infodoc.c revision 116525
1/* infodoc.c -- functions which build documentation nodes. 2 $Id: infodoc.c,v 1.6 2003/05/13 16:22:11 karl Exp $ 3 4 Copyright (C) 1993, 1997, 1998, 1999, 2001, 2002, 2003 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 (); 189 190void 191dump_map_to_message_buffer (prefix, map) 192 char *prefix; 193 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 new_prefix[prefix_len] = last - 1; 231 printf_to_message_buffer ("%s\t", pretty_keyseq (new_prefix)); 232 i = last - 1; 233 } 234 else 235 printf_to_message_buffer ("%s\t", pretty_keyseq (new_prefix)); 236 237#if defined (NAMED_FUNCTIONS) 238 /* Print the name of the function, and some padding before the 239 documentation string is printed. */ 240 { 241 int length_so_far; 242 int desired_doc_start = 40; /* Must be multiple of 8. */ 243 244 printf_to_message_buffer ("(%s)", name); 245 length_so_far = message_buffer_length_this_line (); 246 247 if ((desired_doc_start + strlen (doc)) >= the_screen->width) 248 printf_to_message_buffer ("\n "); 249 else 250 { 251 while (length_so_far < desired_doc_start) 252 { 253 printf_to_message_buffer ("\t"); 254 length_so_far += character_width ('\t', length_so_far); 255 } 256 } 257 } 258#endif /* NAMED_FUNCTIONS */ 259 printf_to_message_buffer ("%s\n", doc); 260 } 261 } 262 free (new_prefix); 263} 264 265/* How to create internal_info_help_node. HELP_IS_ONLY_WINDOW_P says 266 whether we're going to end up in a second (or more) window of our 267 own, or whether there's only one window and we're going to usurp it. 268 This determines how to quit the help window. Maybe we should just 269 make q do the right thing in both cases. */ 270 271static void 272create_internal_info_help_node (help_is_only_window_p) 273 int help_is_only_window_p; 274{ 275 register int i; 276 NODE *node; 277 char *contents = NULL; 278 char *exec_keys; 279 280#ifndef HELP_NODE_GETS_REGENERATED 281 if (internal_info_help_node_contents) 282 contents = internal_info_help_node_contents; 283#endif /* !HELP_NODE_GETS_REGENERATED */ 284 285 if (!contents) 286 { 287 int printed_one_mx = 0; 288 289 initialize_message_buffer (); 290 291 for (i = 0; info_internal_help_text[i]; i++) 292 { 293#ifdef INFOKEY 294 printf_to_message_buffer (replace_in_documentation ( 295 _(info_internal_help_text[i]), help_is_only_window_p)); 296#else 297 /* Don't translate blank lines, gettext outputs the po file 298 header in that case. We want a blank line. */ 299 char *msg = *(info_internal_help_text[i]) 300 ? _(info_internal_help_text[i]) 301 : info_internal_help_text[i]; 302 char *key = info_help_keys_text[i][vi_keys_p]; 303 304 /* If we have only one window (because the window size was too 305 small to split it), CTRL-x 0 doesn't work to `quit' help. */ 306 if (STREQ (key, "CTRL-x 0") && help_is_only_window_p) 307 key = "l"; 308 309 printf_to_message_buffer (msg, key); 310#endif /* !INFOKEY */ 311 } 312 313 printf_to_message_buffer ("---------------------\n\n"); 314 printf_to_message_buffer (_("The current search path is:\n")); 315 printf_to_message_buffer (" %s\n", infopath); 316 printf_to_message_buffer ("---------------------\n\n"); 317 printf_to_message_buffer (_("Commands available in Info windows:\n\n")); 318 dump_map_to_message_buffer ("", info_keymap); 319 printf_to_message_buffer ("---------------------\n\n"); 320 printf_to_message_buffer (_("Commands available in the echo area:\n\n")); 321 dump_map_to_message_buffer ("", echo_area_keymap); 322 323#if defined (NAMED_FUNCTIONS) 324 /* Get a list of commands which have no keystroke equivs. */ 325 exec_keys = where_is (info_keymap, InfoCmd(info_execute_command)); 326 if (exec_keys) 327 exec_keys = xstrdup (exec_keys); 328 for (i = 0; function_doc_array[i].func; i++) 329 { 330 InfoCommand *cmd = DocInfoCmd(&function_doc_array[i]); 331 332 if (InfoFunction(cmd) != info_do_lowercase_version 333 && !where_is_internal (info_keymap, cmd) 334 && !where_is_internal (echo_area_keymap, cmd)) 335 { 336 if (!printed_one_mx) 337 { 338 printf_to_message_buffer ("---------------------\n\n"); 339 if (exec_keys && exec_keys[0]) 340 printf_to_message_buffer 341 (_("The following commands can only be invoked via %s:\n\n"), exec_keys); 342 else 343 printf_to_message_buffer 344 (_("The following commands cannot be invoked at all:\n\n")); 345 printed_one_mx = 1; 346 } 347 348 printf_to_message_buffer 349 ("%s %s\n %s\n", 350 exec_keys, 351 function_doc_array[i].func_name, 352 replace_in_documentation (strlen (function_doc_array[i].doc) 353 ? _(function_doc_array[i].doc) 354 : "") 355 ); 356 357 } 358 } 359 360 if (printed_one_mx) 361 printf_to_message_buffer ("\n"); 362 363 maybe_free (exec_keys); 364#endif /* NAMED_FUNCTIONS */ 365 366 printf_to_message_buffer 367 ("%s", replace_in_documentation 368 (_("--- Use `\\[history-node]' or `\\[kill-node]' to exit ---\n"))); 369 node = message_buffer_to_node (); 370 internal_info_help_node_contents = node->contents; 371 } 372 else 373 { 374 /* We already had the right contents, so simply use them. */ 375 node = build_message_node ("", 0, 0); 376 free (node->contents); 377 node->contents = contents; 378 node->nodelen = 1 + strlen (contents); 379 } 380 381 internal_info_help_node = node; 382 383 /* Do not GC this node's contents. It never changes, and we never need 384 to delete it once it is made. If you change some things (such as 385 placing information about dynamic variables in the help text) then 386 you will need to allow the contents to be gc'd, and you will have to 387 arrange to always regenerate the help node. */ 388#if defined (HELP_NODE_GETS_REGENERATED) 389 add_gcable_pointer (internal_info_help_node->contents); 390#endif 391 392 name_internal_node (internal_info_help_node, info_help_nodename); 393 394 /* Even though this is an internal node, we don't want the window 395 system to treat it specially. So we turn off the internalness 396 of it here. */ 397 internal_info_help_node->flags &= ~N_IsInternal; 398} 399 400/* Return a window which is the window showing help in this Info. */ 401 402/* If the eligible window's height is >= this, split it to make the help 403 window. Otherwise display the help window in the current window. */ 404#define HELP_SPLIT_SIZE 24 405 406static WINDOW * 407info_find_or_create_help_window () 408{ 409 int help_is_only_window_p; 410 WINDOW *eligible = NULL; 411 WINDOW *help_window = get_window_of_node (internal_info_help_node); 412 413 /* If we couldn't find the help window, then make it. */ 414 if (!help_window) 415 { 416 WINDOW *window; 417 int max = 0; 418 419 for (window = windows; window; window = window->next) 420 { 421 if (window->height > max) 422 { 423 max = window->height; 424 eligible = window; 425 } 426 } 427 428 if (!eligible) 429 return NULL; 430 } 431#ifndef HELP_NODE_GETS_REGENERATED 432 else 433 /* help window is static, just return it. */ 434 return help_window; 435#endif /* not HELP_NODE_GETS_REGENERATED */ 436 437 /* Make sure that we have a node containing the help text. The 438 argument is false if help will be the only window (so l must be used 439 to quit help), true if help will be one of several visible windows 440 (so CTRL-x 0 must be used to quit help). */ 441 help_is_only_window_p 442 = ((help_window && !windows->next) 443 || !help_window && eligible->height < HELP_SPLIT_SIZE); 444 create_internal_info_help_node (help_is_only_window_p); 445 446 /* Either use the existing window to display the help node, or create 447 a new window if there was no existing help window. */ 448 if (!help_window) 449 { /* Split the largest window into 2 windows, and show the help text 450 in that window. */ 451 if (eligible->height >= HELP_SPLIT_SIZE) 452 { 453 active_window = eligible; 454 help_window = window_make_window (internal_info_help_node); 455 } 456 else 457 { 458 set_remembered_pagetop_and_point (active_window); 459 window_set_node_of_window (active_window, internal_info_help_node); 460 help_window = active_window; 461 } 462 } 463 else 464 { /* Case where help node always gets regenerated, and we have an 465 existing window in which to place the node. */ 466 if (active_window != help_window) 467 { 468 set_remembered_pagetop_and_point (active_window); 469 active_window = help_window; 470 } 471 window_set_node_of_window (active_window, internal_info_help_node); 472 } 473 remember_window_and_node (help_window, help_window->node); 474 return help_window; 475} 476 477/* Create or move to the help window. */ 478DECLARE_INFO_COMMAND (info_get_help_window, _("Display help message")) 479{ 480 WINDOW *help_window; 481 482 help_window = info_find_or_create_help_window (); 483 if (help_window) 484 { 485 active_window = help_window; 486 active_window->flags |= W_UpdateWindow; 487 } 488 else 489 { 490 info_error (msg_cant_make_help); 491 } 492} 493 494/* Show the Info help node. This means that the "info" file is installed 495 where it can easily be found on your system. */ 496DECLARE_INFO_COMMAND (info_get_info_help_node, _("Visit Info node `(info)Help'")) 497{ 498 NODE *node; 499 char *nodename; 500 501 /* If there is a window on the screen showing the node "(info)Help" or 502 the node "(info)Help-Small-Screen", simply select that window. */ 503 { 504 WINDOW *win; 505 506 for (win = windows; win; win = win->next) 507 { 508 if (win->node && win->node->filename && 509 (strcasecmp 510 (filename_non_directory (win->node->filename), "info") == 0) && 511 ((strcmp (win->node->nodename, "Help") == 0) || 512 (strcmp (win->node->nodename, "Help-Small-Screen") == 0))) 513 { 514 active_window = win; 515 return; 516 } 517 } 518 } 519 520 /* If the current window is small, show the small screen help. */ 521 if (active_window->height < 24) 522 nodename = "Help-Small-Screen"; 523 else 524 nodename = "Help"; 525 526 /* Try to get the info file for Info. */ 527 node = info_get_node ("Info", nodename); 528 529 if (!node) 530 { 531 if (info_recent_file_error) 532 info_error (info_recent_file_error); 533 else 534 info_error (msg_cant_file_node, "Info", nodename); 535 } 536 else 537 { 538 /* If the current window is very large (greater than 45 lines), 539 then split it and show the help node in another window. 540 Otherwise, use the current window. */ 541 542 if (active_window->height > 45) 543 active_window = window_make_window (node); 544 else 545 { 546 set_remembered_pagetop_and_point (active_window); 547 window_set_node_of_window (active_window, node); 548 } 549 550 remember_window_and_node (active_window, node); 551 } 552} 553 554/* **************************************************************** */ 555/* */ 556/* Groveling Info Keymaps and Docs */ 557/* */ 558/* **************************************************************** */ 559 560/* Return the documentation associated with the Info command FUNCTION. */ 561char * 562function_documentation (cmd) 563 InfoCommand *cmd; 564{ 565 char *doc; 566 567#if defined (INFOKEY) 568 569 doc = cmd->doc; 570 571#else /* !INFOKEY */ 572 573 register int i; 574 575 for (i = 0; function_doc_array[i].func; i++) 576 if (InfoFunction(cmd) == function_doc_array[i].func) 577 break; 578 579 doc = function_doc_array[i].func ? function_doc_array[i].doc : ""; 580 581#endif /* !INFOKEY */ 582 583 return replace_in_documentation ((strlen (doc) == 0) ? doc : _(doc)); 584} 585 586#if defined (NAMED_FUNCTIONS) 587/* Return the user-visible name of the function associated with the 588 Info command FUNCTION. */ 589char * 590function_name (cmd) 591 InfoCommand *cmd; 592{ 593#if defined (INFOKEY) 594 595 return cmd->func_name; 596 597#else /* !INFOKEY */ 598 599 register int i; 600 601 for (i = 0; function_doc_array[i].func; i++) 602 if (InfoFunction(cmd) == function_doc_array[i].func) 603 break; 604 605 return (function_doc_array[i].func_name); 606 607#endif /* !INFOKEY */ 608} 609 610/* Return a pointer to the info command for function NAME. */ 611InfoCommand * 612named_function (name) 613 char *name; 614{ 615 register int i; 616 617 for (i = 0; function_doc_array[i].func; i++) 618 if (strcmp (function_doc_array[i].func_name, name) == 0) 619 break; 620 621 return (DocInfoCmd(&function_doc_array[i])); 622} 623#endif /* NAMED_FUNCTIONS */ 624 625/* Return the documentation associated with KEY in MAP. */ 626char * 627key_documentation (key, map) 628 char key; 629 Keymap map; 630{ 631 InfoCommand *function = map[key].function; 632 633 if (function) 634 return (function_documentation (function)); 635 else 636 return ((char *)NULL); 637} 638 639DECLARE_INFO_COMMAND (describe_key, _("Print documentation for KEY")) 640{ 641 char keys[50]; 642 unsigned char keystroke; 643 char *k = keys; 644 Keymap map; 645 646 *k = '\0'; 647 map = window->keymap; 648 649 for (;;) 650 { 651 message_in_echo_area (_("Describe key: %s"), pretty_keyseq (keys)); 652 keystroke = info_get_input_char (); 653 unmessage_in_echo_area (); 654 655#if !defined (INFOKEY) 656 if (Meta_p (keystroke)) 657 { 658 if (map[ESC].type != ISKMAP) 659 { 660 window_message_in_echo_area 661 (_("ESC %s is undefined."), pretty_keyname (UnMeta (keystroke))); 662 return; 663 } 664 665 *k++ = '\e'; 666 keystroke = UnMeta (keystroke); 667 map = (Keymap)map[ESC].function; 668 } 669#endif /* !INFOKEY */ 670 671 /* Add the KEYSTROKE to our list. */ 672 *k++ = keystroke; 673 *k = '\0'; 674 675 if (map[keystroke].function == (InfoCommand *)NULL) 676 { 677 message_in_echo_area (_("%s is undefined."), pretty_keyseq (keys)); 678 return; 679 } 680 else if (map[keystroke].type == ISKMAP) 681 { 682 map = (Keymap)map[keystroke].function; 683 continue; 684 } 685 else 686 { 687 char *keyname, *message, *fundoc, *funname = ""; 688 689#if defined (INFOKEY) 690 /* If the key is bound to do-lowercase-version, but its 691 lower-case variant is undefined, say that this key is 692 also undefined. This is especially important for unbound 693 edit keys that emit an escape sequence: it's terribly 694 confusing to see a message "Home (do-lowercase-version)" 695 or some such when Home is unbound. */ 696 if (InfoFunction(map[keystroke].function) == info_do_lowercase_version) 697 { 698 unsigned char lowerkey = Meta_p(keystroke) 699 ? Meta (tolower (UnMeta (keystroke))) 700 : tolower (keystroke); 701 702 if (map[lowerkey].function == (InfoCommand *)NULL) 703 { 704 message_in_echo_area (_("%s is undefined."), 705 pretty_keyseq (keys)); 706 return; 707 } 708 } 709#endif 710 711 keyname = pretty_keyseq (keys); 712 713#if defined (NAMED_FUNCTIONS) 714 funname = function_name (map[keystroke].function); 715#endif /* NAMED_FUNCTIONS */ 716 717 fundoc = function_documentation (map[keystroke].function); 718 719 message = (char *)xmalloc 720 (10 + strlen (keyname) + strlen (fundoc) + strlen (funname)); 721 722#if defined (NAMED_FUNCTIONS) 723 sprintf (message, "%s (%s): %s.", keyname, funname, fundoc); 724#else 725 sprintf (message, _("%s is defined to %s."), keyname, fundoc); 726#endif /* !NAMED_FUNCTIONS */ 727 728 window_message_in_echo_area ("%s", message); 729 free (message); 730 break; 731 } 732 } 733} 734 735/* Return the pretty printable name of a single character. */ 736char * 737pretty_keyname (key) 738 unsigned char key; 739{ 740 static char rep_buffer[30]; 741 char *rep; 742 743 if (Meta_p (key)) 744 { 745 char temp[20]; 746 747 rep = pretty_keyname (UnMeta (key)); 748 749#if defined (INFOKEY) 750 sprintf (temp, "M-%s", rep); 751#else /* !INFOKEY */ 752 sprintf (temp, "ESC %s", rep); 753#endif /* !INFOKEY */ 754 strcpy (rep_buffer, temp); 755 rep = rep_buffer; 756 } 757 else if (Control_p (key)) 758 { 759 switch (key) 760 { 761 case '\n': rep = "LFD"; break; 762 case '\t': rep = "TAB"; break; 763 case '\r': rep = "RET"; break; 764 case ESC: rep = "ESC"; break; 765 766 default: 767 sprintf (rep_buffer, "C-%c", UnControl (key)); 768 rep = rep_buffer; 769 } 770 } 771 else 772 { 773 switch (key) 774 { 775 case ' ': rep = "SPC"; break; 776 case DEL: rep = "DEL"; break; 777 default: 778 rep_buffer[0] = key; 779 rep_buffer[1] = '\0'; 780 rep = rep_buffer; 781 } 782 } 783 return (rep); 784} 785 786/* Return the pretty printable string which represents KEYSEQ. */ 787 788static void pretty_keyseq_internal (); 789 790char * 791pretty_keyseq (keyseq) 792 char *keyseq; 793{ 794 static char keyseq_rep[200]; 795 796 keyseq_rep[0] = '\0'; 797 if (*keyseq) 798 pretty_keyseq_internal (keyseq, keyseq_rep); 799 return (keyseq_rep); 800} 801 802static void 803pretty_keyseq_internal (keyseq, rep) 804 char *keyseq, *rep; 805{ 806 if (term_kP && strncmp(keyseq, term_kP, strlen(term_kP)) == 0) 807 { 808 strcpy(rep, "PgUp"); 809 keyseq += strlen(term_kP); 810 } 811 else if (term_kN && strncmp(keyseq, term_kN, strlen(term_kN)) == 0) 812 { 813 strcpy(rep, "PgDn"); 814 keyseq += strlen(term_kN); 815 } 816#if defined(INFOKEY) 817 else if (term_kh && strncmp(keyseq, term_kh, strlen(term_kh)) == 0) 818 { 819 strcpy(rep, "Home"); 820 keyseq += strlen(term_kh); 821 } 822 else if (term_ke && strncmp(keyseq, term_ke, strlen(term_ke)) == 0) 823 { 824 strcpy(rep, "End"); 825 keyseq += strlen(term_ke); 826 } 827 else if (term_ki && strncmp(keyseq, term_ki, strlen(term_ki)) == 0) 828 { 829 strcpy(rep, "INS"); 830 keyseq += strlen(term_ki); 831 } 832 else if (term_kx && strncmp(keyseq, term_kx, strlen(term_kx)) == 0) 833 { 834 strcpy(rep, "DEL"); 835 keyseq += strlen(term_kx); 836 } 837#endif /* INFOKEY */ 838 else if (term_ku && strncmp(keyseq, term_ku, strlen(term_ku)) == 0) 839 { 840 strcpy(rep, "Up"); 841 keyseq += strlen(term_ku); 842 } 843 else if (term_kd && strncmp(keyseq, term_kd, strlen(term_kd)) == 0) 844 { 845 strcpy(rep, "Down"); 846 keyseq += strlen(term_kd); 847 } 848 else if (term_kl && strncmp(keyseq, term_kl, strlen(term_kl)) == 0) 849 { 850 strcpy(rep, "Left"); 851 keyseq += strlen(term_kl); 852 } 853 else if (term_kr && strncmp(keyseq, term_kr, strlen(term_kr)) == 0) 854 { 855 strcpy(rep, "Right"); 856 keyseq += strlen(term_kr); 857 } 858 else 859 { 860 strcpy (rep, pretty_keyname (keyseq[0])); 861 keyseq++; 862 } 863 if (*keyseq) 864 { 865 strcat (rep, " "); 866 pretty_keyseq_internal (keyseq, rep + strlen(rep)); 867 } 868} 869 870/* Return a pointer to the last character in s that is found in f. */ 871static char * 872strrpbrk (s, f) 873 const char *s, *f; 874{ 875 register const char *e = s + strlen(s); 876 register const char *t; 877 878 while (e-- != s) 879 { 880 for (t = f; *t; t++) 881 if (*e == *t) 882 return (char *)e; 883 } 884 return NULL; 885} 886 887/* Replace the names of functions with the key that invokes them. */ 888char * 889replace_in_documentation (string, help_is_only_window_p) 890 char *string; 891 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 (map, cmd) 1062 Keymap map; 1063 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. */ 1095static char * 1096where_is_internal (map, cmd) 1097 Keymap map; 1098 InfoCommand *cmd; 1099{ 1100#if defined(INFOKEY) 1101 1102 register FUNCTION_KEYSEQ *k; 1103 1104 for (k = cmd->keys; k; k = k->next) 1105 if (k->map == map) 1106 return pretty_keyseq (k->keyseq); 1107 1108 return NULL; 1109 1110#else /* !INFOKEY */ 1111 /* There is a bug in that create_internal_info_help_node calls 1112 where_is_internal without setting where_is_rep_index to zero. This 1113 was found by Mandrake and reported by Thierry Vignaud 1114 <tvignaud@mandrakesoft.com> around April 24, 2002. 1115 1116 I think the best fix is to make where_is_rep_index another 1117 parameter to this recursively-called function, instead of a static 1118 variable. But this [!INFOKEY] branch of the code is not enabled 1119 any more, so let's just skip the whole thing. --karl, 28sep02. */ 1120 register int i; 1121 1122 /* If the function is directly invokable in MAP, return the representation 1123 of that keystroke. */ 1124 for (i = 0; i < 256; i++) 1125 if ((map[i].type == ISFUNC) && map[i].function == cmd) 1126 { 1127 sprintf (where_is_rep + where_is_rep_index, "%s", pretty_keyname (i)); 1128 return (where_is_rep); 1129 } 1130 1131 /* Okay, search subsequent maps for this function. */ 1132 for (i = 0; i < 256; i++) 1133 { 1134 if (map[i].type == ISKMAP) 1135 { 1136 int saved_index = where_is_rep_index; 1137 char *rep; 1138 1139 sprintf (where_is_rep + where_is_rep_index, "%s ", 1140 pretty_keyname (i)); 1141 1142 where_is_rep_index = strlen (where_is_rep); 1143 rep = where_is_internal ((Keymap)map[i].function, cmd); 1144 1145 if (rep) 1146 return (where_is_rep); 1147 1148 where_is_rep_index = saved_index; 1149 } 1150 } 1151 1152 return NULL; 1153 1154#endif /* INFOKEY */ 1155} 1156 1157extern char *read_function_name (); 1158 1159DECLARE_INFO_COMMAND (info_where_is, 1160 _("Show what to type to execute a given command")) 1161{ 1162 char *command_name; 1163 1164 command_name = read_function_name (_("Where is command: "), window); 1165 1166 if (!command_name) 1167 { 1168 info_abort_key (active_window, count, key); 1169 return; 1170 } 1171 1172 if (*command_name) 1173 { 1174 InfoCommand *command; 1175 1176 command = named_function (command_name); 1177 1178 if (command) 1179 { 1180 char *location; 1181 1182 location = where_is (active_window->keymap, command); 1183 1184 if (!location || !location[0]) 1185 { 1186 info_error (_("`%s' is not on any keys"), command_name); 1187 } 1188 else 1189 { 1190 if (strstr (location, function_name (command))) 1191 window_message_in_echo_area 1192 (_("%s can only be invoked via %s."), command_name, location); 1193 else 1194 window_message_in_echo_area 1195 (_("%s can be invoked via %s."), command_name, location); 1196 } 1197 } 1198 else 1199 info_error (_("There is no function named `%s'"), command_name); 1200 } 1201 1202 free (command_name); 1203} 1204