Fix incomplete stack traces by gdb.
[dragonfly.git] / contrib / texinfo-4 / util / install-info.c
blobbbc7a8c25a5095ec870163a25c9cdd71c4ee7f88
1 /* install-info -- create Info directory entry(ies) for an Info file.
2 $Id: install-info.c,v 1.12 2004/04/11 17:56:47 karl Exp $
4 Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
5 Foundation, Inc.
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 of the License, or
10 (at your option) any later version.
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.
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.*/
21 #include "system.h"
22 #include <getopt.h>
24 static char *progname = "install-info";
26 struct spec_entry;
27 struct spec_section;
29 struct line_data *findlines (char *data, int size, int *nlinesp);
30 void insert_entry_here (struct spec_entry *entry, int line_number,
31 struct line_data *dir_lines, int n_entries);
32 int compare_section_names (const void *s1, const void *s2);
33 int compare_entries_text (const void *e1, const void *e2);
35 /* Data structures. */
38 /* Record info about a single line from a file as read into core. */
39 struct line_data
41 /* The start of the line. */
42 char *start;
43 /* The number of characters in the line,
44 excluding the terminating newline. */
45 int size;
46 /* Vector containing pointers to the entries to add before this line.
47 The vector is null-terminated. */
48 struct spec_entry **add_entries_before;
49 /* 1 means output any needed new sections before this line. */
50 int add_sections_before;
51 /* 1 means don't output this line. */
52 int delete;
56 /* This is used for a list of the specified menu section names
57 in which entries should be added. */
58 struct spec_section
60 struct spec_section *next;
61 char *name;
62 /* 1 means we have not yet found an existing section with this name
63 in the dir file--so we will need to add a new section. */
64 int missing;
68 /* This is used for a list of the entries specified to be added. */
69 struct spec_entry
71 struct spec_entry *next;
72 char *text;
73 int text_len;
74 /* A pointer to the list of sections to which this entry should be
75 added. */
76 struct spec_section *entry_sections;
77 /* A pointer to a section that is beyond the end of the chain whose
78 head is pointed to by entry_sections. */
79 struct spec_section *entry_sections_tail;
83 /* This is used for a list of nodes found by parsing the dir file. */
84 struct node
86 struct node *next;
87 /* The node name. */
88 char *name;
89 /* The line number of the line where the node starts.
90 This is the line that contains control-underscore. */
91 int start_line;
92 /* The line number of the line where the node ends,
93 which is the end of the file or where the next line starts. */
94 int end_line;
95 /* Start of first line in this node's menu
96 (the line after the * Menu: line). */
97 char *menu_start;
98 /* The start of the chain of sections in this node's menu. */
99 struct menu_section *sections;
100 /* The last menu section in the chain. */
101 struct menu_section *last_section;
105 /* This is used for a list of sections found in a node's menu.
106 Each struct node has such a list in the sections field. */
107 struct menu_section
109 struct menu_section *next;
110 char *name;
111 /* Line number of start of section. */
112 int start_line;
113 /* Line number of end of section. */
114 int end_line;
117 /* This table defines all the long-named options, says whether they
118 use an argument, and maps them into equivalent single-letter options. */
120 struct option longopts[] =
122 { "delete", no_argument, NULL, 'r' },
123 { "dir-file", required_argument, NULL, 'd' },
124 { "entry", required_argument, NULL, 'e' },
125 { "help", no_argument, NULL, 'h' },
126 { "infodir", required_argument, NULL, 'D' },
127 { "info-dir", required_argument, NULL, 'D' },
128 { "info-file", required_argument, NULL, 'i' },
129 { "item", required_argument, NULL, 'e' },
130 { "quiet", no_argument, NULL, 'q' },
131 { "remove", no_argument, NULL, 'r' },
132 { "section", required_argument, NULL, 's' },
133 { "version", no_argument, NULL, 'V' },
134 { 0 }
137 /* Error message functions. */
139 /* Print error message. S1 is printf control string, S2 and S3 args for it. */
141 /* VARARGS1 */
142 void
143 error (const char *s1, const char *s2, const char *s3)
145 fprintf (stderr, "%s: ", progname);
146 fprintf (stderr, s1, s2, s3);
147 putc ('\n', stderr);
150 /* VARARGS1 */
151 void
152 warning (const char *s1, const char *s2, const char *s3)
154 fprintf (stderr, _("%s: warning: "), progname);
155 fprintf (stderr, s1, s2, s3);
156 putc ('\n', stderr);
159 /* Print error message and exit. */
161 void
162 fatal (const char *s1, const char *s2, const char *s3)
164 error (s1, s2, s3);
165 xexit (1);
168 /* Return a newly-allocated string
169 whose contents concatenate those of S1, S2, S3. */
170 char *
171 concat (const char *s1, const char *s2, const char *s3)
173 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
174 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
176 strcpy (result, s1);
177 strcpy (result + len1, s2);
178 strcpy (result + len1 + len2, s3);
179 *(result + len1 + len2 + len3) = 0;
181 return result;
184 /* Return a string containing SIZE characters
185 copied from starting at STRING. */
187 char *
188 copy_string (const char *string, int size)
190 int i;
191 char *copy = (char *) xmalloc (size + 1);
192 for (i = 0; i < size; i++)
193 copy[i] = string[i];
194 copy[size] = 0;
195 return copy;
198 /* Print fatal error message based on errno, with file name NAME. */
200 void
201 pfatal_with_name (const char *name)
203 char *s = concat ("", strerror (errno), _(" for %s"));
204 fatal (s, name, 0);
207 /* Compare the menu item names in LINE1 (line length LEN1)
208 and LINE2 (line length LEN2). Return 1 if the item name
209 in LINE1 is less, 0 otherwise. */
211 static int
212 menu_line_lessp (char *line1, int len1, char *line2, int len2)
214 int minlen = (len1 < len2 ? len1 : len2);
215 int i;
217 for (i = 0; i < minlen; i++)
219 /* If one item name is a prefix of the other,
220 the former one is less. */
221 if (line1[i] == ':' && line2[i] != ':')
222 return 1;
223 if (line2[i] == ':' && line1[i] != ':')
224 return 0;
225 /* If they both continue and differ, one is less. */
226 if (line1[i] < line2[i])
227 return 1;
228 if (line1[i] > line2[i])
229 return 0;
231 /* With a properly formatted dir file,
232 we can only get here if the item names are equal. */
233 return 0;
236 /* Compare the menu item names in LINE1 (line length LEN1)
237 and LINE2 (line length LEN2). Return 1 if the item names are equal,
238 0 otherwise. */
240 static int
241 menu_line_equal (char *line1, int len1, char *line2, int len2)
243 int minlen = (len1 < len2 ? len1 : len2);
244 int i;
246 for (i = 0; i < minlen; i++)
248 /* If both item names end here, they are equal. */
249 if (line1[i] == ':' && line2[i] == ':')
250 return 1;
251 /* If they both continue and differ, one is less. */
252 if (line1[i] != line2[i])
253 return 0;
255 /* With a properly formatted dir file,
256 we can only get here if the item names are equal. */
257 return 1;
261 /* Given the full text of a menu entry, null terminated,
262 return just the menu item name (copied). */
264 char *
265 extract_menu_item_name (char *item_text)
267 char *p;
269 if (*item_text == '*')
270 item_text++;
271 while (*item_text == ' ')
272 item_text++;
274 p = item_text;
275 while (*p && *p != ':') p++;
276 return copy_string (item_text, p - item_text);
279 /* Given the full text of a menu entry, terminated by null or newline,
280 return just the menu item file (copied). */
282 char *
283 extract_menu_file_name (char *item_text)
285 char *p = item_text;
287 /* If we have text that looks like * ITEM: (FILE)NODE...,
288 extract just FILE. Otherwise return "(none)". */
290 if (*p == '*')
291 p++;
292 while (*p == ' ')
293 p++;
295 /* Skip to and past the colon. */
296 while (*p && *p != '\n' && *p != ':') p++;
297 if (*p == ':') p++;
299 /* Skip past the open-paren. */
300 while (1)
302 if (*p == '(')
303 break;
304 else if (*p == ' ' || *p == '\t')
305 p++;
306 else
307 return "(none)";
309 p++;
311 item_text = p;
313 /* File name ends just before the close-paren. */
314 while (*p && *p != '\n' && *p != ')') p++;
315 if (*p != ')')
316 return "(none)";
318 return copy_string (item_text, p - item_text);
323 /* Return FNAME with any [.info][.gz] suffix removed. */
325 static char *
326 strip_info_suffix (char *fname)
328 char *ret = xstrdup (fname);
329 unsigned len = strlen (ret);
331 if (len > 3 && FILENAME_CMP (ret + len - 3, ".gz") == 0)
333 len -= 3;
334 ret[len] = 0;
336 else if (len > 4 && FILENAME_CMP (ret + len - 4, ".bz2") == 0)
338 len -= 4;
339 ret[len] = 0;
342 if (len > 5 && FILENAME_CMP (ret + len - 5, ".info") == 0)
344 len -= 5;
345 ret[len] = 0;
347 else if (len > 4 && FILENAME_CMP (ret + len - 4, ".inf") == 0)
349 len -= 4;
350 ret[len] = 0;
352 #ifdef __MSDOS__
353 else if (len > 4 && (FILENAME_CMP (ret + len - 4, ".inz") == 0
354 || FILENAME_CMP (ret + len - 4, ".igz") == 0))
356 len -= 4;
357 ret[len] = 0;
359 #endif /* __MSDOS__ */
361 return ret;
365 /* Return true if ITEM matches NAME and is followed by TERM_CHAR. ITEM
366 can also be followed by `.gz', `.info.gz', or `.info' (and then
367 TERM_CHAR) and still match. */
369 static int
370 menu_item_equal (const char *item, char term_char, const char *name)
372 int ret;
373 const char *item_basename = item;
374 unsigned name_len = strlen (name);
376 /* We must compare the basename in ITEM, since we are passed the
377 basename of the original info file. Otherwise, a new entry like
378 "lilypond/lilypond" won't match "lilypond".
380 Actually, it seems to me that we should really compare the whole
381 name, and not just the basename. Couldn't there be dir1/foo.info
382 and dir2/foo.info? Also, it seems like we should be using the
383 filename from the new dir entries, not the filename on the command
384 line. Not worrying about those things right now, though. --karl,
385 26mar04. */
386 while (*item_basename && !IS_SLASH (*item_basename)
387 && *item_basename != term_char)
388 item_basename++;
389 if (! *item_basename || *item_basename == term_char)
390 item_basename = item; /* no /, use original */
391 else
392 item_basename++; /* have /, move past it */
394 /* First, ITEM must actually match NAME (usually it won't). */
395 ret = strncasecmp (item_basename, name, name_len) == 0;
396 if (ret)
398 /* Then, `foobar' doesn't match `foo', so be sure we've got all of
399 ITEM. The various suffixes should never actually appear in the
400 dir file, but sometimes people put them in. */
401 static char *suffixes[]
402 = { "", ".info.gz", ".info", ".inf", ".gz",
403 #ifdef __MSDOS__
404 ".inz", ".igz",
405 #endif
406 NULL };
407 unsigned i;
408 ret = 0;
409 for (i = 0; !ret && suffixes[i]; i++)
411 char *suffix = suffixes[i];
412 unsigned suffix_len = strlen (suffix);
413 ret = strncasecmp (item_basename + name_len, suffix, suffix_len) == 0
414 && item_basename[name_len + suffix_len] == term_char;
418 return ret;
423 void
424 suggest_asking_for_help (void)
426 fprintf (stderr, _("\tTry `%s --help' for a complete list of options.\n"),
427 progname);
428 xexit (1);
431 void
432 print_help (void)
434 printf (_("Usage: %s [OPTION]... [INFO-FILE [DIR-FILE]]\n\
436 Install or delete dir entries from INFO-FILE in the Info directory file\n\
437 DIR-FILE.\n\
439 Options:\n\
440 --delete delete existing entries for INFO-FILE from DIR-FILE;\n\
441 don't insert any new entries.\n\
442 --dir-file=NAME specify file name of Info directory file.\n\
443 This is equivalent to using the DIR-FILE argument.\n\
444 --entry=TEXT insert TEXT as an Info directory entry.\n\
445 TEXT should have the form of an Info menu item line\n\
446 plus zero or more extra lines starting with whitespace.\n\
447 If you specify more than one entry, they are all added.\n\
448 If you don't specify any entries, they are determined\n\
449 from information in the Info file itself.\n\
450 --help display this help and exit.\n\
451 --info-file=FILE specify Info file to install in the directory.\n\
452 This is equivalent to using the INFO-FILE argument.\n\
453 --info-dir=DIR same as --dir-file=DIR/dir.\n\
454 --item=TEXT same as --entry TEXT.\n\
455 An Info directory entry is actually a menu item.\n\
456 --quiet suppress warnings.\n\
457 --remove same as --delete.\n\
458 --section=SEC put this file's entries in section SEC of the directory.\n\
459 If you specify more than one section, all the entries\n\
460 are added in each of the sections.\n\
461 If you don't specify any sections, they are determined\n\
462 from information in the Info file itself.\n\
463 --version display version information and exit.\n\
464 "), progname);
466 puts (_("\n\
467 Email bug reports to bug-texinfo@gnu.org,\n\
468 general questions and discussion to help-texinfo@gnu.org.\n\
469 Texinfo home page: http://www.gnu.org/software/texinfo/"));
473 /* If DIRFILE does not exist, create a minimal one (or abort). If it
474 already exists, do nothing. */
476 void
477 ensure_dirfile_exists (char *dirfile)
479 int desc = open (dirfile, O_RDONLY);
480 if (desc < 0 && errno == ENOENT)
482 FILE *f;
483 char *readerr = strerror (errno);
484 close (desc);
485 f = fopen (dirfile, "w");
486 if (f)
488 fprintf (f, _("This is the file .../info/dir, which contains the\n\
489 topmost node of the Info hierarchy, called (dir)Top.\n\
490 The first time you invoke Info you start off looking at this node.\n\
491 \x1f\n\
492 %s\tThis is the top of the INFO tree\n\
494 This (the Directory node) gives a menu of major topics.\n\
495 Typing \"q\" exits, \"?\" lists all Info commands, \"d\" returns here,\n\
496 \"h\" gives a primer for first-timers,\n\
497 \"mEmacs<Return>\" visits the Emacs manual, etc.\n\
499 In Emacs, you can click mouse button 2 on a menu item or cross reference\n\
500 to select it.\n\
502 %s\n\
503 "), "File: dir,\tNode: Top", /* These keywords must not be translated. */
504 "* Menu:"
506 if (fclose (f) < 0)
507 pfatal_with_name (dirfile);
509 else
511 /* Didn't exist, but couldn't open for writing. */
512 fprintf (stderr,
513 _("%s: could not read (%s) and could not create (%s)\n"),
514 dirfile, readerr, strerror (errno));
515 xexit (1);
518 else
519 close (desc); /* It already existed, so fine. */
522 /* Open FILENAME and return the resulting stream pointer. If it doesn't
523 exist, try FILENAME.gz. If that doesn't exist either, call
524 CREATE_CALLBACK (with FILENAME as arg) to create it, if that is
525 non-NULL. If still no luck, fatal error.
527 If we do open it, return the actual name of the file opened in
528 OPENED_FILENAME and the compress program to use to (de)compress it in
529 COMPRESSION_PROGRAM. The compression program is determined by the
530 magic number, not the filename. */
532 FILE *
533 open_possibly_compressed_file (char *filename,
534 void (*create_callback) (char *),
535 char **opened_filename, char **compression_program, int *is_pipe)
537 char *local_opened_filename, *local_compression_program;
538 int nread;
539 char data[4];
540 FILE *f;
542 /* We let them pass NULL if they don't want this info, but it's easier
543 to always determine it. */
544 if (!opened_filename)
545 opened_filename = &local_opened_filename;
547 *opened_filename = filename;
548 f = fopen (*opened_filename, FOPEN_RBIN);
549 if (!f)
551 *opened_filename = concat (filename, ".gz", "");
552 f = fopen (*opened_filename, FOPEN_RBIN);
553 if (!f)
555 free (*opened_filename);
556 *opened_filename = concat (filename, ".bz2", "");
557 f = fopen (*opened_filename, FOPEN_RBIN);
560 #ifdef __MSDOS__
561 if (!f)
563 free (*opened_filename);
564 *opened_filename = concat (filename, ".igz", "");
565 f = fopen (*opened_filename, FOPEN_RBIN);
567 if (!f)
569 free (*opened_filename);
570 *opened_filename = concat (filename, ".inz", "");
571 f = fopen (*opened_filename, FOPEN_RBIN);
573 #endif
574 if (!f)
576 if (create_callback)
577 { /* That didn't work either. Create the file if we can. */
578 (*create_callback) (filename);
580 /* And try opening it again. */
581 free (*opened_filename);
582 *opened_filename = filename;
583 f = fopen (*opened_filename, FOPEN_RBIN);
584 if (!f)
585 pfatal_with_name (filename);
587 else
588 pfatal_with_name (filename);
592 /* Read first few bytes of file rather than relying on the filename.
593 If the file is shorter than this it can't be usable anyway. */
594 nread = fread (data, sizeof (data), 1, f);
595 if (nread != 1)
597 /* Empty files don't set errno, so we get something like
598 "install-info: No error for foo", which is confusing. */
599 if (nread == 0)
600 fatal (_("%s: empty file"), *opened_filename, 0);
601 pfatal_with_name (*opened_filename);
604 if (!compression_program)
605 compression_program = &local_compression_program;
607 if (data[0] == '\x1f' && data[1] == '\x8b')
608 #if STRIP_DOT_EXE
609 /* An explicit .exe yields a better diagnostics from popen below
610 if they don't have gzip installed. */
611 *compression_program = "gzip.exe";
612 #else
613 *compression_program = "gzip";
614 #endif
615 else if(data[0] == 'B' && data[1] == 'Z' && data[2] == 'h')
616 #ifndef STRIP_DOT_EXE
617 *compression_program = "bzip2.exe";
618 #else
619 *compression_program = "bzip2";
620 #endif
621 else if(data[0] == 'B' && data[1] == 'Z' && data[2] == '0')
622 #ifndef STRIP_DOT_EXE
623 *compression_program = "bzip.exe";
624 #else
625 *compression_program = "bzip";
626 #endif
627 else
628 *compression_program = NULL;
630 if (*compression_program)
631 { /* It's compressed, so fclose the file and then open a pipe. */
632 char *command = concat (*compression_program," -cd <", *opened_filename);
633 if (fclose (f) < 0)
634 pfatal_with_name (*opened_filename);
635 f = popen (command, "r");
636 if (f)
637 *is_pipe = 1;
638 else
639 pfatal_with_name (command);
641 else
642 { /* It's a plain file, seek back over the magic bytes. */
643 if (fseek (f, 0, 0) < 0)
644 pfatal_with_name (*opened_filename);
645 #if O_BINARY
646 /* Since this is a text file, and we opened it in binary mode,
647 switch back to text mode. */
648 f = freopen (*opened_filename, "r", f);
649 #endif
650 *is_pipe = 0;
653 return f;
656 /* Read all of file FILENAME into memory and return the address of the
657 data. Store the size of the data into SIZEP. If need be, uncompress
658 (i.e., try FILENAME.gz et al. if FILENAME does not exist) and store
659 the actual file name that was opened into OPENED_FILENAME (if it is
660 non-NULL), and the companion compression program (if any, else NULL)
661 into COMPRESSION_PROGRAM (if that is non-NULL). If trouble, do
662 a fatal error. */
664 char *
665 readfile (char *filename, int *sizep,
666 void (*create_callback) (char *), char **opened_filename,
667 char **compression_program)
669 char *real_name;
670 FILE *f;
671 int pipe_p;
672 int filled = 0;
673 int data_size = 8192;
674 char *data = xmalloc (data_size);
676 /* If they passed the space for the file name to return, use it. */
677 f = open_possibly_compressed_file (filename, create_callback,
678 opened_filename ? opened_filename
679 : &real_name,
680 compression_program, &pipe_p);
682 for (;;)
684 int nread = fread (data + filled, 1, data_size - filled, f);
685 if (nread < 0)
686 pfatal_with_name (real_name);
687 if (nread == 0)
688 break;
690 filled += nread;
691 if (filled == data_size)
693 data_size += 65536;
694 data = xrealloc (data, data_size);
698 /* We'll end up wasting space if we're not passing the filename back
699 and it is not just FILENAME, but so what. */
700 /* We need to close the stream, since on some systems the pipe created
701 by popen is simulated by a temporary file which only gets removed
702 inside pclose. */
703 if (pipe_p)
704 pclose (f);
705 else
706 fclose (f);
708 *sizep = filled;
709 return data;
712 /* Output the old dir file, interpolating the new sections
713 and/or new entries where appropriate. If COMPRESSION_PROGRAM is not
714 null, pipe to it to create DIRFILE. Thus if we read dir.gz on input,
715 we'll write dir.gz on output. */
717 static void
718 output_dirfile (char *dirfile, int dir_nlines, struct line_data *dir_lines,
719 int n_entries_to_add, struct spec_entry *entries_to_add,
720 struct spec_section *input_sections, char *compression_program)
722 int i;
723 FILE *output;
725 if (compression_program)
727 char *command = concat (compression_program, ">", dirfile);
728 output = popen (command, "w");
730 else
731 output = fopen (dirfile, "w");
733 if (!output)
735 perror (dirfile);
736 xexit (1);
739 for (i = 0; i <= dir_nlines; i++)
741 int j;
743 /* If we decided to output some new entries before this line,
744 output them now. */
745 if (dir_lines[i].add_entries_before)
746 for (j = 0; j < n_entries_to_add; j++)
748 struct spec_entry *this = dir_lines[i].add_entries_before[j];
749 if (this == 0)
750 break;
751 fputs (this->text, output);
753 /* If we decided to add some sections here
754 because there are no such sections in the file,
755 output them now. */
756 if (dir_lines[i].add_sections_before)
758 struct spec_section *spec;
759 struct spec_section **sections;
760 int n_sections = 0;
761 struct spec_entry *entry;
762 struct spec_entry **entries;
763 int n_entries = 0;
765 /* Count the sections and allocate a vector for all of them. */
766 for (spec = input_sections; spec; spec = spec->next)
767 n_sections++;
768 sections = ((struct spec_section **)
769 xmalloc (n_sections * sizeof (struct spec_section *)));
771 /* Fill the vector SECTIONS with pointers to all the sections,
772 and sort them. */
773 j = 0;
774 for (spec = input_sections; spec; spec = spec->next)
775 sections[j++] = spec;
776 qsort (sections, n_sections, sizeof (struct spec_section *),
777 compare_section_names);
779 /* Count the entries and allocate a vector for all of them. */
780 for (entry = entries_to_add; entry; entry = entry->next)
781 n_entries++;
782 entries = ((struct spec_entry **)
783 xmalloc (n_entries * sizeof (struct spec_entry *)));
785 /* Fill the vector ENTRIES with pointers to all the sections,
786 and sort them. */
787 j = 0;
788 for (entry = entries_to_add; entry; entry = entry->next)
789 entries[j++] = entry;
790 qsort (entries, n_entries, sizeof (struct spec_entry *),
791 compare_entries_text);
793 /* Generate the new sections in alphabetical order. In each
794 new section, output all of the entries that belong to that
795 section, in alphabetical order. */
796 for (j = 0; j < n_sections; j++)
798 spec = sections[j];
799 if (spec->missing)
801 int k;
803 putc ('\n', output);
804 fputs (spec->name, output);
805 putc ('\n', output);
806 for (k = 0; k < n_entries; k++)
808 struct spec_section *spec1;
809 /* Did they at all want this entry to be put into
810 this section? */
811 entry = entries[k];
812 for (spec1 = entry->entry_sections;
813 spec1 && spec1 != entry->entry_sections_tail;
814 spec1 = spec1->next)
816 if (!strcmp (spec1->name, spec->name))
817 break;
819 if (spec1 && spec1 != entry->entry_sections_tail)
820 fputs (entry->text, output);
825 free (entries);
826 free (sections);
829 /* Output the original dir lines unless marked for deletion. */
830 if (i < dir_nlines && !dir_lines[i].delete)
832 fwrite (dir_lines[i].start, 1, dir_lines[i].size, output);
833 putc ('\n', output);
837 /* Some systems, such as MS-DOS, simulate pipes with temporary files.
838 On those systems, the compressor actually gets run inside pclose,
839 so we must call pclose. */
840 if (compression_program)
841 pclose (output);
842 else
843 fclose (output);
846 /* Parse the input to find the section names and the entry names it
847 specifies. Return the number of entries to add from this file. */
849 parse_input (const struct line_data *lines, int nlines,
850 struct spec_section **sections, struct spec_entry **entries)
852 int n_entries = 0;
853 int prefix_length = strlen ("INFO-DIR-SECTION ");
854 struct spec_section *head = *sections, *tail = NULL;
855 int reset_tail = 0;
856 char *start_of_this_entry = 0;
857 int ignore_sections = *sections != 0;
858 int ignore_entries = *entries != 0;
860 int i;
862 if (ignore_sections && ignore_entries)
863 return 0;
865 /* Loop here processing lines from the input file. Each
866 INFO-DIR-SECTION entry is added to the SECTIONS linked list.
867 Each START-INFO-DIR-ENTRY block is added to the ENTRIES linked
868 list, and all its entries inherit the chain of SECTION entries
869 defined by the last group of INFO-DIR-SECTION entries we have
870 seen until that point. */
871 for (i = 0; i < nlines; i++)
873 if (!ignore_sections
874 && !strncmp ("INFO-DIR-SECTION ", lines[i].start, prefix_length))
876 struct spec_section *next
877 = (struct spec_section *) xmalloc (sizeof (struct spec_section));
878 next->name = copy_string (lines[i].start + prefix_length,
879 lines[i].size - prefix_length);
880 next->next = *sections;
881 next->missing = 1;
882 if (reset_tail)
884 tail = *sections;
885 reset_tail = 0;
887 *sections = next;
888 head = *sections;
890 /* If entries were specified explicitly with command options,
891 ignore the entries in the input file. */
892 else if (!ignore_entries)
894 if (!strncmp ("START-INFO-DIR-ENTRY", lines[i].start, lines[i].size)
895 && sizeof ("START-INFO-DIR-ENTRY") - 1 == lines[i].size)
897 if (!*sections)
899 /* We found an entry, but didn't yet see any sections
900 specified. Default to section "Miscellaneous". */
901 *sections = (struct spec_section *)
902 xmalloc (sizeof (struct spec_section));
903 (*sections)->name = "Miscellaneous";
904 (*sections)->next = 0;
905 (*sections)->missing = 1;
906 head = *sections;
908 /* Next time we see INFO-DIR-SECTION, we will reset the
909 tail pointer. */
910 reset_tail = 1;
912 if (start_of_this_entry != 0)
913 fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"), 0, 0);
914 start_of_this_entry = lines[i + 1].start;
916 else if (start_of_this_entry)
918 if ((!strncmp ("* ", lines[i].start, 2)
919 && lines[i].start > start_of_this_entry)
920 || (!strncmp ("END-INFO-DIR-ENTRY",
921 lines[i].start, lines[i].size)
922 && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size))
924 /* We found an end of this entry. Allocate another
925 entry, fill its data, and add it to the linked
926 list. */
927 struct spec_entry *next
928 = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
929 next->text
930 = copy_string (start_of_this_entry,
931 lines[i].start - start_of_this_entry);
932 next->text_len = lines[i].start - start_of_this_entry;
933 next->entry_sections = head;
934 next->entry_sections_tail = tail;
935 next->next = *entries;
936 *entries = next;
937 n_entries++;
938 if (!strncmp ("END-INFO-DIR-ENTRY",
939 lines[i].start, lines[i].size)
940 && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size)
941 start_of_this_entry = 0;
942 else
943 start_of_this_entry = lines[i].start;
945 else if (!strncmp ("END-INFO-DIR-ENTRY",
946 lines[i].start, lines[i].size)
947 && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size)
948 fatal (_("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY"), 0, 0);
952 if (start_of_this_entry != 0)
953 fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"),
954 0, 0);
956 /* If we ignored the INFO-DIR-ENTRY directives, we need now go back
957 and plug the names of all the sections we found into every
958 element of the ENTRIES list. */
959 if (ignore_entries && *entries)
961 struct spec_entry *entry;
963 for (entry = *entries; entry; entry = entry->next)
965 entry->entry_sections = head;
966 entry->entry_sections_tail = tail;
970 return n_entries;
973 /* Parse the dir file whose basename is BASE_NAME. Find all the
974 nodes, and their menus, and the sections of their menus. */
976 parse_dir_file (struct line_data *lines, int nlines, struct node **nodes,
977 const char *base_name)
979 int node_header_flag = 0;
980 int something_deleted = 0;
981 int i;
983 *nodes = 0;
984 for (i = 0; i < nlines; i++)
986 /* Parse node header lines. */
987 if (node_header_flag)
989 int j, end;
990 for (j = 0; j < lines[i].size; j++)
991 /* Find the node name and store it in the `struct node'. */
992 if (!strncmp ("Node:", lines[i].start + j, 5))
994 char *line = lines[i].start;
995 /* Find the start of the node name. */
996 j += 5;
997 while (line[j] == ' ' || line[j] == '\t')
998 j++;
999 /* Find the end of the node name. */
1000 end = j;
1001 while (line[end] != 0 && line[end] != ',' && line[end] != '\n'
1002 && line[end] != '\t')
1003 end++;
1004 (*nodes)->name = copy_string (line + j, end - j);
1006 node_header_flag = 0;
1009 /* Notice the start of a node. */
1010 if (*lines[i].start == 037)
1012 struct node *next = (struct node *) xmalloc (sizeof (struct node));
1014 next->next = *nodes;
1015 next->name = NULL;
1016 next->start_line = i;
1017 next->end_line = 0;
1018 next->menu_start = NULL;
1019 next->sections = NULL;
1020 next->last_section = NULL;
1022 if (*nodes != 0)
1023 (*nodes)->end_line = i;
1024 /* Fill in the end of the last menu section
1025 of the previous node. */
1026 if (*nodes != 0 && (*nodes)->last_section != 0)
1027 (*nodes)->last_section->end_line = i;
1029 *nodes = next;
1031 /* The following line is the header of this node;
1032 parse it. */
1033 node_header_flag = 1;
1036 /* Notice the lines that start menus. */
1037 if (*nodes != 0 && !strncmp ("* Menu:", lines[i].start, 7))
1038 (*nodes)->menu_start = lines[i + 1].start;
1040 /* Notice sections in menus. */
1041 if (*nodes != 0
1042 && (*nodes)->menu_start != 0
1043 && *lines[i].start != '\n'
1044 && *lines[i].start != '*'
1045 && *lines[i].start != ' '
1046 && *lines[i].start != '\t')
1048 /* Add this menu section to the node's list.
1049 This list grows in forward order. */
1050 struct menu_section *next
1051 = (struct menu_section *) xmalloc (sizeof (struct menu_section));
1053 next->start_line = i + 1;
1054 next->next = 0;
1055 next->end_line = 0;
1056 next->name = copy_string (lines[i].start, lines[i].size);
1057 if ((*nodes)->sections)
1059 (*nodes)->last_section->next = next;
1060 (*nodes)->last_section->end_line = i;
1062 else
1063 (*nodes)->sections = next;
1064 (*nodes)->last_section = next;
1067 /* Check for an existing entry that should be deleted.
1068 Delete all entries which specify this file name. */
1069 if (*lines[i].start == '*')
1071 char *q;
1072 char *p = lines[i].start;
1074 p++; /* skip * */
1075 while (*p == ' ') p++; /* ignore following spaces */
1076 q = p; /* remember this, it's the beginning of the menu item. */
1078 /* Read menu item. */
1079 while (*p != 0 && *p != ':')
1080 p++;
1081 p++; /* skip : */
1083 if (*p == ':')
1084 { /* XEmacs-style entry, as in * Mew::Messaging. */
1085 if (menu_item_equal (q, ':', base_name))
1087 lines[i].delete = 1;
1088 something_deleted = 1;
1091 else
1092 { /* Emacs-style entry, as in * Emacs: (emacs). */
1093 while (*p == ' ') p++; /* skip spaces after : */
1094 if (*p == '(') /* if at parenthesized (FILENAME) */
1096 p++;
1097 if (menu_item_equal (p, ')', base_name))
1099 lines[i].delete = 1;
1100 something_deleted = 1;
1106 /* Treat lines that start with whitespace
1107 as continuations; if we are deleting an entry,
1108 delete all its continuations as well. */
1109 else if (i > 0 && (*lines[i].start == ' ' || *lines[i].start == '\t'))
1111 lines[i].delete = lines[i - 1].delete;
1115 /* Finish the info about the end of the last node. */
1116 if (*nodes != 0)
1118 (*nodes)->end_line = nlines;
1119 if ((*nodes)->last_section != 0)
1120 (*nodes)->last_section->end_line = nlines;
1123 return something_deleted;
1127 main (int argc, char **argv)
1129 char *opened_dirfilename;
1130 char *compression_program;
1131 char *infile_sans_info;
1132 char *infile = 0, *dirfile = 0;
1134 /* Record the text of the Info file, as a sequence of characters
1135 and as a sequence of lines. */
1136 char *input_data = NULL;
1137 int input_size = 0;
1138 struct line_data *input_lines = NULL;
1139 int input_nlines = 0;
1141 /* Record here the specified section names and directory entries. */
1142 struct spec_section *input_sections = NULL;
1143 struct spec_entry *entries_to_add = NULL;
1144 int n_entries_to_add = 0;
1146 /* Record the old text of the dir file, as plain characters,
1147 as lines, and as nodes. */
1148 char *dir_data;
1149 int dir_size;
1150 int dir_nlines;
1151 struct line_data *dir_lines;
1152 struct node *dir_nodes;
1154 /* Nonzero means --delete was specified (just delete existing entries). */
1155 int delete_flag = 0;
1156 int something_deleted = 0;
1157 /* Nonzero means -q was specified. */
1158 int quiet_flag = 0;
1160 int i;
1162 #ifdef HAVE_SETLOCALE
1163 /* Set locale via LC_ALL. */
1164 setlocale (LC_ALL, "");
1165 #endif
1167 /* Set the text message domain. */
1168 bindtextdomain (PACKAGE, LOCALEDIR);
1169 textdomain (PACKAGE);
1171 while (1)
1173 int opt = getopt_long (argc, argv, "i:d:e:s:hHr", longopts, 0);
1175 if (opt == EOF)
1176 break;
1178 switch (opt)
1180 case 0:
1181 /* If getopt returns 0, then it has already processed a
1182 long-named option. We should do nothing. */
1183 break;
1185 case 1:
1186 abort ();
1188 case 'd':
1189 if (dirfile)
1191 fprintf (stderr, _("%s: already have dir file: %s\n"),
1192 progname, dirfile);
1193 suggest_asking_for_help ();
1195 dirfile = optarg;
1196 break;
1198 case 'D':
1199 if (dirfile)
1201 fprintf (stderr, _("%s: already have dir file: %s\n"),
1202 progname, dirfile);
1203 suggest_asking_for_help ();
1205 dirfile = concat (optarg, "", "/dir");
1206 break;
1208 case 'e':
1210 struct spec_entry *next
1211 = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
1212 int olen = strlen (optarg);
1213 if (! (*optarg != 0 && optarg[olen - 1] == '\n'))
1215 optarg = concat (optarg, "\n", "");
1216 olen++;
1218 next->text = optarg;
1219 next->text_len = olen;
1220 next->entry_sections = NULL;
1221 next->entry_sections_tail = NULL;
1222 next->next = entries_to_add;
1223 entries_to_add = next;
1224 n_entries_to_add++;
1226 break;
1228 case 'h':
1229 case 'H':
1230 print_help ();
1231 xexit (0);
1233 case 'i':
1234 if (infile)
1236 fprintf (stderr, _("%s: Specify the Info file only once.\n"),
1237 progname);
1238 suggest_asking_for_help ();
1240 infile = optarg;
1241 break;
1243 case 'q':
1244 quiet_flag = 1;
1245 break;
1247 case 'r':
1248 delete_flag = 1;
1249 break;
1251 case 's':
1253 struct spec_section *next
1254 = (struct spec_section *) xmalloc (sizeof (struct spec_section));
1255 next->name = optarg;
1256 next->next = input_sections;
1257 next->missing = 1;
1258 input_sections = next;
1260 break;
1262 case 'V':
1263 printf ("install-info (GNU %s) %s\n", PACKAGE, VERSION);
1264 puts ("");
1265 puts ("Copyright (C) 2004 Free Software Foundation, Inc.");
1266 printf (_("There is NO warranty. You may redistribute this software\n\
1267 under the terms of the GNU General Public License.\n\
1268 For more information about these matters, see the files named COPYING.\n"));
1269 xexit (0);
1271 default:
1272 suggest_asking_for_help ();
1276 /* Interpret the non-option arguments as file names. */
1277 for (; optind < argc; ++optind)
1279 if (infile == 0)
1280 infile = argv[optind];
1281 else if (dirfile == 0)
1282 dirfile = argv[optind];
1283 else
1284 error (_("excess command line argument `%s'"), argv[optind], 0);
1287 if (!infile)
1288 fatal (_("No input file specified; try --help for more information."),
1289 0, 0);
1290 if (!dirfile)
1291 fatal (_("No dir file specified; try --help for more information."), 0, 0);
1293 /* Read the Info file and parse it into lines, unless we're deleting. */
1294 if (!delete_flag)
1296 input_data = readfile (infile, &input_size, NULL, NULL, NULL);
1297 input_lines = findlines (input_data, input_size, &input_nlines);
1300 i = parse_input (input_lines, input_nlines,
1301 &input_sections, &entries_to_add);
1302 if (i > n_entries_to_add)
1303 n_entries_to_add = i;
1305 if (!delete_flag)
1307 if (entries_to_add == 0)
1308 { /* No need to abort here, the original info file may not
1309 have the requisite Texinfo commands. This is not
1310 something an installer should have to correct (it's a
1311 problem for the maintainer), and there's no need to cause
1312 subsequent parts of `make install' to fail. */
1313 warning (_("no info dir entry in `%s'"), infile, 0);
1314 xexit (0);
1317 /* If the entries came from the command-line arguments, their
1318 entry_sections pointers are not yet set. Walk the chain of
1319 the entries and for each entry update entry_sections to point
1320 to the head of the list of sections where this entry should
1321 be put. Note that all the entries specified on the command
1322 line get put into ALL the sections we've got, either from the
1323 Info file, or (under --section) from the command line,
1324 because in the loop below every entry inherits the entire
1325 chain of sections. */
1326 if (n_entries_to_add > 0 && entries_to_add->entry_sections == NULL)
1328 struct spec_entry *ep;
1330 /* If we got no sections, default to "Miscellaneous". */
1331 if (input_sections == NULL)
1333 input_sections = (struct spec_section *)
1334 xmalloc (sizeof (struct spec_section));
1335 input_sections->name = "Miscellaneous";
1336 input_sections->next = NULL;
1337 input_sections->missing = 1;
1339 for (ep = entries_to_add; ep; ep = ep->next)
1340 ep->entry_sections = input_sections;
1344 /* Now read in the Info dir file. */
1345 dir_data = readfile (dirfile, &dir_size, ensure_dirfile_exists,
1346 &opened_dirfilename, &compression_program);
1347 dir_lines = findlines (dir_data, dir_size, &dir_nlines);
1349 /* We will be comparing the entries in the dir file against the
1350 current filename, so need to strip off any directory prefix and/or
1351 [.info][.gz] suffix. */
1353 char *infile_basename = infile + strlen (infile);
1355 if (HAVE_DRIVE (infile))
1356 infile += 2; /* get past the drive spec X: */
1358 while (infile_basename > infile && !IS_SLASH (infile_basename[-1]))
1359 infile_basename--;
1361 infile_sans_info = strip_info_suffix (infile_basename);
1364 something_deleted
1365 = parse_dir_file (dir_lines, dir_nlines, &dir_nodes, infile_sans_info);
1367 /* Decide where to add the new entries (unless --delete was used).
1368 Find the menu sections to add them in.
1369 In each section, find the proper alphabetical place to add
1370 each of the entries. */
1371 if (!delete_flag)
1373 struct node *node;
1374 struct menu_section *section;
1375 struct spec_section *spec;
1377 for (node = dir_nodes; node; node = node->next)
1378 for (section = node->sections; section; section = section->next)
1380 for (i = section->end_line; i > section->start_line; i--)
1381 if (dir_lines[i - 1].size != 0)
1382 break;
1383 section->end_line = i;
1385 for (spec = input_sections; spec; spec = spec->next)
1386 if (!strcmp (spec->name, section->name))
1387 break;
1388 if (spec)
1390 int add_at_line = section->end_line;
1391 struct spec_entry *entry;
1392 /* Say we have found at least one section with this name,
1393 so we need not add such a section. */
1394 spec->missing = 0;
1395 /* For each entry, find the right place in this section
1396 to add it. */
1397 for (entry = entries_to_add; entry; entry = entry->next)
1399 /* Did they at all want this entry to be put into
1400 this section? */
1401 for (spec = entry->entry_sections;
1402 spec && spec != entry->entry_sections_tail;
1403 spec = spec->next)
1405 if (!strcmp (spec->name, section->name))
1406 break;
1408 if (!spec || spec == entry->entry_sections_tail)
1409 continue;
1411 /* Subtract one because dir_lines is zero-based,
1412 but the `end_line' and `start_line' members are
1413 one-based. */
1414 for (i = section->end_line - 1;
1415 i >= section->start_line - 1; i--)
1417 /* If an entry exists with the same name,
1418 and was not marked for deletion
1419 (which means it is for some other file),
1420 we are in trouble. */
1421 if (dir_lines[i].start[0] == '*'
1422 && menu_line_equal (entry->text, entry->text_len,
1423 dir_lines[i].start,
1424 dir_lines[i].size)
1425 && !dir_lines[i].delete)
1426 fatal (_("menu item `%s' already exists, for file `%s'"),
1427 extract_menu_item_name (entry->text),
1428 extract_menu_file_name (dir_lines[i].start));
1429 if (dir_lines[i].start[0] == '*'
1430 && menu_line_lessp (entry->text, entry->text_len,
1431 dir_lines[i].start,
1432 dir_lines[i].size))
1433 add_at_line = i;
1435 insert_entry_here (entry, add_at_line,
1436 dir_lines, n_entries_to_add);
1441 /* Mark the end of the Top node as the place to add any
1442 new sections that are needed. */
1443 for (node = dir_nodes; node; node = node->next)
1444 if (node->name && strcmp (node->name, "Top") == 0)
1445 dir_lines[node->end_line].add_sections_before = 1;
1448 if (delete_flag && !something_deleted && !quiet_flag)
1449 warning (_("no entries found for `%s'; nothing deleted"), infile, 0);
1451 output_dirfile (opened_dirfilename, dir_nlines, dir_lines, n_entries_to_add,
1452 entries_to_add, input_sections, compression_program);
1454 xexit (0);
1455 return 0; /* Avoid bogus warnings. */
1458 /* Divide the text at DATA (of SIZE bytes) into lines.
1459 Return a vector of struct line_data describing the lines.
1460 Store the length of that vector into *NLINESP. */
1462 struct line_data *
1463 findlines (char *data, int size, int *nlinesp)
1465 int i;
1466 int lineflag = 1;
1467 int lines_allocated = 511;
1468 int filled = 0;
1469 struct line_data *lines
1470 = xmalloc ((lines_allocated + 1) * sizeof (struct line_data));
1472 for (i = 0; i < size; i++)
1474 if (lineflag)
1476 if (filled == lines_allocated)
1478 /* try to keep things somewhat page-aligned */
1479 lines_allocated = ((lines_allocated + 1) * 2) - 1;
1480 lines = xrealloc (lines, (lines_allocated + 1)
1481 * sizeof (struct line_data));
1483 lines[filled].start = &data[i];
1484 lines[filled].add_entries_before = 0;
1485 lines[filled].add_sections_before = 0;
1486 lines[filled].delete = 0;
1487 if (filled > 0)
1488 lines[filled - 1].size
1489 = lines[filled].start - lines[filled - 1].start - 1;
1490 filled++;
1492 lineflag = (data[i] == '\n');
1494 if (filled > 0)
1495 lines[filled - 1].size = &data[i] - lines[filled - 1].start - lineflag;
1497 /* Do not leave garbage in the last element. */
1498 lines[filled].start = NULL;
1499 lines[filled].add_entries_before = NULL;
1500 lines[filled].add_sections_before = 0;
1501 lines[filled].delete = 0;
1502 lines[filled].size = 0;
1504 *nlinesp = filled;
1505 return lines;
1508 /* This is the comparison function for qsort for a vector of pointers to
1509 struct spec_section. (Have to use const void * as the parameter type
1510 to avoid incompatible-with-qsort warnings.)
1511 Compare the section names. */
1514 compare_section_names (const void *p1, const void *p2)
1516 struct spec_section **sec1 = (struct spec_section **) p1;
1517 struct spec_section **sec2 = (struct spec_section **) p2;
1518 char *name1 = (*sec1)->name;
1519 char *name2 = (*sec2)->name;
1520 return strcmp (name1, name2);
1523 /* This is the comparison function for qsort
1524 for a vector of pointers to struct spec_entry.
1525 Compare the entries' text. */
1528 compare_entries_text (const void *p1, const void *p2)
1530 struct spec_entry **entry1 = (struct spec_entry **) p1;
1531 struct spec_entry **entry2 = (struct spec_entry **) p2;
1532 char *text1 = (*entry1)->text;
1533 char *text2 = (*entry2)->text;
1534 char *colon1 = strchr (text1, ':');
1535 char *colon2 = strchr (text2, ':');
1536 int len1, len2;
1538 if (!colon1)
1539 len1 = strlen (text1);
1540 else
1541 len1 = colon1 - text1;
1542 if (!colon2)
1543 len2 = strlen (text2);
1544 else
1545 len2 = colon2 - text2;
1546 return strncmp (text1, text2, len1 <= len2 ? len1 : len2);
1549 /* Insert ENTRY into the add_entries_before vector
1550 for line number LINE_NUMBER of the dir file.
1551 DIR_LINES and N_ENTRIES carry information from like-named variables
1552 in main. */
1554 void
1555 insert_entry_here (struct spec_entry *entry, int line_number,
1556 struct line_data *dir_lines, int n_entries)
1558 int i, j;
1560 if (dir_lines[line_number].add_entries_before == 0)
1562 dir_lines[line_number].add_entries_before
1563 = (struct spec_entry **) xmalloc (n_entries * sizeof (struct spec_entry *));
1564 for (i = 0; i < n_entries; i++)
1565 dir_lines[line_number].add_entries_before[i] = 0;
1568 /* Find the place where this entry belongs. If there are already
1569 several entries to add before LINE_NUMBER, make sure they are in
1570 alphabetical order. */
1571 for (i = 0; i < n_entries; i++)
1572 if (dir_lines[line_number].add_entries_before[i] == 0
1573 || menu_line_lessp (entry->text, strlen (entry->text),
1574 dir_lines[line_number].add_entries_before[i]->text,
1575 strlen (dir_lines[line_number].add_entries_before[i]->text)))
1576 break;
1578 if (i == n_entries)
1579 abort ();
1581 /* If we need to plug ENTRY into the middle of the
1582 ADD_ENTRIES_BEFORE array, move the entries which should be output
1583 after this one down one notch, before adding a new one. */
1584 if (dir_lines[line_number].add_entries_before[i] != 0)
1585 for (j = n_entries - 1; j > i; j--)
1586 dir_lines[line_number].add_entries_before[j]
1587 = dir_lines[line_number].add_entries_before[j - 1];
1589 dir_lines[line_number].add_entries_before[i] = entry;