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