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