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