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