]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/texinfo/info/infodoc.c
This commit was generated by cvs2svn to compensate for changes in r38889,
[FreeBSD/FreeBSD.git] / contrib / texinfo / info / infodoc.c
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. */
42 static char *info_help_nodename = "*Info Help*";
43
44 /* A node containing printed key bindings and their documentation. */
45 static NODE *internal_info_help_node = (NODE *)NULL;
46
47 /* A pointer to the contents of the help node. */
48 static char *internal_info_help_node_contents = (char *)NULL;
49
50 /* The static text which appears in the internal info help node. */
51 static 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
88 static char *where_is (), *where_is_internal ();
89
90 void
91 dump_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. */
168 static void
169 create_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. */
262 static WINDOW *
263 info_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. */
329 DECLARE_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. */
347 DECLARE_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 \f
405 /* **************************************************************** */
406 /*                                                                  */
407 /*                   Groveling Info Keymaps and Docs                */
408 /*                                                                  */
409 /* **************************************************************** */
410
411 /* Return the documentation associated with the Info command FUNCTION. */
412 char *
413 function_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. */
428 char *
429 function_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. */
443 VFunction *
444 named_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. */
458 char *
459 key_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
471 DECLARE_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. */
547 static char rep_buffer[30];
548
549 char *
550 pretty_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. */
595 char *
596 replace_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. */
652 static char *where_is_rep = (char *)NULL;
653 static int where_is_rep_index = 0;
654 static int where_is_rep_size = 0;
655
656 static char *
657 where_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. */
685 static char *
686 where_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
725 extern char *read_function_name ();
726
727 DECLARE_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 }