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