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