]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/texinfo/util/install-info.c
Import of stripped down GNU texinfo 4.2
[FreeBSD/FreeBSD.git] / contrib / texinfo / util / install-info.c
1 /* install-info -- create Info directory entry(ies) for an Info file.
2    $Id: install-info.c,v 1.55 2002/03/11 19:55:23 karl Exp $
3
4    Copyright (C) 1996, 97, 98, 99, 2000, 01, 02 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 \f
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 \f
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 \f
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 \f
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, 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, 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, 0);
234 }
235 \f
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 \f
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 \f
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 "), progname);
420
421   puts (_("\n\
422 Email bug reports to bug-texinfo@gnu.org,\n\
423 general questions and discussion to help-texinfo@gnu.org.\n\
424 Texinfo home page: http://www.gnu.org/software/texinfo/"));
425 }
426
427 \f
428 /* If DIRFILE does not exist, create a minimal one (or abort).  If it
429    already exists, do nothing.  */
430
431 void
432 ensure_dirfile_exists (dirfile)
433      char *dirfile;
434 {
435   int desc = open (dirfile, O_RDONLY);
436   if (desc < 0 && errno == ENOENT)
437     {
438       FILE *f;
439       char *readerr = strerror (errno);
440       close (desc);
441       f = fopen (dirfile, "w");
442       if (f)
443         {
444           fprintf (f, _("This is the file .../info/dir, which contains the\n\
445 topmost node of the Info hierarchy, called (dir)Top.\n\
446 The first time you invoke Info you start off looking at this node.\n\
447 \1f\n\
448 %s\tThis is the top of the INFO tree\n\
449 \n\
450   This (the Directory node) gives a menu of major topics.\n\
451   Typing \"q\" exits, \"?\" lists all Info commands, \"d\" returns here,\n\
452   \"h\" gives a primer for first-timers,\n\
453   \"mEmacs<Return>\" visits the Emacs manual, etc.\n\
454 \n\
455   In Emacs, you can click mouse button 2 on a menu item or cross reference\n\
456   to select it.\n\
457 \n\
458 * Menu:\n\
459 "), "File: dir,\tNode: Top"); /* This part must not be translated.  */
460           if (fclose (f) < 0)
461             pfatal_with_name (dirfile);
462         }
463       else
464         {
465           /* Didn't exist, but couldn't open for writing.  */
466           fprintf (stderr,
467                    _("%s: could not read (%s) and could not create (%s)\n"),
468                    dirfile, readerr, strerror (errno));
469           xexit (1);
470         }
471     }
472   else
473     close (desc); /* It already existed, so fine.  */
474 }
475 \f
476 /* Open FILENAME and return the resulting stream pointer.  If it doesn't
477    exist, try FILENAME.gz.  If that doesn't exist either, call
478    CREATE_CALLBACK (with FILENAME as arg) to create it, if that is
479    non-NULL.  If still no luck, fatal error.
480
481    If we do open it, return the actual name of the file opened in
482    OPENED_FILENAME and the compress program to use to (de)compress it in
483    COMPRESSION_PROGRAM.  The compression program is determined by the
484    magic number, not the filename.  */
485
486 FILE *
487 open_possibly_compressed_file (filename, create_callback,
488                                opened_filename, compression_program, is_pipe)
489      char *filename;
490      void (*create_callback) ();
491      char **opened_filename;
492      char **compression_program;
493      int  *is_pipe;
494 {
495   char *local_opened_filename, *local_compression_program;
496   int nread;
497   char data[4];
498   FILE *f;
499
500   /* We let them pass NULL if they don't want this info, but it's easier
501      to always determine it.  */
502   if (!opened_filename)
503     opened_filename = &local_opened_filename;
504
505   *opened_filename = filename;
506   f = fopen (*opened_filename, FOPEN_RBIN);
507   if (!f)
508     {
509       *opened_filename = concat (filename, ".gz", "");
510       f = fopen (*opened_filename, FOPEN_RBIN);
511 #ifdef __MSDOS__
512       if (!f)
513         {
514           free (*opened_filename);
515           *opened_filename = concat (filename, ".igz", "");
516           f = fopen (*opened_filename, FOPEN_RBIN);
517         }
518       if (!f)
519         {
520           free (*opened_filename);
521           *opened_filename = concat (filename, ".inz", "");
522           f = fopen (*opened_filename, FOPEN_RBIN);
523         }
524 #endif
525       if (!f)
526         {
527           if (create_callback)
528             { /* That didn't work either.  Create the file if we can.  */
529               (*create_callback) (filename);
530
531               /* And try opening it again.  */
532               free (*opened_filename);
533               *opened_filename = filename;
534               f = fopen (*opened_filename, FOPEN_RBIN);
535               if (!f)
536                 pfatal_with_name (filename);
537             }
538           else
539             pfatal_with_name (filename);
540         }
541     }
542
543   /* Read first few bytes of file rather than relying on the filename.
544      If the file is shorter than this it can't be usable anyway.  */
545   nread = fread (data, sizeof (data), 1, f);
546   if (nread != 1)
547     {
548       /* Empty files don't set errno, so we get something like
549          "install-info: No error for foo", which is confusing.  */
550       if (nread == 0)
551         fatal (_("%s: empty file"), *opened_filename, 0);
552       pfatal_with_name (*opened_filename);
553     }
554
555   if (!compression_program)
556     compression_program = &local_compression_program;
557
558   if (data[0] == '\x1f' && data[1] == '\x8b')
559 #if STRIP_DOT_EXE
560     /* An explicit .exe yields a better diagnostics from popen below
561        if they don't have gzip installed.  */
562     *compression_program = "gzip.exe";
563 #else
564     *compression_program = "gzip";
565 #endif
566   else
567     *compression_program = NULL;
568
569   if (*compression_program)
570     { /* It's compressed, so fclose the file and then open a pipe.  */
571       char *command = concat (*compression_program," -cd <", *opened_filename);
572       if (fclose (f) < 0)
573         pfatal_with_name (*opened_filename);
574       f = popen (command, "r");
575       if (f)
576         *is_pipe = 1;
577       else
578         pfatal_with_name (command);
579     }
580   else
581     { /* It's a plain file, seek back over the magic bytes.  */
582       if (fseek (f, 0, 0) < 0)
583         pfatal_with_name (*opened_filename);
584 #if O_BINARY
585       /* Since this is a text file, and we opened it in binary mode,
586          switch back to text mode.  */
587       f = freopen (*opened_filename, "r", f);
588 #endif
589       *is_pipe = 0;
590     }
591
592   return f;
593 }
594 \f
595 /* Read all of file FILENAME into memory and return the address of the
596    data.  Store the size of the data into SIZEP.  If need be, uncompress
597    (i.e., try FILENAME.gz et al. if FILENAME does not exist) and store
598    the actual file name that was opened into OPENED_FILENAME (if it is
599    non-NULL), and the companion compression program (if any, else NULL)
600    into COMPRESSION_PROGRAM (if that is non-NULL).  If trouble, do
601    a fatal error.  */
602
603 char *
604 readfile (filename, sizep, create_callback,
605           opened_filename, compression_program)
606      char *filename;
607      int *sizep;
608      void (*create_callback) ();
609      char **opened_filename;
610      char **compression_program;
611 {
612   char *real_name;
613   FILE *f;
614   int pipe_p;
615   int filled = 0;
616   int data_size = 8192;
617   char *data = xmalloc (data_size);
618
619   /* If they passed the space for the file name to return, use it.  */
620   f = open_possibly_compressed_file (filename, create_callback,
621                                      opened_filename ? opened_filename
622                                                      : &real_name,
623                                      compression_program, &pipe_p);
624
625   for (;;)
626     {
627       int nread = fread (data + filled, 1, data_size - filled, f);
628       if (nread < 0)
629         pfatal_with_name (real_name);
630       if (nread == 0)
631         break;
632
633       filled += nread;
634       if (filled == data_size)
635         {
636           data_size += 65536;
637           data = xrealloc (data, data_size);
638         }
639     }
640
641   /* We'll end up wasting space if we're not passing the filename back
642      and it is not just FILENAME, but so what.  */
643   /* We need to close the stream, since on some systems the pipe created
644      by popen is simulated by a temporary file which only gets removed
645      inside pclose.  */
646   if (pipe_p)
647     pclose (f);
648   else
649     fclose (f);
650
651   *sizep = filled;
652   return data;
653 }
654 \f
655 /* Output the old dir file, interpolating the new sections
656    and/or new entries where appropriate.  If COMPRESSION_PROGRAM is not
657    null, pipe to it to create DIRFILE.  Thus if we read dir.gz on input,
658    we'll write dir.gz on output.  */
659
660 static void
661 output_dirfile (dirfile, dir_nlines, dir_lines,
662                 n_entries_to_add, entries_to_add, input_sections,
663                 compression_program)
664       char *dirfile;
665       int dir_nlines;
666       struct line_data *dir_lines;
667       int n_entries_to_add;
668       struct spec_entry *entries_to_add;
669       struct spec_section *input_sections;
670       char *compression_program;
671 {
672   int i;
673   FILE *output;
674
675   if (compression_program)
676     {
677       char *command = concat (compression_program, ">", dirfile);
678       output = popen (command, "w");
679     }
680   else
681     output = fopen (dirfile, "w");
682
683   if (!output)
684     {
685       perror (dirfile);
686       xexit (1);
687     }
688
689   for (i = 0; i <= dir_nlines; i++)
690     {
691       int j;
692
693       /* If we decided to output some new entries before this line,
694          output them now.  */
695       if (dir_lines[i].add_entries_before)
696         for (j = 0; j < n_entries_to_add; j++)
697           {
698             struct spec_entry *this = dir_lines[i].add_entries_before[j];
699             if (this == 0)
700               break;
701             fputs (this->text, output);
702           }
703       /* If we decided to add some sections here
704          because there are no such sections in the file,
705          output them now.  */
706       if (dir_lines[i].add_sections_before)
707         {
708           struct spec_section *spec;
709           struct spec_section **sections;
710           int n_sections = 0;
711           struct spec_entry *entry;
712           struct spec_entry **entries;
713           int n_entries = 0;
714
715           /* Count the sections and allocate a vector for all of them.  */
716           for (spec = input_sections; spec; spec = spec->next)
717             n_sections++;
718           sections = ((struct spec_section **)
719                       xmalloc (n_sections * sizeof (struct spec_section *)));
720
721           /* Fill the vector SECTIONS with pointers to all the sections,
722              and sort them.  */
723           j = 0;
724           for (spec = input_sections; spec; spec = spec->next)
725             sections[j++] = spec;
726           qsort (sections, n_sections, sizeof (struct spec_section *),
727                  compare_section_names);
728
729           /* Count the entries and allocate a vector for all of them.  */
730           for (entry = entries_to_add; entry; entry = entry->next)
731             n_entries++;
732           entries = ((struct spec_entry **)
733                      xmalloc (n_entries * sizeof (struct spec_entry *)));
734
735           /* Fill the vector ENTRIES with pointers to all the sections,
736              and sort them.  */
737           j = 0;
738           for (entry = entries_to_add; entry; entry = entry->next)
739             entries[j++] = entry;
740           qsort (entries, n_entries, sizeof (struct spec_entry *),
741                  compare_entries_text);
742
743           /* Generate the new sections in alphabetical order.  In each
744              new section, output all of the entries that belong to that
745              section, in alphabetical order.  */
746           for (j = 0; j < n_sections; j++)
747             {
748               spec = sections[j];
749               if (spec->missing)
750                 {
751                   int k;
752
753                   putc ('\n', output);
754                   fputs (spec->name, output);
755                   putc ('\n', output);
756                   for (k = 0; k < n_entries; k++)
757                     {
758                       struct spec_section *spec1;
759                       /* Did they at all want this entry to be put into
760                          this section?  */
761                       entry = entries[k];
762                       for (spec1 = entry->entry_sections;
763                            spec1 && spec1 != entry->entry_sections_tail;
764                            spec1 = spec1->next)
765                         {
766                           if (!strcmp (spec1->name, spec->name))
767                             break;
768                         }
769                       if (spec1 && spec1 != entry->entry_sections_tail)
770                         fputs (entry->text, output);
771                     }
772                 }
773             }
774
775           free (entries);
776           free (sections);
777         }
778
779       /* Output the original dir lines unless marked for deletion.  */
780       if (i < dir_nlines && !dir_lines[i].delete)
781         {
782           fwrite (dir_lines[i].start, 1, dir_lines[i].size, output);
783           putc ('\n', output);
784         }
785     }
786
787   /* Some systems, such as MS-DOS, simulate pipes with temporary files.
788      On those systems, the compressor actually gets run inside pclose,
789      so we must call pclose.  */
790   if (compression_program)
791     pclose (output);
792   else
793     fclose (output);
794 }
795 \f
796 /* Parse the input to find the section names and the entry names it
797    specifies.  Return the number of entries to add from this file.  */
798 int
799 parse_input (lines, nlines, sections, entries)
800      const struct line_data *lines;
801      int nlines;
802      struct spec_section **sections;
803      struct spec_entry **entries;
804 {
805   int n_entries = 0;
806   int prefix_length = strlen ("INFO-DIR-SECTION ");
807   struct spec_section *head = *sections, *tail = NULL;
808   int reset_tail = 0;
809   char *start_of_this_entry = 0;
810   int ignore_sections = *sections != 0;
811   int ignore_entries  = *entries  != 0;
812
813   int i;
814
815   if (ignore_sections && ignore_entries)
816     return 0;
817
818   /* Loop here processing lines from the input file.  Each
819      INFO-DIR-SECTION entry is added to the SECTIONS linked list.
820      Each START-INFO-DIR-ENTRY block is added to the ENTRIES linked
821      list, and all its entries inherit the chain of SECTION entries
822      defined by the last group of INFO-DIR-SECTION entries we have
823      seen until that point.  */
824   for (i = 0; i < nlines; i++)
825     {
826       if (!ignore_sections
827           && !strncmp ("INFO-DIR-SECTION ", lines[i].start, prefix_length))
828         {
829           struct spec_section *next
830             = (struct spec_section *) xmalloc (sizeof (struct spec_section));
831           next->name = copy_string (lines[i].start + prefix_length,
832                                     lines[i].size - prefix_length);
833           next->next = *sections;
834           next->missing = 1;
835           if (reset_tail)
836             {
837               tail = *sections;
838               reset_tail = 0;
839             }
840           *sections = next;
841           head = *sections;
842         }
843       /* If entries were specified explicitly with command options,
844          ignore the entries in the input file.  */
845       else if (!ignore_entries)
846         {
847           if (!strncmp ("START-INFO-DIR-ENTRY", lines[i].start, lines[i].size)
848               && sizeof ("START-INFO-DIR-ENTRY") - 1 == lines[i].size)
849             {
850               if (!*sections)
851                 {
852                   /* We found an entry, but didn't yet see any sections
853                      specified.  Default to section "Miscellaneous".  */
854                   *sections = (struct spec_section *)
855                     xmalloc (sizeof (struct spec_section));
856                   (*sections)->name = "Miscellaneous";
857                   (*sections)->next = 0;
858                   (*sections)->missing = 1;
859                   head = *sections;
860                 }
861               /* Next time we see INFO-DIR-SECTION, we will reset the
862                  tail pointer.  */
863               reset_tail = 1;
864
865               if (start_of_this_entry != 0)
866                 fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"), 0, 0);
867               start_of_this_entry = lines[i + 1].start;
868             }
869           else if (start_of_this_entry)
870             {
871               if ((!strncmp ("* ", lines[i].start, 2)
872                    && lines[i].start > start_of_this_entry)
873                   || (!strncmp ("END-INFO-DIR-ENTRY",
874                                 lines[i].start, lines[i].size)
875                       && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size))
876                 {
877                   /* We found an end of this entry.  Allocate another
878                      entry, fill its data, and add it to the linked
879                      list.  */
880                   struct spec_entry *next
881                     = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
882                   next->text
883                     = copy_string (start_of_this_entry,
884                                    lines[i].start - start_of_this_entry);
885                   next->text_len = lines[i].start - start_of_this_entry;
886                   next->entry_sections = head;
887                   next->entry_sections_tail = tail;
888                   next->next = *entries;
889                   *entries = next;
890                   n_entries++;
891                   if (!strncmp ("END-INFO-DIR-ENTRY",
892                                 lines[i].start, lines[i].size)
893                       && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size)
894                     start_of_this_entry = 0;
895                   else
896                     start_of_this_entry = lines[i].start;
897                 }
898               else if (!strncmp ("END-INFO-DIR-ENTRY",
899                                  lines[i].start, lines[i].size)
900                        && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size)
901                 fatal (_("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY"), 0, 0);
902             }
903         }
904     }
905   if (start_of_this_entry != 0)
906     fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"),
907            0, 0);
908
909   /* If we ignored the INFO-DIR-ENTRY directives, we need now go back
910      and plug the names of all the sections we found into every
911      element of the ENTRIES list.  */
912   if (ignore_entries && *entries)
913     {
914       struct spec_entry *entry;
915
916       for (entry = *entries; entry; entry = entry->next)
917         {
918           entry->entry_sections = head;
919           entry->entry_sections_tail = tail;
920         }
921     }
922
923   return n_entries;
924 }
925
926 /* Parse the dir file whose basename is BASE_NAME.  Find all the
927    nodes, and their menus, and the sections of their menus.  */
928 int
929 parse_dir_file (lines, nlines, nodes, base_name)
930      struct line_data *lines;
931      int nlines;
932      struct node **nodes;
933      const char *base_name;
934 {
935   int node_header_flag = 0;
936   int something_deleted = 0;
937   int i;
938
939   *nodes = 0;
940   for (i = 0; i < nlines; i++)
941     {
942       /* Parse node header lines.  */
943       if (node_header_flag)
944         {
945           int j, end;
946           for (j = 0; j < lines[i].size; j++)
947             /* Find the node name and store it in the `struct node'.  */
948             if (!strncmp ("Node:", lines[i].start + j, 5))
949               {
950                 char *line = lines[i].start;
951                 /* Find the start of the node name.  */
952                 j += 5;
953                 while (line[j] == ' ' || line[j] == '\t')
954                   j++;
955                 /* Find the end of the node name.  */
956                 end = j;
957                 while (line[end] != 0 && line[end] != ',' && line[end] != '\n'
958                        && line[end] != '\t')
959                   end++;
960                 (*nodes)->name = copy_string (line + j, end - j);
961               }
962           node_header_flag = 0;
963         }
964
965       /* Notice the start of a node.  */
966       if (*lines[i].start == 037)
967         {
968           struct node *next = (struct node *) xmalloc (sizeof (struct node));
969
970           next->next = *nodes;
971           next->name = NULL;
972           next->start_line = i;
973           next->end_line = 0;
974           next->menu_start = NULL;
975           next->sections = NULL;
976           next->last_section = NULL;
977
978           if (*nodes != 0)
979             (*nodes)->end_line = i;
980           /* Fill in the end of the last menu section
981              of the previous node.  */
982           if (*nodes != 0 && (*nodes)->last_section != 0)
983             (*nodes)->last_section->end_line = i;
984
985           *nodes = next;
986
987           /* The following line is the header of this node;
988              parse it.  */
989           node_header_flag = 1;
990         }
991
992       /* Notice the lines that start menus.  */
993       if (*nodes != 0 && !strncmp ("* Menu:", lines[i].start, 7))
994         (*nodes)->menu_start = lines[i + 1].start;
995
996       /* Notice sections in menus.  */
997       if (*nodes != 0
998           && (*nodes)->menu_start != 0
999           && *lines[i].start != '\n'
1000           && *lines[i].start != '*'
1001           && *lines[i].start != ' '
1002           && *lines[i].start != '\t')
1003         {
1004           /* Add this menu section to the node's list.
1005              This list grows in forward order.  */
1006           struct menu_section *next
1007             = (struct menu_section *) xmalloc (sizeof (struct menu_section));
1008
1009           next->start_line = i + 1;
1010           next->next = 0;
1011           next->end_line = 0;
1012           next->name = copy_string (lines[i].start, lines[i].size);
1013           if ((*nodes)->sections)
1014             {
1015               (*nodes)->last_section->next = next;
1016               (*nodes)->last_section->end_line = i;
1017             }
1018           else
1019             (*nodes)->sections = next;
1020           (*nodes)->last_section = next;
1021         }
1022
1023       /* Check for an existing entry that should be deleted.
1024          Delete all entries which specify this file name.  */
1025       if (*lines[i].start == '*')
1026         {
1027           char *q;
1028           char *p = lines[i].start;
1029
1030           p++; /* skip * */
1031           while (*p == ' ') p++; /* ignore following spaces */
1032           q = p; /* remember this, it's the beginning of the menu item.  */
1033
1034           /* Read menu item.  */
1035           while (*p != 0 && *p != ':')
1036             p++;
1037           p++; /* skip : */
1038
1039           if (*p == ':')
1040             { /* XEmacs-style entry, as in * Mew::Messaging.  */
1041               if (menu_item_equal (q, ':', base_name))
1042                 {
1043                   lines[i].delete = 1;
1044                   something_deleted = 1;
1045                 }
1046             }
1047           else
1048             { /* Emacs-style entry, as in * Emacs: (emacs).  */
1049               while (*p == ' ') p++; /* skip spaces after : */
1050               if (*p == '(')         /* if at parenthesized (FILENAME) */
1051                 {
1052                   p++;
1053                   if (menu_item_equal (p, ')', base_name))
1054                     {
1055                       lines[i].delete = 1;
1056                       something_deleted = 1;
1057                     }
1058                 }
1059             }
1060         }
1061
1062       /* Treat lines that start with whitespace
1063          as continuations; if we are deleting an entry,
1064          delete all its continuations as well.  */
1065       else if (i > 0 && (*lines[i].start == ' ' || *lines[i].start == '\t'))
1066         {
1067           lines[i].delete = lines[i - 1].delete;
1068         }
1069     }
1070
1071   /* Finish the info about the end of the last node.  */
1072   if (*nodes != 0)
1073     {
1074       (*nodes)->end_line = nlines;
1075       if ((*nodes)->last_section != 0)
1076         (*nodes)->last_section->end_line = nlines;
1077     }
1078
1079   return something_deleted;
1080 }
1081
1082 int
1083 main (argc, argv)
1084      int argc;
1085      char **argv;
1086 {
1087   char *opened_dirfilename;
1088   char *compression_program;
1089   char *infile_sans_info;
1090   char *infile = 0, *dirfile = 0;
1091   unsigned infilelen_sans_info;
1092
1093   /* Record the text of the Info file, as a sequence of characters
1094      and as a sequence of lines.  */
1095   char *input_data = NULL;
1096   int input_size = 0;
1097   struct line_data *input_lines = NULL;
1098   int input_nlines = 0;
1099
1100   /* Record here the specified section names and directory entries.  */
1101   struct spec_section *input_sections = NULL;
1102   struct spec_entry *entries_to_add = NULL;
1103   int n_entries_to_add = 0;
1104
1105   /* Record the old text of the dir file, as plain characters,
1106      as lines, and as nodes.  */
1107   char *dir_data;
1108   int dir_size;
1109   int dir_nlines;
1110   struct line_data *dir_lines;
1111   struct node *dir_nodes;
1112
1113   /* Nonzero means --delete was specified (just delete existing entries).  */
1114   int delete_flag = 0;
1115   int something_deleted = 0;
1116   /* Nonzero means -q was specified.  */
1117   int quiet_flag = 0;
1118
1119   int i;
1120
1121 #ifdef HAVE_SETLOCALE
1122   /* Set locale via LC_ALL.  */
1123   setlocale (LC_ALL, "");
1124 #endif
1125
1126   /* Set the text message domain.  */
1127   bindtextdomain (PACKAGE, LOCALEDIR);
1128   textdomain (PACKAGE);
1129
1130   while (1)
1131     {
1132       int opt = getopt_long (argc, argv, "i:d:e:s:hHr", longopts, 0);
1133
1134       if (opt == EOF)
1135         break;
1136
1137       switch (opt)
1138         {
1139         case 0:
1140           /* If getopt returns 0, then it has already processed a
1141              long-named option.  We should do nothing.  */
1142           break;
1143
1144         case 1:
1145           abort ();
1146
1147         case 'd':
1148           if (dirfile)
1149             {
1150               fprintf (stderr, _("%s: Specify the Info directory only once.\n"),
1151                        progname);
1152               suggest_asking_for_help ();
1153             }
1154           dirfile = optarg;
1155           break;
1156
1157         case 'D':
1158           if (dirfile)
1159             {
1160               fprintf (stderr, _("%s: Specify the Info directory only once.\n"),
1161                        progname);
1162               suggest_asking_for_help ();
1163             }
1164           dirfile = concat (optarg, "", "/dir");
1165           break;
1166
1167         case 'e':
1168           {
1169             struct spec_entry *next
1170               = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
1171             int olen = strlen (optarg);
1172             if (! (*optarg != 0 && optarg[olen - 1] == '\n'))
1173               {
1174                 optarg = concat (optarg, "\n", "");
1175                 olen++;
1176               }
1177             next->text = optarg;
1178             next->text_len = olen;
1179             next->entry_sections = NULL;
1180             next->entry_sections_tail = NULL;
1181             next->next = entries_to_add;
1182             entries_to_add = next;
1183             n_entries_to_add++;
1184           }
1185           break;
1186
1187         case 'h':
1188         case 'H':
1189           print_help ();
1190           xexit (0);
1191
1192         case 'i':
1193           if (infile)
1194             {
1195               fprintf (stderr, _("%s: Specify the Info file only once.\n"),
1196                        progname);
1197               suggest_asking_for_help ();
1198             }
1199           infile = optarg;
1200           break;
1201
1202         case 'q':
1203           quiet_flag = 1;
1204           break;
1205
1206         case 'r':
1207           delete_flag = 1;
1208           break;
1209
1210         case 's':
1211           {
1212             struct spec_section *next
1213               = (struct spec_section *) xmalloc (sizeof (struct spec_section));
1214             next->name = optarg;
1215             next->next = input_sections;
1216             next->missing = 1;
1217             input_sections = next;
1218           }
1219           break;
1220
1221         case 'V':
1222           printf ("install-info (GNU %s) %s\n", PACKAGE, VERSION);
1223           puts ("");
1224           printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
1225 There is NO warranty.  You may redistribute this software\n\
1226 under the terms of the GNU General Public License.\n\
1227 For more information about these matters, see the files named COPYING.\n"),
1228                   "2002");
1229           xexit (0);
1230
1231         default:
1232           suggest_asking_for_help ();
1233         }
1234     }
1235
1236   /* Interpret the non-option arguments as file names.  */
1237   for (; optind < argc; ++optind)
1238     {
1239       if (infile == 0)
1240         infile = argv[optind];
1241       else if (dirfile == 0)
1242         dirfile = argv[optind];
1243       else
1244         error (_("excess command line argument `%s'"), argv[optind], 0);
1245     }
1246
1247   if (!infile)
1248     fatal (_("No input file specified; try --help for more information."),
1249            0, 0);
1250   if (!dirfile)
1251     fatal (_("No dir file specified; try --help for more information."), 0, 0);
1252
1253   /* Read the Info file and parse it into lines, unless we're deleting.  */
1254   if (!delete_flag)
1255     {
1256       input_data = readfile (infile, &input_size, NULL, NULL, NULL);
1257       input_lines = findlines (input_data, input_size, &input_nlines);
1258     }
1259
1260   i = parse_input (input_lines, input_nlines,
1261                    &input_sections, &entries_to_add);
1262   if (i > n_entries_to_add)
1263     n_entries_to_add = i;
1264
1265   if (!delete_flag)
1266     {
1267       if (entries_to_add == 0)
1268         { /* No need to abort here, the original info file may not
1269              have the requisite Texinfo commands.  This is not
1270              something an installer should have to correct (it's a
1271              problem for the maintainer), and there's no need to cause
1272              subsequent parts of `make install' to fail.  */
1273           warning (_("no info dir entry in `%s'"), infile, 0);
1274           xexit (0);
1275         }
1276
1277       /* If the entries came from the command-line arguments, their
1278          entry_sections pointers are not yet set.  Walk the chain of
1279          the entries and for each entry update entry_sections to point
1280          to the head of the list of sections where this entry should
1281          be put.  Note that all the entries specified on the command
1282          line get put into ALL the sections we've got, either from the
1283          Info file, or (under --section) from the command line,
1284          because in the loop below every entry inherits the entire
1285          chain of sections.  */
1286       if (n_entries_to_add > 0 && entries_to_add->entry_sections == NULL)
1287         {
1288           struct spec_entry *ep;
1289
1290           /* If we got no sections, default to "Miscellaneous".  */
1291           if (input_sections == NULL)
1292             {
1293               input_sections = (struct spec_section *)
1294                 xmalloc (sizeof (struct spec_section));
1295               input_sections->name = "Miscellaneous";
1296               input_sections->next = NULL;
1297               input_sections->missing = 1;
1298             }
1299           for (ep = entries_to_add; ep; ep = ep->next)
1300             ep->entry_sections = input_sections;
1301         }
1302     }
1303
1304   /* Now read in the Info dir file.  */
1305   dir_data = readfile (dirfile, &dir_size, ensure_dirfile_exists,
1306                        &opened_dirfilename, &compression_program);
1307   dir_lines = findlines (dir_data, dir_size, &dir_nlines);
1308
1309   /* We will be comparing the entries in the dir file against the
1310      current filename, so need to strip off any directory prefix and/or
1311      [.info][.gz] suffix.  */
1312   {
1313     char *infile_basename = infile + strlen (infile);
1314
1315     if (HAVE_DRIVE (infile))
1316       infile += 2;      /* get past the drive spec X: */
1317
1318     while (infile_basename > infile && !IS_SLASH (infile_basename[-1]))
1319       infile_basename--;
1320
1321     infile_sans_info = strip_info_suffix (infile_basename);
1322     infilelen_sans_info = strlen (infile_sans_info);
1323   }
1324
1325   something_deleted
1326     = parse_dir_file (dir_lines, dir_nlines, &dir_nodes, infile_sans_info);
1327
1328   /* Decide where to add the new entries (unless --delete was used).
1329      Find the menu sections to add them in.
1330      In each section, find the proper alphabetical place to add
1331      each of the entries.  */
1332
1333   if (!delete_flag)
1334     {
1335       struct node *node;
1336       struct menu_section *section;
1337       struct spec_section *spec;
1338
1339       for (node = dir_nodes; node; node = node->next)
1340         for (section = node->sections; section; section = section->next)
1341           {
1342             for (i = section->end_line; i > section->start_line; i--)
1343               if (dir_lines[i - 1].size != 0)
1344                 break;
1345             section->end_line = i;
1346
1347             for (spec = input_sections; spec; spec = spec->next)
1348               if (!strcmp (spec->name, section->name))
1349                 break;
1350             if (spec)
1351               {
1352                 int add_at_line = section->end_line;
1353                 struct spec_entry *entry;
1354                 /* Say we have found at least one section with this name,
1355                    so we need not add such a section.  */
1356                 spec->missing = 0;
1357                 /* For each entry, find the right place in this section
1358                    to add it.  */
1359                 for (entry = entries_to_add; entry; entry = entry->next)
1360                   {
1361                     /* Did they at all want this entry to be put into
1362                        this section?  */
1363                     for (spec = entry->entry_sections;
1364                          spec && spec != entry->entry_sections_tail;
1365                          spec = spec->next)
1366                       {
1367                         if (!strcmp (spec->name, section->name))
1368                           break;
1369                       }
1370                     if (!spec || spec == entry->entry_sections_tail)
1371                       continue;
1372                     
1373                     /* Subtract one because dir_lines is zero-based,
1374                        but the `end_line' and `start_line' members are
1375                        one-based.  */
1376                     for (i = section->end_line - 1;
1377                          i >= section->start_line - 1; i--)
1378                       {
1379                         /* If an entry exists with the same name,
1380                            and was not marked for deletion
1381                            (which means it is for some other file),
1382                            we are in trouble.  */
1383                         if (dir_lines[i].start[0] == '*'
1384                             && menu_line_equal (entry->text, entry->text_len,
1385                                                 dir_lines[i].start,
1386                                                 dir_lines[i].size)
1387                             && !dir_lines[i].delete)
1388                           fatal (_("menu item `%s' already exists, for file `%s'"),
1389                                  extract_menu_item_name (entry->text),
1390                                  extract_menu_file_name (dir_lines[i].start));
1391                         if (dir_lines[i].start[0] == '*'
1392                             && menu_line_lessp (entry->text, entry->text_len,
1393                                                 dir_lines[i].start,
1394                                                 dir_lines[i].size))
1395                           add_at_line = i;
1396                       }
1397                     insert_entry_here (entry, add_at_line,
1398                                        dir_lines, n_entries_to_add);
1399                   }
1400               }
1401           }
1402
1403       /* Mark the end of the Top node as the place to add any
1404          new sections that are needed.  */
1405       for (node = dir_nodes; node; node = node->next)
1406         if (node->name && strcmp (node->name, "Top") == 0)
1407           dir_lines[node->end_line].add_sections_before = 1;
1408     }
1409
1410   if (delete_flag && !something_deleted && !quiet_flag)
1411     warning (_("no entries found for `%s'; nothing deleted"), infile, 0);
1412
1413   output_dirfile (opened_dirfilename, dir_nlines, dir_lines, n_entries_to_add,
1414                   entries_to_add, input_sections, compression_program);
1415
1416   xexit (0);
1417 }
1418 \f
1419 /* Divide the text at DATA (of SIZE bytes) into lines.
1420    Return a vector of struct line_data describing the lines.
1421    Store the length of that vector into *NLINESP.  */
1422
1423 struct line_data *
1424 findlines (data, size, nlinesp)
1425      char *data;
1426      int size;
1427      int *nlinesp;
1428 {
1429   int i;
1430   int lineflag = 1;
1431   int lines_allocated = 511;
1432   int filled = 0;
1433   struct line_data *lines
1434     = xmalloc ((lines_allocated + 1) * sizeof (struct line_data));
1435
1436   for (i = 0; i < size; i++)
1437     {
1438       if (lineflag)
1439         {
1440           if (filled == lines_allocated)
1441             {
1442               /* try to keep things somewhat page-aligned */
1443               lines_allocated = ((lines_allocated + 1) * 2) - 1;
1444               lines = xrealloc (lines, (lines_allocated + 1)
1445                                        * sizeof (struct line_data));
1446             }
1447           lines[filled].start = &data[i];
1448           lines[filled].add_entries_before = 0;
1449           lines[filled].add_sections_before = 0;
1450           lines[filled].delete = 0;
1451           if (filled > 0)
1452             lines[filled - 1].size
1453               = lines[filled].start - lines[filled - 1].start - 1;
1454           filled++;
1455         }
1456       lineflag = (data[i] == '\n');
1457     }
1458   if (filled > 0)
1459     lines[filled - 1].size = &data[i] - lines[filled - 1].start - lineflag;
1460
1461   /* Do not leave garbage in the last element.  */
1462   lines[filled].start = NULL;
1463   lines[filled].add_entries_before = NULL;
1464   lines[filled].add_sections_before = 0;
1465   lines[filled].delete = 0;
1466   lines[filled].size = 0;
1467
1468   *nlinesp = filled;
1469   return lines;
1470 }
1471 \f
1472 /* Compare the menu item names in LINE1 (line length LEN1)
1473    and LINE2 (line length LEN2).  Return 1 if the item name
1474    in LINE1 is less, 0 otherwise.  */
1475
1476 int
1477 menu_line_lessp (line1, len1, line2, len2)
1478      char *line1;
1479      int len1;
1480      char *line2;
1481      int len2;
1482 {
1483   int minlen = (len1 < len2 ? len1 : len2);
1484   int i;
1485
1486   for (i = 0; i < minlen; i++)
1487     {
1488       /* If one item name is a prefix of the other,
1489          the former one is less.  */
1490       if (line1[i] == ':' && line2[i] != ':')
1491         return 1;
1492       if (line2[i] == ':' && line1[i] != ':')
1493         return 0;
1494       /* If they both continue and differ, one is less.  */
1495       if (line1[i] < line2[i])
1496         return 1;
1497       if (line1[i] > line2[i])
1498         return 0;
1499     }
1500   /* With a properly formatted dir file,
1501      we can only get here if the item names are equal.  */
1502   return 0;
1503 }
1504
1505 /* Compare the menu item names in LINE1 (line length LEN1)
1506    and LINE2 (line length LEN2).  Return 1 if the item names are equal,
1507    0 otherwise.  */
1508
1509 int
1510 menu_line_equal (line1, len1, line2, len2)
1511      char *line1;
1512      int len1;
1513      char *line2;
1514      int len2;
1515 {
1516   int minlen = (len1 < len2 ? len1 : len2);
1517   int i;
1518
1519   for (i = 0; i < minlen; i++)
1520     {
1521       /* If both item names end here, they are equal.  */
1522       if (line1[i] == ':' && line2[i] == ':')
1523         return 1;
1524       /* If they both continue and differ, one is less.  */
1525       if (line1[i] != line2[i])
1526         return 0;
1527     }
1528   /* With a properly formatted dir file,
1529      we can only get here if the item names are equal.  */
1530   return 1;
1531 }
1532 \f
1533 /* This is the comparison function for qsort
1534    for a vector of pointers to struct spec_section.
1535    Compare the section names.  */
1536
1537 int
1538 compare_section_names (sec1, sec2)
1539      struct spec_section **sec1, **sec2;
1540 {
1541   char *name1 = (*sec1)->name;
1542   char *name2 = (*sec2)->name;
1543   return strcmp (name1, name2);
1544 }
1545
1546 /* This is the comparison function for qsort
1547    for a vector of pointers to struct spec_entry.
1548    Compare the entries' text.  */
1549
1550 int
1551 compare_entries_text (entry1, entry2)
1552      struct spec_entry **entry1, **entry2;
1553 {
1554   char *text1 = (*entry1)->text;
1555   char *text2 = (*entry2)->text;
1556   char *colon1 = strchr (text1, ':');
1557   char *colon2 = strchr (text2, ':');
1558   int len1, len2;
1559
1560   if (!colon1)
1561     len1 = strlen (text1);
1562   else
1563     len1 = colon1 - text1;
1564   if (!colon2)
1565     len2 = strlen (text2);
1566   else
1567     len2 = colon2 - text2;
1568   return strncmp (text1, text2, len1 <= len2 ? len1 : len2);
1569 }
1570
1571 /* Insert ENTRY into the add_entries_before vector
1572    for line number LINE_NUMBER of the dir file.
1573    DIR_LINES and N_ENTRIES carry information from like-named variables
1574    in main.  */
1575
1576 void
1577 insert_entry_here (entry, line_number, dir_lines, n_entries)
1578      struct spec_entry *entry;
1579      int line_number;
1580      struct line_data *dir_lines;
1581      int n_entries;
1582 {
1583   int i, j;
1584
1585   if (dir_lines[line_number].add_entries_before == 0)
1586     {
1587       dir_lines[line_number].add_entries_before
1588         = (struct spec_entry **) xmalloc (n_entries * sizeof (struct spec_entry *));
1589       for (i = 0; i < n_entries; i++)
1590         dir_lines[line_number].add_entries_before[i] = 0;
1591     }
1592
1593   /* Find the place where this entry belongs.  If there are already
1594      several entries to add before LINE_NUMBER, make sure they are in
1595      alphabetical order.  */
1596   for (i = 0; i < n_entries; i++)
1597     if (dir_lines[line_number].add_entries_before[i] == 0
1598         || menu_line_lessp (entry->text, strlen (entry->text),
1599                             dir_lines[line_number].add_entries_before[i]->text,
1600                             strlen (dir_lines[line_number].add_entries_before[i]->text)))
1601       break;
1602
1603   if (i == n_entries)
1604     abort ();
1605
1606   /* If we need to plug ENTRY into the middle of the
1607      ADD_ENTRIES_BEFORE array, move the entries which should be output
1608      after this one down one notch, before adding a new one.  */
1609   if (dir_lines[line_number].add_entries_before[i] != 0)
1610     for (j = n_entries - 1; j > i; j--)
1611       dir_lines[line_number].add_entries_before[j]
1612         = dir_lines[line_number].add_entries_before[j - 1];
1613
1614   dir_lines[line_number].add_entries_before[i] = entry;
1615 }