1214152Sed/* install-info -- create Info directory entry(ies) for an Info file.
2214152Sed   $Id: install-info.c,v 1.1 2004/10/28 18:14:11 zooey Exp $
3214152Sed
4214152Sed   Copyright (C) 1996, 97, 98 Free Software Foundation, Inc.
5222656Sed
6222656Sed   This program is free software; you can redistribute it and/or modify
7214152Sed   it under the terms of the GNU General Public License as published by
8214152Sed   the Free Software Foundation; either version 2 of the License, or
9214152Sed   (at your option) any later version.
10214152Sed
11214152Sed   This program is distributed in the hope that it will be useful,
12214152Sed   but WITHOUT ANY WARRANTY; without even the implied warranty of
13214152Sed   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14214152Sed   GNU General Public License for more details.
15263560Sdim
16263560Sdim   You should have received a copy of the GNU General Public License
17263764Sdim   along with this program; if not, write to the Free Software
18214152Sed   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/
19214152Sed
20214152Sed#include "system.h"
21214152Sed#include <getopt.h>
22214152Sed
23214152Sed#ifdef HAVE_LIBZ
24214152Sed#include <zlib.h>
25214152Sed#endif
26214152Sed
27214152Sed/* Name this program was invoked with.  */
28214152Sedchar *progname;
29214152Sed
30214152Sedchar *readfile ();
31214152Sedstruct line_data *findlines ();
32214152Sedvoid fatal ();
33214152Sedvoid insert_entry_here ();
34214152Sedint compare_section_names ();
35214152Sed
36214152Sedstruct spec_entry;
37214152Sed
38214152Sed/* Data structures.  */
39214152Sed
40214152Sed
41214152Sed/* Record info about a single line from a file as read into core.  */
42214152Sedstruct line_data
43214152Sed{
44214152Sed  /* The start of the line.  */
45263764Sdim  char *start;
46  /* The number of characters in the line,
47     excluding the terminating newline.  */
48  int size;
49  /* Vector containing pointers to the entries to add before this line.
50     The vector is null-terminated.  */
51  struct spec_entry **add_entries_before;
52  /* 1 means output any needed new sections before this line.  */
53  int add_sections_before;
54  /* 1 means don't output this line.  */
55  int delete;
56};
57
58
59/* This is used for a list of the specified menu section names
60   in which entries should be added.  */
61struct spec_section
62{
63  struct spec_section *next;
64  char *name;
65  /* 1 means we have not yet found an existing section with this name
66     in the dir file--so we will need to add a new section.  */
67  int missing;
68};
69
70
71/* This is used for a list of the entries specified to be added.  */
72struct spec_entry
73{
74  struct spec_entry *next;
75  char *text;
76};
77
78
79/* This is used for a list of nodes found by parsing the dir file.  */
80struct node
81{
82  struct node *next;
83  /* The node name.  */
84  char *name;
85  /* The line number of the line where the node starts.
86     This is the line that contains control-underscore.  */
87  int start_line;
88  /* The line number of the line where the node ends,
89     which is the end of the file or where the next line starts.  */
90  int end_line;
91  /* Start of first line in this node's menu
92     (the line after the * Menu: line).  */
93  char *menu_start;
94  /* The start of the chain of sections in this node's menu.  */
95  struct menu_section *sections;
96  /* The last menu section in the chain.  */
97  struct menu_section *last_section;
98};
99
100
101/* This is used for a list of sections found in a node's menu.
102   Each  struct node  has such a list in the  sections  field.  */
103struct menu_section
104{
105  struct menu_section *next;
106  char *name;
107  /* Line number of start of section.  */
108  int start_line;
109  /* Line number of end of section.  */
110  int end_line;
111};
112
113/* Memory allocation and string operations.  */
114
115/* Like malloc but get fatal error if memory is exhausted.  */
116void *
117xmalloc (size)
118     unsigned int size;
119{
120  extern void *malloc ();
121  void *result = malloc (size);
122  if (result == NULL)
123    fatal (_("virtual memory exhausted"), 0);
124  return result;
125}
126
127/* Like realloc but get fatal error if memory is exhausted.  */
128void *
129xrealloc (obj, size)
130     void *obj;
131     unsigned int size;
132{
133  extern void *realloc ();
134  void *result = realloc (obj, size);
135  if (result == NULL)
136    fatal (_("virtual memory exhausted"), 0);
137  return result;
138}
139
140/* Return a newly-allocated string
141   whose contents concatenate those of S1, S2, S3.  */
142char *
143concat (s1, s2, s3)
144     char *s1, *s2, *s3;
145{
146  int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
147  char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
148
149  strcpy (result, s1);
150  strcpy (result + len1, s2);
151  strcpy (result + len1 + len2, s3);
152  *(result + len1 + len2 + len3) = 0;
153
154  return result;
155}
156
157/* Return a string containing SIZE characters
158   copied from starting at STRING.  */
159
160char *
161copy_string (string, size)
162     char *string;
163     int size;
164{
165  int i;
166  char *copy = (char *) xmalloc (size + 1);
167  for (i = 0; i < size; i++)
168    copy[i] = string[i];
169  copy[size] = 0;
170  return copy;
171}
172
173/* Error message functions.  */
174
175/* Print error message.  S1 is printf control string, S2 and S3 args for it. */
176
177/* VARARGS1 */
178void
179error (s1, s2, s3)
180     char *s1, *s2, *s3;
181{
182  fprintf (stderr, "%s: ", progname);
183  fprintf (stderr, s1, s2, s3);
184  putc ('\n', stderr);
185}
186
187/* VARARGS1 */
188void
189warning (s1, s2, s3)
190     char *s1, *s2, *s3;
191{
192  fprintf (stderr, _("%s: warning: "), progname);
193  fprintf (stderr, s1, s2, s3);
194  putc ('\n', stderr);
195}
196
197/* Print error message and exit.  */
198
199void
200fatal (s1, s2, s3)
201     char *s1, *s2, *s3;
202{
203  error (s1, s2, s3);
204  exit (1);
205}
206
207/* Print fatal error message based on errno, with file name NAME.  */
208
209void
210pfatal_with_name (name)
211     char *name;
212{
213  char *s = concat ("", strerror (errno), _(" for %s"));
214  fatal (s, name);
215}
216
217/* Given the full text of a menu entry, null terminated,
218   return just the menu item name (copied).  */
219
220char *
221extract_menu_item_name (item_text)
222     char *item_text;
223{
224  char *p;
225
226  if (*item_text == '*')
227    item_text++;
228  while (*item_text == ' ')
229    item_text++;
230
231  p = item_text;
232  while (*p && *p != ':') p++;
233  return copy_string (item_text, p - item_text);
234}
235
236/* Given the full text of a menu entry, terminated by null or newline,
237   return just the menu item file (copied).  */
238
239char *
240extract_menu_file_name (item_text)
241     char *item_text;
242{
243  char *p = item_text;
244
245  /* If we have text that looks like * ITEM: (FILE)NODE...,
246     extract just FILE.  Otherwise return "(none)".  */
247
248  if (*p == '*')
249    p++;
250  while (*p == ' ')
251    p++;
252
253  /* Skip to and past the colon.  */
254  while (*p && *p != '\n' && *p != ':') p++;
255  if (*p == ':') p++;
256
257  /* Skip past the open-paren.  */
258  while (1)
259    {
260      if (*p == '(')
261        break;
262      else if (*p == ' ' || *p == '\t')
263        p++;
264      else
265        return "(none)";
266    }
267  p++;
268
269  item_text = p;
270
271  /* File name ends just before the close-paren.  */
272  while (*p && *p != '\n' && *p != ')') p++;
273  if (*p != ')')
274    return "(none)";
275
276  return copy_string (item_text, p - item_text);
277}
278
279void
280suggest_asking_for_help ()
281{
282  fprintf (stderr, _("\tTry `%s --help' for a complete list of options.\n"),
283           progname);
284  exit (1);
285}
286
287void
288print_help ()
289{
290  printf (_("Usage: %s [OPTION]... [INFO-FILE [DIR-FILE]]\n\
291\n\
292Install INFO-FILE in the Info directory file DIR-FILE.\n\
293\n\
294Options:\n\
295--delete          Delete existing entries in INFO-FILE;\n\
296                    don't insert any new entries.\n\
297--dir-file=NAME   Specify file name of Info directory file.\n\
298                    This is equivalent to using the DIR-FILE argument.\n\
299--entry=TEXT      Insert TEXT as an Info directory entry.\n\
300                    TEXT should have the form of an Info menu item line\n\
301                    plus zero or more extra lines starting with whitespace.\n\
302                    If you specify more than one entry, they are all added.\n\
303                    If you don't specify any entries, they are determined\n\
304                    from information in the Info file itself.\n\
305--help            Display this help and exit.\n\
306--info-file=FILE  Specify Info file to install in the directory.\n\
307                    This is equivalent to using the INFO-FILE argument.\n\
308--info-dir=DIR    Same as --dir-file=DIR/dir.\n\
309--item=TEXT       Same as --entry TEXT.\n\
310                    An Info directory entry is actually a menu item.\n\
311--quiet           Suppress warnings.\n\
312--remove          Same as --delete.\n\
313--section=SEC     Put this file's entries in section SEC of the directory.\n\
314                    If you specify more than one section, all the entries\n\
315                    are added in each of the sections.\n\
316                    If you don't specify any sections, they are determined\n\
317                    from information in the Info file itself.\n\
318--version         Display version information and exit.\n\
319\n\
320Email bug reports to bug-texinfo@gnu.org.\n\
321"), progname);
322}
323
324
325/* If DIRFILE does not exist, create a minimal one (or abort).  If it
326   already exists, do nothing.  */
327
328void
329ensure_dirfile_exists (dirfile)
330     char *dirfile;
331{
332  int desc = open (dirfile, O_RDONLY);
333  if (desc < 0 && errno == ENOENT)
334    {
335      FILE *f;
336      char *readerr = strerror (errno);
337      close (desc);
338      f = fopen (dirfile, "w");
339      if (f)
340        {
341          fputs (_("This is the file .../info/dir, which contains the\n\
342topmost node of the Info hierarchy, called (dir)Top.\n\
343The first time you invoke Info you start off looking at this node.\n\
344\n\
345File: dir,\tNode: Top,\tThis is the top of the INFO tree\n\
346\n\
347  This (the Directory node) gives a menu of major topics.\n\
348  Typing \"q\" exits, \"?\" lists all Info commands, \"d\" returns here,\n\
349  \"h\" gives a primer for first-timers,\n\
350  \"mEmacs<Return>\" visits the Emacs manual, etc.\n\
351\n\
352  In Emacs, you can click mouse button 2 on a menu item or cross reference\n\
353  to select it.\n\
354\n\
355* Menu:\n\
356"), f);
357          if (fclose (f) < 0)
358            pfatal_with_name (dirfile);
359        }
360      else
361        {
362          /* Didn't exist, but couldn't open for writing.  */
363          fprintf (stderr,
364                   _("%s: could not read (%s) and could not create (%s)\n"),
365                   dirfile, readerr, strerror (errno));
366          exit (1);
367        }
368    }
369  else
370    close (desc); /* It already existed, so fine.  */
371}
372
373/* This table defines all the long-named options, says whether they
374   use an argument, and maps them into equivalent single-letter options.  */
375
376struct option longopts[] =
377{
378  { "delete",    no_argument, NULL, 'r' },
379  { "dir-file",  required_argument, NULL, 'd' },
380  { "entry",     required_argument, NULL, 'e' },
381  { "help",      no_argument, NULL, 'h' },
382  { "info-dir",  required_argument, NULL, 'D' },
383  { "info-file", required_argument, NULL, 'i' },
384  { "item",      required_argument, NULL, 'e' },
385  { "quiet",     no_argument, NULL, 'q' },
386  { "remove",    no_argument, NULL, 'r' },
387  { "section",   required_argument, NULL, 's' },
388  { "version",   no_argument, NULL, 'V' },
389  { 0 }
390};
391
392
393int
394main (argc, argv)
395     int argc;
396     char **argv;
397{
398  char *infile = 0, *dirfile = 0;
399  char *infile_sans_info;
400  unsigned infilelen_sans_info;
401  FILE *output;
402
403  /* Record the text of the Info file, as a sequence of characters
404     and as a sequence of lines.  */
405  char *input_data;
406  int input_size;
407  struct line_data *input_lines;
408  int input_nlines;
409
410  /* Record here the specified section names and directory entries.  */
411  struct spec_section *input_sections = NULL;
412  struct spec_entry *entries_to_add = NULL;
413  int n_entries_to_add = 0;
414
415  /* Record the old text of the dir file, as plain characters,
416     as lines, and as nodes.  */
417  char *dir_data;
418  int dir_size;
419  int dir_nlines;
420  struct line_data *dir_lines;
421  struct node *dir_nodes;
422
423  /* Nonzero means --delete was specified (just delete existing entries).  */
424  int delete_flag = 0;
425  int something_deleted = 0;
426  /* Nonzero means -q was specified.  */
427  int quiet_flag = 0;
428
429  int node_header_flag;
430  int prefix_length;
431  int i;
432
433  progname = argv[0];
434
435#ifdef HAVE_SETLOCALE
436  /* Set locale via LC_ALL.  */
437  setlocale (LC_ALL, "");
438#endif
439
440  /* Set the text message domain.  */
441  bindtextdomain (PACKAGE, LOCALEDIR);
442  textdomain (PACKAGE);
443
444  while (1)
445    {
446      int opt = getopt_long (argc, argv, "i:d:e:s:hHr", longopts, 0);
447
448      if (opt == EOF)
449        break;
450
451      switch (opt)
452        {
453        case 0:
454          /* If getopt returns 0, then it has already processed a
455             long-named option.  We should do nothing.  */
456          break;
457
458        case 1:
459          abort ();
460
461        case 'd':
462          if (dirfile)
463            {
464              fprintf (stderr, _("%s: Specify the Info directory only once.\n"),
465                       progname);
466              suggest_asking_for_help ();
467            }
468          dirfile = optarg;
469          break;
470
471        case 'D':
472          if (dirfile)
473            {
474              fprintf (stderr, _("%s: Specify the Info directory only once.\n"),
475                       progname);
476              suggest_asking_for_help ();
477            }
478          dirfile = concat (optarg, "", "/dir");
479          break;
480
481        case 'e':
482          {
483            struct spec_entry *next
484              = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
485            if (! (*optarg != 0 && optarg[strlen (optarg) - 1] == '\n'))
486              optarg = concat (optarg, "\n", "");
487            next->text = optarg;
488            next->next = entries_to_add;
489            entries_to_add = next;
490            n_entries_to_add++;
491          }
492          break;
493
494        case 'h':
495        case 'H':
496          print_help ();
497          exit (0);
498
499        case 'i':
500          if (infile)
501            {
502              fprintf (stderr, _("%s: Specify the Info file only once.\n"),
503                       progname);
504              suggest_asking_for_help ();
505            }
506          infile = optarg;
507          break;
508
509        case 'q':
510          quiet_flag = 1;
511          break;
512
513        case 'r':
514          delete_flag = 1;
515          break;
516
517        case 's':
518          {
519            struct spec_section *next
520              = (struct spec_section *) xmalloc (sizeof (struct spec_section));
521            next->name = optarg;
522            next->next = input_sections;
523            next->missing = 1;
524            input_sections = next;
525          }
526          break;
527
528        case 'V':
529          printf ("install-info (GNU %s) %s\n", PACKAGE, VERSION);
530	  printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
531There is NO warranty.  You may redistribute this software\n\
532under the terms of the GNU General Public License.\n\
533For more information about these matters, see the files named COPYING.\n"),
534		  "1998");
535          exit (0);
536
537        default:
538          suggest_asking_for_help ();
539        }
540    }
541
542  /* Interpret the non-option arguments as file names.  */
543  for (; optind < argc; ++optind)
544    {
545      if (infile == 0)
546        infile = argv[optind];
547      else if (dirfile == 0)
548        dirfile = argv[optind];
549      else
550        error (_("excess command line argument `%s'"), argv[optind]);
551    }
552
553  if (!infile)
554    fatal (_("No input file specified; try --help for more information."));
555  if (!dirfile)
556    fatal (_("No dir file specified; try --help for more information."));
557
558  /* Read the Info file and parse it into lines.  */
559
560  input_data = readfile (infile, &input_size);
561  input_lines = findlines (input_data, input_size, &input_nlines);
562
563  /* Parse the input file to find the section names it specifies.  */
564
565  if (input_sections == 0)
566    {
567      prefix_length = strlen ("INFO-DIR-SECTION ");
568      for (i = 0; i < input_nlines; i++)
569        {
570          if (!strncmp ("INFO-DIR-SECTION ", input_lines[i].start,
571                        prefix_length))
572            {
573              struct spec_section *next
574                = (struct spec_section *) xmalloc (sizeof (struct spec_section));
575              next->name = copy_string (input_lines[i].start + prefix_length,
576                                        input_lines[i].size - prefix_length);
577              next->next = input_sections;
578              next->missing = 1;
579              input_sections = next;
580            }
581        }
582    }
583
584  /* Default to section "Miscellaneous" if no sections specified.  */
585  if (input_sections == 0)
586    {
587      input_sections
588        = (struct spec_section *) xmalloc (sizeof (struct spec_section));
589      input_sections->name = "Miscellaneous";
590      input_sections->next = 0;
591      input_sections->missing = 1;
592    }
593
594  /* Now find the directory entries specified in the file
595     and put them on entries_to_add.  But not if entries
596     were specified explicitly with command options.  */
597
598  if (entries_to_add == 0)
599    {
600      char *start_of_this_entry = 0;
601      for (i = 0; i < input_nlines; i++)
602        {
603          if (!strncmp ("START-INFO-DIR-ENTRY", input_lines[i].start,
604                        input_lines[i].size)
605              && sizeof ("START-INFO-DIR-ENTRY") - 1 == input_lines[i].size)
606            {
607              if (start_of_this_entry != 0)
608                fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"));
609              start_of_this_entry = input_lines[i + 1].start;
610            }
611          if (!strncmp ("END-INFO-DIR-ENTRY", input_lines[i].start,
612                        input_lines[i].size)
613              && sizeof ("END-INFO-DIR-ENTRY") - 1 == input_lines[i].size)
614            {
615              if (start_of_this_entry != 0)
616                {
617                  struct spec_entry *next
618                    = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
619                  next->text = copy_string (start_of_this_entry,
620                                            input_lines[i].start - start_of_this_entry);
621                  next->next = entries_to_add;
622                  entries_to_add = next;
623                  n_entries_to_add++;
624                  start_of_this_entry = 0;
625                }
626              else
627                fatal (_("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY"));
628            }
629        }
630      if (start_of_this_entry != 0)
631        fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"));
632    }
633
634  if (!delete_flag)
635    if (entries_to_add == 0)
636      { /* No need to abort here, the original info file may not have
637           the requisite Texinfo commands.  This is not something an
638           installer should have to correct (it's a problem for the
639           maintainer), and there's no need to cause subsequent parts of
640           `make install' to fail.  */
641        warning (_("no info dir entry in `%s'"), infile);
642        exit (0);
643      }
644
645  /* Now read in the Info dir file.  */
646  ensure_dirfile_exists (dirfile);
647  dir_data = readfile (dirfile, &dir_size);
648  dir_lines = findlines (dir_data, dir_size, &dir_nlines);
649
650  /* We will be comparing the entries in the dir file against the
651     current filename, so need to strip off any directory prefix and any
652     .info suffix.  */
653  {
654    unsigned basename_len;
655    char *infile_basename = strrchr (infile, '/');
656    if (infile_basename)
657      infile_basename++;
658    else
659      infile_basename = infile;
660
661    basename_len = strlen (infile_basename);
662    infile_sans_info
663      = (strlen (infile_basename) > 5
664         && strcmp (infile_basename + basename_len - 5, ".info") == 0)
665        ? copy_string (infile_basename, basename_len - 5)
666        : infile_basename;
667
668    infilelen_sans_info = strlen (infile_sans_info);
669  }
670
671  /* Parse the dir file.  Find all the nodes, and their menus,
672     and the sections of their menus.  */
673
674  dir_nodes = 0;
675  node_header_flag = 0;
676  for (i = 0; i < dir_nlines; i++)
677    {
678      /* Parse node header lines.  */
679      if (node_header_flag)
680        {
681          int j, end;
682          for (j = 0; j < dir_lines[i].size; j++)
683            /* Find the node name and store it in the `struct node'.  */
684            if (!strncmp ("Node:", dir_lines[i].start + j, 5))
685              {
686                char *line = dir_lines[i].start;
687                /* Find the start of the node name.  */
688                j += 5;
689                while (line[j] == ' ' || line[j] == '\t')
690                  j++;
691                /* Find the end of the node name.  */
692                end = j;
693                while (line[end] != 0 && line[end] != ',' && line[end] != '\n'
694                       && line[end] != '\t')
695                  end++;
696                dir_nodes->name = copy_string (line + j, end - j);
697              }
698          node_header_flag = 0;
699        }
700
701      /* Notice the start of a node.  */
702      if (*dir_lines[i].start == 037)
703        {
704          struct node *next
705            = (struct node *) xmalloc (sizeof (struct node));
706          next->next = dir_nodes;
707          next->name = NULL;
708          next->start_line = i;
709          next->end_line = 0;
710          next->menu_start = NULL;
711          next->sections = NULL;
712          next->last_section = NULL;
713
714          if (dir_nodes != 0)
715            dir_nodes->end_line = i;
716          /* Fill in the end of the last menu section
717             of the previous node.  */
718          if (dir_nodes != 0 && dir_nodes->last_section != 0)
719            dir_nodes->last_section->end_line = i;
720
721          dir_nodes = next;
722
723          /* The following line is the header of this node;
724             parse it.  */
725          node_header_flag = 1;
726        }
727
728      /* Notice the lines that start menus.  */
729      if (dir_nodes != 0
730          && !strncmp ("* Menu:", dir_lines[i].start, 7))
731        dir_nodes->menu_start = dir_lines[i + 1].start;
732
733      /* Notice sections in menus.  */
734      if (dir_nodes != 0
735          && dir_nodes->menu_start != 0
736          && *dir_lines[i].start != '\n'
737          && *dir_lines[i].start != '*'
738          && *dir_lines[i].start != ' '
739          && *dir_lines[i].start != '\t')
740        {
741          /* Add this menu section to the node's list.
742             This list grows in forward order.  */
743          struct menu_section *next
744            = (struct menu_section *) xmalloc (sizeof (struct menu_section));
745          next->start_line = i + 1;
746          next->next = 0;
747          next->end_line = 0;
748          next->name = copy_string (dir_lines[i].start, dir_lines[i].size);
749          if (dir_nodes->sections)
750            {
751              dir_nodes->last_section->next = next;
752              dir_nodes->last_section->end_line = i;
753            }
754          else
755            dir_nodes->sections = next;
756          dir_nodes->last_section = next;
757        }
758
759      /* Check for an existing entry that should be deleted.
760         Delete all entries which specify this file name.  */
761      if (*dir_lines[i].start == '*')
762        {
763          char *p = dir_lines[i].start;
764
765          while (*p != 0 && *p != ':')
766            p++;
767          p++;
768          while (*p == ' ') p++;
769          if (*p == '(')
770            {
771              p++;
772              if ((dir_lines[i].size
773                   > (p - dir_lines[i].start + infilelen_sans_info))
774                  && !strncmp (p, infile_sans_info, infilelen_sans_info)
775                  && (p[infilelen_sans_info] == ')'
776                      || !strncmp (p + infilelen_sans_info, ".info)", 6)))
777                {
778                  dir_lines[i].delete = 1;
779                  something_deleted = 1;
780                }
781            }
782        }
783      /* Treat lines that start with whitespace
784         as continuations; if we are deleting an entry,
785         delete all its continuations as well.  */
786      else if (i > 0
787               && (*dir_lines[i].start == ' '
788                   || *dir_lines[i].start == '\t'))
789        {
790          dir_lines[i].delete = dir_lines[i - 1].delete;
791          something_deleted = 1;
792        }
793    }
794
795  /* Finish the info about the end of the last node.  */
796  if (dir_nodes != 0)
797    {
798      dir_nodes->end_line = dir_nlines;
799      if (dir_nodes->last_section != 0)
800        dir_nodes->last_section->end_line = dir_nlines;
801    }
802
803  /* Decide where to add the new entries (unless --delete was used).
804     Find the menu sections to add them in.
805     In each section, find the proper alphabetical place to add
806     each of the entries.  */
807
808  if (!delete_flag)
809    {
810      struct node *node;
811      struct menu_section *section;
812      struct spec_section *spec;
813
814      for (node = dir_nodes; node; node = node->next)
815        for (section = node->sections; section; section = section->next)
816          {
817            for (i = section->end_line; i > section->start_line; i--)
818              if (dir_lines[i - 1].size != 0)
819                break;
820            section->end_line = i;
821
822            for (spec = input_sections; spec; spec = spec->next)
823              if (!strcmp (spec->name, section->name))
824                break;
825            if (spec)
826              {
827                int add_at_line = section->end_line;
828                struct spec_entry *entry;
829                /* Say we have found at least one section with this name,
830                   so we need not add such a section.  */
831                spec->missing = 0;
832                /* For each entry, find the right place in this section
833                   to add it.  */
834                for (entry = entries_to_add; entry; entry = entry->next)
835                  {
836                    int textlen = strlen (entry->text);
837                    /* Subtract one because dir_lines is zero-based,
838                       but the `end_line' and `start_line' members are
839                       one-based.  */
840                    for (i = section->end_line - 1;
841                         i >= section->start_line - 1; i--)
842                      {
843                        /* If an entry exists with the same name,
844                           and was not marked for deletion
845                           (which means it is for some other file),
846                           we are in trouble.  */
847                        if (dir_lines[i].start[0] == '*'
848                            && menu_line_equal (entry->text, textlen,
849                                                dir_lines[i].start,
850                                                dir_lines[i].size)
851                            && !dir_lines[i].delete)
852                          fatal (_("menu item `%s' already exists, for file `%s'"),
853                                 extract_menu_item_name (entry->text),
854                                 extract_menu_file_name (dir_lines[i].start));
855                        if (dir_lines[i].start[0] == '*'
856                            && menu_line_lessp (entry->text, textlen,
857                                                dir_lines[i].start,
858                                                dir_lines[i].size))
859                          add_at_line = i;
860                      }
861                    insert_entry_here (entry, add_at_line,
862                                       dir_lines, n_entries_to_add);
863                  }
864              }
865          }
866
867      /* Mark the end of the Top node as the place to add any
868         new sections that are needed.  */
869      for (node = dir_nodes; node; node = node->next)
870        if (node->name && strcmp (node->name, "Top") == 0)
871          dir_lines[node->end_line].add_sections_before = 1;
872    }
873
874  if (delete_flag && !something_deleted && !quiet_flag)
875    warning (_("no entries found for `%s'; nothing deleted"), infile);
876
877  /* Output the old dir file, interpolating the new sections
878     and/or new entries where appropriate.  */
879
880  output = fopen (dirfile, "w");
881  if (!output)
882    {
883      perror (dirfile);
884      exit (1);
885    }
886
887  for (i = 0; i <= dir_nlines; i++)
888    {
889      int j;
890
891      /* If we decided to output some new entries before this line,
892         output them now.  */
893      if (dir_lines[i].add_entries_before)
894        for (j = 0; j < n_entries_to_add; j++)
895          {
896            struct spec_entry *this = dir_lines[i].add_entries_before[j];
897            if (this == 0)
898              break;
899            fputs (this->text, output);
900          }
901      /* If we decided to add some sections here
902         because there are no such sections in the file,
903         output them now.  */
904      if (dir_lines[i].add_sections_before)
905        {
906          struct spec_section *spec;
907          struct spec_section **sections;
908          int n_sections = 0;
909
910          /* Count the sections and allocate a vector for all of them.  */
911          for (spec = input_sections; spec; spec = spec->next)
912            n_sections++;
913          sections = ((struct spec_section **)
914                      xmalloc (n_sections * sizeof (struct spec_section *)));
915
916          /* Fill the vector SECTIONS with pointers to all the sections,
917             and sort them.  */
918          j = 0;
919          for (spec = input_sections; spec; spec = spec->next)
920            sections[j++] = spec;
921          qsort (sections, n_sections, sizeof (struct spec_section *),
922                 compare_section_names);
923
924          /* Generate the new sections in alphabetical order.
925             In each new section, output all of our entries.  */
926          for (j = 0; j < n_sections; j++)
927            {
928              spec = sections[j];
929              if (spec->missing)
930                {
931                  struct spec_entry *entry;
932
933                  putc ('\n', output);
934                  fputs (spec->name, output);
935                  putc ('\n', output);
936                  for (entry = entries_to_add; entry; entry = entry->next)
937                    fputs (entry->text, output);
938                }
939            }
940
941          free (sections);
942        }
943
944      /* Output the original dir lines unless marked for deletion.  */
945      if (i < dir_nlines && !dir_lines[i].delete)
946        {
947          fwrite (dir_lines[i].start, 1, dir_lines[i].size, output);
948          putc ('\n', output);
949        }
950    }
951
952  fclose (output);
953
954  exit (0);
955}
956
957/* Read all of file FILNAME into memory
958   and return the address of the data.
959   Store the size into SIZEP.
960   If there is trouble, do a fatal error.  */
961
962char *
963readfile (filename, sizep)
964     char *filename;
965     int *sizep;
966{
967  int desc;
968  int data_size = 1024;
969  char *data = (char *) xmalloc (data_size);
970  int filled = 0;
971  int nread = 0;
972#ifdef HAVE_LIBZ
973  int isGZ = 0;
974  gzFile zdesc;
975#endif
976
977  desc = open (filename, O_RDONLY);
978  if (desc < 0)
979    pfatal_with_name (filename);
980
981#ifdef HAVE_LIBZ
982  /* The file should always be two bytes long.  */
983  if (read (desc, data, 2) != 2)
984    pfatal_with_name (filename);
985
986  /* Undo that read.  */
987  lseek (desc, 0, SEEK_SET);
988
989  /* If we see gzip magic, use gzdopen. */
990  if (data[0] == '\x1f' && data[1] == '\x8b')
991    {
992      isGZ = 1;
993      zdesc = gzdopen (desc, "r");
994      if (zdesc == NULL) {
995        close (desc);
996        pfatal_with_name (filename);
997      }
998    }
999#endif /* HAVE_LIBZ */
1000
1001  while (1)
1002    {
1003#ifdef HAVE_LIBZ
1004      if (isGZ)
1005	nread = gzread (zdesc, data + filled, data_size - filled);
1006      else
1007#endif
1008        nread = read (desc, data + filled, data_size - filled);
1009
1010      if (nread < 0)
1011        pfatal_with_name (filename);
1012      if (nread == 0)
1013        break;
1014
1015      filled += nread;
1016      if (filled == data_size)
1017        {
1018          data_size *= 2;
1019          data = (char *) xrealloc (data, data_size);
1020        }
1021    }
1022
1023  *sizep = filled;
1024
1025#ifdef HAVE_LIBZ
1026  if (isGZ)
1027    gzclose (zdesc);
1028  else
1029#endif
1030    close(desc);
1031
1032  return data;
1033}
1034
1035/* Divide the text at DATA (of SIZE bytes) into lines.
1036   Return a vector of struct line_data describing the lines.
1037   Store the length of that vector into *NLINESP.  */
1038
1039struct line_data *
1040findlines (data, size, nlinesp)
1041     char *data;
1042     int size;
1043     int *nlinesp;
1044{
1045  struct line_data *lines;
1046  int lines_allocated = 512;
1047  int filled = 0;
1048  int i = 0;
1049  int lineflag;
1050
1051  lines = (struct line_data *) xmalloc (lines_allocated * sizeof (struct line_data));
1052
1053  lineflag = 1;
1054  for (i = 0; i < size; i++)
1055    {
1056      if (lineflag)
1057        {
1058          if (filled == lines_allocated)
1059            {
1060              lines_allocated *= 2;
1061              lines = (struct line_data *) xrealloc (lines, lines_allocated * sizeof (struct line_data));
1062            }
1063          lines[filled].start = &data[i];
1064          lines[filled].add_entries_before = 0;
1065          lines[filled].add_sections_before = 0;
1066          lines[filled].delete = 0;
1067          if (filled > 0)
1068            lines[filled - 1].size
1069              = lines[filled].start - lines[filled - 1].start - 1;
1070          filled++;
1071        }
1072      lineflag = (data[i] == '\n');
1073    }
1074  if (filled > 0)
1075    lines[filled - 1].size = &data[i] - lines[filled - 1].start - lineflag;
1076
1077  /* Do not leave garbage in the last element.  */
1078  lines[filled].start = NULL;
1079  lines[filled].add_entries_before = NULL;
1080  lines[filled].add_sections_before = 0;
1081  lines[filled].delete = 0;
1082  lines[filled].size = 0;
1083
1084  *nlinesp = filled;
1085  return lines;
1086}
1087
1088/* Compare the menu item names in LINE1 (line length LEN1)
1089   and LINE2 (line length LEN2).  Return 1 if the item name
1090   in LINE1 is less, 0 otherwise.  */
1091
1092int
1093menu_line_lessp (line1, len1, line2, len2)
1094     char *line1;
1095     int len1;
1096     char *line2;
1097     int len2;
1098{
1099  int minlen = (len1 < len2 ? len1 : len2);
1100  int i;
1101
1102  for (i = 0; i < minlen; i++)
1103    {
1104      /* If one item name is a prefix of the other,
1105         the former one is less.  */
1106      if (line1[i] == ':' && line2[i] != ':')
1107        return 1;
1108      if (line2[i] == ':' && line1[i] != ':')
1109        return 0;
1110      /* If they both continue and differ, one is less.  */
1111      if (line1[i] < line2[i])
1112        return 1;
1113      if (line1[i] > line2[i])
1114        return 0;
1115    }
1116  /* With a properly formatted dir file,
1117     we can only get here if the item names are equal.  */
1118  return 0;
1119}
1120
1121/* Compare the menu item names in LINE1 (line length LEN1)
1122   and LINE2 (line length LEN2).  Return 1 if the item names are equal,
1123   0 otherwise.  */
1124
1125int
1126menu_line_equal (line1, len1, line2, len2)
1127     char *line1;
1128     int len1;
1129     char *line2;
1130     int len2;
1131{
1132  int minlen = (len1 < len2 ? len1 : len2);
1133  int i;
1134
1135  for (i = 0; i < minlen; i++)
1136    {
1137      /* If both item names end here, they are equal.  */
1138      if (line1[i] == ':' && line2[i] == ':')
1139        return 1;
1140      /* If they both continue and differ, one is less.  */
1141      if (line1[i] != line2[i])
1142        return 0;
1143    }
1144  /* With a properly formatted dir file,
1145     we can only get here if the item names are equal.  */
1146  return 1;
1147}
1148
1149/* This is the comparison function for qsort
1150   for a vector of pointers to struct spec_section.
1151   Compare the section names.  */
1152
1153int
1154compare_section_names (sec1, sec2)
1155     struct spec_section **sec1, **sec2;
1156{
1157  char *name1 = (*sec1)->name;
1158  char *name2 = (*sec2)->name;
1159  return strcmp (name1, name2);
1160}
1161
1162/* Insert ENTRY into the add_entries_before vector
1163   for line number LINE_NUMBER of the dir file.
1164   DIR_LINES and N_ENTRIES carry information from like-named variables
1165   in main.  */
1166
1167void
1168insert_entry_here (entry, line_number, dir_lines, n_entries)
1169     struct spec_entry *entry;
1170     int line_number;
1171     struct line_data *dir_lines;
1172     int n_entries;
1173{
1174  int i;
1175
1176  if (dir_lines[line_number].add_entries_before == 0)
1177    {
1178      dir_lines[line_number].add_entries_before
1179        = (struct spec_entry **) xmalloc (n_entries * sizeof (struct spec_entry *));
1180      for (i = 0; i < n_entries; i++)
1181        dir_lines[line_number].add_entries_before[i] = 0;
1182    }
1183
1184  for (i = 0; i < n_entries; i++)
1185    if (dir_lines[line_number].add_entries_before[i] == 0)
1186      break;
1187
1188  if (i == n_entries)
1189    abort ();
1190
1191  dir_lines[line_number].add_entries_before[i] = entry;
1192}
1193