d61697dc09d74609819e9afddeb7a3e7f957219c
[dragonfly.git] / contrib / texinfo / install-info / install-info.c
1 /* install-info -- create Info directory entry(ies) for an Info file.
2    $Id: install-info.c,v 1.13 2008/05/18 16:54:02 karl Exp $
3
4    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
5    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 #include "system.h"
21 #include <getopt.h>
22 #include <regex.h>
23 #include <argz.h>
24
25 #define TAB_WIDTH 8
26
27 static char *progname = "install-info";
28
29 struct spec_entry;
30 struct spec_section;
31
32 struct line_data *findlines (char *data, int size, int *nlinesp);
33 void insert_entry_here (struct spec_entry *entry, int line_number,
34                         struct line_data *dir_lines, int n_entries); 
35 int compare_section_names (const void *s1, const void *s2);
36 int compare_entries_text (const void *e1, const void *e2); 
37 \f
38 /* Data structures.  */
39
40
41 /* Record info about a single line from a file as read into core.  */
42 struct line_data
43 {
44   /* The start of the line.  */
45   char *start;
46   /* The number of characters in the line,
47      excluding the terminating newline.  */
48   int size;
49   /* Vector containing pointers to the entries to add before this line.
50      The vector is null-terminated.  */
51   struct spec_entry **add_entries_before;
52   /* Vector containing pointers to the sections to add before this line.
53      The vector is not null-terminated.  */
54   struct spec_section **add_sections_before;
55   /* The vector ADD_SECTIONS_BEFORE_HERE contains exactly this many
56      pointers to sections. */
57   int num_sections_to_add;
58   /* 1 means don't output this line.  */
59   int delete;
60 };
61
62
63 /* This is used for a list of the specified menu section names
64    in which entries should be added.  */
65 struct spec_section
66 {
67   struct spec_section *next;
68   char *name;
69   /* 1 means we have not yet found an existing section with this name
70      in the dir file--so we will need to add a new section.  */
71   int missing;
72 };
73
74
75 /* This is used for a list of the entries specified to be added.  */
76 struct spec_entry
77 {
78   struct spec_entry *next;
79   char *text;
80   size_t text_len;
81   /* A pointer to the list of sections to which this entry should be
82      added.  */
83   struct spec_section *entry_sections;
84   /* A pointer to a section that is beyond the end of the chain whose
85      head is pointed to by entry_sections.  */
86   struct spec_section *entry_sections_tail;
87   /* Non-zero means that the entry doesn't have a name specified.  This
88      can only happen if a --description preceeds a --name option. */
89   int missing_name;
90   /* Non-zero means that the entry doesn't have a description.  This
91      happens when a --name option is given prior to a --description 
92      option. */
93   int missing_description;
94   /* Non-zero means that the entry doesn't have an Info file specified.  
95      This means that the entry was taken from the command-line but it
96      only contains the name, and not the info file's basename, which
97      we get later on.  This only happens on entries that originate
98      from --name options. */
99   int missing_basename;
100 };
101
102
103 /* This is used for a list of nodes found by parsing the dir file.  */
104 struct node
105 {
106   struct node *next;
107   /* The node name.  */
108   char *name;
109   /* The line number of the line where the node starts.
110      This is the line that contains control-underscore.  */
111   int start_line;
112   /* The line number of the line where the node ends,
113      which is the end of the file or where the next line starts.  */
114   int end_line;
115   /* Start of first line in this node's menu
116      (the line after the * Menu: line).  */
117   char *menu_start;
118   /* The start of the chain of sections in this node's menu.  */
119   struct menu_section *sections;
120   /* The last menu section in the chain.  */
121   struct menu_section *last_section;
122 };
123
124
125 /* This is used for a list of sections found in a node's menu.
126    Each  struct node  has such a list in the  sections  field.  */
127 struct menu_section
128 {
129   struct menu_section *next;
130   char *name;
131   /* Line number of start of section.  */
132   int start_line;
133   /* Line number of end of section.  */
134   int end_line;
135 };
136 \f
137 /* This table defines all the long-named options, says whether they
138    use an argument, and maps them into equivalent single-letter options.  */
139
140 struct option longopts[] =
141 {
142   { "add-once",  no_argument, NULL, '1'},
143   { "align",     required_argument, NULL, 'A'},
144   { "append-new-sections", no_argument, NULL, 'a'},
145   { "calign",    required_argument, NULL, 'C'},
146   { "debug",     no_argument, NULL, 'g' },
147   { "delete",    no_argument, NULL, 'r' },
148   { "dir-file",  required_argument, NULL, 'd' },
149   { "entry",     required_argument, NULL, 'e' },
150   { "name",      required_argument, NULL, 't' },
151   { "menuentry", required_argument, NULL, 't' },
152   { "description", required_argument, NULL, 'c' },
153   { "help",      no_argument, NULL, 'h' },
154   { "no-indent", no_argument, NULL, 'I' },
155   { "infodir",   required_argument, NULL, 'D' },
156   { "info-dir",  required_argument, NULL, 'D' },
157   { "info-file", required_argument, NULL, 'i' },
158   { "item",      required_argument, NULL, 'e' },
159   { "keep-old",  no_argument, NULL, 'k' },
160   { "maxwidth",  required_argument, NULL, 'W'},
161   { "max-width", required_argument, NULL, 'W'},
162   { "quiet",     no_argument, NULL, 'q' },
163   { "remove",    no_argument, NULL, 'r' },
164   { "remove-exactly",    no_argument, NULL, 'x' },
165   { "section",           required_argument, NULL, 's' },
166   { "regex",     required_argument, NULL, 'R' },
167   { "silent",    no_argument, NULL, 'q' },
168   { "test",      no_argument, NULL, 'n' },
169   { "dry-run",   no_argument, NULL, 'n' },
170   { "version",   no_argument, NULL, 'V' },
171   { 0 }
172 };
173
174 regex_t *psecreg = NULL;
175
176 /* Nonzero means that the name specified for the Info file will be used
177    (without removing .gz, .info extension or leading path) to match the
178    entries that must be removed.  */
179 int remove_exactly = 0;
180
181 /* Nonzero means that sections that don't have entries in them will be
182    deleted. */
183 int remove_empty_sections = 1;
184
185 /* Nonzero means that new Info entries into the DIR file will go into all 
186    sections that match with --section-regex or --section.  Zero means 
187    that new entries wil go into the first section that matches.*/
188 int add_entries_into_all_matching_sections = 1;
189
190 /* Nonzero means we do not replace same-named info entries. */
191 int keep_old_flag = 0;
192
193 /* Nonzero means --test was specified, to inhibit updating the dir file.  */
194 int chicken_flag = 0;
195
196 /* Zero means that entries will not be formatted when they are either 
197    added or replaced. */
198 int indent_flag = 1;
199
200 /* Zero means that new sections will be added at the end of the DIR file. */
201 int order_new_sections_alphabetically_flag = 1;
202
203 \f
204 /* Error message functions.  */
205
206 /* Print error message.  S1 is printf control string, S2 and S3 args for it. */
207
208 /* VARARGS1 */
209 void
210 error (const char *s1, const char *s2, const char *s3)
211 {
212   fprintf (stderr, "%s: ", progname);
213   fprintf (stderr, s1, s2, s3);
214   putc ('\n', stderr);
215 }
216
217 /* VARARGS1 */
218 void
219 warning (const char *s1, const char *s2, const char *s3)
220 {
221   fprintf (stderr, _("%s: warning: "), progname);
222   fprintf (stderr, s1, s2, s3);
223   putc ('\n', stderr);
224 }
225
226 /* Print error message and exit.  */
227
228 void
229 fatal (const char *s1, const char *s2, const char *s3)
230 {
231   error (s1, s2, s3);
232   xexit (1);
233 }
234 \f
235 /* Return a newly-allocated string
236    whose contents concatenate those of S1, S2, S3.  */
237 char *
238 concat (const char *s1, const char *s2, const char *s3)
239 {
240   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
241   char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
242
243   strcpy (result, s1);
244   strcpy (result + len1, s2);
245   strcpy (result + len1 + len2, s3);
246   *(result + len1 + len2 + len3) = 0;
247
248   return result;
249 }
250
251 /* Return a string containing SIZE characters
252    copied from starting at STRING.  */
253
254 char *
255 copy_string (const char *string, int size)
256 {
257   int i;
258   char *copy = (char *) xmalloc (size + 1);
259   for (i = 0; i < size; i++)
260     copy[i] = string[i];
261   copy[size] = 0;
262   return copy;
263 }
264
265 /* Print fatal error message based on errno, with file name NAME.  */
266
267 void
268 pfatal_with_name (const char *name)
269 {
270   char *s = concat ("", strerror (errno), _(" for %s"));
271   fatal (s, name, 0);
272 }
273 \f
274 /* Compare the menu item names in LINE1 (line length LEN1)
275    and LINE2 (line length LEN2).  Return 1 if the item name
276    in LINE1 is less, 0 otherwise.  */
277
278 static int
279 menu_line_lessp (char *line1, int len1, char *line2, int len2)
280 {
281   int minlen = (len1 < len2 ? len1 : len2);
282   int i;
283
284   for (i = 0; i < minlen; i++)
285     {
286       /* If one item name is a prefix of the other,
287          the former one is less.  */
288       if (line1[i] == ':' && line2[i] != ':')
289         return 1;
290       if (line2[i] == ':' && line1[i] != ':')
291         return 0;
292       /* If they both continue and differ, one is less.  */
293       if (line1[i] < line2[i])
294         return 1;
295       if (line1[i] > line2[i])
296         return 0;
297     }
298   /* With a properly formatted dir file,
299      we can only get here if the item names are equal.  */
300   return 0;
301 }
302
303 /* Compare the menu item names in LINE1 (line length LEN1)
304    and LINE2 (line length LEN2).  Return 1 if the item names are equal,
305    0 otherwise.  */
306
307 static int
308 menu_line_equal (char *line1, int len1, char *line2, int len2)
309 {
310   int minlen = (len1 < len2 ? len1 : len2);
311   int i;
312
313   for (i = 0; i < minlen; i++)
314     {
315       /* If both item names end here, they are equal.  */
316       if (line1[i] == ':' && line2[i] == ':')
317         return 1;
318       /* If they both continue and differ, one is less.  */
319       if (line1[i] != line2[i])
320         return 0;
321     }
322   /* With a properly formatted dir file,
323      we can only get here if the item names are equal.  */
324   return 1;
325 }
326
327 \f
328 /* Given the full text of a menu entry, null terminated,
329    return just the menu item name (copied).  */
330
331 char *
332 extract_menu_item_name (char *item_text)
333 {
334   char *p;
335
336   if (*item_text == '*')
337     item_text++;
338   while (*item_text == ' ')
339     item_text++;
340
341   p = item_text;
342   while (*p && *p != ':') p++;
343   return copy_string (item_text, p - item_text);
344 }
345
346 /* Given the full text of a menu entry, terminated by null or newline,
347    return just the menu item file (copied).  */
348
349 char *
350 extract_menu_file_name (char *item_text)
351 {
352   char *p = item_text;
353
354   /* If we have text that looks like * ITEM: (FILE)NODE...,
355      extract just FILE.  Otherwise return "(none)".  */
356
357   if (*p == '*')
358     p++;
359   while (*p == ' ')
360     p++;
361
362   /* Skip to and past the colon.  */
363   while (*p && *p != '\n' && *p != ':') p++;
364   if (*p == ':') p++;
365
366   /* Skip past the open-paren.  */
367   while (1)
368     {
369       if (*p == '(')
370         break;
371       else if (*p == ' ' || *p == '\t')
372         p++;
373       else
374         return "(none)";
375     }
376   p++;
377
378   item_text = p;
379
380   /* File name ends just before the close-paren.  */
381   while (*p && *p != '\n' && *p != ')') p++;
382   if (*p != ')')
383     return "(none)";
384
385   return copy_string (item_text, p - item_text);
386 }
387
388
389 \f
390 /* Return FNAME with any [.info][.gz] suffix removed.  */
391
392 static char *
393 strip_info_suffix (char *fname)
394 {
395   char *ret = xstrdup (fname);
396   unsigned len = strlen (ret);
397
398   if (len > 3 && FILENAME_CMP (ret + len - 3, ".gz") == 0)
399     {
400       len -= 3;
401       ret[len] = 0;
402     }
403   else if (len > 4 && FILENAME_CMP (ret + len - 4, ".bz2") == 0)
404     {
405       len -= 4;
406       ret[len] = 0;
407     }
408   else if (len > 5 && FILENAME_CMP (ret + len - 5, ".lzma") == 0)
409    {
410       len -= 5;
411       ret[len] =0;
412    }
413
414   if (len > 5 && FILENAME_CMP (ret + len - 5, ".info") == 0)
415     {
416       len -= 5;
417       ret[len] = 0;
418     }
419   else if (len > 4 && FILENAME_CMP (ret + len - 4, ".inf") == 0)
420     {
421       len -= 4;
422       ret[len] = 0;
423     }
424 #ifdef __MSDOS__
425   else if (len > 4 && (FILENAME_CMP (ret + len - 4, ".inz") == 0
426                        || FILENAME_CMP (ret + len - 4, ".igz") == 0))
427     {
428       len -= 4;
429       ret[len] = 0;
430     }
431 #endif /* __MSDOS__ */
432
433   return ret;
434 }
435
436
437 /* Return true if ITEM matches NAME and is followed by TERM_CHAR.  ITEM
438    can also be followed by `.gz', `.info.gz', or `.info' (and then
439    TERM_CHAR) and still match.  */
440
441 static int
442 menu_item_equal (const char *item, char term_char, const char *name)
443 {
444   int ret;
445   const char *item_basename = item;
446   unsigned name_len = strlen (name);
447
448   /* We must compare the basename in ITEM, since we are passed the
449      basename of the original info file.  Otherwise, a new entry like
450      "lilypond/lilypond" won't match "lilypond".
451      
452      Actually, it seems to me that we should really compare the whole
453      name, and not just the basename.  Couldn't there be dir1/foo.info
454      and dir2/foo.info?  Also, it seems like we should be using the
455      filename from the new dir entries, not the filename on the command
456      line.  Not worrying about those things right now, though.  --karl,
457      26mar04.  */
458   if (!remove_exactly) {
459   while (*item_basename && !IS_SLASH (*item_basename)
460          && *item_basename != term_char)
461     item_basename++;
462   if (! *item_basename || *item_basename == term_char)
463     item_basename = item;  /* no /, use original */
464   else
465     item_basename++;       /* have /, move past it */
466   }
467     
468   /* First, ITEM must actually match NAME (usually it won't).  */
469   ret = mbsncasecmp (item_basename, name, name_len) == 0;
470   if (ret)
471     {
472       /* Then, `foobar' doesn't match `foo', so be sure we've got all of
473          ITEM.  The various suffixes should never actually appear in the
474          dir file, but sometimes people put them in.  */
475       static char *suffixes[]
476         = { "", ".info.gz", ".info", ".inf", ".gz",
477 #ifdef __MSDOS__
478             ".inz", ".igz",
479 #endif
480             NULL };
481       unsigned i;
482       ret = 0;
483       for (i = 0; !ret && suffixes[i]; i++)
484         {
485           char *suffix = suffixes[i];
486           unsigned suffix_len = strlen (suffix);
487           ret = mbsncasecmp (item_basename + name_len, suffix, suffix_len) == 0
488                 && item_basename[name_len + suffix_len] == term_char;
489         }
490     }
491
492   return ret;
493 }
494
495
496 \f
497 void
498 suggest_asking_for_help (void)
499 {
500   fprintf (stderr, _("\tTry `%s --help' for a complete list of options.\n"),
501            progname);
502   xexit (1);
503 }
504
505 void
506 print_help (void)
507 {
508   printf (_("Usage: %s [OPTION]... [INFO-FILE [DIR-FILE]]\n"), progname);
509   puts ("");
510   puts (_("Add or remove entries in INFO-FILE from the Info directory DIR-FILE."));
511   puts ("");
512
513   puts (_("\
514 Options:\n\
515  --debug             report what is being done.\n\
516  --delete            delete existing entries for INFO-FILE from DIR-FILE;\n\
517                       don't insert any new entries.\n\
518  --description=TEXT  the description of the entry is TEXT; used with\n\
519                       the --name option to become synonymous with the\n\
520                       --entry option.\n\
521  --dir-file=NAME     specify file name of Info directory file;\n\
522                       equivalent to using the DIR-FILE argument.\n\
523  --dry-run           same as --test."));
524
525   puts (_("\
526  --entry=TEXT        insert TEXT as an Info directory entry.\n\
527                       TEXT is written as an Info menu item line followed\n\
528                        by zero or more extra lines starting with whitespace.\n\
529                       If you specify more than one entry, all are added.\n\
530                       If you don't specify any entries, they are determined\n\
531                        from information in the Info file itself.\n\
532                       When removing, TEXT specifies the entry to remove.\n\
533                       TEXT is only removed as a last resort, if the\n\
534                       entry as determined from the Info file is not present,\n\
535                       and the basename of the Info file isn't found either."));
536
537   puts (_("\
538  --help              display this help and exit.\n\
539  --info-dir=DIR      same as --dir-file=DIR/dir.\n\
540  --info-file=FILE    specify Info file to install in the directory;\n\
541                       equivalent to using the INFO-FILE argument.\n\
542  --item=TEXT         same as --entry=TEXT.\n\
543  --keep-old          do not replace entries, or remove empty sections.\n\
544  --menuentry=TEXT    same as --name=TEXT.\n\
545  --name=TEXT         the name of the entry is TEXT; used with --description\n\
546                       to become synonymous with the --entry option.\n\
547  --no-indent         do not format new entries in the DIR file.\n\
548  --quiet             suppress warnings."));
549
550   puts (_("\
551  --regex=R           put this file's entries in all sections that match the\n\
552                       regular expression R (ignoring case).\n\
553  --remove            same as --delete.\n\
554  --remove-exactly    only remove if the info file name matches exactly;\n\
555                       suffixes such as .info and .gz are not ignored.\n\
556  --section=SEC       put entries in section SEC of the directory.\n\
557                       If you specify more than one section, all the entries\n\
558                        are added in each of the sections.\n\
559                       If you don't specify any sections, they are determined\n\
560                        from information in the Info file itself.\n\
561  --section R SEC     equivalent to --regex=R --section=SEC --add-once."));
562
563   puts (_("\
564  --silent            suppress warnings.\n\
565  --test              suppress updating of DIR-FILE.\n\
566  --version           display version information and exit."));
567
568   puts ("");
569   
570   puts (_("\
571 Email bug reports to bug-texinfo@gnu.org,\n\
572 general questions and discussion to help-texinfo@gnu.org.\n\
573 Texinfo home page: http://www.gnu.org/software/texinfo/"));
574 }
575
576 \f
577 /* If DIRFILE does not exist, and we are not in test mode, create a
578    minimal one (or abort).  If it already exists, do nothing.  */
579
580 static void
581 ensure_dirfile_exists (char *dirfile)
582 {
583   int desc;
584   
585   if (chicken_flag)
586     return;
587     
588   desc = open (dirfile, O_RDONLY);
589   if (desc < 0 && errno == ENOENT)
590     {
591       FILE *f;
592       char *readerr = strerror (errno);
593       close (desc);
594       f = fopen (dirfile, "w");
595       if (f)
596         {
597           fprintf (f, _("This is the file .../info/dir, which contains the\n\
598 topmost node of the Info hierarchy, called (dir)Top.\n\
599 The first time you invoke Info you start off looking at this node.\n\
600 \x1f\n\
601 %s\tThis is the top of the INFO tree\n\
602 \n\
603   This (the Directory node) gives a menu of major topics.\n\
604   Typing \"q\" exits, \"?\" lists all Info commands, \"d\" returns here,\n\
605   \"h\" gives a primer for first-timers,\n\
606   \"mEmacs<Return>\" visits the Emacs manual, etc.\n\
607 \n\
608   In Emacs, you can click mouse button 2 on a menu item or cross reference\n\
609   to select it.\n\
610 \n\
611 %s\n\
612 "), "File: dir,\tNode: Top",  /* These keywords must not be translated.  */
613     "* Menu:"
614 );
615           if (fclose (f) < 0)
616             pfatal_with_name (dirfile);
617         }
618       else
619         {
620           /* Didn't exist, but couldn't open for writing.  */
621           fprintf (stderr,
622                    _("%s: could not read (%s) and could not create (%s)\n"),
623                    dirfile, readerr, strerror (errno));
624           xexit (1);
625         }
626     }
627   else
628     close (desc); /* It already existed, so fine.  */
629 }
630 \f
631 /* Open FILENAME and return the resulting stream pointer.  If it doesn't
632    exist, try FILENAME.gz.  If that doesn't exist either, call
633    CREATE_CALLBACK (with FILENAME as arg) to create it, if that is
634    non-NULL.  If still no luck, fatal error.
635
636    If we do open it, return the actual name of the file opened in
637    OPENED_FILENAME and the compress program to use to (de)compress it in
638    COMPRESSION_PROGRAM.  The compression program is determined by the
639    magic number, not the filename.  */
640
641 FILE *
642 open_possibly_compressed_file (char *filename,
643     void (*create_callback) (char *),
644     char **opened_filename, char **compression_program, int *is_pipe) 
645 {
646   char *local_opened_filename, *local_compression_program;
647   int nread;
648   char data[13];
649   FILE *f;
650
651   /* We let them pass NULL if they don't want this info, but it's easier
652      to always determine it.  */
653   if (!opened_filename)
654     opened_filename = &local_opened_filename;
655
656   *opened_filename = filename;
657   f = fopen (*opened_filename, FOPEN_RBIN);
658   if (!f)
659     {
660       *opened_filename = concat (filename, ".gz", "");
661       f = fopen (*opened_filename, FOPEN_RBIN);
662   if (!f)
663     {
664       free (*opened_filename);
665       *opened_filename = concat (filename, ".bz2", "");
666       f = fopen (*opened_filename, FOPEN_RBIN);
667     }
668   if (!f)
669     {
670      free (*opened_filename);
671      *opened_filename = concat (filename, ".lzma", "");
672      f = fopen (*opened_filename, FOPEN_RBIN);
673     }
674
675 #ifdef __MSDOS__
676       if (!f)
677         {
678           free (*opened_filename);
679           *opened_filename = concat (filename, ".igz", "");
680           f = fopen (*opened_filename, FOPEN_RBIN);
681         }
682       if (!f)
683         {
684           free (*opened_filename);
685           *opened_filename = concat (filename, ".inz", "");
686           f = fopen (*opened_filename, FOPEN_RBIN);
687         }
688 #endif
689       if (!f)
690         {
691           if (create_callback)
692             { /* That didn't work either.  Create the file if we can.  */
693               (*create_callback) (filename);
694
695               /* And try opening it again.  */
696               free (*opened_filename);
697               *opened_filename = filename;
698               f = fopen (*opened_filename, FOPEN_RBIN);
699               if (!f)
700                 pfatal_with_name (filename);
701             }
702           else
703             pfatal_with_name (filename);
704         }
705     }
706
707   /* Read first few bytes of file rather than relying on the filename.
708      If the file is shorter than this it can't be usable anyway.  */
709   nread = fread (data, sizeof (data), 1, f);
710   if (nread != 1)
711     {
712       /* Empty files don't set errno, so we get something like
713          "install-info: No error for foo", which is confusing.  */
714       if (nread == 0)
715         fatal (_("%s: empty file"), *opened_filename, 0);
716       pfatal_with_name (*opened_filename);
717     }
718
719   if (!compression_program)
720     compression_program = &local_compression_program;
721
722   if (data[0] == '\x1f' && data[1] == '\x8b')
723 #if STRIP_DOT_EXE
724     /* An explicit .exe yields a better diagnostics from popen below
725        if they don't have gzip installed.  */
726     *compression_program = "gzip.exe";
727 #else
728     *compression_program = "gzip";
729 #endif
730   else if (data[0] == 'B' && data[1] == 'Z' && data[2] == 'h')
731 #ifndef STRIP_DOT_EXE
732     *compression_program = "bzip2.exe";
733 #else
734     *compression_program = "bzip2";
735 #endif
736   else if (data[0] == 'B' && data[1] == 'Z' && data[2] == '0')
737 #ifndef STRIP_DOT_EXE
738     *compression_program = "bzip.exe";
739 #else
740     *compression_program = "bzip";
741 #endif
742     /* We (try to) match against old lzma format (which lacks proper
743        header, two first matches), as well as the new format (last match).  */
744   else if ((data[9] == 0x00 && data[10] == 0x00 && data[11] == 0x00
745             && data[12] == 0x00)
746            || (data[5] == '\xFF' && data[6] == '\xFF' && data[7] == '\xFF'
747                && data[8] == '\xFF' && data[9] == '\xFF' && data[10] == '\xFF'
748                && data[11] == '\xFF' && data[12] == '\xFF') 
749            || (data[0] == '\xFF' && data[1] == 'L' && data[2] == 'Z'
750                && data[3] == 'M' && data[4] == 'A' && data[5] == 0x00))
751 #ifndef STRIP_DOT_EXE
752     *compression_program = "lzma.exe";
753 #else
754     *compression_program = "lzma";
755 #endif
756   else
757     *compression_program = NULL;
758
759   if (*compression_program)
760     { /* It's compressed, so fclose the file and then open a pipe.  */
761       char *command = concat (*compression_program," -cd <", *opened_filename);
762       if (fclose (f) < 0)
763         pfatal_with_name (*opened_filename);
764       f = popen (command, "r");
765       if (f)
766         *is_pipe = 1;
767       else
768         pfatal_with_name (command);
769     }
770   else
771     { /* It's a plain file, seek back over the magic bytes.  */
772       if (fseek (f, 0, 0) < 0)
773         pfatal_with_name (*opened_filename);
774 #if O_BINARY
775       /* Since this is a text file, and we opened it in binary mode,
776          switch back to text mode.  */
777       f = freopen (*opened_filename, "r", f);
778 #endif
779       *is_pipe = 0;
780     }
781
782   return f;
783 }
784 \f
785 /* Read all of file FILENAME into memory and return the address of the
786    data.  Store the size of the data into SIZEP.  If need be, uncompress
787    (i.e., try FILENAME.gz et al. if FILENAME does not exist) and store
788    the actual file name that was opened into OPENED_FILENAME (if it is
789    non-NULL), and the companion compression program (if any, else NULL)
790    into COMPRESSION_PROGRAM (if that is non-NULL).  If trouble, do
791    a fatal error.  */
792
793 char *
794 readfile (char *filename, int *sizep,
795     void (*create_callback) (char *), char **opened_filename,
796     char **compression_program)
797 {
798   char *real_name;
799   FILE *f;
800   int pipe_p;
801   int filled = 0;
802   int data_size = 8192;
803   char *data = xmalloc (data_size);
804
805   /* If they passed the space for the file name to return, use it.  */
806   f = open_possibly_compressed_file (filename, create_callback,
807                                      opened_filename ? opened_filename
808                                                      : &real_name,
809                                      compression_program, &pipe_p);
810
811   for (;;)
812     {
813       int nread = fread (data + filled, 1, data_size - filled, f);
814       if (nread < 0)
815         pfatal_with_name (real_name);
816       if (nread == 0)
817         break;
818
819       filled += nread;
820       if (filled == data_size)
821         {
822           data_size += 65536;
823           data = xrealloc (data, data_size);
824         }
825     }
826
827   /* We'll end up wasting space if we're not passing the filename back
828      and it is not just FILENAME, but so what.  */
829   /* We need to close the stream, since on some systems the pipe created
830      by popen is simulated by a temporary file which only gets removed
831      inside pclose.  */
832   if (pipe_p)
833     pclose (f);
834   else
835     fclose (f);
836
837   *sizep = filled;
838   return data;
839 }
840 \f
841 /* Output the old dir file, interpolating the new sections
842    and/or new entries where appropriate.  If COMPRESSION_PROGRAM is not
843    null, pipe to it to create DIRFILE.  Thus if we read dir.gz on input,
844    we'll write dir.gz on output.  */
845
846 static void
847 output_dirfile (char *dirfile, int dir_nlines, struct line_data *dir_lines,
848                 int n_entries_to_add, struct spec_entry *entries_to_add,
849                 struct spec_section *input_sections, char *compression_program)
850 {
851   int n_entries_added = 0;
852   int i;
853   FILE *output;
854
855   if (compression_program)
856     {
857       char *command = concat (compression_program, ">", dirfile);
858       output = popen (command, "w");
859     }
860   else
861     output = fopen (dirfile, "w");
862
863   if (!output)
864     {
865       perror (dirfile);
866       xexit (1);
867     }
868
869   for (i = 0; i <= dir_nlines; i++)
870     {
871       int j;
872
873       /* If we decided to output some new entries before this line,
874          output them now.  */
875       if (dir_lines[i].add_entries_before)
876         for (j = 0; j < n_entries_to_add; j++)
877           {
878             struct spec_entry *this = dir_lines[i].add_entries_before[j];
879             if (this == 0)
880               break;
881             if (n_entries_added >= 1 && 
882                 !add_entries_into_all_matching_sections)
883               break;
884             fputs (this->text, output);
885             n_entries_added++;
886           }
887       /* If we decided to add some sections here
888          because there are no such sections in the file,
889          output them now.  
890          FIXME:  we add all sections here, but they should
891          be interspersed throughout the DIR file in 
892          alphabetic order. */
893       if (dir_lines[i].add_sections_before)
894         {
895           struct spec_section *spec;
896           struct spec_entry *entry;
897           struct spec_entry **entries;
898           int n_entries = 0;
899
900           /* If we specified --add-once, and we've added an entry, then
901              it's time to bail. */
902           if (n_entries_added >= 1 && 
903               !add_entries_into_all_matching_sections)
904             break;
905
906           qsort (dir_lines[i].add_sections_before, 
907                  dir_lines[i].num_sections_to_add, 
908                  sizeof (struct spec_section *), compare_section_names);
909
910           /* Count the entries and allocate a vector for all of them.  */
911           for (entry = entries_to_add; entry; entry = entry->next)
912             n_entries++;
913           entries = ((struct spec_entry **)
914                      xmalloc (n_entries * sizeof (struct spec_entry *)));
915
916           /* Fill the vector ENTRIES with pointers to all the sections,
917              and sort them.  */
918           j = 0;
919           for (entry = entries_to_add; entry; entry = entry->next)
920             entries[j++] = entry;
921           qsort (entries, n_entries, sizeof (struct spec_entry *),
922                  compare_entries_text);
923
924           /* Generate the new sections in alphabetical order.  In each
925              new section, output all of the entries that belong to that
926              section, in alphabetical order.  */
927           for (j = 0; j < dir_lines[i].num_sections_to_add; j++)
928             {
929               spec = dir_lines[i].add_sections_before[j];
930               if (spec->missing)
931                 {
932                   int k;
933
934                   putc ('\n', output);
935                   fputs (spec->name, output);
936                   putc ('\n', output);
937                   spec->missing = 0;
938                   for (k = 0; k < n_entries; k++)
939                     {
940                       struct spec_section *spec1;
941                       /* Did they at all want this entry to be put into
942                          this section?  */
943                       entry = entries[k];
944                       for (spec1 = entry->entry_sections;
945                            spec1 && spec1 != entry->entry_sections_tail;
946                            spec1 = spec1->next)
947                         {
948                           if (!strcmp (spec1->name, spec->name))
949                             break;
950                         }
951                       if (spec1 && spec1 != entry->entry_sections_tail)
952                         fputs (entry->text, output);
953                     }
954                 }
955             }
956
957           n_entries_added++;
958           free (entries);
959         }
960
961       /* Output the original dir lines unless marked for deletion.  */
962       if (i < dir_nlines && !dir_lines[i].delete)
963         {
964           fwrite (dir_lines[i].start, 1, dir_lines[i].size, output);
965           putc ('\n', output);
966         }
967     }
968
969   /* Some systems, such as MS-DOS, simulate pipes with temporary files.
970      On those systems, the compressor actually gets run inside pclose,
971      so we must call pclose.  */
972   if (compression_program)
973     pclose (output);
974   else
975     fclose (output);
976 }
977 \f
978 /* Parse the input to find the section names and the entry names it
979    specifies.  Return the number of entries to add from this file.  */
980 int
981 parse_input (const struct line_data *lines, int nlines,
982              struct spec_section **sections, struct spec_entry **entries,
983              int delete_flag) 
984 {
985   int n_entries = 0;
986   int prefix_length = strlen ("INFO-DIR-SECTION ");
987   struct spec_section *head = *sections, *tail = NULL;
988   int reset_tail = 0;
989   char *start_of_this_entry = 0;
990   int ignore_sections = *sections != 0;
991   int ignore_entries  = delete_flag ? 0: *entries  != 0;
992
993   int i;
994
995   if (ignore_sections && ignore_entries)
996     return 0;
997
998   /* Loop here processing lines from the input file.  Each
999      INFO-DIR-SECTION entry is added to the SECTIONS linked list.
1000      Each START-INFO-DIR-ENTRY block is added to the ENTRIES linked
1001      list, and all its entries inherit the chain of SECTION entries
1002      defined by the last group of INFO-DIR-SECTION entries we have
1003      seen until that point.  */
1004   for (i = 0; i < nlines; i++)
1005     {
1006       if (!ignore_sections
1007           && !strncmp ("INFO-DIR-SECTION ", lines[i].start, prefix_length))
1008         {
1009           struct spec_section *next
1010             = (struct spec_section *) xmalloc (sizeof (struct spec_section));
1011           next->name = copy_string (lines[i].start + prefix_length,
1012                                     lines[i].size - prefix_length);
1013           next->next = *sections;
1014           next->missing = 1;
1015           if (reset_tail)
1016             {
1017               tail = *sections;
1018               reset_tail = 0;
1019             }
1020           *sections = next;
1021           head = *sections;
1022         }
1023       /* If entries were specified explicitly with command options,
1024          ignore the entries in the input file.  */
1025       else if (!ignore_entries)
1026         {
1027           if (!strncmp ("START-INFO-DIR-ENTRY", lines[i].start, lines[i].size)
1028               && sizeof ("START-INFO-DIR-ENTRY") - 1 == lines[i].size)
1029             {
1030               if (!*sections)
1031                 {
1032                   /* We found an entry, but didn't yet see any sections
1033                      specified.  Default to section "Miscellaneous".  */
1034                   *sections = (struct spec_section *)
1035                     xmalloc (sizeof (struct spec_section));
1036                   (*sections)->name = "Miscellaneous";
1037                   (*sections)->next = 0;
1038                   (*sections)->missing = 1;
1039                   head = *sections;
1040                 }
1041               /* Next time we see INFO-DIR-SECTION, we will reset the
1042                  tail pointer.  */
1043               reset_tail = 1;
1044
1045               if (start_of_this_entry != 0)
1046                 fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"), 0, 0);
1047               start_of_this_entry = lines[i + 1].start;
1048             }
1049           else if (start_of_this_entry)
1050             {
1051               if ((!strncmp ("* ", lines[i].start, 2)
1052                    && lines[i].start > start_of_this_entry)
1053                   || (!strncmp ("END-INFO-DIR-ENTRY",
1054                                 lines[i].start, lines[i].size)
1055                       && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size))
1056                 {
1057                   /* We found an end of this entry.  Allocate another
1058                      entry, fill its data, and add it to the linked
1059                      list.  */
1060                   struct spec_entry *next
1061                     = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
1062                   next->text
1063                     = copy_string (start_of_this_entry,
1064                                    lines[i].start - start_of_this_entry);
1065                   next->text_len = lines[i].start - start_of_this_entry;
1066                   next->entry_sections = head;
1067                   next->entry_sections_tail = tail;
1068                   next->next = *entries;
1069                   *entries = next;
1070                   n_entries++;
1071                   if (!strncmp ("END-INFO-DIR-ENTRY",
1072                                 lines[i].start, lines[i].size)
1073                       && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size)
1074                     start_of_this_entry = 0;
1075                   else
1076                     start_of_this_entry = lines[i].start;
1077                 }
1078               else if (!strncmp ("END-INFO-DIR-ENTRY",
1079                                  lines[i].start, lines[i].size)
1080                        && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size)
1081                 fatal (_("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY"), 0, 0);
1082             }
1083         }
1084     }
1085   if (start_of_this_entry != 0)
1086     fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"),
1087            0, 0);
1088
1089   /* If we ignored the INFO-DIR-ENTRY directives, we need now go back
1090      and plug the names of all the sections we found into every
1091      element of the ENTRIES list.  */
1092   if (ignore_entries && *entries)
1093     {
1094       struct spec_entry *entry;
1095
1096       for (entry = *entries; entry; entry = entry->next)
1097         {
1098           entry->entry_sections = head;
1099           entry->entry_sections_tail = tail;
1100         }
1101     }
1102
1103   return n_entries;
1104 }
1105
1106 \f
1107 /* Parse the dir file whose basename is BASE_NAME.  Find all the
1108    nodes, and their menus, and the sections of their menus.  */
1109 static void
1110 parse_dir_file (struct line_data *lines, int nlines, struct node **nodes)
1111 {
1112   int node_header_flag = 0;
1113   int i;
1114
1115   *nodes = 0;
1116   for (i = 0; i < nlines; i++)
1117     {
1118       /* Parse node header lines.  */
1119       if (node_header_flag)
1120         {
1121           int j, end;
1122           for (j = 0; j < lines[i].size; j++)
1123             /* Find the node name and store it in the `struct node'.  */
1124             if (!strncmp ("Node:", lines[i].start + j, 5))
1125               {
1126                 char *line = lines[i].start;
1127                 /* Find the start of the node name.  */
1128                 j += 5;
1129                 while (line[j] == ' ' || line[j] == '\t')
1130                   j++;
1131                 /* Find the end of the node name.  */
1132                 end = j;
1133                 while (line[end] != 0 && line[end] != ',' && line[end] != '\n'
1134                        && line[end] != '\t')
1135                   end++;
1136                 (*nodes)->name = copy_string (line + j, end - j);
1137               }
1138           node_header_flag = 0;
1139         }
1140
1141       /* Notice the start of a node.  */
1142       if (*lines[i].start == 037)
1143         {
1144           struct node *next = (struct node *) xmalloc (sizeof (struct node));
1145
1146           next->next = *nodes;
1147           next->name = NULL;
1148           next->start_line = i;
1149           next->end_line = 0;
1150           next->menu_start = NULL;
1151           next->sections = NULL;
1152           next->last_section = NULL;
1153
1154           if (*nodes != 0)
1155             (*nodes)->end_line = i;
1156           /* Fill in the end of the last menu section
1157              of the previous node.  */
1158           if (*nodes != 0 && (*nodes)->last_section != 0)
1159             (*nodes)->last_section->end_line = i;
1160
1161           *nodes = next;
1162
1163           /* The following line is the header of this node;
1164              parse it.  */
1165           node_header_flag = 1;
1166         }
1167
1168       /* Notice the lines that start menus.  */
1169       if (*nodes != 0 && !strncmp ("* Menu:", lines[i].start, 7))
1170         (*nodes)->menu_start = lines[i + 1].start;
1171
1172       /* Notice sections in menus.  */
1173       if (*nodes != 0
1174           && (*nodes)->menu_start != 0
1175           && *lines[i].start != '\n'
1176           && *lines[i].start != '*'
1177           && *lines[i].start != ' '
1178           && *lines[i].start != '\t')
1179         {
1180           /* Add this menu section to the node's list.
1181              This list grows in forward order.  */
1182           struct menu_section *next
1183             = (struct menu_section *) xmalloc (sizeof (struct menu_section));
1184
1185           next->start_line = i + 1;
1186           next->next = 0;
1187           next->end_line = 0;
1188           next->name = copy_string (lines[i].start, lines[i].size);
1189           if ((*nodes)->sections)
1190             {
1191               (*nodes)->last_section->next = next;
1192               (*nodes)->last_section->end_line = i;
1193             }
1194           else
1195             (*nodes)->sections = next;
1196           (*nodes)->last_section = next;
1197         }
1198
1199     }
1200
1201   /* Finish the info about the end of the last node.  */
1202   if (*nodes != 0)
1203     {
1204       (*nodes)->end_line = nlines;
1205       if ((*nodes)->last_section != 0)
1206         (*nodes)->last_section->end_line = nlines;
1207     }
1208 }
1209
1210 \f
1211 /* Iterate through NLINES LINES looking for an entry that has a name
1212    that matches NAME.  If such an entry is found, flag the entry for 
1213    deletion later on. */
1214
1215 int
1216 mark_entry_for_deletion (struct line_data *lines, int nlines, char *name)
1217 {
1218   int something_deleted = 0;
1219   int i;
1220   for (i = 0; i < nlines; i++)
1221     {
1222       /* Check for an existing entry that should be deleted.
1223          Delete all entries which specify this file name.  */
1224       if (*lines[i].start == '*')
1225         {
1226           char *q;
1227           char *p = lines[i].start;
1228
1229           p++; /* skip * */
1230           while (*p == ' ') p++; /* ignore following spaces */
1231           q = p; /* remember this, it's the beginning of the menu item.  */
1232
1233           /* Read menu item.  */
1234           while (*p != 0 && *p != ':')
1235             p++;
1236           p++; /* skip : */
1237
1238           if (*p == ':')
1239             { /* XEmacs-style entry, as in * Mew::Messaging.  */
1240               if (menu_item_equal (q, ':', name))
1241                 {
1242                   lines[i].delete = 1;
1243                   something_deleted = 1;
1244                 }
1245             }
1246           else
1247             { /* Emacs-style entry, as in * Emacs: (emacs).  */
1248               while (*p == ' ') p++; /* skip spaces after : */
1249               if (*p == '(')         /* if at parenthesized (FILENAME) */
1250                 {
1251                   p++;
1252                   if (menu_item_equal (p, ')', name))
1253                     {
1254                       lines[i].delete = 1;
1255                       something_deleted = 1;
1256                     }
1257                 }
1258             }
1259         }
1260
1261       /* Treat lines that start with whitespace
1262          as continuations; if we are deleting an entry,
1263          delete all its continuations as well.  */
1264       else if (i > 0 && (*lines[i].start == ' ' || *lines[i].start == '\t'))
1265         {
1266           lines[i].delete = lines[i - 1].delete;
1267         }
1268     }
1269   return something_deleted;
1270 }
1271
1272 \f
1273 /* Assuming the current column is COLUMN, return the column that
1274    printing C will move the cursor to.
1275    The first column is 0.
1276    This function is used to assist in indenting of entries. */
1277
1278 static size_t
1279 adjust_column (size_t column, char c)
1280 {
1281   if (c == '\b')
1282     {
1283       if (column > 0)
1284         column--;
1285     }
1286   else if (c == '\r')
1287     column = 0;
1288   else if (c == '\t')
1289     column += TAB_WIDTH - column % TAB_WIDTH;
1290   else                          /* if (isprint (c)) */
1291     column++;
1292   return column;
1293 }
1294
1295 /* Indent the Info entry's NAME and DESCRIPTION.  Lines are wrapped at the
1296    WIDTH column.  The description on first line is indented at the CALIGN-th 
1297    column, and all subsequent lines are indented at the ALIGN-th column.  
1298    The resulting Info entry is put into OUTSTR.
1299    NAME is of the form "* TEXT (TEXT)[:TEXT].".
1300  */
1301 static int
1302 format_entry (char *name, size_t name_len, char *desc, size_t desc_len, 
1303               int calign, int align, size_t width, 
1304               char **outstr, size_t *outstr_len)
1305 {
1306   int i, j;
1307   char c;
1308   size_t column = 0;            /* Screen column where next char will go */
1309   size_t offset_out = 0;        /* Index in `line_out' for next char. */
1310   static char *line_out = NULL;
1311   static size_t allocated_out = 0;
1312   int saved_errno;
1313   if (!desc || !name)
1314     return 1;
1315
1316   *outstr = malloc (width  + 
1317                     (((desc_len  + width) / (width - align)) * width) * 2 
1318                     * sizeof (char));
1319   *outstr[0] = '\0';
1320
1321   strncat (*outstr, name, name_len);
1322
1323   column = name_len;
1324
1325   if (name_len > calign - 2)
1326     {
1327       /* Name is too long to have description on the same line. */
1328       if (desc_len > 1)
1329         {
1330           strncat (*outstr, "\n", 1);
1331           column = 0;
1332           for (j = 0; j < calign - 1; j++)
1333             {
1334               column = adjust_column (column, ' ');
1335               strncat (*outstr, " ", 1);
1336             }
1337         }
1338     }
1339   else
1340     for (j = 0; j < calign - name_len - 1; j++)
1341       {
1342         if (desc_len <= 2)
1343           break;
1344         column = adjust_column (column, ' ');
1345         strncat (*outstr, " ", 1);
1346       }
1347
1348   for (i = 0; i < desc_len; i++)
1349     {
1350       if (desc_len <= 2)
1351         break;
1352       c = desc[i];
1353       if (offset_out + 1 >= allocated_out)
1354         {
1355           allocated_out = offset_out + 1;
1356           line_out = (char *) realloc ((void *)line_out, allocated_out);
1357         }
1358
1359       if (c == '\n')
1360         {
1361           line_out[offset_out++] = c;
1362           strncat (*outstr, line_out, offset_out);
1363           column = offset_out = 0;
1364           continue;
1365         }
1366
1367     rescan:
1368       column = adjust_column (column, c);
1369
1370       if (column > width)
1371         {
1372           /* This character would make the line too long.
1373              Print the line plus a newline, and make this character
1374              start the next line. */
1375
1376           int found_blank = 0;
1377           size_t logical_end = offset_out;
1378
1379           /* Look for the last blank. */
1380           while (logical_end)
1381             {
1382               --logical_end;
1383               if (line_out[logical_end] == ' '
1384                   || line_out[logical_end] == '\t')
1385                 {
1386                   found_blank = 1;
1387                   break;
1388                 }
1389             }
1390
1391           if (found_blank)
1392             {
1393               size_t i;
1394
1395               /* Found a blank.  Don't output the part after it. */
1396               logical_end++;
1397               strncat (*outstr, line_out, logical_end);
1398               strncat (*outstr, "\n", 1);
1399               for (j = 0; j < align - 1; j++)
1400                 {
1401                   column = adjust_column (column, ' ');
1402                   strncat (*outstr, " ", 1);
1403                 }
1404
1405               /* Move the remainder to the beginning of the next 
1406                  line.
1407                  The areas being copied here might overlap. */
1408               memmove (line_out, line_out + logical_end,
1409                        offset_out - logical_end);
1410               offset_out -= logical_end;
1411               for (column = i = 0; i < offset_out; i++)
1412                 column = adjust_column (column, line_out[i]);
1413               goto rescan;
1414             }
1415
1416           if (offset_out == 0)
1417             {
1418               line_out[offset_out++] = c;
1419               continue;
1420             }
1421
1422           line_out[offset_out++] = '\n';
1423           strncat (*outstr, line_out, offset_out);
1424           column = offset_out = 0;
1425           goto rescan;
1426         }
1427
1428       line_out[offset_out++] = c;
1429     }
1430
1431   saved_errno = errno;
1432
1433   if (desc_len <= 2)
1434     strncat (*outstr, "\n", 1);
1435
1436   if (offset_out)
1437     strncat (*outstr, line_out, offset_out);
1438
1439   *outstr_len = strlen (*outstr);
1440   return 1;
1441 }
1442
1443 \f
1444 /* Extract the NAME and DESCRIPTION from ENTRY.  NAME and DESCRIPTION must be
1445    free'd.
1446  */
1447 static void
1448 split_entry (const char *entry, char **name, size_t *name_len,
1449              char **description, size_t *description_len)
1450 {
1451   char *endptr;
1452
1453   /* on the first line, the description starts after the first ". ";
1454      that's a period and space -- our heuristic to handle item names like
1455      "config.status", and node names like "config.status Invocation".
1456      Also accept period-tab and period-newline.  */
1457   char *ptr = strchr (entry, '.');
1458   while (ptr && ptr[1] != ' ' && ptr[1] != '\t' && ptr[1] != '\n') {
1459     ptr = strchr (ptr + 1, '.');
1460   }
1461   
1462   /* Maybe there's no period, and no description */
1463   if (!ptr)
1464     {
1465       size_t length = strlen (entry);
1466       if (length == 0)
1467         return;
1468       *name = strdup (entry);
1469       *name_len = length + 1;
1470       return;
1471     }
1472
1473   /* The name is everything up to and including the period. */
1474   *name_len = (size_t) (ptr - entry + 1);
1475   *name = xmalloc (*name_len + 1);
1476   (*name)[0] = '\0';
1477   strncat (*name, entry, *name_len);
1478
1479   ptr++;
1480   *description = xmalloc (strlen (entry));
1481   (*description)[0] = '\0';
1482
1483   while (ptr[0] != '\0')
1484     {
1485       /* Eat up the whitespace after the name, and at the start of a line. */
1486       while (isspace(ptr[0]))
1487         ptr++;
1488
1489       /* Okay, we're at the start of the description. */
1490       if (ptr[0] == '\0')
1491         continue;
1492
1493       /* See how far the description goes... */
1494       endptr = strchr (ptr, '\n');
1495       /* Either the description continues up to the next newline. */
1496       if (endptr)
1497         {
1498           size_t length  = (size_t) (endptr - ptr) / sizeof (char);
1499           strncat (*description, ptr, length);
1500           ptr = endptr;
1501           /* First of all, we eat the newline here.  But then what?
1502              Sometimes the newline separates 2 sentences, so we
1503              end up with the next word starting directly after the period,
1504              instead of after the customary 2 spaces in english. 
1505              If the previous character was a `.', then we should add 2
1506              spaces if there is anything on the next line.
1507              if it's a comma, then we should put one space.
1508              If it's neither, we just put a space.
1509              If it's some other whitespace, we shouldn't do anything. */
1510           ptr++;
1511           if (length > 1 && strlen (ptr) > 0)
1512             {
1513               endptr--;
1514               /* *ENDPTR is the 2nd last character */
1515               if (*endptr == '.')
1516                 strncat (*description, "  ", 2);
1517               else if (!isspace (*endptr))
1518                 strncat (*description, " ", 1);
1519             }
1520         }
1521       /* Or the description continues to the end of the string. */
1522       else
1523         {
1524           /* Just show the rest when there's no newline. */
1525           size_t length = strlen (ptr);
1526           strncat (*description, ptr, length);
1527           ptr += length;
1528         }
1529     }
1530   /* Descriptions end in a new line. */
1531   strncat (*description, "\n", 1);
1532   *description_len = strlen (*description);
1533 }
1534
1535 \f
1536 /* Indent all ENTRIES according to some formatting options. 
1537    CALIGN_CLI is the starting column for the first line of the description.
1538    ALIGN_CLI is the starting column for all subsequent lines of the 
1539    description.  MAXWIDTH_CLI is the number of columns in the line. 
1540    When CALIGN_CLI, ALIGN_CLI, or MAXWIDTH_CLI is -1, choose a sane default. */
1541
1542 static void
1543 reformat_new_entries (struct spec_entry *entries, int calign_cli, int align_cli, 
1544                       int maxwidth_cli)
1545 {
1546   struct spec_entry *entry;
1547   for (entry = entries; entry; entry = entry->next)
1548     {
1549       int calign = -1, align = -1, maxwidth = -1;
1550       char *name = NULL, *desc = NULL;
1551       size_t name_len = 0, desc_len = 0;
1552       split_entry (entry->text, &name, &name_len, &desc, &desc_len);
1553       free (entry->text);
1554
1555       /* Specify sane defaults if we need to */
1556       if (calign_cli == -1 || align_cli == -1)
1557         {
1558           struct spec_section *section;
1559           calign = calign_cli;
1560           align = align_cli;
1561           for (section = entry->entry_sections; 
1562                section && section != entry->entry_sections_tail;
1563                section = section->next)
1564             {
1565               if (!strcmp (section->name, "Individual utilities"))
1566                 {
1567                   if (calign == -1)
1568                     calign = 48 + 1;
1569                   if (align == -1)
1570                     align = 50 + 1;
1571                   break;
1572                 }
1573             }
1574           if (calign == -1)
1575             calign = 32 + 1;
1576           if (align == -1)
1577             align = 34 + 1;
1578         }
1579       else
1580         {
1581           calign = calign_cli;
1582           align = align_cli;
1583         }
1584
1585       if (maxwidth_cli == -1)
1586         maxwidth = 79;
1587
1588       format_entry (name, name_len, desc, desc_len, calign, align, 
1589                     maxwidth, &entry->text, &entry->text_len);
1590     }
1591 }
1592
1593 /* Insert NAME into every entry in ENTRIES that requires it. 
1594    NAME is the basename of the Info file being installed. 
1595    The idea here is that there was a --name on the command-line
1596    and we need to put the basename in the empty parentheses. */
1597 void
1598 add_missing_basenames (struct spec_entry *entries, char *name)
1599 {
1600   struct spec_entry *entry;
1601   for (entry = entries; entry; entry = entry->next)
1602     {
1603       if (entry->missing_basename)
1604         {
1605           /* Insert NAME into the right place in ENTRY->TEXT. */
1606           char *info, *rest, *text;
1607           size_t name_len = strlen (name);
1608           char *ptr = strstr (entry->text, ": (). ");
1609           if (!ptr)
1610             return;
1611           ptr[0] = '\0';
1612           rest = ptr += strlen (": (). ");
1613
1614           info = xmalloc (name_len + 7);
1615           snprintf (info, name_len + 7, ": (%s). ", name);
1616           text = concat (entry->text, info, rest);
1617           free (info);
1618           if (entry->text)
1619             free (entry->text);
1620           entry->text = text;
1621           entry->text_len = strlen (entry->text);
1622           entry->missing_name = 0;
1623           entry->missing_basename = 0;
1624         }
1625     }
1626 }
1627
1628 \f
1629 /* Add NAME to the start of any entry in ENTRIES that is missing a name 
1630    component.  If NAME doesn't start with `*', it is formatted to look 
1631    like an Info entry.  */
1632 void
1633 add_missing_names (struct spec_entry *entries, char *name)
1634 {
1635   struct spec_entry *entry;
1636   for (entry = entries; entry; entry = entry->next)
1637     {
1638       if (entry->missing_name)
1639         {
1640           char *text;
1641           /* Prepend NAME onto ENTRY->TEXT. */
1642           int add_nl = 1;
1643           if (entry->text)
1644             if (entry->text[entry->text_len - 1] == '\n')
1645               add_nl = 0;
1646
1647           if (name[0] == '*')
1648             text = concat (name, entry->text == NULL ? "" : entry->text, 
1649                            add_nl ? "\n" : "");
1650           else
1651             {
1652               size_t full_name_len = strlen (name) * 2 + 9;
1653               char *full_name = xmalloc (full_name_len);
1654               snprintf (full_name, full_name_len, "* %s: (%s).", name, name);
1655               text = concat (full_name, 
1656                              entry->text == NULL ? "" : entry->text, 
1657                              add_nl ? "\n" : "");
1658               free (full_name);
1659             }
1660           if (entry->text)
1661             free (entry->text);
1662           entry->text = text;
1663           entry->text_len = strlen (entry->text);
1664           entry->missing_name = 0;
1665           entry->missing_basename = 0;
1666         }
1667     }
1668 }
1669
1670 /* Append DESC to every entry in ENTRIES that needs it. */
1671
1672 void
1673 add_missing_descriptions (struct spec_entry *entries, char *desc)
1674 {
1675   struct spec_entry *entry;
1676   for (entry = entries; entry; entry = entry->next)
1677     {
1678       if (entry->missing_description)
1679         {
1680           char *text;
1681           int add_nl = 1;
1682           if (strlen (desc) > 1)
1683             if (desc[strlen (desc) - 1] == '\n')
1684               add_nl = 0;
1685           /* Append DESC onto ENTRY->TEXT. */
1686           text = concat (entry->text == NULL ? "" : entry->text, desc,
1687                                add_nl ? "\n" : "");
1688           if (entry->text)
1689             free (entry->text);
1690           entry->text = text;
1691           entry->text_len = strlen (entry->text);
1692         }
1693     }
1694 }
1695
1696 \f
1697 /* Detect old-style Debian `--section REGEX TITLE' semantics in ARGV.
1698    When detected the options are munged to look like:
1699      `--regex REGEX --section TITLE --add-once'
1700    Return 1 if munging took place, return 0 if not.
1701    Otherwise return a negative number if something went wrong.
1702    NEW_ARGC, and NEW_ARGV are filled with the newly munged options
1703    when munging took place.
1704  */
1705 static int
1706 munge_old_style_debian_options (int argc, char **argv, 
1707                                 int *new_argc, char ***new_argv)
1708 {
1709   char *opt = NULL;
1710   int i, err;
1711   char *argz = NULL;
1712   size_t argz_len = 0;
1713   const char *regex, *title;
1714   int munge = 0;
1715
1716   /* Flip through the options to detect the old `--section REGEX TITLE' 
1717      syntax */
1718   for (i = 0; i < argc; i++)
1719     {
1720       if (strcmp (argv[i], "--section") == 0)
1721         {
1722           FILE *fileptr;
1723           /* Go forward one arg and obtain the REGEX. */
1724           if (i + 1 < argc)
1725             i++;
1726           else
1727             return -1;
1728           regex = argv[i];
1729           /* Go forward another arg and obtain the TITLE. */
1730           if (i + 1 < argc)
1731             i++;
1732           else
1733             return -1;
1734           title = argv[i];
1735           /* When the title starts with a `-' it's probably an option,
1736              and not a title. */
1737           if (title[0] == '-')
1738             break;
1739           /* When the title is a filename it's probably an Info file, or
1740              a dir file, and not a title. */
1741           fileptr = fopen (title, "r");
1742           if (fileptr)
1743             {
1744               fclose (fileptr);
1745               break;
1746             }
1747           /* Okay, it looks like we're using the old debian syntax 
1748              for --section. */
1749           munge = 1;
1750         
1751           /* Okay, we munge the options to look like this:
1752              --regex=REGEX --section=TITLE --add-once */
1753           opt = xmalloc (strlen (regex) + sizeof ("--regex="));
1754           if (sprintf (opt, "--regex=%s", regex) == -1)
1755             err = 1;
1756           if (!err)
1757             err = argz_add (&argz, &argz_len, opt);
1758           free (opt); opt = NULL;
1759
1760           opt = xmalloc (strlen (regex) + sizeof ("--section="));
1761           if (sprintf (opt, "--section=%s", title) == -1)
1762             err = 1;
1763           if (!err)
1764             err = argz_add (&argz, &argz_len, opt);
1765           free (opt); opt = NULL;
1766
1767           if (!err)
1768             err = argz_add (&argz, &argz_len, "--add-once");
1769         }
1770       else
1771         err = argz_add (&argz, &argz_len, argv[i]); 
1772       if (err)
1773         return -1;
1774     }
1775
1776   if (munge)
1777     {
1778       *new_argc = argz_count (argz, argz_len);
1779       *new_argv = xmalloc ((*new_argc + 1) * sizeof (char *));
1780
1781       opt = NULL; i = 0;
1782       while ((opt = argz_next (argz, argz_len, opt)))
1783         {
1784           (*new_argv)[i] = xstrdup (opt);
1785           i++;
1786         }
1787       (*new_argv)[*new_argc] = NULL;
1788     }
1789   free (argz);
1790   return munge;
1791 }
1792 \f
1793
1794 int
1795 main (int argc, char *argv[])
1796 {
1797   char *opened_dirfilename;
1798   char *compression_program;
1799   char *infile_sans_info;
1800   char *infile = 0, *dirfile = 0;
1801   int calign = -1;
1802   int align  = -1;
1803   int maxwidth = -1;
1804
1805   /* Record the text of the Info file, as a sequence of characters
1806      and as a sequence of lines.  */
1807   char *input_data = NULL;
1808   int input_size = 0;
1809   struct line_data *input_lines = NULL;
1810   int input_nlines = 0;
1811
1812   /* Record here the specified section names and directory entries.  */
1813   struct spec_section *input_sections = NULL;
1814   struct spec_entry *entries_to_add = NULL;
1815   struct spec_entry *entries_to_add_from_file = NULL;
1816   int n_entries_to_add = 0;
1817
1818   /* Record the old text of the dir file, as plain characters,
1819      as lines, and as nodes.  */
1820   char *dir_data;
1821   int dir_size;
1822   int dir_nlines;
1823   struct line_data *dir_lines;
1824   struct node *dir_nodes;
1825
1826   /* Nonzero means --delete was specified (just delete existing entries).  */
1827   int delete_flag = 0;
1828   int something_deleted = 0;
1829
1830   /* Nonzero means -quiet/--silent was specified.  */
1831   int quiet_flag = 0;
1832
1833   /* Nonzero means --debug was specified.  */
1834   int debug_flag = 0;
1835
1836   int i;
1837
1838 #ifdef HAVE_SETLOCALE
1839   /* Set locale via LC_ALL.  */
1840   setlocale (LC_ALL, "");
1841 #endif
1842
1843   /* Set the text message domain.  */
1844   bindtextdomain (PACKAGE, LOCALEDIR);
1845   textdomain (PACKAGE);
1846
1847   munge_old_style_debian_options (argc, argv, &argc, &argv);
1848
1849   while (1)
1850     {
1851       int opt = getopt_long (argc, argv, 
1852                              "i:d:e:s:t:E:c:C:W:A:hHrk1Ia", longopts, 0);
1853
1854       if (opt == EOF)
1855         break;
1856
1857       switch (opt)
1858         {
1859         case 0:
1860           /* If getopt returns 0, then it has already processed a
1861              long-named option.  We should do nothing.  */
1862           break;
1863
1864         case 1:
1865           abort ();
1866
1867         case '1':
1868           add_entries_into_all_matching_sections = 0;
1869           break;
1870
1871         case 'a':
1872           order_new_sections_alphabetically_flag = 0;
1873           break;
1874
1875         case 'A':
1876           {
1877             char *end = NULL;
1878             unsigned long int val;
1879             val = strtoul (optarg, &end, 0);
1880             if (end == NULL || end == optarg || *end != '\0')
1881               suggest_asking_for_help ();
1882             align = val;
1883             if (align <= 0)
1884               suggest_asking_for_help ();
1885           }
1886           break;
1887
1888         case 'c':
1889         
1890           {
1891             struct spec_entry *next;
1892             size_t length = strlen (optarg);
1893
1894             if (!entries_to_add)
1895               {
1896                 next = 
1897                   (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
1898             
1899                 next->text = NULL;
1900                 next->text_len = 0;
1901                 next->entry_sections = NULL;
1902                 next->entry_sections_tail = NULL;
1903                 next->missing_name = 1;
1904                 next->missing_basename = 1;
1905                 next->next = entries_to_add;
1906                 entries_to_add = next;
1907                 n_entries_to_add++;
1908               }
1909             else
1910               next = entries_to_add;
1911                 
1912             next->missing_description = 0;
1913             if (next->text)
1914               {
1915                 char *nl = strrchr (next->text, '\n');
1916                 if (nl)
1917                   nl[0] = '\0';
1918               }
1919             /* Concat the description onto the current entry, adding a 
1920                newline if we need one.  Prepend a space if we have no
1921                previous text, since eventually we will be adding the
1922                "* foo ()." and we want to end up with a ". " for parsing.  */
1923             next->text = concat (next->text ? next->text : " ",
1924                                  optarg, 
1925                                  optarg[length - 1] == '\n' ? "" : "\n");
1926             next->text_len = strlen (next->text);
1927           }
1928           break;
1929
1930         case 'C':
1931           {
1932             char *end = NULL;
1933             unsigned long int val;
1934             val = strtoul (optarg, &end, 0);
1935             if (end == NULL || end == optarg || *end != '\0')
1936               suggest_asking_for_help ();
1937             calign = val;
1938             if (calign <= 0)
1939               suggest_asking_for_help ();
1940           }
1941           break;
1942
1943         case 'd':
1944           if (dirfile)
1945             {
1946               fprintf (stderr, _("%s: already have dir file: %s\n"),
1947                        progname, dirfile);
1948               suggest_asking_for_help ();
1949             }
1950           dirfile = optarg;
1951           break;
1952
1953         case 'D':
1954           if (dirfile)
1955             {
1956               fprintf (stderr, _("%s: already have dir file: %s\n"),
1957                        progname, dirfile);
1958               suggest_asking_for_help ();
1959             }
1960           dirfile = concat (optarg, "", "/dir");
1961           break;
1962
1963         case 't':
1964           {
1965             struct spec_entry *next
1966               = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
1967
1968             size_t length;
1969             if (optarg[0] != '*')
1970               {
1971                 /* Make enough space for "* foo: (). ". */
1972                 length = strlen (optarg) + 9;
1973                 next->text = xmalloc (length);
1974                 snprintf (next->text, length, "* %s: (). ", optarg);
1975                 next->missing_basename = 1;
1976                 /* The basename will be inserted in between the parentheses
1977                    at a later time.  See add_missing_basenames. */
1978               }
1979             else
1980               {
1981                 /* Make enough space for "foo ". */
1982                 length = strlen (optarg) + 2;
1983                 next->text = xmalloc (length);
1984                 snprintf (next->text, length, "%s ", optarg);
1985                 next->missing_basename = 0;
1986                 /* FIXME: check for info entry correctness in TEXT. 
1987                    e.g. `* Aaa: (bbb).' */
1988               }
1989
1990             next->text_len = length - 1;
1991             next->entry_sections = NULL;
1992             next->entry_sections_tail = NULL;
1993             next->next = entries_to_add;
1994             next->missing_name = 0;
1995             next->missing_description = 1;
1996             entries_to_add = next;
1997             n_entries_to_add++;
1998           }
1999           break;
2000
2001         case 'e':
2002           {
2003             struct spec_entry *next
2004               = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
2005             int olen = strlen (optarg);
2006             if (! (*optarg != 0 && optarg[olen - 1] == '\n'))
2007               {
2008                 optarg = concat (optarg, "\n", "");
2009                 olen++;
2010               }
2011             next->text = optarg;
2012             next->text_len = olen;
2013             next->entry_sections = NULL;
2014             next->entry_sections_tail = NULL;
2015             next->next = entries_to_add;
2016             next->missing_name = 0;
2017             next->missing_basename = 0;
2018             next->missing_description = 0;
2019             entries_to_add = next;
2020             n_entries_to_add++;
2021           }
2022           break;
2023
2024         case 'g':
2025           debug_flag = 1;
2026           break;
2027
2028         case 'h':
2029         case 'H':
2030           print_help ();
2031           xexit (0);
2032
2033         case 'i':
2034           if (infile)
2035             {
2036               fprintf (stderr, _("%s: Specify the Info file only once.\n"),
2037                        progname);
2038               suggest_asking_for_help ();
2039             }
2040           infile = optarg;
2041           break;
2042
2043         case 'I':
2044           indent_flag = 0;
2045           break;
2046
2047         case 'k':
2048           keep_old_flag = 1;
2049           break;
2050
2051         case 'n':
2052           chicken_flag = 1;
2053           break;
2054
2055         case 'q':
2056           quiet_flag = 1;
2057           break;
2058
2059         case 'r':
2060           delete_flag = 1;
2061           break;
2062
2063         case 'R':
2064           {
2065             int error;
2066             if (psecreg)
2067               {
2068                 warning 
2069                   (_("Extra regular expression specified, ignoring `%s'"),
2070                    optarg, 0);
2071                 break;
2072               }
2073             psecreg = (regex_t *) xmalloc (sizeof (regex_t));
2074
2075             error = regcomp (psecreg, optarg, REG_ICASE|REG_NOSUB);
2076             if (error != 0)
2077               {
2078                 int errbuf_size = regerror (error, psecreg, NULL, 0);
2079                 char *errbuf = (char *) xmalloc (errbuf_size);
2080                 regerror (error, psecreg, errbuf, errbuf_size);
2081                 fatal (_("Error in regular expression `%s': %s"),
2082                        optarg, errbuf);
2083               };
2084           }
2085           break;
2086
2087         case 's':
2088           {
2089             struct spec_section *next
2090               = (struct spec_section *) xmalloc (sizeof (struct spec_section));
2091             next->name = optarg;
2092             next->next = input_sections;
2093             next->missing = 1;
2094             input_sections = next;
2095           }
2096           break;
2097
2098         case 'V':
2099           printf ("install-info (GNU %s) %s\n", PACKAGE, VERSION);
2100           puts ("");
2101           printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
2102 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
2103 This is free software: you are free to change and redistribute it.\n\
2104 There is NO WARRANTY, to the extent permitted by law.\n"),
2105               "2008");
2106           xexit (0);
2107
2108         case 'W':
2109           {
2110             char *end = NULL;
2111             unsigned long int val;
2112             val = strtoul (optarg, &end, 0);
2113             if (end == NULL || end == optarg || *end != '\0')
2114               suggest_asking_for_help ();
2115             maxwidth = val;
2116             if (maxwidth <= 0)
2117               suggest_asking_for_help ();
2118           }
2119           break;
2120
2121         case 'x':
2122           delete_flag = 1;
2123           remove_exactly = 1;
2124           break;
2125
2126         default:
2127           suggest_asking_for_help ();
2128         }
2129     }
2130
2131   /* Interpret the non-option arguments as file names.  */
2132   for (; optind < argc; ++optind)
2133     {
2134       if (infile == 0)
2135         infile = argv[optind];
2136       else if (dirfile == 0)
2137         dirfile = argv[optind];
2138       else
2139         error (_("excess command line argument `%s'"), argv[optind], 0);
2140     }
2141
2142   if (!infile)
2143     fatal (_("No input file specified; try --help for more information."),
2144            0, 0);
2145   if (!dirfile)
2146     fatal (_("No dir file specified; try --help for more information."), 0, 0);
2147
2148   /* Now read in the Info dir file.  */
2149   if (debug_flag)
2150     printf ("debug: reading dir file %s\n", dirfile);
2151   dir_data = readfile (dirfile, &dir_size, ensure_dirfile_exists,
2152                        &opened_dirfilename, &compression_program);
2153   dir_lines = findlines (dir_data, dir_size, &dir_nlines);
2154
2155   parse_dir_file (dir_lines, dir_nlines, &dir_nodes);
2156
2157   if (!delete_flag)
2158     {
2159       /* Find which sections match our regular expression. */
2160       if (psecreg)
2161         {
2162           struct node *node;
2163           struct menu_section *section;
2164           for (node = dir_nodes; node ; node = node->next)
2165             for (section = node->sections; section ; section = section->next)
2166               if (regexec (psecreg, section->name, 0, NULL, 0) == 0)
2167                 {
2168                   /* we have a match! */
2169                   struct spec_section *next = 
2170                     (struct spec_section *) xmalloc 
2171                     (sizeof (struct spec_section));
2172                   next->name = section->name;
2173                   next->next = input_sections;
2174                   next->missing = 0;
2175                   input_sections = next;
2176                 }
2177         }
2178
2179     }
2180
2181   /* We will be comparing the entries in the dir file against the
2182      current filename, so need to strip off any directory prefix and/or
2183      [.info][.gz] suffix.  */
2184   if (!remove_exactly) {
2185     char *infile_basename = infile + strlen (infile);
2186
2187     if (HAVE_DRIVE (infile))
2188       infile += 2;      /* get past the drive spec X: */
2189
2190     while (infile_basename > infile && !IS_SLASH (infile_basename[-1]))
2191       infile_basename--;
2192
2193     infile_sans_info = strip_info_suffix (infile_basename);
2194   } else
2195     infile_sans_info = xstrdup(infile);
2196
2197   /* Now Read the Info file and parse it into lines, unless we're 
2198      removing exactly.  */
2199   if (!remove_exactly)
2200     {
2201       if (debug_flag)
2202         printf ("debug: reading input file %s\n", infile);
2203       input_data = readfile (infile, &input_size, NULL, NULL, NULL);
2204       input_lines = findlines (input_data, input_size, &input_nlines);
2205     }
2206
2207   i = parse_input (input_lines, input_nlines,
2208                    &input_sections, &entries_to_add_from_file, delete_flag);
2209   if (!delete_flag)
2210     {
2211       /* If there are no entries on the command-line at all, so we use the 
2212          entries found in the Info file itself (if any). */
2213       if (entries_to_add == NULL)
2214         {
2215           entries_to_add = entries_to_add_from_file;
2216           n_entries_to_add = i;
2217         }
2218       /* There are entries on the command-line, and they override the entries
2219          found in the Info file. */
2220       else if (entries_to_add)
2221         {
2222           if (entries_to_add_from_file == NULL)
2223             {
2224               /* No entries found in the file anyway.  Fill in any 
2225                  missing names with the info file's basename.  We're out
2226                  of luck for any missing descriptions. */
2227               add_missing_names (entries_to_add, infile_sans_info);
2228               /* add_missing_descriptions (entries_to_add, "\n"); */
2229             }
2230           else
2231             {
2232               /* Fill in any missing names or descriptions with what was
2233                  found in the Info file. */
2234               char *desc = NULL;
2235               size_t desc_len = 0;
2236               char *name = NULL;
2237               size_t name_len = 0;
2238               split_entry (entries_to_add_from_file->text, &name, &name_len,
2239                            &desc, &desc_len);
2240               if (name)
2241                 {
2242                   /* If the name doesn't look right, bail and use the 
2243                      name based on the Info file. */
2244                   if (name[0] != '*')
2245                     add_missing_names (entries_to_add, infile_sans_info);
2246                   else
2247                     add_missing_names (entries_to_add, name);
2248                   free (name);
2249                 }
2250
2251               if (desc)
2252                 {
2253                   add_missing_descriptions (entries_to_add, desc);
2254                   free (desc);
2255                 }
2256             }
2257         }
2258             
2259       /* Lastly, fill in any missing basenames that might still be hanging
2260          around from --name options on the command-line. */
2261       add_missing_basenames (entries_to_add, infile_sans_info);
2262
2263       /* Reformat the new entries if we're doing that. */
2264       if (indent_flag)
2265         {
2266           char *no_indent = getenv ("INSTALL_INFO_NO_INDENT");
2267           if (!no_indent)
2268             reformat_new_entries (entries_to_add, calign, align, maxwidth);
2269         }
2270
2271       /* If we got no sections, default to "Miscellaneous".  */
2272       if (input_sections == NULL)
2273         {
2274           input_sections = (struct spec_section *)
2275             xmalloc (sizeof (struct spec_section));
2276           input_sections->name = "Miscellaneous";
2277           input_sections->next = NULL;
2278           input_sections->missing = 1;
2279         }
2280
2281       if (entries_to_add == 0)
2282         { /* No need to abort here, the original info file may not
2283              have the requisite Texinfo commands.  This is not
2284              something an installer should have to correct (it's a
2285              problem for the maintainer), and there's no need to cause
2286              subsequent parts of `make install' to fail.  */
2287           if (!quiet_flag)
2288             warning (_("no info dir entry in `%s'"), infile, 0);
2289           xexit (0);
2290         }
2291
2292       /* If the entries came from the command-line arguments, their
2293          entry_sections pointers are not yet set.  Walk the chain of
2294          the entries and for each entry update entry_sections to point
2295          to the head of the list of sections where this entry should
2296          be put.  Note that all the entries specified on the command
2297          line get put into ALL the sections we've got, either from the
2298          Info file, or (under --section) from the command line,
2299          because in the loop below every entry inherits the entire
2300          chain of sections.  */
2301       if (n_entries_to_add > 0 && entries_to_add->entry_sections == NULL)
2302         {
2303           struct spec_entry *ep;
2304
2305           for (ep = entries_to_add; ep; ep = ep->next)
2306             ep->entry_sections = input_sections;
2307         }
2308     }
2309
2310   if (delete_flag)
2311     {
2312       something_deleted = mark_entry_for_deletion (dir_lines, dir_nlines, 
2313                                                    infile_sans_info);
2314       if (!something_deleted && !remove_exactly)
2315         {
2316           struct spec_entry *entry;
2317           for (entry = entries_to_add; entry; entry = entry->next)
2318             {
2319               /* If the entry came from the info file... */
2320               if (entry->entry_sections != NULL)
2321                 {
2322                   char *name = extract_menu_item_name (entry->text);
2323                   something_deleted = 
2324                     mark_entry_for_deletion (dir_lines, dir_nlines, name);
2325                   free (name);
2326                 }
2327             }
2328       
2329           if (!something_deleted)
2330             {
2331               struct spec_entry *entry;
2332               for (entry = entries_to_add; entry; entry = entry->next)
2333                 {
2334                   /* If the entry came from the command-line... */
2335                   if (entry->entry_sections == NULL)
2336                     something_deleted = 
2337                       mark_entry_for_deletion (dir_lines, dir_nlines, 
2338                                                entry->text);
2339                 }
2340             }
2341         }
2342     }
2343     
2344   /* Check for sections with zero entries and mark them for deletion. */
2345   if (delete_flag && something_deleted && !keep_old_flag)
2346     {
2347       struct node *node;
2348       struct menu_section *section;
2349       int section_empty;
2350
2351       for (node = dir_nodes; node ; node = node->next)
2352         for (section = node->sections; section ; section = section->next)
2353           {
2354             section_empty = 1;
2355             for (i = section->end_line; i > section->start_line; i--)
2356               {
2357                 if (dir_lines[i - 1].delete == 0 && 
2358                     dir_lines[i - 1].size != 0)
2359                   {
2360                     section_empty = 0;
2361                     break;
2362                   }
2363               }
2364
2365             if (section_empty)
2366               {
2367                 /* This gets rid of any trailing empty lines at the end  
2368                    of the section, and the title too. */
2369                 for (i = section->end_line; i >= section->start_line; i--)
2370                   dir_lines[i - 1].delete = 1;
2371               }
2372           }
2373     }
2374
2375   /* Decide where to add the new entries (unless --delete was used).
2376      Find the menu sections to add them in.
2377      In each section, find the proper alphabetical place to add
2378      each of the entries.  */
2379   if (!delete_flag)
2380     {
2381       struct node *node;
2382       struct menu_section *section;
2383       struct spec_section *spec;
2384
2385       for (node = dir_nodes; node; node = node->next)
2386         for (section = node->sections; section; section = section->next)
2387           {
2388             for (i = section->end_line; i > section->start_line; i--)
2389               if (dir_lines[i - 1].size != 0)
2390                 break;
2391             section->end_line = i;
2392
2393             for (spec = input_sections; spec; spec = spec->next)
2394               if (!strcmp (spec->name, section->name))
2395                 break;
2396             if (spec)
2397               {
2398                 int add_at_line = section->end_line;
2399                 struct spec_entry *entry;
2400                 /* Say we have found at least one section with this name,
2401                    so we need not add such a section.  */
2402                 spec->missing = 0;
2403                 /* For each entry, find the right place in this section
2404                    to add it.  */
2405                 for (entry = entries_to_add; entry; entry = entry->next)
2406                   {
2407                     /* Did they at all want this entry to be put into
2408                        this section?  */
2409                     for (spec = entry->entry_sections;
2410                          spec && spec != entry->entry_sections_tail;
2411                          spec = spec->next)
2412                       {
2413                         if (!strcmp (spec->name, section->name))
2414                           break;
2415                       }
2416                     if (!spec || spec == entry->entry_sections_tail)
2417                       continue;
2418
2419                     /* Subtract one because dir_lines is zero-based,
2420                        but the `end_line' and `start_line' members are
2421                        one-based.  */
2422                     for (i = section->end_line - 1;
2423                          i >= section->start_line - 1; i--)
2424                       {
2425                         /* If an entry exists with the same name,
2426                            and was not marked for deletion
2427                            (which means it is for some other file),
2428                            we are in trouble.  */
2429                         if (dir_lines[i].start[0] == '*'
2430                             && menu_line_equal (entry->text, entry->text_len,
2431                                                 dir_lines[i].start,
2432                                                 dir_lines[i].size)
2433                             && !dir_lines[i].delete)
2434                           {
2435                             if (keep_old_flag)
2436                               {
2437                                 add_at_line = -1;
2438                                 break;
2439                               }
2440                             else
2441                               {
2442                                 int j;
2443                                 dir_lines[i].delete = 1;
2444                                 for (j = i + 1; j < section->end_line; j++)
2445                                   {
2446                                     if (dir_lines[j].start[0] == '*')
2447                                       break;
2448                                     dir_lines[j].delete = 1;
2449                                   }
2450                               }
2451                           }
2452                         if (dir_lines[i].start[0] == '*'
2453                             && menu_line_lessp (entry->text, entry->text_len,
2454                                                 dir_lines[i].start,
2455                                                 dir_lines[i].size))
2456                           add_at_line = i;
2457                       }
2458                     if (add_at_line < 0)
2459                       continue;
2460                     insert_entry_here (entry, add_at_line,
2461                                        dir_lines, n_entries_to_add);
2462                   }
2463               }
2464           }
2465
2466     }
2467   /* Decide where to add the new sections (unless --delete was used).
2468      Alphabetically find the menu sections to add them before.  */
2469   if (!delete_flag)
2470     {
2471       struct node *node;
2472       struct node *top = NULL;
2473
2474       /* Find the `Top' node. */
2475       for (node = dir_nodes; node; node = node->next)
2476         if (node->name && strcmp (node->name, "Top") == 0)
2477           top = node;
2478
2479       if (top)
2480         {
2481           struct spec_section *spec;
2482           int found = 0;
2483           struct line_data *target_line = NULL;
2484           for (spec = input_sections; spec; spec = spec->next)
2485             {
2486               found = 0;
2487               target_line = NULL;
2488               if (!spec->missing)
2489                 continue;
2490               if (order_new_sections_alphabetically_flag)
2491                 {
2492                   struct menu_section *section;
2493                   struct menu_section *prev_section = NULL;
2494               
2495                   /* Look for the first section name that 
2496                      exceeds SPEC->NAME. */
2497                   for (section = top->sections; section ; 
2498                        section = section->next)
2499                     {
2500                       found = (mbscasecmp (spec->name, section->name) < 0);
2501                       if (found)
2502                         {
2503                           /* Mark the section for addition at this point. */
2504                           if (prev_section)
2505                             target_line = &dir_lines[prev_section->end_line];
2506                           else
2507                             target_line = 
2508                               &dir_lines[top->sections->start_line - 2];
2509
2510                           break;
2511                         }
2512                       prev_section = section;
2513                     }
2514                 }
2515                   
2516               /* When we can't put a section anywhere, we put it at the 
2517                  bottom of the file. */
2518               if (!found)
2519                 target_line = &dir_lines[top->end_line];
2520
2521               /* Add the section to our list of sections being added
2522                  at this point of the DIR file. */
2523               target_line->num_sections_to_add++;
2524               target_line->add_sections_before = 
2525                 (struct spec_section **) xrealloc 
2526                 (target_line->add_sections_before, 
2527                  (target_line->num_sections_to_add *
2528                   sizeof (struct spec_section *)));
2529               i = target_line->num_sections_to_add - 1;
2530               target_line->add_sections_before[i] = spec;
2531             }
2532         }
2533     }
2534
2535   if (delete_flag && !something_deleted && !quiet_flag)
2536     warning (_("no entries found for `%s'; nothing deleted"), infile, 0);
2537
2538   if (debug_flag)
2539     printf ("debug: writing dir file %s\n", opened_dirfilename);
2540   if (chicken_flag)
2541     printf ("test mode, not updating dir file %s\n", opened_dirfilename);
2542   else
2543     output_dirfile (opened_dirfilename, dir_nlines, dir_lines,
2544                     n_entries_to_add, entries_to_add,
2545                     input_sections, compression_program);
2546
2547   xexit (0);
2548   return 0; /* Avoid bogus warnings.  */
2549 }
2550 \f
2551 /* Divide the text at DATA (of SIZE bytes) into lines.
2552    Return a vector of struct line_data describing the lines.
2553    Store the length of that vector into *NLINESP.  */
2554
2555 struct line_data *
2556 findlines (char *data, int size, int *nlinesp)
2557 {
2558   int i;
2559   int lineflag = 1;
2560   int lines_allocated = 511;
2561   int filled = 0;
2562   struct line_data *lines
2563     = xmalloc ((lines_allocated + 1) * sizeof (struct line_data));
2564
2565   for (i = 0; i < size; i++)
2566     {
2567       if (lineflag)
2568         {
2569           if (filled == lines_allocated)
2570             {
2571               /* try to keep things somewhat page-aligned */
2572               lines_allocated = ((lines_allocated + 1) * 2) - 1;
2573               lines = xrealloc (lines, (lines_allocated + 1)
2574                                        * sizeof (struct line_data));
2575             }
2576           lines[filled].start = &data[i];
2577           lines[filled].add_entries_before = 0;
2578           lines[filled].add_sections_before = NULL;
2579           lines[filled].num_sections_to_add = 0;
2580           lines[filled].delete = 0;
2581           if (filled > 0)
2582             lines[filled - 1].size
2583               = lines[filled].start - lines[filled - 1].start - 1;
2584           filled++;
2585         }
2586       lineflag = (data[i] == '\n');
2587     }
2588   if (filled > 0)
2589     lines[filled - 1].size = &data[i] - lines[filled - 1].start - lineflag;
2590
2591   /* Do not leave garbage in the last element.  */
2592   lines[filled].start = NULL;
2593   lines[filled].add_entries_before = NULL;
2594   lines[filled].add_sections_before = NULL;
2595   lines[filled].num_sections_to_add = 0;
2596   lines[filled].delete = 0;
2597   lines[filled].size = 0;
2598
2599   *nlinesp = filled;
2600   return lines;
2601 }
2602 \f
2603 /* This is the comparison function for qsort for a vector of pointers to
2604    struct spec_section.  (Have to use const void * as the parameter type
2605    to avoid incompatible-with-qsort warnings.)
2606    Compare the section names.  */
2607
2608 int
2609 compare_section_names (const void *p1, const void *p2)
2610 {
2611   struct spec_section **sec1 = (struct spec_section **) p1;
2612   struct spec_section **sec2 = (struct spec_section **) p2;
2613   char *name1 = (*sec1)->name;
2614   char *name2 = (*sec2)->name;
2615   return strcmp (name1, name2);
2616 }
2617
2618 /* This is the comparison function for qsort
2619    for a vector of pointers to struct spec_entry.
2620    Compare the entries' text.  */
2621
2622 int
2623 compare_entries_text (const void *p1, const void *p2)
2624 {
2625   struct spec_entry **entry1 = (struct spec_entry **) p1;
2626   struct spec_entry **entry2 = (struct spec_entry **) p2;
2627   char *text1 = (*entry1)->text;
2628   char *text2 = (*entry2)->text;
2629   char *colon1 = strchr (text1, ':');
2630   char *colon2 = strchr (text2, ':');
2631   int len1, len2;
2632
2633   if (!colon1)
2634     len1 = strlen (text1);
2635   else
2636     len1 = colon1 - text1;
2637   if (!colon2)
2638     len2 = strlen (text2);
2639   else
2640     len2 = colon2 - text2;
2641   return mbsncasecmp (text1, text2, len1 <= len2 ? len1 : len2);
2642 }
2643
2644 /* Insert ENTRY into the add_entries_before vector
2645    for line number LINE_NUMBER of the dir file.
2646    DIR_LINES and N_ENTRIES carry information from like-named variables
2647    in main.  */
2648
2649 void
2650 insert_entry_here (struct spec_entry *entry, int line_number,
2651                    struct line_data *dir_lines, int n_entries)
2652 {
2653   int i, j;
2654
2655   if (dir_lines[line_number].add_entries_before == 0)
2656     {
2657       dir_lines[line_number].add_entries_before
2658         = (struct spec_entry **) xmalloc (n_entries * sizeof (struct spec_entry *));
2659       for (i = 0; i < n_entries; i++)
2660         dir_lines[line_number].add_entries_before[i] = 0;
2661     }
2662
2663   /* Find the place where this entry belongs.  If there are already
2664      several entries to add before LINE_NUMBER, make sure they are in
2665      alphabetical order.  */
2666   for (i = 0; i < n_entries; i++)
2667     if (dir_lines[line_number].add_entries_before[i] == 0
2668         || menu_line_lessp (entry->text, strlen (entry->text),
2669                             dir_lines[line_number].add_entries_before[i]->text,
2670                             strlen (dir_lines[line_number].add_entries_before[i]->text)))
2671       break;
2672
2673   if (i == n_entries)
2674     abort ();
2675
2676   /* If we need to plug ENTRY into the middle of the
2677      ADD_ENTRIES_BEFORE array, move the entries which should be output
2678      after this one down one notch, before adding a new one.  */
2679   if (dir_lines[line_number].add_entries_before[i] != 0)
2680     for (j = n_entries - 1; j > i; j--)
2681       dir_lines[line_number].add_entries_before[j]
2682         = dir_lines[line_number].add_entries_before[j - 1];
2683
2684   dir_lines[line_number].add_entries_before[i] = entry;
2685 }