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