1/* infodoc.c -- Functions which build documentation nodes.
2   $Id: infodoc.c,v 1.1 2004/10/28 18:14:09 zooey 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. */
40static char *info_help_nodename = "*Info Help*";
41
42/* A node containing printed key bindings and their documentation. */
43static NODE *internal_info_help_node = (NODE *)NULL;
44
45/* A pointer to the contents of the help node. */
46static char *internal_info_help_node_contents = (char *)NULL;
47
48/* The static text which appears in the internal info help node. */
49static 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
87static char *where_is (), *where_is_internal ();
88
89void
90dump_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. */
167static void
168create_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. */
261static WINDOW *
262info_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. */
328DECLARE_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. */
346DECLARE_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. */
411char *
412function_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. */
427char *
428function_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. */
442VFunction *
443named_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. */
457char *
458key_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
470DECLARE_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. */
546static char rep_buffer[30];
547
548char *
549pretty_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. */
594char *
595replace_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. */
651static char *where_is_rep = (char *)NULL;
652static int where_is_rep_index = 0;
653static int where_is_rep_size = 0;
654
655static char *
656where_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. */
684static char *
685where_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
724extern char *read_function_name ();
725
726DECLARE_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