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