Merge branch 'vendor/GCC50' - gcc 5.0 snapshot 1 FEB 2015
[dragonfly.git] / contrib / texinfo / info / info.c
1 /* info.c -- Display nodes of Info files in multiple windows.
2    $Id: info.c,v 1.33 2008/08/14 17:36:13 karl Exp $
3
4    Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
5    2004, 2005, 2007, 2008 Free Software Foundation, Inc.
6
7    This program is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20    Originally written by Brian Fox (bfox@ai.mit.edu). */
21
22 #include "info.h"
23 #include "indices.h"
24 #include "dribble.h"
25 #include "getopt.h"
26 #if defined (HANDLE_MAN_PAGES)
27 #  include "man.h"
28 #endif /* HANDLE_MAN_PAGES */
29
30 char *program_name = "info";
31
32 /* Non-zero means search all indices for APROPOS_SEARCH_STRING. */
33 static int apropos_p = 0;
34
35 /* Variable containing the string to search for when apropos_p is non-zero. */
36 static char *apropos_search_string = NULL;
37
38 /* Non-zero means search all indices for INDEX_SEARCH_STRING.  Unlike
39    apropos, this puts the user at the node, running info. */
40 static int index_search_p = 0;
41
42 /* Non-zero means look for the node which describes the invocation
43    and command-line options of the program, and start the info
44    session at that node.  */
45 static int goto_invocation_p = 0;
46
47 /* Variable containing the string to search for when index_search_p is
48    non-zero. */
49 static char *index_search_string = NULL;
50
51 /* Non-zero means print version info only. */
52 static int print_version_p = 0;
53
54 /* Non-zero means print a short description of the options. */
55 static int print_help_p = 0;
56
57 /* Array of the names of nodes that the user specified with "--node" on the
58    command line. */
59 static char **user_nodenames = NULL;
60 static int user_nodenames_index = 0;
61 static int user_nodenames_slots = 0;
62
63 /* String specifying the first file to load.  This string can only be set
64    by the user specifying "--file" on the command line. */
65 static char *user_filename = NULL;
66
67 /* String specifying the name of the file to dump nodes to.  This value is
68    filled if the user speficies "--output" on the command line. */
69 static char *user_output_filename = NULL;
70
71 /* Non-zero indicates that when "--output" is specified, all of the menu
72    items of the specified nodes (and their subnodes as well) should be
73    dumped in the order encountered.  This basically can print a book. */
74 int dump_subnodes = 0;
75
76 /* Non-zero means make default keybindings be loosely modeled on vi(1).  */
77 int vi_keys_p = 0;
78
79 /* Non-zero means don't remove ANSI escape sequences.  */
80 int raw_escapes_p = 1;
81
82 /* Non-zero means print the absolute location of the file to be loaded.  */
83 static int print_where_p = 0;
84
85 #ifdef __MSDOS__
86 /* Non-zero indicates that screen output should be made 'speech-friendly'.
87    Since on MSDOS the usual behavior is to write directly to the video
88    memory, speech synthesizer software cannot grab the output.  Therefore,
89    we provide a user option which tells us to avoid direct screen output
90    and use stdout instead (which loses the color output).  */
91 int speech_friendly = 0;
92 #endif
93
94 /* Structure describing the options that Info accepts.  We pass this structure
95    to getopt_long ().  If you add or otherwise change this structure, you must
96    also change the string which follows it. */
97 #define DRIBBLE_OPTION 2
98 #define RESTORE_OPTION 3
99 #define IDXSRCH_OPTION 4
100 static struct option long_options[] = {
101   { "apropos", 1, 0, 'k' },
102   { "directory", 1, 0, 'd' },
103   { "dribble", 1, 0, DRIBBLE_OPTION },
104   { "file", 1, 0, 'f' },
105   { "help", 0, &print_help_p, 1 },
106   { "index-search", 1, 0, IDXSRCH_OPTION },
107   { "location", 0, &print_where_p, 1 },
108   { "node", 1, 0, 'n' },
109   { "output", 1, 0, 'o' },
110   { "raw-escapes", 0, &raw_escapes_p, 1 },
111   { "no-raw-escapes", 0, &raw_escapes_p, 0 },
112   { "show-malformed-multibytes", 0, &show_malformed_multibyte_p, 1 },
113   { "no-show-malformed-multibytes", 0, &show_malformed_multibyte_p, 0 },
114   { "restore", 1, 0, RESTORE_OPTION },
115   { "show-options", 0, 0, 'O' },
116   { "subnodes", 0, &dump_subnodes, 1 },
117   { "usage", 0, 0, 'O' },
118   { "version", 0, &print_version_p, 1 },
119   { "vi-keys", 0, &vi_keys_p, 1 },
120   { "where", 0, &print_where_p, 1 },
121 #ifdef __MSDOS__
122   { "speech-friendly", 0, &speech_friendly, 1 },
123 #endif
124   {NULL, 0, NULL, 0}
125 };
126
127 /* String describing the shorthand versions of the long options found above. */
128 #ifdef __MSDOS__
129 static char *short_options = "k:d:n:f:ho:ORswb";
130 #else
131 static char *short_options = "k:d:n:f:ho:ORws";
132 #endif
133
134 /* When non-zero, the Info window system has been initialized. */
135 int info_windows_initialized_p = 0;
136
137 /* Some "forward" declarations. */
138 static void info_short_help (void);
139 static void init_messages (void);
140
141 \f
142 /* **************************************************************** */
143 /*                                                                  */
144 /*                Main Entry Point to the Info Program              */
145 /*                                                                  */
146 /* **************************************************************** */
147
148 int
149 main (int argc, char *argv[])
150 {
151   int getopt_long_index;        /* Index returned by getopt_long (). */
152   NODE *initial_node;           /* First node loaded by Info. */
153
154 #ifdef HAVE_SETLOCALE
155   /* Set locale via LC_ALL.  */
156   setlocale (LC_ALL, "");
157 #endif /* HAVE_SETLOCALE */
158
159 #ifdef ENABLE_NLS
160   /* Set the text message domain.  */
161   bindtextdomain (PACKAGE, LOCALEDIR);
162   textdomain (PACKAGE);
163 #endif
164
165   init_messages ();
166
167   while (1)
168     {
169       int option_character;
170
171       option_character = getopt_long
172         (argc, argv, short_options, long_options, &getopt_long_index);
173
174       /* getopt_long returns EOF when there are no more long options. */
175       if (option_character == EOF)
176         break;
177
178       /* If this is a long option, then get the short version of it. */
179       if (option_character == 0 && long_options[getopt_long_index].flag == 0)
180         option_character = long_options[getopt_long_index].val;
181
182       /* Case on the option that we have received. */
183       switch (option_character)
184         {
185         case 0:
186           break;
187
188           /* User wants to add a directory. */
189         case 'd':
190           info_add_path (optarg, INFOPATH_PREPEND);
191           break;
192
193           /* User is specifying a particular node. */
194         case 'n':
195           add_pointer_to_array (optarg, user_nodenames_index, user_nodenames,
196                                 user_nodenames_slots, 10, char *);
197           break;
198
199           /* User is specifying a particular Info file. */
200         case 'f':
201           if (user_filename)
202             free (user_filename);
203
204           user_filename = xstrdup (optarg);
205           break;
206
207           /* Treat -h like --help. */
208         case 'h':
209           print_help_p = 1;
210           break;
211
212           /* User is specifying the name of a file to output to. */
213         case 'o':
214           if (user_output_filename)
215             free (user_output_filename);
216           user_output_filename = xstrdup (optarg);
217           break;
218
219          /* User has specified that she wants to find the "Options"
220              or "Invocation" node for the program.  */
221         case 'O':
222           goto_invocation_p = 1;
223           break;
224
225           /* User has specified that she wants the escape sequences
226              in man pages to be passed thru unaltered.  */
227         case 'R':
228           raw_escapes_p = 1;
229           break;
230
231           /* User is specifying that she wishes to dump the subnodes of
232              the node that she is dumping. */
233         case 's':
234           dump_subnodes = 1;
235           break;
236
237           /* For compatibility with man, -w is --where.  */
238         case 'w':
239           print_where_p = 1;
240           break;
241
242 #ifdef __MSDOS__
243           /* User wants speech-friendly output.  */
244         case 'b':
245           speech_friendly = 1;
246           break;
247 #endif /* __MSDOS__ */
248
249           /* User has specified a string to search all indices for. */
250         case 'k':
251           apropos_p = 1;
252           maybe_free (apropos_search_string);
253           apropos_search_string = xstrdup (optarg);
254           break;
255
256           /* User has specified a dribble file to receive keystrokes. */
257         case DRIBBLE_OPTION:
258           close_dribble_file ();
259           open_dribble_file (optarg);
260           break;
261
262           /* User has specified an alternate input stream. */
263         case RESTORE_OPTION:
264           info_set_input_from_file (optarg);
265           break;
266
267           /* User has specified a string to search all indices for. */
268         case IDXSRCH_OPTION:
269           index_search_p = 1;
270           maybe_free (index_search_string);
271           index_search_string = xstrdup (optarg);
272           break;
273
274         default:
275           fprintf (stderr, _("Try --help for more information.\n"));
276           xexit (1);
277         }
278     }
279
280   /* If the output device is not a terminal, and no output filename has been
281      specified, make user_output_filename be "-", so that the info is written
282      to stdout, and turn on the dumping of subnodes. */
283   if ((!isatty (fileno (stdout))) && (user_output_filename == NULL))
284     {
285       user_output_filename = xstrdup ("-");
286       dump_subnodes = 1;
287     }
288
289   /* If the user specified --version, then show the version and exit. */
290   if (print_version_p)
291     {
292       printf ("info (GNU %s) %s\n", PACKAGE, VERSION);
293       puts ("");
294       printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
295 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
296 This is free software: you are free to change and redistribute it.\n\
297 There is NO WARRANTY, to the extent permitted by law.\n"),
298               "2008");
299       xexit (0);
300     }
301
302   /* If the `--help' option was present, show the help and exit. */
303   if (print_help_p)
304     {
305       info_short_help ();
306       xexit (0);
307     }
308
309   /* If the user hasn't specified a path for Info files, default it.
310      Lowest priority is our messy hardwired list in filesys.h.
311      Then comes the user's INFODIR from the Makefile.
312      Highest priority is the environment variable, if set.  */
313   if (!infopath)
314     {
315       char *path_from_env = getenv ("INFOPATH");
316
317       if (path_from_env)
318         {
319           unsigned len = strlen (path_from_env);
320           /* Trailing : on INFOPATH means insert the default path.  */
321           if (len && path_from_env[len - 1] == PATH_SEP[0])
322             {
323               path_from_env[len - 1] = 0;
324               info_add_path (DEFAULT_INFOPATH, INFOPATH_PREPEND);
325             }
326 #ifdef INFODIR /* from the Makefile */
327           info_add_path (INFODIR, INFOPATH_PREPEND);
328 #endif
329           info_add_path (path_from_env, INFOPATH_PREPEND);
330         }
331       else
332         {
333           info_add_path (DEFAULT_INFOPATH, INFOPATH_PREPEND);
334 #ifdef INFODIR /* from the Makefile */
335           info_add_path (INFODIR, INFOPATH_PREPEND);
336 #endif
337 #ifdef INFODIR2 /* from the Makefile, too */
338 #  ifdef INFODIR
339           if (!STREQ (INFODIR, INFODIR2))
340 #  endif
341             info_add_path (INFODIR2, INFOPATH_PREPEND);
342 #endif
343         }
344     }
345
346   /* If the user specified a particular filename, add the path of that
347      file to the contents of INFOPATH. */
348   if (user_filename)
349     add_file_directory_to_path (user_filename);
350
351   /* If the user wants to search every known index for a given string,
352      do that now, and report the results. */
353   if (apropos_p)
354     {
355       info_apropos (apropos_search_string);
356       xexit (0);
357     }
358
359   /* Get the initial Info node.  It is either "(dir)Top", or what the user
360      specifed with values in user_filename and user_nodenames. */
361   initial_node = info_get_node (user_filename,
362                                 user_nodenames ? user_nodenames[0] : 0);
363
364   /* If we couldn't get the initial node, this user is in trouble. */
365   if (!initial_node)
366     {
367       if (info_recent_file_error)
368         info_error (info_recent_file_error, NULL, NULL);
369       else
370         info_error (msg_cant_find_node,
371                     user_nodenames ? user_nodenames[0] : "Top", NULL);
372       xexit (1);
373     }
374
375   /* Special cases for when the user specifies multiple nodes.  If we
376      are dumping to an output file, dump all of the nodes specified.
377      Otherwise, attempt to create enough windows to handle the nodes
378      that this user wants displayed. */
379   if (user_nodenames_index > 1)
380     {
381       free (initial_node);
382
383       if (print_where_p)
384         printf ("%s\n", user_filename ? user_filename : "unknown?!");
385       else if (user_output_filename)
386         dump_nodes_to_file
387           (user_filename, user_nodenames, user_output_filename, dump_subnodes);
388       else
389         begin_multiple_window_info_session (user_filename, user_nodenames);
390
391       xexit (0);
392     }
393
394   /* If there are arguments remaining, they are the names of menu items
395      in sequential info files starting from the first one loaded.  That
396      file name is either "dir", or the contents of user_filename if one
397      was specified. */
398   {
399     const char *errstr;
400     char *errarg1, *errarg2;
401     NODE *new_initial_node;
402
403    /* If they say info -O info, we want to show them the invocation node
404       for standalone info; there's nothing useful in info.texi.  */
405    if (goto_invocation_p && argv[optind]
406        && mbscasecmp (argv[optind], "info") == 0)
407      argv[optind] = "info-stnd";
408
409     new_initial_node = info_follow_menus (initial_node, argv + optind,
410         &errstr, &errarg1, &errarg2);
411
412     if (new_initial_node && new_initial_node != initial_node)
413       initial_node = new_initial_node;
414
415     if (print_where_p)
416       {
417         if (initial_node->parent)
418           printf ("%s\n", initial_node->parent);
419         else if (initial_node->filename
420             && !is_dir_name (filename_non_directory (initial_node->filename)))
421           printf ("%s\n", initial_node->filename);
422         else
423           xexit (1);
424         xexit (0);
425       }
426
427     /* If the user specified that this node should be output, then do that
428        now.  Otherwise, start the Info session with this node.  Or act
429        accordingly if the initial node was not found.  */
430     if (user_output_filename && !goto_invocation_p)
431       {
432         if (!errstr)
433           dump_node_to_file (initial_node, user_output_filename,
434                              dump_subnodes);
435         else
436           info_error (errstr, errarg1, errarg2);
437       }
438     else
439       {
440
441         if (errstr)
442           begin_info_session_with_error (initial_node, errstr,
443               errarg1, errarg2);
444         /* If the user specified `--index-search=STRING' or
445            --show-options, start the info session in the node
446            corresponding to what they want. */
447         else if (index_search_p || goto_invocation_p)
448           {
449             int status = 0;
450
451             initialize_info_session (initial_node, 0);
452
453             if (goto_invocation_p
454                 || index_entry_exists (windows, index_search_string))
455               {
456                 terminal_prep_terminal ();
457                 terminal_clear_screen ();
458                 info_last_executed_command = NULL;
459
460                 if (index_search_p)
461                   do_info_index_search (windows, 0, index_search_string);
462                 else
463                   {
464                     /* If they said "info --show-options foo bar baz",
465                        the last of the arguments is the program whose
466                        options they want to see.  */
467                     char **p = argv + optind;
468                     char *program;
469
470                     if (*p)
471                       {
472                         while (p[1])
473                           p++;
474                         program = xstrdup (*p);
475                       }
476                     else if (user_filename)
477                       /* If there's no command-line arguments to
478                          supply the program name, use the Info file
479                          name (sans extension and leading directories)
480                          instead.  */
481                       program = program_name_from_file_name (user_filename);
482                     else
483                       program = xstrdup ("");
484
485                     info_intuit_options_node (windows, initial_node, program);
486                     free (program);
487                   }
488
489                 if (user_output_filename)
490                   {
491                     dump_node_to_file (windows->node, user_output_filename,
492                                        dump_subnodes);
493                   }
494                 else
495                   info_read_and_dispatch ();
496
497                 /* On program exit, leave the cursor at the bottom of the
498                    window, and restore the terminal IO. */
499                 terminal_goto_xy (0, screenheight - 1);
500                 terminal_clear_to_eol ();
501                 fflush (stdout);
502                 terminal_unprep_terminal ();
503               }
504             else
505               {
506                 fprintf (stderr, _("no index entries found for `%s'\n"),
507                          index_search_string);
508                 status = 2;
509               }
510
511             close_dribble_file ();
512             xexit (status);
513           }
514         else
515           begin_info_session (initial_node);
516       }
517
518     xexit (0);
519   }
520
521   return 0; /* Avoid bogus warnings.  */
522 }
523
524 void
525 add_file_directory_to_path (char *filename)
526 {
527   char *directory_name = xstrdup (filename);
528   char *temp = filename_non_directory (directory_name);
529
530   if (temp != directory_name)
531     {
532       if (HAVE_DRIVE (directory_name) && temp == directory_name + 2)
533         {
534           /* The directory of "d:foo" is stored as "d:.", to avoid
535              mixing it with "d:/" when a slash is appended.  */
536           *temp = '.';
537           temp += 2;
538         }
539       temp[-1] = 0;
540       info_add_path (directory_name, INFOPATH_PREPEND);
541     }
542
543   free (directory_name);
544 }
545
546 \f
547 /* Error handling.  */
548
549 /* Non-zero if an error has been signalled. */
550 int info_error_was_printed = 0;
551
552 /* Non-zero means ring terminal bell on errors. */
553 int info_error_rings_bell_p = 1;
554
555 /* Print FORMAT with ARG1 and ARG2.  If the window system was initialized,
556    then the message is printed in the echo area.  Otherwise, a message is
557    output to stderr. */
558 void
559 info_error (const char *format, void *arg1, void *arg2)
560 {
561   info_error_was_printed = 1;
562
563   if (!info_windows_initialized_p || display_inhibited)
564     {
565       fprintf (stderr, "%s: ", program_name);
566       if (arg1)
567         fprintf (stderr, format, arg1, arg2);
568       else
569         /* If we're passed a string, just print it.  Otherwise a % in a
570            filename gets treated as a format specifier.  */
571         fputs (format, stderr);
572       fprintf (stderr, "\n");
573       fflush (stderr);
574     }
575   else
576     {
577       if (!echo_area_is_active)
578         {
579           if (info_error_rings_bell_p)
580             terminal_ring_bell ();
581           window_message_in_echo_area (format, arg1, arg2);
582         }
583       else
584         {
585           NODE *temp = build_message_node (format, arg1, arg2);
586           if (info_error_rings_bell_p)
587             terminal_ring_bell ();
588           inform_in_echo_area (temp->contents);
589           free (temp->contents);
590           free (temp);
591         }
592     }
593 }
594
595 \f
596 /* Produce a scaled down description of the available options to Info. */
597 static void
598 info_short_help (void)
599 {
600   printf (_("\
601 Usage: %s [OPTION]... [MENU-ITEM...]\n\
602 \n\
603 Read documentation in Info format.\n"), program_name);
604   puts ("");
605
606   puts (_("\
607 Options:\n\
608   -k, --apropos=STRING         look up STRING in all indices of all manuals.\n\
609   -d, --directory=DIR          add DIR to INFOPATH.\n\
610       --dribble=FILENAME       remember user keystrokes in FILENAME.\n\
611   -f, --file=FILENAME          specify Info file to visit."));
612
613   puts (_("\
614   -h, --help                   display this help and exit.\n\
615       --index-search=STRING    go to node pointed by index entry STRING.\n\
616   -n, --node=NODENAME          specify nodes in first visited Info file.\n\
617   -o, --output=FILENAME        output selected nodes to FILENAME."));
618
619   puts (_("\
620   -R, --raw-escapes            output \"raw\" ANSI escapes (default).\n\
621       --no-raw-escapes         output escapes as literal text.\n\
622       --restore=FILENAME       read initial keystrokes from FILENAME.\n\
623   -O, --show-options, --usage  go to command-line options node."));
624
625 #ifdef __MSDOS__
626   puts (_("\
627   -b, --speech-friendly        be friendly to speech synthesizers."));
628 #endif
629
630   puts (_("\
631       --subnodes               recursively output menu items.\n\
632       --vi-keys                use vi-like and less-like key bindings.\n\
633       --version                display version information and exit.\n\
634   -w, --where, --location      print physical location of Info file."));
635
636   puts (_("\n\
637 The first non-option argument, if present, is the menu entry to start from;\n\
638 it is searched for in all `dir' files along INFOPATH.\n\
639 If it is not present, info merges all `dir' files and shows the result.\n\
640 Any remaining arguments are treated as the names of menu\n\
641 items relative to the initial node visited."));
642
643   puts (_("\n\
644 For a summary of key bindings, type h within Info."));
645
646   puts (_("\n\
647 Examples:\n\
648   info                       show top-level dir menu\n\
649   info info                  show the general manual for Info readers\n\
650   info info-stnd             show the manual specific to this Info program\n\
651   info emacs                 start at emacs node from top-level dir\n\
652   info emacs buffers         start at buffers node within emacs manual\n\
653   info --show-options emacs  start at node with emacs' command line options\n\
654   info --subnodes -o out.txt emacs  dump entire manual to out.txt\n\
655   info -f ./foo.info         show file ./foo.info, not searching dir"));
656
657   puts (_("\n\
658 Email bug reports to bug-texinfo@gnu.org,\n\
659 general questions and discussion to help-texinfo@gnu.org.\n\
660 Texinfo home page: http://www.gnu.org/software/texinfo/"));
661
662   xexit (0);
663 }
664
665 \f
666 /* Initialize strings for gettext.  Because gettext doesn't handle N_ or
667    _ within macro definitions, we put shared messages into variables and
668    use them that way.  This also has the advantage that there's only one
669    copy of the strings.  */
670
671 const char *msg_cant_find_node;
672 const char *msg_cant_file_node;
673 const char *msg_cant_find_window;
674 const char *msg_cant_find_point;
675 const char *msg_cant_kill_last;
676 const char *msg_no_menu_node;
677 const char *msg_no_foot_node;
678 const char *msg_no_xref_node;
679 const char *msg_no_pointer;
680 const char *msg_unknown_command;
681 const char *msg_term_too_dumb;
682 const char *msg_at_node_bottom;
683 const char *msg_at_node_top;
684 const char *msg_one_window;
685 const char *msg_win_too_small;
686 const char *msg_cant_make_help;
687
688 static void
689 init_messages (void)
690 {
691   msg_cant_find_node   = _("Cannot find node `%s'.");
692   msg_cant_file_node   = _("Cannot find node `(%s)%s'.");
693   msg_cant_find_window = _("Cannot find a window!");
694   msg_cant_find_point  = _("Point doesn't appear within this window's node!");
695   msg_cant_kill_last   = _("Cannot delete the last window.");
696   msg_no_menu_node     = _("No menu in this node.");
697   msg_no_foot_node     = _("No footnotes in this node.");
698   msg_no_xref_node     = _("No cross references in this node.");
699   msg_no_pointer       = _("No `%s' pointer for this node.");
700   msg_unknown_command  = _("Unknown Info command `%c'; try `?' for help.");
701   msg_term_too_dumb    = _("Terminal type `%s' is not smart enough to run Info.");
702   msg_at_node_bottom   = _("You are already at the last page of this node.");
703   msg_at_node_top      = _("You are already at the first page of this node.");
704   msg_one_window       = _("Only one window.");
705   msg_win_too_small    = _("Resulting window would be too small.");
706   msg_cant_make_help   = _("Not enough room for a help window, please delete a window.");
707 }