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