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