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