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