OSDN Git Service

(This trying to counter the effect of the PR 195 check in -- this should
[pf3gnuchains/gcc-fork.git] / texinfo / util / install-info.c
1 /* install-info -- create Info directory entry(ies) for an Info file.
2    $Id: install-info.c,v 1.1.1.3 1998/03/24 18:20:30 law Exp $
3
4    Copyright (C) 1996, 97, 98 Free Software Foundation, Inc.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/
19
20 #include "system.h"
21 #include <getopt.h>
22
23 #ifdef HAVE_LIBZ
24 #include <zlib.h>
25 #endif
26
27 /* Name this program was invoked with.  */
28 char *progname;
29
30 char *readfile ();
31 struct line_data *findlines ();
32 void fatal ();
33 void insert_entry_here ();
34 int compare_section_names ();
35
36 struct spec_entry;
37 \f
38 /* Data structures.  */
39
40
41 /* Record info about a single line from a file as read into core.  */
42 struct line_data
43 {
44   /* The start of the line.  */
45   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.  */
61 struct 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.  */
72 struct 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.  */
80 struct node
81 {
82   struct node *next;
83   /* The node name.  */
84   char *name;
85   /* The line number of the line where the node starts.
86      This is the line that contains control-underscore.  */
87   int start_line;
88   /* The line number of the line where the node ends,
89      which is the end of the file or where the next line starts.  */
90   int end_line;
91   /* Start of first line in this node's menu
92      (the line after the * Menu: line).  */
93   char *menu_start;
94   /* The start of the chain of sections in this node's menu.  */
95   struct menu_section *sections;
96   /* The last menu section in the chain.  */
97   struct menu_section *last_section;
98 };
99
100
101 /* This is used for a list of sections found in a node's menu.
102    Each  struct node  has such a list in the  sections  field.  */
103 struct menu_section
104 {
105   struct menu_section *next;
106   char *name;
107   /* Line number of start of section.  */
108   int start_line;
109   /* Line number of end of section.  */
110   int end_line;
111 };
112 \f
113 /* Memory allocation and string operations.  */
114
115 /* Like malloc but get fatal error if memory is exhausted.  */
116 void *
117 xmalloc (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.  */
128 void *
129 xrealloc (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.  */
142 char *
143 concat (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
160 char *
161 copy_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 \f
173 /* Error message functions.  */
174
175 /* Print error message.  S1 is printf control string, S2 and S3 args for it. */
176
177 /* VARARGS1 */
178 void
179 error (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 */
188 void
189 warning (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
199 void
200 fatal (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
209 void
210 pfatal_with_name (name)
211      char *name;
212 {
213   char *s = concat ("", strerror (errno), _(" for %s"));
214   fatal (s, name);
215 }
216 \f
217 /* Given the full text of a menu entry, null terminated,
218    return just the menu item name (copied).  */
219
220 char *
221 extract_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
239 char *
240 extract_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 \f
279 void
280 suggest_asking_for_help ()
281 {
282   fprintf (stderr, _("\tTry `%s --help' for a complete list of options.\n"),
283            progname);
284   exit (1);
285 }
286
287 void
288 print_help ()
289 {
290   printf (_("Usage: %s [OPTION]... [INFO-FILE [DIR-FILE]]\n\
291 \n\
292 Install INFO-FILE in the Info directory file DIR-FILE.\n\
293 \n\
294 Options:\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\
320 Email bug reports to bug-texinfo@gnu.org.\n\
321 "), progname);
322 }
323
324 \f
325 /* If DIRFILE does not exist, create a minimal one (or abort).  If it
326    already exists, do nothing.  */
327
328 void
329 ensure_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\
342 topmost node of the Info hierarchy, called (dir)Top.\n\
343 The first time you invoke Info you start off looking at this node.\n\
344 \1f\n\
345 File: 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 \f
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
376 struct 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 \f
393 int
394 main (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\
531 There is NO warranty.  You may redistribute this software\n\
532 under the terms of the GNU General Public License.\n\
533 For 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 \f
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
962 char *
963 readfile (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 \f
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
1039 struct line_data *
1040 findlines (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 \f
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
1092 int
1093 menu_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
1125 int
1126 menu_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 \f
1149 /* This is the comparison function for qsort
1150    for a vector of pointers to struct spec_section.
1151    Compare the section names.  */
1152
1153 int
1154 compare_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
1167 void
1168 insert_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 }