* gcse.c (struct null_pointer_info): New type.
[official-gcc.git] / texinfo / info / info.c
blob22b831a20ef39d893265d92d4d6f7d9d04a709e9
1 /* info.c -- Display nodes of Info files in multiple windows.
2 $Id: info.c,v 1.1.1.3 1998/03/24 18:20:13 law Exp $
4 Copyright (C) 1993, 96, 97, 98 Free Software Foundation, Inc.
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.
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.
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.
20 Written by Brian Fox (bfox@ai.mit.edu). */
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 */
30 /* The version numbers of this version of Info. */
31 int info_major_version = 2;
32 int info_minor_version = 18;
34 /* basename (argv[0]) */
35 static char *program_name = NULL;
37 /* Non-zero means search all indices for APROPOS_SEARCH_STRING. */
38 static int apropos_p = 0;
40 /* Variable containing the string to search for when apropos_p is non-zero. */
41 static char *apropos_search_string = (char *)NULL;
43 /* Non-zero means search all indices for INDEX_SEARCH_STRING. Unlike
44 apropos, this puts the user at the node, running info. */
45 static int index_search_p = 0;
47 /* Variable containing the string to search for when index_search_p is
48 non-zero. */
49 static char *index_search_string = (char *)NULL;
51 /* Non-zero means print version info only. */
52 static int print_version_p = 0;
54 /* Non-zero means print a short description of the options. */
55 static int print_help_p = 0;
57 /* Array of the names of nodes that the user specified with "--node" on the
58 command line. */
59 static char **user_nodenames = (char **)NULL;
60 static int user_nodenames_index = 0;
61 static int user_nodenames_slots = 0;
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 = (char *)NULL;
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 = (char *)NULL;
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;
76 /* Structure describing the options that Info accepts. We pass this structure
77 to getopt_long (). If you add or otherwise change this structure, you must
78 also change the string which follows it. */
79 #define APROPOS_OPTION 1
80 #define DRIBBLE_OPTION 2
81 #define RESTORE_OPTION 3
82 #define IDXSRCH_OPTION 4
83 static struct option long_options[] = {
84 { "apropos", 1, 0, APROPOS_OPTION },
85 { "directory", 1, 0, 'd' },
86 { "node", 1, 0, 'n' },
87 { "file", 1, 0, 'f' },
88 { "subnodes", 0, &dump_subnodes, 1 },
89 { "output", 1, 0, 'o' },
90 { "help", 0, &print_help_p, 1 },
91 { "version", 0, &print_version_p, 1 },
92 { "dribble", 1, 0, DRIBBLE_OPTION },
93 { "restore", 1, 0, RESTORE_OPTION },
94 { "index-search", 1, 0, IDXSRCH_OPTION },
95 {NULL, 0, NULL, 0}
98 /* String describing the shorthand versions of the long options found above. */
99 static char *short_options = "d:n:f:o:s";
101 /* When non-zero, the Info window system has been initialized. */
102 int info_windows_initialized_p = 0;
104 /* Some "forward" declarations. */
105 static void info_short_help (), remember_info_program_name ();
108 /* **************************************************************** */
109 /* */
110 /* Main Entry Point to the Info Program */
111 /* */
112 /* **************************************************************** */
115 main (argc, argv)
116 int argc;
117 char **argv;
119 int getopt_long_index; /* Index returned by getopt_long (). */
120 NODE *initial_node; /* First node loaded by Info. */
122 remember_info_program_name (argv[0]);
124 #ifdef HAVE_SETLOCALE
125 /* Set locale via LC_ALL. */
126 setlocale (LC_ALL, "");
127 #endif
129 /* Set the text message domain. */
130 bindtextdomain (PACKAGE, LOCALEDIR);
131 textdomain (PACKAGE);
133 while (1)
135 int option_character;
137 option_character = getopt_long
138 (argc, argv, short_options, long_options, &getopt_long_index);
140 /* getopt_long () returns EOF when there are no more long options. */
141 if (option_character == EOF)
142 break;
144 /* If this is a long option, then get the short version of it. */
145 if (option_character == 0 && long_options[getopt_long_index].flag == 0)
146 option_character = long_options[getopt_long_index].val;
148 /* Case on the option that we have received. */
149 switch (option_character)
151 case 0:
152 break;
154 /* User wants to add a directory. */
155 case 'd':
156 info_add_path (optarg, INFOPATH_PREPEND);
157 break;
159 /* User is specifying a particular node. */
160 case 'n':
161 add_pointer_to_array (optarg, user_nodenames_index, user_nodenames,
162 user_nodenames_slots, 10, char *);
163 break;
165 /* User is specifying a particular Info file. */
166 case 'f':
167 if (user_filename)
168 free (user_filename);
170 user_filename = xstrdup (optarg);
171 break;
173 /* User is specifying the name of a file to output to. */
174 case 'o':
175 if (user_output_filename)
176 free (user_output_filename);
177 user_output_filename = xstrdup (optarg);
178 break;
180 /* User is specifying that she wishes to dump the subnodes of
181 the node that she is dumping. */
182 case 's':
183 dump_subnodes = 1;
184 break;
186 /* User has specified a string to search all indices for. */
187 case APROPOS_OPTION:
188 apropos_p = 1;
189 maybe_free (apropos_search_string);
190 apropos_search_string = xstrdup (optarg);
191 break;
193 /* User has specified a dribble file to receive keystrokes. */
194 case DRIBBLE_OPTION:
195 close_dribble_file ();
196 open_dribble_file (optarg);
197 break;
199 /* User has specified an alternate input stream. */
200 case RESTORE_OPTION:
201 info_set_input_from_file (optarg);
202 break;
204 /* User has specified a string to search all indices for. */
205 case IDXSRCH_OPTION:
206 index_search_p = 1;
207 maybe_free (index_search_string);
208 index_search_string = xstrdup (optarg);
209 break;
211 default:
212 fprintf (stderr, _("Try --help for more information."));
213 exit (1);
217 /* If the output device is not a terminal, and no output filename has been
218 specified, make user_output_filename be "-", so that the info is written
219 to stdout, and turn on the dumping of subnodes. */
220 if ((!isatty (fileno (stdout))) && (user_output_filename == (char *)NULL))
222 user_output_filename = xstrdup ("-");
223 dump_subnodes = 1;
226 /* If the user specified --version, then show the version and exit. */
227 if (print_version_p)
229 printf ("%s (GNU %s %s) %s\n", program_name, PACKAGE, VERSION,
230 version_string ());
231 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
232 There is NO warranty. You may redistribute this software\n\
233 under the terms of the GNU General Public License.\n\
234 For more information about these matters, see the files named COPYING.\n"),
235 "1998");
236 exit (0);
239 /* If the `--help' option was present, show the help and exit. */
240 if (print_help_p)
242 info_short_help ();
243 exit (0);
246 /* If the user hasn't specified a path for Info files, default it.
247 Lowest priority is our messy hardwired list in filesys.h.
248 Then comes the user's INFODIR from the Makefile.
249 Highest priority is the environment variable, if set. */
250 if (!infopath)
252 char *path_from_env = getenv ("INFOPATH");
254 if (path_from_env)
256 unsigned len = strlen (path_from_env);
257 /* Trailing : on INFOPATH means insert the default path. */
258 if (len && path_from_env[len - 1] == ':')
260 path_from_env[len - 1] = 0;
261 info_add_path (DEFAULT_INFOPATH, INFOPATH_PREPEND);
263 #ifdef INFODIR /* from the Makefile */
264 info_add_path (INFODIR, INFOPATH_PREPEND);
265 #endif
266 info_add_path (path_from_env, INFOPATH_PREPEND);
268 else
270 info_add_path (DEFAULT_INFOPATH, INFOPATH_PREPEND);
271 #ifdef INFODIR /* from the Makefile */
272 info_add_path (INFODIR, INFOPATH_PREPEND);
273 #endif
277 /* If the user specified a particular filename, add the path of that
278 file to the contents of INFOPATH. */
279 if (user_filename)
281 char *directory_name = xstrdup (user_filename);
282 char *temp = filename_non_directory (directory_name);
284 if (temp != directory_name)
286 *temp = 0;
287 info_add_path (directory_name, INFOPATH_PREPEND);
290 free (directory_name);
293 /* If the user wants to search every known index for a given string,
294 do that now, and report the results. */
295 if (apropos_p)
297 info_apropos (apropos_search_string);
298 exit (0);
301 /* Get the initial Info node. It is either "(dir)Top", or what the user
302 specifed with values in user_filename and user_nodenames. */
303 initial_node = info_get_node (user_filename,
304 user_nodenames ? user_nodenames[0] : NULL);
306 /* If we couldn't get the initial node, this user is in trouble. */
307 if (!initial_node)
309 if (info_recent_file_error)
310 info_error (info_recent_file_error);
311 else
312 info_error
313 (CANT_FIND_NODE, user_nodenames ? user_nodenames[0] : "Top");
314 exit (1);
317 /* Special cases for when the user specifies multiple nodes. If we
318 are dumping to an output file, dump all of the nodes specified.
319 Otherwise, attempt to create enough windows to handle the nodes
320 that this user wants displayed. */
321 if (user_nodenames_index > 1)
323 free (initial_node);
325 if (user_output_filename)
326 dump_nodes_to_file
327 (user_filename, user_nodenames, user_output_filename, dump_subnodes);
328 else
329 begin_multiple_window_info_session (user_filename, user_nodenames);
331 exit (0);
334 /* If the user specified `--index-search=STRING', start the info
335 session in the node corresponding to the first match. */
336 if (index_search_p)
338 int status = 0;
340 initialize_info_session (initial_node, 0);
342 if (index_entry_exists (windows, index_search_string))
344 terminal_clear_screen ();
345 terminal_prep_terminal ();
346 display_update_display (windows);
347 info_last_executed_command = (VFunction *)NULL;
349 do_info_index_search (windows, 0, index_search_string);
351 info_read_and_dispatch ();
353 terminal_unprep_terminal ();
355 /* On program exit, leave the cursor at the bottom of the
356 window, and restore the terminal IO. */
357 terminal_goto_xy (0, screenheight - 1);
358 terminal_clear_to_eol ();
359 fflush (stdout);
361 else
363 fputs (_("no entries found\n"), stderr);
364 status = 2;
367 close_dribble_file ();
368 exit (status);
371 /* If there are arguments remaining, they are the names of menu items
372 in sequential info files starting from the first one loaded. That
373 file name is either "dir", or the contents of user_filename if one
374 was specified. */
375 while (optind != argc)
377 REFERENCE **menu;
378 REFERENCE *entry;
379 NODE *node;
380 char *arg;
381 static char *first_arg = (char *)NULL;
383 /* Remember the name of the menu entry we want. */
384 arg = argv[optind++];
386 if (!first_arg)
387 first_arg = arg;
389 /* Build and return a list of the menu items in this node. */
390 menu = info_menu_of_node (initial_node);
392 /* If there wasn't a menu item in this node, stop here, but let
393 the user continue to use Info. Perhaps they wanted this node
394 and didn't realize it. */
395 if (!menu)
397 #if defined (HANDLE_MAN_PAGES)
398 if (first_arg == arg)
400 node = make_manpage_node (first_arg);
401 if (node)
402 goto maybe_got_node;
404 #endif /* HANDLE_MAN_PAGES */
405 begin_info_session_with_error
406 (initial_node, _("There is no menu in this node."));
407 exit (0);
410 /* Find the specified menu item. */
411 entry = info_get_labeled_reference (arg, menu);
413 /* If the item wasn't found, search the list sloppily. Perhaps this
414 user typed "buffer" when they really meant "Buffers". */
415 if (!entry)
417 register int i;
418 int best_guess = -1;
420 for (i = 0; (entry = menu[i]); i++)
422 if (strcasecmp (entry->label, arg) == 0)
423 break;
424 else
425 if (strncasecmp (entry->label, arg, strlen (arg)) == 0)
426 best_guess = i;
429 if (!entry && best_guess != -1)
430 entry = menu[best_guess];
433 /* If we failed to find the reference, start Info with the current
434 node anyway. It is probably a misspelling. */
435 if (!entry)
437 char *error_message = _("There is no menu item \"%s\" in this node.");
439 #if defined (HANDLE_MAN_PAGES)
440 if (first_arg == arg)
442 node = make_manpage_node (first_arg);
443 if (node)
444 goto maybe_got_node;
446 #endif /* HANDLE_MAN_PAGES */
448 info_free_references (menu);
450 /* If we were supposed to dump this node, complain. */
451 if (user_output_filename)
452 info_error (error_message, arg);
453 else
454 begin_info_session_with_error (initial_node, error_message, arg);
456 exit (0);
459 /* We have found the reference that the user specified. Clean it
460 up a little bit. */
461 if (!entry->filename)
463 if (initial_node->parent)
464 entry->filename = xstrdup (initial_node->parent);
465 else
466 entry->filename = xstrdup (initial_node->filename);
469 /* Find this node. If we can find it, then turn the initial_node
470 into this one. If we cannot find it, try using the label of the
471 entry as a file (i.e., "(LABEL)Top"). Otherwise the Info file is
472 malformed in some way, and we will just use the current value of
473 initial node. */
474 node = info_get_node (entry->filename, entry->nodename);
476 #if defined (HANDLE_MAN_PAGES)
477 if ((first_arg == arg) && !node)
479 node = make_manpage_node (first_arg);
480 if (node)
481 goto maybe_got_node;
483 #endif /* HANDLE_MAN_PAGES */
485 if (!node && entry->nodename &&
486 (strcmp (entry->label, entry->nodename) == 0))
487 node = info_get_node (entry->label, "Top");
489 maybe_got_node:
490 if (node)
492 free (initial_node);
493 initial_node = node;
494 info_free_references (menu);
496 else
498 char *temp = xstrdup (entry->label);
499 char *error_message;
501 error_message = _("Unable to find the node referenced by \"%s\".");
503 info_free_references (menu);
505 /* If we were trying to dump the node, then give up. Otherwise,
506 start the session with an error message. */
507 if (user_output_filename)
508 info_error (error_message, temp);
509 else
510 begin_info_session_with_error (initial_node, error_message, temp);
512 exit (0);
516 /* If the user specified that this node should be output, then do that
517 now. Otherwise, start the Info session with this node. */
518 if (user_output_filename)
519 dump_node_to_file (initial_node, user_output_filename, dump_subnodes);
520 else
521 begin_info_session (initial_node);
523 exit (0);
526 /* Return a string describing the current version of Info. */
527 char *
528 version_string ()
530 static char *vstring = (char *)NULL;
532 if (!vstring)
534 vstring = (char *)xmalloc (50);
535 sprintf (vstring, "%d.%d", info_major_version, info_minor_version);
537 return (vstring);
541 /* Error handling. */
543 static void
544 remember_info_program_name (fullpath)
545 char *fullpath;
547 char *filename;
549 filename = filename_non_directory (fullpath);
550 program_name = xstrdup (filename);
553 /* Non-zero if an error has been signalled. */
554 int info_error_was_printed = 0;
556 /* Non-zero means ring terminal bell on errors. */
557 int info_error_rings_bell_p = 1;
559 /* Print FORMAT with ARG1 and ARG2. If the window system was initialized,
560 then the message is printed in the echo area. Otherwise, a message is
561 output to stderr. */
562 void
563 info_error (format, arg1, arg2)
564 char *format;
565 void *arg1, *arg2;
567 info_error_was_printed = 1;
569 if (!info_windows_initialized_p || display_inhibited)
571 fprintf (stderr, "%s: ", program_name);
572 fprintf (stderr, format, arg1, arg2);
573 fprintf (stderr, "\n");
574 fflush (stderr);
576 else
578 if (!echo_area_is_active)
580 if (info_error_rings_bell_p)
581 terminal_ring_bell ();
582 window_message_in_echo_area (format, arg1, arg2);
584 else
586 NODE *temp;
588 temp = build_message_node (format, arg1, arg2);
589 if (info_error_rings_bell_p)
590 terminal_ring_bell ();
591 inform_in_echo_area (temp->contents);
592 free (temp->contents);
593 free (temp);
598 /* Produce a scaled down description of the available options to Info. */
599 static void
600 info_short_help ()
602 printf (_("\
603 Usage: %s [OPTION]... [INFO-FILE [MENU-ITEM...]]\n\
605 Read documentation in Info format.\n\
606 For more complete documentation on how to use Info, run `info info options'.\n\
608 Options:\n\
609 --directory DIR add DIR to INFOPATH.\n\
610 --dribble FILENAME remember user keystrokes in FILENAME.\n\
611 --file FILENAME specify Info file to visit.\n\
612 --node NODENAME specify nodes in first visited Info file.\n\
613 --output FILENAME output selected nodes to FILENAME.\n\
614 --restore FILENAME read initial keystrokes from FILENAME.\n\
615 --subnodes recursively output menu items.\n\
616 --help display this help and exit.\n\
617 --version display version information and exit.\n\
619 The first argument, if present, is the name of the Info file to read.\n\
620 Any remaining arguments are treated as the names of menu\n\
621 items in the initial node visited. For example, `info emacs buffers'\n\
622 moves to the node `buffers' in the info file `emacs'.\n\
624 Email bug reports to bug-texinfo@gnu.org."), program_name);
626 exit (0);