]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/texinfo/util/install-info.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / texinfo / util / install-info.c
1 /* $FreeBSD$ */
2 /* install-info -- create Info directory entry(ies) for an Info file.
3    $Id: install-info.c,v 1.12 2004/04/11 17:56:47 karl Exp $
4
5    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
6    Foundation, Inc.
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/
21
22 #include "system.h"
23 #include <getopt.h>
24
25 static char *progname = "install-info";
26 static char *default_section = NULL;
27
28 struct spec_entry;
29 struct spec_section;
30
31 struct line_data *findlines (char *data, int size, int *nlinesp);
32 void insert_entry_here (struct spec_entry *entry, int line_number,
33                         struct line_data *dir_lines, int n_entries); 
34 int compare_section_names (const void *s1, const void *s2);
35 int compare_entries_text (const void *e1, const void *e2); 
36 \f
37 /* Data structures.  */
38
39
40 /* Record info about a single line from a file as read into core.  */
41 struct line_data
42 {
43   /* The start of the line.  */
44   char *start;
45   /* The number of characters in the line,
46      excluding the terminating newline.  */
47   int size;
48   /* Vector containing pointers to the entries to add before this line.
49      The vector is null-terminated.  */
50   struct spec_entry **add_entries_before;
51   /* 1 means output any needed new sections before this line.  */
52   int add_sections_before;
53   /* 1 means don't output this line.  */
54   int delete;
55 };
56
57
58 /* This is used for a list of the specified menu section names
59    in which entries should be added.  */
60 struct spec_section
61 {
62   struct spec_section *next;
63   char *name;
64   /* 1 means we have not yet found an existing section with this name
65      in the dir file--so we will need to add a new section.  */
66   int missing;
67 };
68
69
70 /* This is used for a list of the entries specified to be added.  */
71 struct spec_entry
72 {
73   struct spec_entry *next;
74   char *text;
75   int text_len;
76   /* A pointer to the list of sections to which this entry should be
77      added.  */
78   struct spec_section *entry_sections;
79   /* A pointer to a section that is beyond the end of the chain whose
80      head is pointed to by entry_sections.  */
81   struct spec_section *entry_sections_tail;
82 };
83
84
85 /* This is used for a list of nodes found by parsing the dir file.  */
86 struct node
87 {
88   struct node *next;
89   /* The node name.  */
90   char *name;
91   /* The line number of the line where the node starts.
92      This is the line that contains control-underscore.  */
93   int start_line;
94   /* The line number of the line where the node ends,
95      which is the end of the file or where the next line starts.  */
96   int end_line;
97   /* Start of first line in this node's menu
98      (the line after the * Menu: line).  */
99   char *menu_start;
100   /* The start of the chain of sections in this node's menu.  */
101   struct menu_section *sections;
102   /* The last menu section in the chain.  */
103   struct menu_section *last_section;
104 };
105
106
107 /* This is used for a list of sections found in a node's menu.
108    Each  struct node  has such a list in the  sections  field.  */
109 struct menu_section
110 {
111   struct menu_section *next;
112   char *name;
113   /* Line number of start of section.  */
114   int start_line;
115   /* Line number of end of section.  */
116   int end_line;
117 };
118 \f
119 /* This table defines all the long-named options, says whether they
120    use an argument, and maps them into equivalent single-letter options.  */
121
122 struct option longopts[] =
123 {
124   { "delete",    no_argument, NULL, 'r' },
125   { "defentry",  required_argument, NULL, 'E' },
126   { "defsection", required_argument, NULL, 'S' },
127   { "dir-file",  required_argument, NULL, 'd' },
128   { "entry",     required_argument, NULL, 'e' },
129   { "help",      no_argument, NULL, 'h' },
130   { "infodir",   required_argument, NULL, 'D' },
131   { "info-dir",  required_argument, NULL, 'D' },
132   { "info-file", required_argument, NULL, 'i' },
133   { "item",      required_argument, NULL, 'e' },
134   { "quiet",     no_argument, NULL, 'q' },
135   { "remove",    no_argument, NULL, 'r' },
136   { "section",   required_argument, NULL, 's' },
137   { "version",   no_argument, NULL, 'V' },
138   { 0 }
139 };
140 \f
141 /* Error message functions.  */
142
143 /* Print error message.  S1 is printf control string, S2 and S3 args for it. */
144
145 /* VARARGS1 */
146 void
147 error (const char *s1, const char *s2, const char *s3)
148 {
149   fprintf (stderr, "%s: ", progname);
150   fprintf (stderr, s1, s2, s3);
151   putc ('\n', stderr);
152 }
153
154 /* VARARGS1 */
155 void
156 warning (const char *s1, const char *s2, const char *s3)
157 {
158   fprintf (stderr, _("%s: warning: "), progname);
159   fprintf (stderr, s1, s2, s3);
160   putc ('\n', stderr);
161 }
162
163 /* Print error message and exit.  */
164
165 void
166 fatal (const char *s1, const char *s2, const char *s3)
167 {
168   error (s1, s2, s3);
169   xexit (1);
170 }
171 \f
172 /* Return a newly-allocated string
173    whose contents concatenate those of S1, S2, S3.  */
174 char *
175 concat (const char *s1, const char *s2, const char *s3)
176 {
177   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
178   char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
179
180   strcpy (result, s1);
181   strcpy (result + len1, s2);
182   strcpy (result + len1 + len2, s3);
183   *(result + len1 + len2 + len3) = 0;
184
185   return result;
186 }
187
188 /* Return a string containing SIZE characters
189    copied from starting at STRING.  */
190
191 char *
192 copy_string (const char *string, int size)
193 {
194   int i;
195   char *copy = (char *) xmalloc (size + 1);
196   for (i = 0; i < size; i++)
197     copy[i] = string[i];
198   copy[size] = 0;
199   return copy;
200 }
201
202 /* Print fatal error message based on errno, with file name NAME.  */
203
204 void
205 pfatal_with_name (const char *name)
206 {
207   char *s = concat ("", strerror (errno), _(" for %s"));
208   fatal (s, name, 0);
209 }
210 \f
211 /* Compare the menu item names in LINE1 (line length LEN1)
212    and LINE2 (line length LEN2).  Return 1 if the item name
213    in LINE1 is less, 0 otherwise.  */
214
215 static int
216 menu_line_lessp (char *line1, int len1, char *line2, int len2)
217 {
218   int minlen = (len1 < len2 ? len1 : len2);
219   int i;
220
221   for (i = 0; i < minlen; i++)
222     {
223       /* If one item name is a prefix of the other,
224          the former one is less.  */
225       if (line1[i] == ':' && line2[i] != ':')
226         return 1;
227       if (line2[i] == ':' && line1[i] != ':')
228         return 0;
229       /* If they both continue and differ, one is less.  */
230       if (line1[i] < line2[i])
231         return 1;
232       if (line1[i] > line2[i])
233         return 0;
234     }
235   /* With a properly formatted dir file,
236      we can only get here if the item names are equal.  */
237   return 0;
238 }
239
240 /* Compare the menu item names in LINE1 (line length LEN1)
241    and LINE2 (line length LEN2).  Return 1 if the item names are equal,
242    0 otherwise.  */
243
244 static int
245 menu_line_equal (char *line1, int len1, char *line2, int len2)
246 {
247   int minlen = (len1 < len2 ? len1 : len2);
248   int i;
249
250   for (i = 0; i < minlen; i++)
251     {
252       /* If both item names end here, they are equal.  */
253       if (line1[i] == ':' && line2[i] == ':')
254         return 1;
255       /* If they both continue and differ, one is less.  */
256       if (line1[i] != line2[i])
257         return 0;
258     }
259   /* With a properly formatted dir file,
260      we can only get here if the item names are equal.  */
261   return 1;
262 }
263
264 \f
265 /* Given the full text of a menu entry, null terminated,
266    return just the menu item name (copied).  */
267
268 char *
269 extract_menu_item_name (char *item_text)
270 {
271   char *p;
272
273   if (*item_text == '*')
274     item_text++;
275   while (*item_text == ' ')
276     item_text++;
277
278   p = item_text;
279   while (*p && *p != ':') p++;
280   return copy_string (item_text, p - item_text);
281 }
282
283 /* Given the full text of a menu entry, terminated by null or newline,
284    return just the menu item file (copied).  */
285
286 char *
287 extract_menu_file_name (char *item_text)
288 {
289   char *p = item_text;
290
291   /* If we have text that looks like * ITEM: (FILE)NODE...,
292      extract just FILE.  Otherwise return "(none)".  */
293
294   if (*p == '*')
295     p++;
296   while (*p == ' ')
297     p++;
298
299   /* Skip to and past the colon.  */
300   while (*p && *p != '\n' && *p != ':') p++;
301   if (*p == ':') p++;
302
303   /* Skip past the open-paren.  */
304   while (1)
305     {
306       if (*p == '(')
307         break;
308       else if (*p == ' ' || *p == '\t')
309         p++;
310       else
311         return "(none)";
312     }
313   p++;
314
315   item_text = p;
316
317   /* File name ends just before the close-paren.  */
318   while (*p && *p != '\n' && *p != ')') p++;
319   if (*p != ')')
320     return "(none)";
321
322   return copy_string (item_text, p - item_text);
323 }
324
325
326 \f
327 /* Return FNAME with any [.info][.gz] suffix removed.  */
328
329 static char *
330 strip_info_suffix (char *fname)
331 {
332   char *ret = xstrdup (fname);
333   unsigned len = strlen (ret);
334
335   if (len > 3 && FILENAME_CMP (ret + len - 3, ".gz") == 0)
336     {
337       len -= 3;
338       ret[len] = 0;
339     }
340   else if (len > 4 && FILENAME_CMP (ret + len - 4, ".bz2") == 0)
341     {
342       len -= 4;
343       ret[len] = 0;
344     }
345
346   if (len > 5 && FILENAME_CMP (ret + len - 5, ".info") == 0)
347     {
348       len -= 5;
349       ret[len] = 0;
350     }
351   else if (len > 4 && FILENAME_CMP (ret + len - 4, ".inf") == 0)
352     {
353       len -= 4;
354       ret[len] = 0;
355     }
356 #ifdef __MSDOS__
357   else if (len > 4 && (FILENAME_CMP (ret + len - 4, ".inz") == 0
358                        || FILENAME_CMP (ret + len - 4, ".igz") == 0))
359     {
360       len -= 4;
361       ret[len] = 0;
362     }
363 #endif /* __MSDOS__ */
364
365   return ret;
366 }
367
368
369 /* Return true if ITEM matches NAME and is followed by TERM_CHAR.  ITEM
370    can also be followed by `.gz', `.info.gz', or `.info' (and then
371    TERM_CHAR) and still match.  */
372
373 static int
374 menu_item_equal (const char *item, char term_char, const char *name)
375 {
376   int ret;
377   const char *item_basename = item;
378   unsigned name_len = strlen (name);
379
380   /* We must compare the basename in ITEM, since we are passed the
381      basename of the original info file.  Otherwise, a new entry like
382      "lilypond/lilypond" won't match "lilypond".
383      
384      Actually, it seems to me that we should really compare the whole
385      name, and not just the basename.  Couldn't there be dir1/foo.info
386      and dir2/foo.info?  Also, it seems like we should be using the
387      filename from the new dir entries, not the filename on the command
388      line.  Not worrying about those things right now, though.  --karl,
389      26mar04.  */
390   while (*item_basename && !IS_SLASH (*item_basename)
391          && *item_basename != term_char)
392     item_basename++;
393   if (! *item_basename || *item_basename == term_char)
394     item_basename = item;  /* no /, use original */
395   else
396     item_basename++;       /* have /, move past it */
397     
398   /* First, ITEM must actually match NAME (usually it won't).  */
399   ret = strncasecmp (item_basename, name, name_len) == 0;
400   if (ret)
401     {
402       /* Then, `foobar' doesn't match `foo', so be sure we've got all of
403          ITEM.  The various suffixes should never actually appear in the
404          dir file, but sometimes people put them in.  */
405       static char *suffixes[]
406         = { "", ".info.gz", ".info", ".inf", ".gz",
407 #ifdef __MSDOS__
408             ".inz", ".igz",
409 #endif
410             NULL };
411       unsigned i;
412       ret = 0;
413       for (i = 0; !ret && suffixes[i]; i++)
414         {
415           char *suffix = suffixes[i];
416           unsigned suffix_len = strlen (suffix);
417           ret = strncasecmp (item_basename + name_len, suffix, suffix_len) == 0
418                 && item_basename[name_len + suffix_len] == term_char;
419         }
420     }
421
422   return ret;
423 }
424
425
426 \f
427 void
428 suggest_asking_for_help (void)
429 {
430   fprintf (stderr, _("\tTry `%s --help' for a complete list of options.\n"),
431            progname);
432   xexit (1);
433 }
434
435 void
436 print_help (void)
437 {
438   printf (_("Usage: %s [OPTION]... [INFO-FILE [DIR-FILE]]\n\
439 \n\
440 Install or delete dir entries from INFO-FILE in the Info directory file\n\
441 DIR-FILE.\n\
442 \n\
443 Options:\n\
444  --delete          delete existing entries for INFO-FILE from DIR-FILE;\n\
445                      don't insert any new entries.\n\
446  --defentry=TEXT   like --entry, but only use TEXT if an entry\n\
447                      is not present in INFO-FILE.\n\
448  --defsection=TEXT like --section, but only use TEXT if a section\n\
449                      is not present in INFO-FILE.\n\
450  --dir-file=NAME   specify file name of Info directory file.\n\
451                      This is equivalent to using the DIR-FILE argument.\n\
452  --entry=TEXT      insert TEXT as an Info directory entry.\n\
453                      TEXT should have the form of an Info menu item line\n\
454                      plus zero or more extra lines starting with whitespace.\n\
455                      If you specify more than one entry, they are all added.\n\
456                      If you don't specify any entries, they are determined\n\
457                      from information in the Info file itself.\n\
458  --help            display this help and exit.\n\
459  --info-file=FILE  specify Info file to install in the directory.\n\
460                      This is equivalent to using the INFO-FILE argument.\n\
461  --info-dir=DIR    same as --dir-file=DIR/dir.\n\
462  --item=TEXT       same as --entry TEXT.\n\
463                      An Info directory entry is actually a menu item.\n\
464  --quiet           suppress warnings.\n\
465  --remove          same as --delete.\n\
466  --section=SEC     put this file's entries in section SEC of the directory.\n\
467                      If you specify more than one section, all the entries\n\
468                      are added in each of the sections.\n\
469                      If you don't specify any sections, they are determined\n\
470                      from information in the Info file itself.\n\
471  --version         display version information and exit.\n\
472 "), progname);
473
474   puts (_("\n\
475 Email bug reports to bug-texinfo@gnu.org,\n\
476 general questions and discussion to help-texinfo@gnu.org.\n\
477 Texinfo home page: http://www.gnu.org/software/texinfo/"));
478 }
479
480 \f
481 /* If DIRFILE does not exist, create a minimal one (or abort).  If it
482    already exists, do nothing.  */
483
484 void
485 ensure_dirfile_exists (char *dirfile)
486 {
487   int desc = open (dirfile, O_RDONLY);
488   if (desc < 0 && errno == ENOENT)
489     {
490       FILE *f;
491       char *readerr = strerror (errno);
492       close (desc);
493       f = fopen (dirfile, "w");
494       if (f)
495         {
496           fprintf (f, _("This is the file .../info/dir, which contains the\n\
497 topmost node of the Info hierarchy, called (dir)Top.\n\
498 The first time you invoke Info you start off looking at this node.\n\
499 \x1f\n\
500 %s\tThis is the top of the INFO tree\n\
501 \n\
502   This (the Directory node) gives a menu of major topics.\n\
503   Typing \"q\" exits, \"?\" lists all Info commands, \"d\" returns here,\n\
504   \"h\" gives a primer for first-timers,\n\
505   \"mEmacs<Return>\" visits the Emacs manual, etc.\n\
506 \n\
507   In Emacs, you can click mouse button 2 on a menu item or cross reference\n\
508   to select it.\n\
509 \n\
510 %s\n\
511 "), "File: dir,\tNode: Top",  /* These keywords must not be translated.  */
512     "* Menu:"
513 );
514           if (fclose (f) < 0)
515             pfatal_with_name (dirfile);
516         }
517       else
518         {
519           /* Didn't exist, but couldn't open for writing.  */
520           fprintf (stderr,
521                    _("%s: could not read (%s) and could not create (%s)\n"),
522                    dirfile, readerr, strerror (errno));
523           xexit (1);
524         }
525     }
526   else
527     close (desc); /* It already existed, so fine.  */
528 }
529 \f
530 /* Open FILENAME and return the resulting stream pointer.  If it doesn't
531    exist, try FILENAME.gz.  If that doesn't exist either, call
532    CREATE_CALLBACK (with FILENAME as arg) to create it, if that is
533    non-NULL.  If still no luck, fatal error.
534
535    If we do open it, return the actual name of the file opened in
536    OPENED_FILENAME and the compress program to use to (de)compress it in
537    COMPRESSION_PROGRAM.  The compression program is determined by the
538    magic number, not the filename.  */
539
540 FILE *
541 open_possibly_compressed_file (char *filename,
542     void (*create_callback) (char *),
543     char **opened_filename, char **compression_program, int *is_pipe) 
544 {
545   char *local_opened_filename, *local_compression_program;
546   int nread;
547   char data[4];
548   FILE *f;
549
550   /* We let them pass NULL if they don't want this info, but it's easier
551      to always determine it.  */
552   if (!opened_filename)
553     opened_filename = &local_opened_filename;
554
555   *opened_filename = filename;
556   f = fopen (*opened_filename, FOPEN_RBIN);
557   if (!f)
558     {
559       *opened_filename = concat (filename, ".gz", "");
560       f = fopen (*opened_filename, FOPEN_RBIN);
561   if (!f)
562     {
563       free (*opened_filename);
564       *opened_filename = concat (filename, ".bz2", "");
565       f = fopen (*opened_filename, FOPEN_RBIN);
566     }
567
568 #ifdef __MSDOS__
569       if (!f)
570         {
571           free (*opened_filename);
572           *opened_filename = concat (filename, ".igz", "");
573           f = fopen (*opened_filename, FOPEN_RBIN);
574         }
575       if (!f)
576         {
577           free (*opened_filename);
578           *opened_filename = concat (filename, ".inz", "");
579           f = fopen (*opened_filename, FOPEN_RBIN);
580         }
581 #endif
582       if (!f)
583         {
584           if (create_callback)
585             { /* That didn't work either.  Create the file if we can.  */
586               (*create_callback) (filename);
587
588               /* And try opening it again.  */
589               free (*opened_filename);
590               *opened_filename = filename;
591               f = fopen (*opened_filename, FOPEN_RBIN);
592               if (!f)
593                 pfatal_with_name (filename);
594             }
595           else
596             pfatal_with_name (filename);
597         }
598     }
599
600   /* Read first few bytes of file rather than relying on the filename.
601      If the file is shorter than this it can't be usable anyway.  */
602   nread = fread (data, sizeof (data), 1, f);
603   if (nread != 1)
604     {
605       /* Empty files don't set errno, so we get something like
606          "install-info: No error for foo", which is confusing.  */
607       if (nread == 0)
608         fatal (_("%s: empty file"), *opened_filename, 0);
609       pfatal_with_name (*opened_filename);
610     }
611
612   if (!compression_program)
613     compression_program = &local_compression_program;
614
615   if (data[0] == '\x1f' && data[1] == '\x8b')
616 #if STRIP_DOT_EXE
617     /* An explicit .exe yields a better diagnostics from popen below
618        if they don't have gzip installed.  */
619     *compression_program = "gzip.exe";
620 #else
621     *compression_program = "gzip";
622 #endif
623   else if(data[0] == 'B' && data[1] == 'Z' && data[2] == 'h')
624 #ifndef STRIP_DOT_EXE
625     *compression_program = "bzip2.exe";
626 #else
627     *compression_program = "bzip2";
628 #endif
629   else if(data[0] == 'B' && data[1] == 'Z' && data[2] == '0')
630 #ifndef STRIP_DOT_EXE
631     *compression_program = "bzip.exe";
632 #else
633     *compression_program = "bzip";
634 #endif
635   else
636     *compression_program = NULL;
637
638   if (*compression_program)
639     { /* It's compressed, so fclose the file and then open a pipe.  */
640       char *command = concat (*compression_program," -cd <", *opened_filename);
641       if (fclose (f) < 0)
642         pfatal_with_name (*opened_filename);
643       f = popen (command, "r");
644       if (f)
645         *is_pipe = 1;
646       else
647         pfatal_with_name (command);
648     }
649   else
650     { /* It's a plain file, seek back over the magic bytes.  */
651       if (fseek (f, 0, 0) < 0)
652         pfatal_with_name (*opened_filename);
653 #if O_BINARY
654       /* Since this is a text file, and we opened it in binary mode,
655          switch back to text mode.  */
656       f = freopen (*opened_filename, "r", f);
657 #endif
658       *is_pipe = 0;
659     }
660
661   return f;
662 }
663 \f
664 /* Read all of file FILENAME into memory and return the address of the
665    data.  Store the size of the data into SIZEP.  If need be, uncompress
666    (i.e., try FILENAME.gz et al. if FILENAME does not exist) and store
667    the actual file name that was opened into OPENED_FILENAME (if it is
668    non-NULL), and the companion compression program (if any, else NULL)
669    into COMPRESSION_PROGRAM (if that is non-NULL).  If trouble, do
670    a fatal error.  */
671
672 char *
673 readfile (char *filename, int *sizep,
674     void (*create_callback) (char *), char **opened_filename,
675     char **compression_program)
676 {
677   char *real_name;
678   FILE *f;
679   int pipe_p;
680   int filled = 0;
681   int data_size = 8192;
682   char *data = xmalloc (data_size);
683
684   /* If they passed the space for the file name to return, use it.  */
685   f = open_possibly_compressed_file (filename, create_callback,
686                                      opened_filename ? opened_filename
687                                                      : &real_name,
688                                      compression_program, &pipe_p);
689
690   for (;;)
691     {
692       int nread = fread (data + filled, 1, data_size - filled, f);
693       if (nread < 0)
694         pfatal_with_name (real_name);
695       if (nread == 0)
696         break;
697
698       filled += nread;
699       if (filled == data_size)
700         {
701           data_size += 65536;
702           data = xrealloc (data, data_size);
703         }
704     }
705
706   /* We'll end up wasting space if we're not passing the filename back
707      and it is not just FILENAME, but so what.  */
708   /* We need to close the stream, since on some systems the pipe created
709      by popen is simulated by a temporary file which only gets removed
710      inside pclose.  */
711   if (pipe_p)
712     pclose (f);
713   else
714     fclose (f);
715
716   *sizep = filled;
717   return data;
718 }
719 \f
720 /* Output the old dir file, interpolating the new sections
721    and/or new entries where appropriate.  If COMPRESSION_PROGRAM is not
722    null, pipe to it to create DIRFILE.  Thus if we read dir.gz on input,
723    we'll write dir.gz on output.  */
724
725 static void
726 output_dirfile (char *dirfile, int dir_nlines, struct line_data *dir_lines,
727                 int n_entries_to_add, struct spec_entry *entries_to_add,
728                 struct spec_section *input_sections, char *compression_program)
729 {
730   int i;
731   FILE *output;
732
733   if (compression_program)
734     {
735       char *command = concat (compression_program, ">", dirfile);
736       output = popen (command, "w");
737     }
738   else
739     output = fopen (dirfile, "w");
740
741   if (!output)
742     {
743       perror (dirfile);
744       xexit (1);
745     }
746
747   for (i = 0; i <= dir_nlines; i++)
748     {
749       int j;
750
751       /* If we decided to output some new entries before this line,
752          output them now.  */
753       if (dir_lines[i].add_entries_before)
754         for (j = 0; j < n_entries_to_add; j++)
755           {
756             struct spec_entry *this = dir_lines[i].add_entries_before[j];
757             if (this == 0)
758               break;
759             fputs (this->text, output);
760           }
761       /* If we decided to add some sections here
762          because there are no such sections in the file,
763          output them now.  */
764       if (dir_lines[i].add_sections_before)
765         {
766           struct spec_section *spec;
767           struct spec_section **sections;
768           int n_sections = 0;
769           struct spec_entry *entry;
770           struct spec_entry **entries;
771           int n_entries = 0;
772
773           /* Count the sections and allocate a vector for all of them.  */
774           for (spec = input_sections; spec; spec = spec->next)
775             n_sections++;
776           sections = ((struct spec_section **)
777                       xmalloc (n_sections * sizeof (struct spec_section *)));
778
779           /* Fill the vector SECTIONS with pointers to all the sections,
780              and sort them.  */
781           j = 0;
782           for (spec = input_sections; spec; spec = spec->next)
783             sections[j++] = spec;
784           qsort (sections, n_sections, sizeof (struct spec_section *),
785                  compare_section_names);
786
787           /* Count the entries and allocate a vector for all of them.  */
788           for (entry = entries_to_add; entry; entry = entry->next)
789             n_entries++;
790           entries = ((struct spec_entry **)
791                      xmalloc (n_entries * sizeof (struct spec_entry *)));
792
793           /* Fill the vector ENTRIES with pointers to all the sections,
794              and sort them.  */
795           j = 0;
796           for (entry = entries_to_add; entry; entry = entry->next)
797             entries[j++] = entry;
798           qsort (entries, n_entries, sizeof (struct spec_entry *),
799                  compare_entries_text);
800
801           /* Generate the new sections in alphabetical order.  In each
802              new section, output all of the entries that belong to that
803              section, in alphabetical order.  */
804           for (j = 0; j < n_sections; j++)
805             {
806               spec = sections[j];
807               if (spec->missing)
808                 {
809                   int k;
810
811                   putc ('\n', output);
812                   fputs (spec->name, output);
813                   putc ('\n', output);
814                   for (k = 0; k < n_entries; k++)
815                     {
816                       struct spec_section *spec1;
817                       /* Did they at all want this entry to be put into
818                          this section?  */
819                       entry = entries[k];
820                       for (spec1 = entry->entry_sections;
821                            spec1 && spec1 != entry->entry_sections_tail;
822                            spec1 = spec1->next)
823                         {
824                           if (!strcmp (spec1->name, spec->name))
825                             break;
826                         }
827                       if (spec1 && spec1 != entry->entry_sections_tail)
828                         fputs (entry->text, output);
829                     }
830                 }
831             }
832
833           free (entries);
834           free (sections);
835         }
836
837       /* Output the original dir lines unless marked for deletion.  */
838       if (i < dir_nlines && !dir_lines[i].delete)
839         {
840           fwrite (dir_lines[i].start, 1, dir_lines[i].size, output);
841           putc ('\n', output);
842         }
843     }
844
845   /* Some systems, such as MS-DOS, simulate pipes with temporary files.
846      On those systems, the compressor actually gets run inside pclose,
847      so we must call pclose.  */
848   if (compression_program)
849     pclose (output);
850   else
851     fclose (output);
852 }
853 \f
854 /* Parse the input to find the section names and the entry names it
855    specifies.  Return the number of entries to add from this file.  */
856 int
857 parse_input (const struct line_data *lines, int nlines,
858              struct spec_section **sections, struct spec_entry **entries) 
859 {
860   int n_entries = 0;
861   int prefix_length = strlen ("INFO-DIR-SECTION ");
862   struct spec_section *head = *sections, *tail = NULL;
863   int reset_tail = 0;
864   char *start_of_this_entry = 0;
865   int ignore_sections = *sections != 0;
866   int ignore_entries  = *entries  != 0;
867
868   int i;
869
870   if (ignore_sections && ignore_entries)
871     return 0;
872
873   /* Loop here processing lines from the input file.  Each
874      INFO-DIR-SECTION entry is added to the SECTIONS linked list.
875      Each START-INFO-DIR-ENTRY block is added to the ENTRIES linked
876      list, and all its entries inherit the chain of SECTION entries
877      defined by the last group of INFO-DIR-SECTION entries we have
878      seen until that point.  */
879   for (i = 0; i < nlines; i++)
880     {
881       if (!ignore_sections
882           && !strncmp ("INFO-DIR-SECTION ", lines[i].start, prefix_length))
883         {
884           struct spec_section *next
885             = (struct spec_section *) xmalloc (sizeof (struct spec_section));
886           next->name = copy_string (lines[i].start + prefix_length,
887                                     lines[i].size - prefix_length);
888           next->next = *sections;
889           next->missing = 1;
890           if (reset_tail)
891             {
892               tail = *sections;
893               reset_tail = 0;
894             }
895           *sections = next;
896           head = *sections;
897         }
898       /* If entries were specified explicitly with command options,
899          ignore the entries in the input file.  */
900       else if (!ignore_entries)
901         {
902           if (!strncmp ("START-INFO-DIR-ENTRY", lines[i].start, lines[i].size)
903               && sizeof ("START-INFO-DIR-ENTRY") - 1 == lines[i].size)
904             {
905               if (!*sections)
906                 {
907                   /* We found an entry, but didn't yet see any sections
908                      specified.  Default to section "Miscellaneous".  */
909                   *sections = (struct spec_section *)
910                     xmalloc (sizeof (struct spec_section));
911                   (*sections)->name =
912                     default_section ? default_section : "Miscellaneous";
913                   (*sections)->next = 0;
914                   (*sections)->missing = 1;
915                   head = *sections;
916                 }
917               /* Next time we see INFO-DIR-SECTION, we will reset the
918                  tail pointer.  */
919               reset_tail = 1;
920
921               if (start_of_this_entry != 0)
922                 fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"), 0, 0);
923               start_of_this_entry = lines[i + 1].start;
924             }
925           else if (start_of_this_entry)
926             {
927               if ((!strncmp ("* ", lines[i].start, 2)
928                    && lines[i].start > start_of_this_entry)
929                   || (!strncmp ("END-INFO-DIR-ENTRY",
930                                 lines[i].start, lines[i].size)
931                       && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size))
932                 {
933                   /* We found an end of this entry.  Allocate another
934                      entry, fill its data, and add it to the linked
935                      list.  */
936                   struct spec_entry *next
937                     = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
938                   next->text
939                     = copy_string (start_of_this_entry,
940                                    lines[i].start - start_of_this_entry);
941                   next->text_len = lines[i].start - start_of_this_entry;
942                   next->entry_sections = head;
943                   next->entry_sections_tail = tail;
944                   next->next = *entries;
945                   *entries = next;
946                   n_entries++;
947                   if (!strncmp ("END-INFO-DIR-ENTRY",
948                                 lines[i].start, lines[i].size)
949                       && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size)
950                     start_of_this_entry = 0;
951                   else
952                     start_of_this_entry = lines[i].start;
953                 }
954               else if (!strncmp ("END-INFO-DIR-ENTRY",
955                                  lines[i].start, lines[i].size)
956                        && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size)
957                 fatal (_("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY"), 0, 0);
958             }
959         }
960     }
961   if (start_of_this_entry != 0)
962     fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"),
963            0, 0);
964
965   /* If we ignored the INFO-DIR-ENTRY directives, we need now go back
966      and plug the names of all the sections we found into every
967      element of the ENTRIES list.  */
968   if (ignore_entries && *entries)
969     {
970       struct spec_entry *entry;
971
972       for (entry = *entries; entry; entry = entry->next)
973         {
974           entry->entry_sections = head;
975           entry->entry_sections_tail = tail;
976         }
977     }
978
979   return n_entries;
980 }
981
982 /* Parse the dir file whose basename is BASE_NAME.  Find all the
983    nodes, and their menus, and the sections of their menus.  */
984 int
985 parse_dir_file (struct line_data *lines, int nlines, struct node **nodes,
986                 const char *base_name)
987 {
988   int node_header_flag = 0;
989   int something_deleted = 0;
990   int i;
991
992   *nodes = 0;
993   for (i = 0; i < nlines; i++)
994     {
995       /* Parse node header lines.  */
996       if (node_header_flag)
997         {
998           int j, end;
999           for (j = 0; j < lines[i].size; j++)
1000             /* Find the node name and store it in the `struct node'.  */
1001             if (!strncmp ("Node:", lines[i].start + j, 5))
1002               {
1003                 char *line = lines[i].start;
1004                 /* Find the start of the node name.  */
1005                 j += 5;
1006                 while (line[j] == ' ' || line[j] == '\t')
1007                   j++;
1008                 /* Find the end of the node name.  */
1009                 end = j;
1010                 while (line[end] != 0 && line[end] != ',' && line[end] != '\n'
1011                        && line[end] != '\t')
1012                   end++;
1013                 (*nodes)->name = copy_string (line + j, end - j);
1014               }
1015           node_header_flag = 0;
1016         }
1017
1018       /* Notice the start of a node.  */
1019       if (*lines[i].start == 037)
1020         {
1021           struct node *next = (struct node *) xmalloc (sizeof (struct node));
1022
1023           next->next = *nodes;
1024           next->name = NULL;
1025           next->start_line = i;
1026           next->end_line = 0;
1027           next->menu_start = NULL;
1028           next->sections = NULL;
1029           next->last_section = NULL;
1030
1031           if (*nodes != 0)
1032             (*nodes)->end_line = i;
1033           /* Fill in the end of the last menu section
1034              of the previous node.  */
1035           if (*nodes != 0 && (*nodes)->last_section != 0)
1036             (*nodes)->last_section->end_line = i;
1037
1038           *nodes = next;
1039
1040           /* The following line is the header of this node;
1041              parse it.  */
1042           node_header_flag = 1;
1043         }
1044
1045       /* Notice the lines that start menus.  */
1046       if (*nodes != 0 && !strncmp ("* Menu:", lines[i].start, 7))
1047         (*nodes)->menu_start = lines[i + 1].start;
1048
1049       /* Notice sections in menus.  */
1050       if (*nodes != 0
1051           && (*nodes)->menu_start != 0
1052           && *lines[i].start != '\n'
1053           && *lines[i].start != '*'
1054           && *lines[i].start != ' '
1055           && *lines[i].start != '\t')
1056         {
1057           /* Add this menu section to the node's list.
1058              This list grows in forward order.  */
1059           struct menu_section *next
1060             = (struct menu_section *) xmalloc (sizeof (struct menu_section));
1061
1062           next->start_line = i + 1;
1063           next->next = 0;
1064           next->end_line = 0;
1065           next->name = copy_string (lines[i].start, lines[i].size);
1066           if ((*nodes)->sections)
1067             {
1068               (*nodes)->last_section->next = next;
1069               (*nodes)->last_section->end_line = i;
1070             }
1071           else
1072             (*nodes)->sections = next;
1073           (*nodes)->last_section = next;
1074         }
1075
1076       /* Check for an existing entry that should be deleted.
1077          Delete all entries which specify this file name.  */
1078       if (*lines[i].start == '*')
1079         {
1080           char *q;
1081           char *p = lines[i].start;
1082
1083           p++; /* skip * */
1084           while (*p == ' ') p++; /* ignore following spaces */
1085           q = p; /* remember this, it's the beginning of the menu item.  */
1086
1087           /* Read menu item.  */
1088           while (*p != 0 && *p != ':')
1089             p++;
1090           p++; /* skip : */
1091
1092           if (*p == ':')
1093             { /* XEmacs-style entry, as in * Mew::Messaging.  */
1094               if (menu_item_equal (q, ':', base_name))
1095                 {
1096                   lines[i].delete = 1;
1097                   something_deleted = 1;
1098                 }
1099             }
1100           else
1101             { /* Emacs-style entry, as in * Emacs: (emacs).  */
1102               while (*p == ' ') p++; /* skip spaces after : */
1103               if (*p == '(')         /* if at parenthesized (FILENAME) */
1104                 {
1105                   p++;
1106                   if (menu_item_equal (p, ')', base_name))
1107                     {
1108                       lines[i].delete = 1;
1109                       something_deleted = 1;
1110                     }
1111                 }
1112             }
1113         }
1114
1115       /* Treat lines that start with whitespace
1116          as continuations; if we are deleting an entry,
1117          delete all its continuations as well.  */
1118       else if (i > 0 && (*lines[i].start == ' ' || *lines[i].start == '\t'))
1119         {
1120           lines[i].delete = lines[i - 1].delete;
1121         }
1122     }
1123
1124   /* Finish the info about the end of the last node.  */
1125   if (*nodes != 0)
1126     {
1127       (*nodes)->end_line = nlines;
1128       if ((*nodes)->last_section != 0)
1129         (*nodes)->last_section->end_line = nlines;
1130     }
1131
1132   return something_deleted;
1133 }
1134
1135 int
1136 main (int argc, char **argv)
1137 {
1138   char *opened_dirfilename;
1139   char *compression_program;
1140   char *infile_sans_info;
1141   char *infile = 0, *dirfile = 0;
1142
1143   /* Record the text of the Info file, as a sequence of characters
1144      and as a sequence of lines.  */
1145   char *input_data = NULL;
1146   int input_size = 0;
1147   struct line_data *input_lines = NULL;
1148   int input_nlines = 0;
1149
1150   /* Record here the specified section names and directory entries.  */
1151   struct spec_section *input_sections = NULL;
1152   struct spec_entry *entries_to_add = NULL;
1153   int n_entries_to_add = 0;
1154   struct spec_entry *default_entries_to_add = NULL;
1155   int n_default_entries_to_add = 0;
1156
1157   /* Record the old text of the dir file, as plain characters,
1158      as lines, and as nodes.  */
1159   char *dir_data;
1160   int dir_size;
1161   int dir_nlines;
1162   struct line_data *dir_lines;
1163   struct node *dir_nodes;
1164
1165   /* Nonzero means --delete was specified (just delete existing entries).  */
1166   int delete_flag = 0;
1167   int something_deleted = 0;
1168   /* Nonzero means -q was specified.  */
1169   int quiet_flag = 0;
1170
1171   int i;
1172
1173 #ifdef HAVE_SETLOCALE
1174   /* Set locale via LC_ALL.  */
1175   setlocale (LC_ALL, "");
1176 #endif
1177
1178   /* Set the text message domain.  */
1179   bindtextdomain (PACKAGE, LOCALEDIR);
1180   textdomain (PACKAGE);
1181
1182   while (1)
1183     {
1184       int opt = getopt_long (argc, argv, "i:d:e:s:hHr", longopts, 0);
1185
1186       if (opt == EOF)
1187         break;
1188
1189       switch (opt)
1190         {
1191         case 0:
1192           /* If getopt returns 0, then it has already processed a
1193              long-named option.  We should do nothing.  */
1194           break;
1195
1196         case 1:
1197           abort ();
1198
1199         case 'd':
1200           if (dirfile)
1201             {
1202               fprintf (stderr, _("%s: already have dir file: %s\n"),
1203                        progname, dirfile);
1204               suggest_asking_for_help ();
1205             }
1206           dirfile = optarg;
1207           break;
1208
1209         case 'D':
1210           if (dirfile)
1211             {
1212               fprintf (stderr, _("%s: already have dir file: %s\n"),
1213                        progname, dirfile);
1214               suggest_asking_for_help ();
1215             }
1216           dirfile = concat (optarg, "", "/dir");
1217           break;
1218
1219         case 'E':
1220         case 'e':
1221           {
1222             struct spec_entry *next
1223               = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
1224             int olen = strlen (optarg);
1225             if (! (*optarg != 0 && optarg[olen - 1] == '\n'))
1226               {
1227                 optarg = concat (optarg, "\n", "");
1228                 olen++;
1229               }
1230             next->text = optarg;
1231             next->text_len = olen;
1232             next->entry_sections = NULL;
1233             next->entry_sections_tail = NULL;
1234             if (opt == 'e')
1235               {
1236                 next->next = entries_to_add;
1237                 entries_to_add = next;
1238                 n_entries_to_add++;
1239               }
1240             else
1241               {
1242                 next->next = default_entries_to_add;
1243                 default_entries_to_add = next;
1244                 n_default_entries_to_add++;
1245               }
1246           }
1247           break;
1248
1249         case 'h':
1250         case 'H':
1251           print_help ();
1252           xexit (0);
1253
1254         case 'i':
1255           if (infile)
1256             {
1257               fprintf (stderr, _("%s: Specify the Info file only once.\n"),
1258                        progname);
1259               suggest_asking_for_help ();
1260             }
1261           infile = optarg;
1262           break;
1263
1264         case 'q':
1265           quiet_flag = 1;
1266           break;
1267
1268         case 'r':
1269           delete_flag = 1;
1270           break;
1271
1272         case 's':
1273           {
1274             struct spec_section *next
1275               = (struct spec_section *) xmalloc (sizeof (struct spec_section));
1276             next->name = optarg;
1277             next->next = input_sections;
1278             next->missing = 1;
1279             input_sections = next;
1280           }
1281           break;
1282
1283         case 'S':
1284           default_section = optarg;
1285           break;
1286
1287         case 'V':
1288           printf ("install-info (GNU %s) %s\n", PACKAGE, VERSION);
1289           puts ("");
1290           puts ("Copyright (C) 2004 Free Software Foundation, Inc.");
1291           printf (_("There is NO warranty.  You may redistribute this software\n\
1292 under the terms of the GNU General Public License.\n\
1293 For more information about these matters, see the files named COPYING.\n"));
1294           xexit (0);
1295
1296         default:
1297           suggest_asking_for_help ();
1298         }
1299     }
1300
1301   /* Interpret the non-option arguments as file names.  */
1302   for (; optind < argc; ++optind)
1303     {
1304       if (infile == 0)
1305         infile = argv[optind];
1306       else if (dirfile == 0)
1307         dirfile = argv[optind];
1308       else
1309         error (_("excess command line argument `%s'"), argv[optind], 0);
1310     }
1311
1312   if (!infile)
1313     fatal (_("No input file specified; try --help for more information."),
1314            0, 0);
1315   if (!dirfile)
1316     fatal (_("No dir file specified; try --help for more information."), 0, 0);
1317
1318   /* Read the Info file and parse it into lines, unless we're deleting.  */
1319   if (!delete_flag)
1320     {
1321       input_data = readfile (infile, &input_size, NULL, NULL, NULL);
1322       input_lines = findlines (input_data, input_size, &input_nlines);
1323     }
1324
1325   i = parse_input (input_lines, input_nlines,
1326                    &input_sections, &entries_to_add);
1327   if (i > n_entries_to_add)
1328     n_entries_to_add = i;
1329   else if (n_entries_to_add == 0)
1330     {
1331       entries_to_add = default_entries_to_add;
1332       n_entries_to_add = n_default_entries_to_add;
1333     }
1334
1335   if (!delete_flag)
1336     {
1337       if (entries_to_add == 0)
1338         { /* No need to abort here, the original info file may not
1339              have the requisite Texinfo commands.  This is not
1340              something an installer should have to correct (it's a
1341              problem for the maintainer), and there's no need to cause
1342              subsequent parts of `make install' to fail.  */
1343           warning (_("no info dir entry in `%s'"), infile, 0);
1344           xexit (0);
1345         }
1346
1347       /* If the entries came from the command-line arguments, their
1348          entry_sections pointers are not yet set.  Walk the chain of
1349          the entries and for each entry update entry_sections to point
1350          to the head of the list of sections where this entry should
1351          be put.  Note that all the entries specified on the command
1352          line get put into ALL the sections we've got, either from the
1353          Info file, or (under --section) from the command line,
1354          because in the loop below every entry inherits the entire
1355          chain of sections.  */
1356       if (n_entries_to_add > 0 && entries_to_add->entry_sections == NULL)
1357         {
1358           struct spec_entry *ep;
1359
1360           /* If we got no sections, default to "Miscellaneous".  */
1361           if (input_sections == NULL)
1362             {
1363               input_sections = (struct spec_section *)
1364                 xmalloc (sizeof (struct spec_section));
1365               input_sections->name =
1366                 default_section ? default_section : "Miscellaneous";
1367               input_sections->next = NULL;
1368               input_sections->missing = 1;
1369             }
1370           for (ep = entries_to_add; ep; ep = ep->next)
1371             ep->entry_sections = input_sections;
1372         }
1373     }
1374
1375   /* Now read in the Info dir file.  */
1376   dir_data = readfile (dirfile, &dir_size, ensure_dirfile_exists,
1377                        &opened_dirfilename, &compression_program);
1378   dir_lines = findlines (dir_data, dir_size, &dir_nlines);
1379
1380   /* We will be comparing the entries in the dir file against the
1381      current filename, so need to strip off any directory prefix and/or
1382      [.info][.gz] suffix.  */
1383   {
1384     char *infile_basename = infile + strlen (infile);
1385
1386     if (HAVE_DRIVE (infile))
1387       infile += 2;      /* get past the drive spec X: */
1388
1389     while (infile_basename > infile && !IS_SLASH (infile_basename[-1]))
1390       infile_basename--;
1391
1392     infile_sans_info = strip_info_suffix (infile_basename);
1393   }
1394
1395   something_deleted
1396     = parse_dir_file (dir_lines, dir_nlines, &dir_nodes, infile_sans_info);
1397
1398   /* Decide where to add the new entries (unless --delete was used).
1399      Find the menu sections to add them in.
1400      In each section, find the proper alphabetical place to add
1401      each of the entries.  */
1402   if (!delete_flag)
1403     {
1404       struct node *node;
1405       struct menu_section *section;
1406       struct spec_section *spec;
1407
1408       for (node = dir_nodes; node; node = node->next)
1409         for (section = node->sections; section; section = section->next)
1410           {
1411             for (i = section->end_line; i > section->start_line; i--)
1412               if (dir_lines[i - 1].size != 0)
1413                 break;
1414             section->end_line = i;
1415
1416             for (spec = input_sections; spec; spec = spec->next)
1417               if (!strcmp (spec->name, section->name))
1418                 break;
1419             if (spec)
1420               {
1421                 int add_at_line = section->end_line;
1422                 struct spec_entry *entry;
1423                 /* Say we have found at least one section with this name,
1424                    so we need not add such a section.  */
1425                 spec->missing = 0;
1426                 /* For each entry, find the right place in this section
1427                    to add it.  */
1428                 for (entry = entries_to_add; entry; entry = entry->next)
1429                   {
1430                     /* Did they at all want this entry to be put into
1431                        this section?  */
1432                     for (spec = entry->entry_sections;
1433                          spec && spec != entry->entry_sections_tail;
1434                          spec = spec->next)
1435                       {
1436                         if (!strcmp (spec->name, section->name))
1437                           break;
1438                       }
1439                     if (!spec || spec == entry->entry_sections_tail)
1440                       continue;
1441
1442                     /* Subtract one because dir_lines is zero-based,
1443                        but the `end_line' and `start_line' members are
1444                        one-based.  */
1445                     for (i = section->end_line - 1;
1446                          i >= section->start_line - 1; i--)
1447                       {
1448                         /* If an entry exists with the same name,
1449                            and was not marked for deletion
1450                            (which means it is for some other file),
1451                            we are in trouble.  */
1452                         if (dir_lines[i].start[0] == '*'
1453                             && menu_line_equal (entry->text, entry->text_len,
1454                                                 dir_lines[i].start,
1455                                                 dir_lines[i].size)
1456                             && !dir_lines[i].delete)
1457                           {
1458                             if (quiet_flag)
1459                               dir_lines[i].delete = 1;
1460                             else
1461                               fatal (_("menu item `%s' already exists, for file `%s'"),
1462                                  extract_menu_item_name (entry->text),
1463                                  extract_menu_file_name (dir_lines[i].start));
1464                           }
1465                         if (dir_lines[i].start[0] == '*'
1466                             && menu_line_lessp (entry->text, entry->text_len,
1467                                                 dir_lines[i].start,
1468                                                 dir_lines[i].size))
1469                           add_at_line = i;
1470                       }
1471                     insert_entry_here (entry, add_at_line,
1472                                        dir_lines, n_entries_to_add);
1473                   }
1474               }
1475           }
1476
1477       /* Mark the end of the Top node as the place to add any
1478          new sections that are needed.  */
1479       for (node = dir_nodes; node; node = node->next)
1480         if (node->name && strcmp (node->name, "Top") == 0)
1481           dir_lines[node->end_line].add_sections_before = 1;
1482     }
1483
1484   if (delete_flag && !something_deleted && !quiet_flag)
1485     warning (_("no entries found for `%s'; nothing deleted"), infile, 0);
1486
1487   output_dirfile (opened_dirfilename, dir_nlines, dir_lines, n_entries_to_add,
1488                   entries_to_add, input_sections, compression_program);
1489
1490   xexit (0);
1491   return 0; /* Avoid bogus warnings.  */
1492 }
1493 \f
1494 /* Divide the text at DATA (of SIZE bytes) into lines.
1495    Return a vector of struct line_data describing the lines.
1496    Store the length of that vector into *NLINESP.  */
1497
1498 struct line_data *
1499 findlines (char *data, int size, int *nlinesp)
1500 {
1501   int i;
1502   int lineflag = 1;
1503   int lines_allocated = 511;
1504   int filled = 0;
1505   struct line_data *lines
1506     = xmalloc ((lines_allocated + 1) * sizeof (struct line_data));
1507
1508   for (i = 0; i < size; i++)
1509     {
1510       if (lineflag)
1511         {
1512           if (filled == lines_allocated)
1513             {
1514               /* try to keep things somewhat page-aligned */
1515               lines_allocated = ((lines_allocated + 1) * 2) - 1;
1516               lines = xrealloc (lines, (lines_allocated + 1)
1517                                        * sizeof (struct line_data));
1518             }
1519           lines[filled].start = &data[i];
1520           lines[filled].add_entries_before = 0;
1521           lines[filled].add_sections_before = 0;
1522           lines[filled].delete = 0;
1523           if (filled > 0)
1524             lines[filled - 1].size
1525               = lines[filled].start - lines[filled - 1].start - 1;
1526           filled++;
1527         }
1528       lineflag = (data[i] == '\n');
1529     }
1530   if (filled > 0)
1531     lines[filled - 1].size = &data[i] - lines[filled - 1].start - lineflag;
1532
1533   /* Do not leave garbage in the last element.  */
1534   lines[filled].start = NULL;
1535   lines[filled].add_entries_before = NULL;
1536   lines[filled].add_sections_before = 0;
1537   lines[filled].delete = 0;
1538   lines[filled].size = 0;
1539
1540   *nlinesp = filled;
1541   return lines;
1542 }
1543 \f
1544 /* This is the comparison function for qsort for a vector of pointers to
1545    struct spec_section.  (Have to use const void * as the parameter type
1546    to avoid incompatible-with-qsort warnings.)
1547    Compare the section names.  */
1548
1549 int
1550 compare_section_names (const void *p1, const void *p2)
1551 {
1552   struct spec_section **sec1 = (struct spec_section **) p1;
1553   struct spec_section **sec2 = (struct spec_section **) p2;
1554   char *name1 = (*sec1)->name;
1555   char *name2 = (*sec2)->name;
1556   return strcmp (name1, name2);
1557 }
1558
1559 /* This is the comparison function for qsort
1560    for a vector of pointers to struct spec_entry.
1561    Compare the entries' text.  */
1562
1563 int
1564 compare_entries_text (const void *p1, const void *p2)
1565 {
1566   struct spec_entry **entry1 = (struct spec_entry **) p1;
1567   struct spec_entry **entry2 = (struct spec_entry **) p2;
1568   char *text1 = (*entry1)->text;
1569   char *text2 = (*entry2)->text;
1570   char *colon1 = strchr (text1, ':');
1571   char *colon2 = strchr (text2, ':');
1572   int len1, len2;
1573
1574   if (!colon1)
1575     len1 = strlen (text1);
1576   else
1577     len1 = colon1 - text1;
1578   if (!colon2)
1579     len2 = strlen (text2);
1580   else
1581     len2 = colon2 - text2;
1582   return strncmp (text1, text2, len1 <= len2 ? len1 : len2);
1583 }
1584
1585 /* Insert ENTRY into the add_entries_before vector
1586    for line number LINE_NUMBER of the dir file.
1587    DIR_LINES and N_ENTRIES carry information from like-named variables
1588    in main.  */
1589
1590 void
1591 insert_entry_here (struct spec_entry *entry, int line_number,
1592                    struct line_data *dir_lines, int n_entries)
1593 {
1594   int i, j;
1595
1596   if (dir_lines[line_number].add_entries_before == 0)
1597     {
1598       dir_lines[line_number].add_entries_before
1599         = (struct spec_entry **) xmalloc (n_entries * sizeof (struct spec_entry *));
1600       for (i = 0; i < n_entries; i++)
1601         dir_lines[line_number].add_entries_before[i] = 0;
1602     }
1603
1604   /* Find the place where this entry belongs.  If there are already
1605      several entries to add before LINE_NUMBER, make sure they are in
1606      alphabetical order.  */
1607   for (i = 0; i < n_entries; i++)
1608     if (dir_lines[line_number].add_entries_before[i] == 0
1609         || menu_line_lessp (entry->text, strlen (entry->text),
1610                             dir_lines[line_number].add_entries_before[i]->text,
1611                             strlen (dir_lines[line_number].add_entries_before[i]->text)))
1612       break;
1613
1614   if (i == n_entries)
1615     abort ();
1616
1617   /* If we need to plug ENTRY into the middle of the
1618      ADD_ENTRIES_BEFORE array, move the entries which should be output
1619      after this one down one notch, before adding a new one.  */
1620   if (dir_lines[line_number].add_entries_before[i] != 0)
1621     for (j = n_entries - 1; j > i; j--)
1622       dir_lines[line_number].add_entries_before[j]
1623         = dir_lines[line_number].add_entries_before[j - 1];
1624
1625   dir_lines[line_number].add_entries_before[i] = entry;
1626 }