1 /* infodoc.c -- Functions which build documentation nodes. 2 $Id: infodoc.c,v 1.1.1.2 1997/08/01 22:00:24 kstailey Exp $ 3 4 Copyright (C) 1993, 97 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 24 /* Normally we do not define HELP_NODE_GETS_REGENERATED because the 25 contents of the help node currently can never change once an info 26 session has been started. You should consider defining this in 27 the case that you place information about dynamic variables in the 28 help text. When that happens, the contents of the help node will 29 change dependent on the value of those variables, and the user will 30 expect to see those changes. */ 31 /* #define HELP_NODE_GETS_REGENERATED 1 */ 32 33 /* **************************************************************** */ 34 /* */ 35 /* Info Help Windows */ 36 /* */ 37 /* **************************************************************** */ 38 39 /* The name of the node used in the help window. */ 40 static char *info_help_nodename = "*Info Help*"; 41 42 /* A node containing printed key bindings and their documentation. */ 43 static NODE *internal_info_help_node = (NODE *)NULL; 44 45 /* A pointer to the contents of the help node. */ 46 static char *internal_info_help_node_contents = (char *)NULL; 47 48 /* The static text which appears in the internal info help node. */ 49 static char *info_internal_help_text[] = { 50 N_ ("Basic Commands in Info Windows"), 51 "******************************", 52 "", 53 " h Invoke the Info tutorial.", 54 " CTRL-x 0 Quit this help.", 55 " q Quit Info altogether.", 56 "", 57 "Selecting other nodes:", 58 "----------------------", 59 " n Move to the \"next\" node of this node.", 60 " p Move to the \"previous\" node of this node.", 61 " u Move \"up\" from this node.", 62 " m Pick menu item specified by name.", 63 " Picking a menu item causes another node to be selected.", 64 " f Follow a cross reference. Reads name of reference.", 65 " l Move to the last node seen in this window.", 66 " d Move to the `directory' node. Equivalent to `g(DIR)'.", 67 "", 68 "Moving within a node:", 69 "---------------------", 70 " SPC Scroll forward a page.", 71 " DEL Scroll backward a page.", 72 " b Go to the beginning of this node.", 73 " e Go to the end of this node.", 74 "", 75 "Other commands:", 76 "--------------------", 77 " 1 Pick first item in node's menu.", 78 " 2-9 Pick second ... ninth item in node's menu.", 79 " 0 Pick last item in node's menu.", 80 " g Move to node specified by name.", 81 " You may include a filename as well, as in (FILENAME)NODENAME.", 82 " s Search through this Info file for a specified string,", 83 " and select the node in which the next occurrence is found.", 84 NULL 85 }; 86 87 static char *where_is (), *where_is_internal (); 88 89 void 90 dump_map_to_message_buffer (prefix, map) 91 char *prefix; 92 Keymap map; 93 { 94 register int i; 95 96 for (i = 0; i < 256; i++) 97 { 98 if (map[i].type == ISKMAP) 99 { 100 char *new_prefix, *keyname; 101 102 keyname = pretty_keyname (i); 103 new_prefix = (char *) 104 xmalloc (3 + strlen (prefix) + strlen (keyname)); 105 sprintf (new_prefix, "%s%s%s ", prefix, *prefix ? " " : "", keyname); 106 107 dump_map_to_message_buffer (new_prefix, (Keymap)map[i].function); 108 free (new_prefix); 109 } 110 else if (map[i].function) 111 { 112 register int last; 113 char *doc, *name; 114 115 doc = function_documentation (map[i].function); 116 name = function_name (map[i].function); 117 118 if (!*doc) 119 continue; 120 121 /* Find out if there is a series of identical functions, as in 122 ea_insert (). */ 123 for (last = i + 1; last < 256; last++) 124 if ((map[last].type != ISFUNC) || 125 (map[last].function != map[i].function)) 126 break; 127 128 if (last - 1 != i) 129 { 130 printf_to_message_buffer 131 ("%s%s .. ", prefix, pretty_keyname (i)); 132 printf_to_message_buffer 133 ("%s%s\t", prefix, pretty_keyname (last - 1)); 134 i = last - 1; 135 } 136 else 137 printf_to_message_buffer ("%s%s\t", prefix, pretty_keyname (i)); 138 139 #if defined (NAMED_FUNCTIONS) 140 /* Print the name of the function, and some padding before the 141 documentation string is printed. */ 142 { 143 int length_so_far; 144 int desired_doc_start = 40; /* Must be multiple of 8. */ 145 146 printf_to_message_buffer ("(%s)", name); 147 length_so_far = message_buffer_length_this_line (); 148 149 if ((desired_doc_start + strlen (doc)) >= the_screen->width) 150 printf_to_message_buffer ("\n "); 151 else 152 { 153 while (length_so_far < desired_doc_start) 154 { 155 printf_to_message_buffer ("\t"); 156 length_so_far += character_width ('\t', length_so_far); 157 } 158 } 159 } 160 #endif /* NAMED_FUNCTIONS */ 161 printf_to_message_buffer ("%s\n", doc); 162 } 163 } 164 } 165 166 /* How to create internal_info_help_node. */ 167 static void 168 create_internal_info_help_node () 169 { 170 register int i; 171 char *contents = (char *)NULL; 172 NODE *node; 173 174 #if !defined (HELP_NODE_GETS_REGENERATED) 175 if (internal_info_help_node_contents) 176 contents = internal_info_help_node_contents; 177 #endif /* !HELP_NODE_GETS_REGENERATED */ 178 179 if (!contents) 180 { 181 int printed_one_mx = 0; 182 183 initialize_message_buffer (); 184 185 for (i = 0; info_internal_help_text[i]; i++) 186 printf_to_message_buffer ("%s\n", info_internal_help_text[i]); 187 188 printf_to_message_buffer ("---------------------\n\n"); 189 printf_to_message_buffer ("The current search path is:\n"); 190 printf_to_message_buffer (" \"%s\"\n", infopath); 191 printf_to_message_buffer ("---------------------\n\n"); 192 printf_to_message_buffer ("Commands available in Info windows:\n\n"); 193 dump_map_to_message_buffer ("", info_keymap); 194 printf_to_message_buffer ("---------------------\n\n"); 195 printf_to_message_buffer ("Commands available in the echo area:\n\n"); 196 dump_map_to_message_buffer ("", echo_area_keymap); 197 198 #if defined (NAMED_FUNCTIONS) 199 /* Get a list of the M-x commands which have no keystroke equivs. */ 200 for (i = 0; function_doc_array[i].func; i++) 201 { 202 VFunction *func = function_doc_array[i].func; 203 204 if ((!where_is_internal (info_keymap, func)) && 205 (!where_is_internal (echo_area_keymap, func))) 206 { 207 if (!printed_one_mx) 208 { 209 printf_to_message_buffer ("---------------------\n\n"); 210 printf_to_message_buffer 211 (_("The following commands can only be invoked via M-x:\n\n")); 212 printed_one_mx = 1; 213 } 214 215 printf_to_message_buffer 216 ("M-x %s\n %s\n", 217 function_doc_array[i].func_name, 218 replace_in_documentation (function_doc_array[i].doc)); 219 } 220 } 221 222 if (printed_one_mx) 223 printf_to_message_buffer ("\n"); 224 #endif /* NAMED_FUNCTIONS */ 225 226 printf_to_message_buffer 227 ("%s", replace_in_documentation 228 (_("--- Use `\\[history-node]' or `\\[kill-node]' to exit ---\n"))); 229 node = message_buffer_to_node (); 230 internal_info_help_node_contents = node->contents; 231 } 232 else 233 { 234 /* We already had the right contents, so simply use them. */ 235 node = build_message_node ("", 0, 0); 236 free (node->contents); 237 node->contents = contents; 238 node->nodelen = 1 + strlen (contents); 239 } 240 241 internal_info_help_node = node; 242 243 /* Do not GC this node's contents. It never changes, and we never need 244 to delete it once it is made. If you change some things (such as 245 placing information about dynamic variables in the help text) then 246 you will need to allow the contents to be gc'd, and you will have to 247 arrange to always regenerate the help node. */ 248 #if defined (HELP_NODE_GETS_REGENERATED) 249 add_gcable_pointer (internal_info_help_node->contents); 250 #endif 251 252 name_internal_node (internal_info_help_node, info_help_nodename); 253 254 /* Even though this is an internal node, we don't want the window 255 system to treat it specially. So we turn off the internalness 256 of it here. */ 257 internal_info_help_node->flags &= ~N_IsInternal; 258 } 259 260 /* Return a window which is the window showing help in this Info. */ 261 static WINDOW * 262 info_find_or_create_help_window () 263 { 264 WINDOW *help_window, *eligible, *window; 265 266 eligible = (WINDOW *)NULL; 267 help_window = get_internal_info_window (info_help_nodename); 268 269 /* If we couldn't find the help window, then make it. */ 270 if (!help_window) 271 { 272 int max = 0; 273 274 for (window = windows; window; window = window->next) 275 { 276 if (window->height > max) 277 { 278 max = window->height; 279 eligible = window; 280 } 281 } 282 283 if (!eligible) 284 return ((WINDOW *)NULL); 285 } 286 #if !defined (HELP_NODE_GETS_REGENERATED) 287 else 288 return (help_window); 289 #endif /* !HELP_NODE_GETS_REGENERATED */ 290 291 /* Make sure that we have a node containing the help text. */ 292 create_internal_info_help_node (); 293 294 /* Either use the existing window to display the help node, or create 295 a new window if there was no existing help window. */ 296 if (!help_window) 297 { 298 /* Split the largest window into 2 windows, and show the help text 299 in that window. */ 300 if (eligible->height > 30) 301 { 302 active_window = eligible; 303 help_window = window_make_window (internal_info_help_node); 304 } 305 else 306 { 307 set_remembered_pagetop_and_point (active_window); 308 window_set_node_of_window (active_window, internal_info_help_node); 309 help_window = active_window; 310 } 311 } 312 else 313 { 314 /* Case where help node always gets regenerated, and we have an 315 existing window in which to place the node. */ 316 if (active_window != help_window) 317 { 318 set_remembered_pagetop_and_point (active_window); 319 active_window = help_window; 320 } 321 window_set_node_of_window (active_window, internal_info_help_node); 322 } 323 remember_window_and_node (help_window, help_window->node); 324 return (help_window); 325 } 326 327 /* Create or move to the help window. */ 328 DECLARE_INFO_COMMAND (info_get_help_window, _("Display help message")) 329 { 330 WINDOW *help_window; 331 332 help_window = info_find_or_create_help_window (); 333 if (help_window) 334 { 335 active_window = help_window; 336 active_window->flags |= W_UpdateWindow; 337 } 338 else 339 { 340 info_error (CANT_MAKE_HELP); 341 } 342 } 343 344 /* Show the Info help node. This means that the "info" file is installed 345 where it can easily be found on your system. */ 346 DECLARE_INFO_COMMAND (info_get_info_help_node, _("Visit Info node `(info)Help'")) 347 { 348 NODE *node; 349 char *nodename; 350 351 /* If there is a window on the screen showing the node "(info)Help" or 352 the node "(info)Help-Small-Screen", simply select that window. */ 353 { 354 WINDOW *win; 355 356 for (win = windows; win; win = win->next) 357 { 358 if (win->node && win->node->filename && 359 (strcasecmp 360 (filename_non_directory (win->node->filename), "info") == 0) && 361 ((strcmp (win->node->nodename, "Help") == 0) || 362 (strcmp (win->node->nodename, "Help-Small-Screen") == 0))) 363 { 364 active_window = win; 365 return; 366 } 367 } 368 } 369 370 /* If the current window is small, show the small screen help. */ 371 if (active_window->height < 24) 372 nodename = "Help-Small-Screen"; 373 else 374 nodename = "Help"; 375 376 /* Try to get the info file for Info. */ 377 node = info_get_node ("Info", nodename); 378 379 if (!node) 380 { 381 if (info_recent_file_error) 382 info_error (info_recent_file_error); 383 else 384 info_error (CANT_FILE_NODE, "Info", nodename); 385 } 386 else 387 { 388 /* If the current window is very large (greater than 45 lines), 389 then split it and show the help node in another window. 390 Otherwise, use the current window. */ 391 392 if (active_window->height > 45) 393 active_window = window_make_window (node); 394 else 395 { 396 set_remembered_pagetop_and_point (active_window); 397 window_set_node_of_window (active_window, node); 398 } 399 400 remember_window_and_node (active_window, node); 401 } 402 } 403 404 /* **************************************************************** */ 405 /* */ 406 /* Groveling Info Keymaps and Docs */ 407 /* */ 408 /* **************************************************************** */ 409 410 /* Return the documentation associated with the Info command FUNCTION. */ 411 char * 412 function_documentation (function) 413 VFunction *function; 414 { 415 register int i; 416 417 for (i = 0; function_doc_array[i].func; i++) 418 if (function == function_doc_array[i].func) 419 break; 420 421 return (replace_in_documentation (function_doc_array[i].doc)); 422 } 423 424 #if defined (NAMED_FUNCTIONS) 425 /* Return the user-visible name of the function associated with the 426 Info command FUNCTION. */ 427 char * 428 function_name (function) 429 430 VFunction *function; 431 { 432 register int i; 433 434 for (i = 0; function_doc_array[i].func; i++) 435 if (function == function_doc_array[i].func) 436 break; 437 438 return (function_doc_array[i].func_name); 439 } 440 441 /* Return a pointer to the function named NAME. */ 442 VFunction * 443 named_function (name) 444 char *name; 445 { 446 register int i; 447 448 for (i = 0; function_doc_array[i].func; i++) 449 if (strcmp (function_doc_array[i].func_name, name) == 0) 450 break; 451 452 return (function_doc_array[i].func); 453 } 454 #endif /* NAMED_FUNCTIONS */ 455 456 /* Return the documentation associated with KEY in MAP. */ 457 char * 458 key_documentation (key, map) 459 char key; 460 Keymap map; 461 { 462 VFunction *function = map[key].function; 463 464 if (function) 465 return (function_documentation (function)); 466 else 467 return ((char *)NULL); 468 } 469 470 DECLARE_INFO_COMMAND (describe_key, _("Print documentation for KEY")) 471 { 472 char keyname[50]; 473 int keyname_index = 0; 474 unsigned char keystroke; 475 char *rep; 476 Keymap map; 477 478 keyname[0] = '\0'; 479 map = window->keymap; 480 481 while (1) 482 { 483 message_in_echo_area (_("Describe key: %s"), keyname); 484 keystroke = info_get_input_char (); 485 unmessage_in_echo_area (); 486 487 if (Meta_p (keystroke) && (!ISO_Latin_p || key < 160)) 488 { 489 if (map[ESC].type != ISKMAP) 490 { 491 window_message_in_echo_area 492 (_("ESC %s is undefined."), pretty_keyname (UnMeta (keystroke))); 493 return; 494 } 495 496 strcpy (keyname + keyname_index, "ESC "); 497 keyname_index = strlen (keyname); 498 keystroke = UnMeta (keystroke); 499 map = (Keymap)map[ESC].function; 500 } 501 502 /* Add the printed representation of KEYSTROKE to our keyname. */ 503 rep = pretty_keyname (keystroke); 504 strcpy (keyname + keyname_index, rep); 505 keyname_index = strlen (keyname); 506 507 if (map[keystroke].function == (VFunction *)NULL) 508 { 509 message_in_echo_area (_("%s is undefined."), keyname); 510 return; 511 } 512 else if (map[keystroke].type == ISKMAP) 513 { 514 map = (Keymap)map[keystroke].function; 515 strcat (keyname, " "); 516 keyname_index = strlen (keyname); 517 continue; 518 } 519 else 520 { 521 char *message, *fundoc, *funname = ""; 522 523 #if defined (NAMED_FUNCTIONS) 524 funname = function_name (map[keystroke].function); 525 #endif /* NAMED_FUNCTIONS */ 526 527 fundoc = function_documentation (map[keystroke].function); 528 529 message = (char *)xmalloc 530 (10 + strlen (keyname) + strlen (fundoc) + strlen (funname)); 531 532 #if defined (NAMED_FUNCTIONS) 533 sprintf (message, "%s (%s): %s.", keyname, funname, fundoc); 534 #else 535 sprintf (message, _("%s is defined to %s."), keyname, fundoc); 536 #endif /* !NAMED_FUNCTIONS */ 537 538 window_message_in_echo_area ("%s", message); 539 free (message); 540 break; 541 } 542 } 543 } 544 545 /* How to get the pretty printable name of a character. */ 546 static char rep_buffer[30]; 547 548 char * 549 pretty_keyname (key) 550 unsigned char key; 551 { 552 char *rep; 553 554 if (Meta_p (key)) 555 { 556 char temp[20]; 557 558 rep = pretty_keyname (UnMeta (key)); 559 560 sprintf (temp, "ESC %s", rep); 561 strcpy (rep_buffer, temp); 562 rep = rep_buffer; 563 } 564 else if (Control_p (key)) 565 { 566 switch (key) 567 { 568 case '\n': rep = "LFD"; break; 569 case '\t': rep = "TAB"; break; 570 case '\r': rep = "RET"; break; 571 case ESC: rep = "ESC"; break; 572 573 default: 574 sprintf (rep_buffer, "C-%c", UnControl (key)); 575 rep = rep_buffer; 576 } 577 } 578 else 579 { 580 switch (key) 581 { 582 case ' ': rep = "SPC"; break; 583 case DEL: rep = "DEL"; break; 584 default: 585 rep_buffer[0] = key; 586 rep_buffer[1] = '\0'; 587 rep = rep_buffer; 588 } 589 } 590 return (rep); 591 } 592 593 /* Replace the names of functions with the key that invokes them. */ 594 char * 595 replace_in_documentation (string) 596 char *string; 597 { 598 register int i, start, next; 599 static char *result = (char *)NULL; 600 601 maybe_free (result); 602 result = (char *)xmalloc (1 + strlen (string)); 603 604 i = next = start = 0; 605 606 /* Skip to the beginning of a replaceable function. */ 607 for (i = start; string[i]; i++) 608 { 609 /* Is this the start of a replaceable function name? */ 610 if (string[i] == '\\' && string[i + 1] == '[') 611 { 612 char *fun_name, *rep; 613 VFunction *function; 614 615 /* Copy in the old text. */ 616 strncpy (result + next, string + start, i - start); 617 next += (i - start); 618 start = i + 2; 619 620 /* Move to the end of the function name. */ 621 for (i = start; string[i] && (string[i] != ']'); i++); 622 623 fun_name = (char *)xmalloc (1 + i - start); 624 strncpy (fun_name, string + start, i - start); 625 fun_name[i - start] = '\0'; 626 627 /* Find a key which invokes this function in the info_keymap. */ 628 function = named_function (fun_name); 629 630 /* If the internal documentation string fails, there is a 631 serious problem with the associated command's documentation. 632 We croak so that it can be fixed immediately. */ 633 if (!function) 634 abort (); 635 636 rep = where_is (info_keymap, function); 637 strcpy (result + next, rep); 638 next = strlen (result); 639 640 start = i; 641 if (string[i]) 642 start++; 643 } 644 } 645 strcpy (result + next, string + start); 646 return (result); 647 } 648 649 /* Return a string of characters which could be typed from the keymap 650 MAP to invoke FUNCTION. */ 651 static char *where_is_rep = (char *)NULL; 652 static int where_is_rep_index = 0; 653 static int where_is_rep_size = 0; 654 655 static char * 656 where_is (map, function) 657 Keymap map; 658 VFunction *function; 659 { 660 char *rep; 661 662 if (!where_is_rep_size) 663 where_is_rep = (char *)xmalloc (where_is_rep_size = 100); 664 where_is_rep_index = 0; 665 666 rep = where_is_internal (map, function); 667 668 /* If it couldn't be found, return "M-x Foo". */ 669 if (!rep) 670 { 671 char *name; 672 673 name = function_name (function); 674 675 if (name) 676 sprintf (where_is_rep, "M-x %s", name); 677 678 rep = where_is_rep; 679 } 680 return (rep); 681 } 682 683 /* Return the printed rep of FUNCTION as found in MAP, or NULL. */ 684 static char * 685 where_is_internal (map, function) 686 Keymap map; 687 VFunction *function; 688 { 689 register int i; 690 691 /* If the function is directly invokable in MAP, return the representation 692 of that keystroke. */ 693 for (i = 0; i < 256; i++) 694 if ((map[i].type == ISFUNC) && map[i].function == function) 695 { 696 sprintf (where_is_rep + where_is_rep_index, "%s", pretty_keyname (i)); 697 return (where_is_rep); 698 } 699 700 /* Okay, search subsequent maps for this function. */ 701 for (i = 0; i < 256; i++) 702 { 703 if (map[i].type == ISKMAP) 704 { 705 int saved_index = where_is_rep_index; 706 char *rep; 707 708 sprintf (where_is_rep + where_is_rep_index, "%s ", 709 pretty_keyname (i)); 710 711 where_is_rep_index = strlen (where_is_rep); 712 rep = where_is_internal ((Keymap)map[i].function, function); 713 714 if (rep) 715 return (where_is_rep); 716 717 where_is_rep_index = saved_index; 718 } 719 } 720 721 return ((char *)NULL); 722 } 723 724 extern char *read_function_name (); 725 726 DECLARE_INFO_COMMAND (info_where_is, 727 "Show what to type to execute a given command") 728 { 729 char *command_name; 730 731 command_name = read_function_name (_("Where is command: "), window); 732 733 if (!command_name) 734 { 735 info_abort_key (active_window, count, key); 736 return; 737 } 738 739 if (*command_name) 740 { 741 VFunction *function; 742 743 function = named_function (command_name); 744 745 if (function) 746 { 747 char *location; 748 749 location = where_is (active_window->keymap, function); 750 751 if (!location) 752 { 753 info_error (_("`%s' is not on any keys"), command_name); 754 } 755 else 756 { 757 if (strncmp (location, "M-x ", 4) == 0) 758 window_message_in_echo_area 759 (_("%s can only be invoked via %s."), command_name, location); 760 else 761 window_message_in_echo_area 762 (_("%s can be invoked via %s."), command_name, location); 763 } 764 } 765 else 766 info_error (_("There is no function named `%s'"), command_name); 767 } 768 769 free (command_name); 770 } 771