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