source: trunk/essentials/sys-apps/texinfo/info/info.c

Last change on this file was 2617, checked in by bird, 19 years ago

GNU Texinfo 4.8

File size: 22.5 KB
Line 
1/* info.c -- Display nodes of Info files in multiple windows.
2 $Id: info.c,v 1.11 2004/04/11 17:56:45 karl Exp $
3
4 Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004 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 2, or (at your option)
10 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, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
21 Written by Brian Fox (bfox@ai.mit.edu). */
22
23#include "info.h"
24#include "indices.h"
25#include "dribble.h"
26#include "getopt.h"
27#if defined (HANDLE_MAN_PAGES)
28# include "man.h"
29#endif /* HANDLE_MAN_PAGES */
30
31static char *program_name = "info";
32
33/* Non-zero means search all indices for APROPOS_SEARCH_STRING. */
34static int apropos_p = 0;
35
36/* Variable containing the string to search for when apropos_p is non-zero. */
37static char *apropos_search_string = (char *)NULL;
38
39/* Non-zero means search all indices for INDEX_SEARCH_STRING. Unlike
40 apropos, this puts the user at the node, running info. */
41static int index_search_p = 0;
42
43/* Non-zero means look for the node which describes the invocation
44 and command-line options of the program, and start the info
45 session at that node. */
46static int goto_invocation_p = 0;
47
48/* Variable containing the string to search for when index_search_p is
49 non-zero. */
50static char *index_search_string = (char *)NULL;
51
52/* Non-zero means print version info only. */
53static int print_version_p = 0;
54
55/* Non-zero means print a short description of the options. */
56static int print_help_p = 0;
57
58/* Array of the names of nodes that the user specified with "--node" on the
59 command line. */
60static char **user_nodenames = (char **)NULL;
61static int user_nodenames_index = 0;
62static int user_nodenames_slots = 0;
63
64/* String specifying the first file to load. This string can only be set
65 by the user specifying "--file" on the command line. */
66static char *user_filename = (char *)NULL;
67
68/* String specifying the name of the file to dump nodes to. This value is
69 filled if the user speficies "--output" on the command line. */
70static char *user_output_filename = (char *)NULL;
71
72/* Non-zero indicates that when "--output" is specified, all of the menu
73 items of the specified nodes (and their subnodes as well) should be
74 dumped in the order encountered. This basically can print a book. */
75int dump_subnodes = 0;
76
77/* Non-zero means make default keybindings be loosely modeled on vi(1). */
78int vi_keys_p = 0;
79
80/* Non-zero means don't remove ANSI escape sequences. */
81int raw_escapes_p = 1;
82
83/* Non-zero means print the absolute location of the file to be loaded. */
84static int print_where_p = 0;
85
86#ifdef __MSDOS__
87/* Non-zero indicates that screen output should be made 'speech-friendly'.
88 Since on MSDOS the usual behavior is to write directly to the video
89 memory, speech synthesizer software cannot grab the output. Therefore,
90 we provide a user option which tells us to avoid direct screen output
91 and use stdout instead (which loses the color output). */
92int speech_friendly = 0;
93#endif
94
95/* Structure describing the options that Info accepts. We pass this structure
96 to getopt_long (). If you add or otherwise change this structure, you must
97 also change the string which follows it. */
98#define APROPOS_OPTION 1
99#define DRIBBLE_OPTION 2
100#define RESTORE_OPTION 3
101#define IDXSRCH_OPTION 4
102static struct option long_options[] = {
103 { "apropos", 1, 0, APROPOS_OPTION },
104 { "directory", 1, 0, 'd' },
105 { "dribble", 1, 0, DRIBBLE_OPTION },
106 { "file", 1, 0, 'f' },
107 { "help", 0, &print_help_p, 1 },
108 { "index-search", 1, 0, IDXSRCH_OPTION },
109 { "location", 0, &print_where_p, 1 },
110 { "node", 1, 0, 'n' },
111 { "output", 1, 0, 'o' },
112 { "raw-escapes", 0, &raw_escapes_p, 1 },
113 { "no-raw-escapes", 0, &raw_escapes_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__
129static char *short_options = "d:n:f:ho:ORswb";
130#else
131static char *short_options = "d:n:f:ho:ORws";
132#endif
133
134/* When non-zero, the Info window system has been initialized. */
135int info_windows_initialized_p = 0;
136
137/* Some "forward" declarations. */
138static void info_short_help (void);
139static void init_messages (void);
140
141
142
143/* **************************************************************** */
144/* */
145/* Main Entry Point to the Info Program */
146/* */
147/* **************************************************************** */
148
149int
150main (int argc, char **argv)
151{
152 int getopt_long_index; /* Index returned by getopt_long (). */
153 NODE *initial_node; /* First node loaded by Info. */
154
155#ifdef HAVE_SETLOCALE
156 /* Set locale via LC_ALL. */
157 setlocale (LC_ALL, "");
158#endif
159
160#ifdef ENABLE_NLS
161 /* Set the text message domain. */
162 bindtextdomain (PACKAGE, LOCALEDIR);
163 textdomain (PACKAGE);
164#endif
165
166 init_messages ();
167
168 while (1)
169 {
170 int option_character;
171
172 option_character = getopt_long
173 (argc, argv, short_options, long_options, &getopt_long_index);
174
175 /* getopt_long returns EOF when there are no more long options. */
176 if (option_character == EOF)
177 break;
178
179 /* If this is a long option, then get the short version of it. */
180 if (option_character == 0 && long_options[getopt_long_index].flag == 0)
181 option_character = long_options[getopt_long_index].val;
182
183 /* Case on the option that we have received. */
184 switch (option_character)
185 {
186 case 0:
187 break;
188
189 /* User wants to add a directory. */
190 case 'd':
191 info_add_path (optarg, INFOPATH_PREPEND);
192 break;
193
194 /* User is specifying a particular node. */
195 case 'n':
196 add_pointer_to_array (optarg, user_nodenames_index, user_nodenames,
197 user_nodenames_slots, 10, char *);
198 break;
199
200 /* User is specifying a particular Info file. */
201 case 'f':
202 if (user_filename)
203 free (user_filename);
204
205 user_filename = xstrdup (optarg);
206 break;
207
208 /* Treat -h like --help. */
209 case 'h':
210 print_help_p = 1;
211 break;
212
213 /* User is specifying the name of a file to output to. */
214 case 'o':
215 if (user_output_filename)
216 free (user_output_filename);
217 user_output_filename = xstrdup (optarg);
218 break;
219
220 /* User has specified that she wants to find the "Options"
221 or "Invocation" node for the program. */
222 case 'O':
223 goto_invocation_p = 1;
224 break;
225
226 /* User has specified that she wants the escape sequences
227 in man pages to be passed thru unaltered. */
228 case 'R':
229 raw_escapes_p = 1;
230 break;
231
232 /* User is specifying that she wishes to dump the subnodes of
233 the node that she is dumping. */
234 case 's':
235 dump_subnodes = 1;
236 break;
237
238 /* For compatibility with man, -w is --where. */
239 case 'w':
240 print_where_p = 1;
241 break;
242
243#ifdef __MSDOS__
244 /* User wants speech-friendly output. */
245 case 'b':
246 speech_friendly = 1;
247 break;
248#endif /* __MSDOS__ */
249
250 /* User has specified a string to search all indices for. */
251 case APROPOS_OPTION:
252 apropos_p = 1;
253 maybe_free (apropos_search_string);
254 apropos_search_string = xstrdup (optarg);
255 break;
256
257 /* User has specified a dribble file to receive keystrokes. */
258 case DRIBBLE_OPTION:
259 close_dribble_file ();
260 open_dribble_file (optarg);
261 break;
262
263 /* User has specified an alternate input stream. */
264 case RESTORE_OPTION:
265 info_set_input_from_file (optarg);
266 break;
267
268 /* User has specified a string to search all indices for. */
269 case IDXSRCH_OPTION:
270 index_search_p = 1;
271 maybe_free (index_search_string);
272 index_search_string = xstrdup (optarg);
273 break;
274
275 default:
276 fprintf (stderr, _("Try --help for more information.\n"));
277 xexit (1);
278 }
279 }
280
281 /* If the output device is not a terminal, and no output filename has been
282 specified, make user_output_filename be "-", so that the info is written
283 to stdout, and turn on the dumping of subnodes. */
284 if ((!isatty (fileno (stdout))) && (user_output_filename == (char *)NULL))
285 {
286 user_output_filename = xstrdup ("-");
287 dump_subnodes = 1;
288 }
289
290 /* If the user specified --version, then show the version and exit. */
291 if (print_version_p)
292 {
293 printf ("%s (GNU %s) %s\n", program_name, PACKAGE, VERSION);
294 puts ("");
295 puts ("Copyright (C) 2004 Free Software Foundation, Inc.");
296 printf (_("There is NO warranty. You may redistribute this software\n\
297under the terms of the GNU General Public License.\n\
298For more information about these matters, see the files named COPYING.\n"));
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 ((char *) 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
402 NODE *new_initial_node = info_follow_menus (initial_node, argv + optind,
403 &errstr, &errarg1, &errarg2);
404
405 if (new_initial_node && new_initial_node != initial_node)
406 initial_node = new_initial_node;
407
408 if (print_where_p)
409 {
410 if (initial_node->parent)
411 printf ("%s\n", initial_node->parent);
412 else if (initial_node->filename
413 && !is_dir_name (filename_non_directory (initial_node->filename)))
414 printf ("%s\n", initial_node->filename);
415 else
416 xexit (1);
417 xexit (0);
418 }
419
420 /* If the user specified that this node should be output, then do that
421 now. Otherwise, start the Info session with this node. Or act
422 accordingly if the initial node was not found. */
423 if (user_output_filename && !goto_invocation_p)
424 {
425 if (!errstr)
426 dump_node_to_file (initial_node, user_output_filename,
427 dump_subnodes);
428 else
429 info_error ((char *) errstr, errarg1, errarg2);
430 }
431 else
432 {
433
434 if (errstr)
435 begin_info_session_with_error (initial_node, (char *) errstr,
436 errarg1, errarg2);
437 /* If the user specified `--index-search=STRING' or
438 --show-options, start the info session in the node
439 corresponding to what they want. */
440 else if (index_search_p || goto_invocation_p)
441 {
442 int status = 0;
443
444 initialize_info_session (initial_node, 0);
445
446 if (goto_invocation_p
447 || index_entry_exists (windows, index_search_string))
448 {
449 terminal_prep_terminal ();
450 terminal_clear_screen ();
451 info_last_executed_command = (VFunction *)NULL;
452
453 if (index_search_p)
454 do_info_index_search (windows, 0, index_search_string);
455 else
456 {
457 /* If they said "info --show-options foo bar baz",
458 the last of the arguments is the program whose
459 options they want to see. */
460 char **p = argv + optind;
461 char *program;
462
463 if (*p)
464 {
465 while (p[1])
466 p++;
467 program = xstrdup (*p);
468 }
469 else if (user_filename)
470 /* If there's no command-line arguments to
471 supply the program name, use the Info file
472 name (sans extension and leading directories)
473 instead. */
474 program = program_name_from_file_name (user_filename);
475 else
476 program = xstrdup ("");
477
478 info_intuit_options_node (windows, initial_node, program);
479 free (program);
480 }
481
482 if (user_output_filename)
483 {
484 dump_node_to_file (windows->node, user_output_filename,
485 dump_subnodes);
486 }
487 else
488 info_read_and_dispatch ();
489
490 /* On program exit, leave the cursor at the bottom of the
491 window, and restore the terminal IO. */
492 terminal_goto_xy (0, screenheight - 1);
493 terminal_clear_to_eol ();
494 fflush (stdout);
495 terminal_unprep_terminal ();
496 }
497 else
498 {
499 fprintf (stderr, _("no index entries found for `%s'\n"),
500 index_search_string);
501 status = 2;
502 }
503
504 close_dribble_file ();
505 xexit (status);
506 }
507 else
508 begin_info_session (initial_node);
509 }
510
511 xexit (0);
512 }
513
514 return 0; /* Avoid bogus warnings. */
515}
516
517void
518add_file_directory_to_path (char *filename)
519{
520 char *directory_name = xstrdup (filename);
521 char *temp = filename_non_directory (directory_name);
522
523 if (temp != directory_name)
524 {
525 if (HAVE_DRIVE (directory_name) && temp == directory_name + 2)
526 {
527 /* The directory of "d:foo" is stored as "d:.", to avoid
528 mixing it with "d:/" when a slash is appended. */
529 *temp = '.';
530 temp += 2;
531 }
532 temp[-1] = 0;
533 info_add_path (directory_name, INFOPATH_PREPEND);
534 }
535
536 free (directory_name);
537}
538
539
540
541/* Error handling. */
542
543/* Non-zero if an error has been signalled. */
544int info_error_was_printed = 0;
545
546/* Non-zero means ring terminal bell on errors. */
547int info_error_rings_bell_p = 1;
548
549/* Print FORMAT with ARG1 and ARG2. If the window system was initialized,
550 then the message is printed in the echo area. Otherwise, a message is
551 output to stderr. */
552void
553info_error (char *format, void *arg1, void *arg2)
554{
555 info_error_was_printed = 1;
556
557 if (!info_windows_initialized_p || display_inhibited)
558 {
559 fprintf (stderr, "%s: ", program_name);
560 fprintf (stderr, format, arg1, arg2);
561 fprintf (stderr, "\n");
562 fflush (stderr);
563 }
564 else
565 {
566 if (!echo_area_is_active)
567 {
568 if (info_error_rings_bell_p)
569 terminal_ring_bell ();
570 window_message_in_echo_area (format, arg1, arg2);
571 }
572 else
573 {
574 NODE *temp;
575
576 temp = build_message_node (format, arg1, arg2);
577 if (info_error_rings_bell_p)
578 terminal_ring_bell ();
579 inform_in_echo_area (temp->contents);
580 free (temp->contents);
581 free (temp);
582 }
583 }
584}
585
586
587
588/* Produce a scaled down description of the available options to Info. */
589static void
590info_short_help (void)
591{
592#ifdef __MSDOS__
593 static const char speech_friendly_string[] = N_("\
594 -b, --speech-friendly be friendly to speech synthesizers.\n");
595#else
596 static const char speech_friendly_string[] = "";
597#endif
598
599
600 printf (_("\
601Usage: %s [OPTION]... [MENU-ITEM...]\n\
602\n\
603Read documentation in Info format.\n\
604\n\
605Options:\n\
606 --apropos=STRING look up STRING in all indices of all manuals.\n\
607 -d, --directory=DIR add DIR to INFOPATH.\n\
608 --dribble=FILENAME remember user keystrokes in FILENAME.\n\
609 -f, --file=FILENAME specify Info file to visit.\n\
610 -h, --help display this help and exit.\n\
611 --index-search=STRING go to node pointed by index entry STRING.\n\
612 -n, --node=NODENAME specify nodes in first visited Info file.\n\
613 -o, --output=FILENAME output selected nodes to FILENAME.\n\
614 -R, --raw-escapes output \"raw\" ANSI escapes (default).\n\
615 --no-raw-escapes output escapes as literal text.\n\
616 --restore=FILENAME read initial keystrokes from FILENAME.\n\
617 -O, --show-options, --usage go to command-line options node.\n%s\
618 --subnodes recursively output menu items.\n\
619 -w, --where, --location print physical location of Info file.\n\
620 --vi-keys use vi-like and less-like key bindings.\n\
621 --version display version information and exit.\n\
622\n\
623The first non-option argument, if present, is the menu entry to start from;\n\
624it is searched for in all `dir' files along INFOPATH.\n\
625If it is not present, info merges all `dir' files and shows the result.\n\
626Any remaining arguments are treated as the names of menu\n\
627items relative to the initial node visited.\n\
628\n\
629Examples:\n\
630 info show top-level dir menu\n\
631 info emacs start at emacs node from top-level dir\n\
632 info emacs buffers start at buffers node within emacs manual\n\
633 info --show-options emacs start at node with emacs' command line options\n\
634 info -f ./foo.info show file ./foo.info, not searching dir\n\
635"),
636 program_name, speech_friendly_string);
637
638 puts (_("\n\
639Email bug reports to bug-texinfo@gnu.org,\n\
640general questions and discussion to help-texinfo@gnu.org.\n\
641Texinfo home page: http://www.gnu.org/software/texinfo/"));
642
643 xexit (0);
644}
645
646
647
648/* Initialize strings for gettext. Because gettext doesn't handle N_ or
649 _ within macro definitions, we put shared messages into variables and
650 use them that way. This also has the advantage that there's only one
651 copy of the strings. */
652
653const char *msg_cant_find_node;
654const char *msg_cant_file_node;
655const char *msg_cant_find_window;
656const char *msg_cant_find_point;
657const char *msg_cant_kill_last;
658const char *msg_no_menu_node;
659const char *msg_no_foot_node;
660const char *msg_no_xref_node;
661const char *msg_no_pointer;
662const char *msg_unknown_command;
663const char *msg_term_too_dumb;
664const char *msg_at_node_bottom;
665const char *msg_at_node_top;
666const char *msg_one_window;
667const char *msg_win_too_small;
668const char *msg_cant_make_help;
669
670static void
671init_messages (void)
672{
673 msg_cant_find_node = _("Cannot find node `%s'.");
674 msg_cant_file_node = _("Cannot find node `(%s)%s'.");
675 msg_cant_find_window = _("Cannot find a window!");
676 msg_cant_find_point = _("Point doesn't appear within this window's node!");
677 msg_cant_kill_last = _("Cannot delete the last window.");
678 msg_no_menu_node = _("No menu in this node.");
679 msg_no_foot_node = _("No footnotes in this node.");
680 msg_no_xref_node = _("No cross references in this node.");
681 msg_no_pointer = _("No `%s' pointer for this node.");
682 msg_unknown_command = _("Unknown Info command `%c'; try `?' for help.");
683 msg_term_too_dumb = _("Terminal type `%s' is not smart enough to run Info.");
684 msg_at_node_bottom = _("You are already at the last page of this node.");
685 msg_at_node_top = _("You are already at the first page of this node.");
686 msg_one_window = _("Only one window.");
687 msg_win_too_small = _("Resulting window would be too small.");
688 msg_cant_make_help = _("Not enough room for a help window, please delete a window.");
689}
Note: See TracBrowser for help on using the repository browser.