xref: /openbsd-src/gnu/usr.bin/texinfo/util/install-info.c (revision 964e42ff5414a442ed73c8ced52f5d912416007e)
140248eceSdownsj /* install-info -- create Info directory entry(ies) for an Info file.
2*964e42ffSderaadt    $Id: install-info.c,v 1.9 2015/11/14 23:06:06 deraadt Exp $
340248eceSdownsj 
41076333cSespie    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
51076333cSespie    Foundation, Inc.
640248eceSdownsj 
740248eceSdownsj    This program is free software; you can redistribute it and/or modify
840248eceSdownsj    it under the terms of the GNU General Public License as published by
940248eceSdownsj    the Free Software Foundation; either version 2 of the License, or
1040248eceSdownsj    (at your option) any later version.
1140248eceSdownsj 
1240248eceSdownsj    This program is distributed in the hope that it will be useful,
1340248eceSdownsj    but WITHOUT ANY WARRANTY; without even the implied warranty of
1440248eceSdownsj    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1540248eceSdownsj    GNU General Public License for more details.
1640248eceSdownsj 
1740248eceSdownsj    You should have received a copy of the GNU General Public License
1840248eceSdownsj    along with this program; if not, write to the Free Software
1940248eceSdownsj    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/
2040248eceSdownsj 
21c7c5c1bbSkstailey #include "system.h"
2240248eceSdownsj #include <getopt.h>
2340248eceSdownsj 
24672dff93Sespie static char *progname = "install-info";
2512dd3952Sespie 
2640248eceSdownsj struct spec_entry;
271076333cSespie struct spec_section;
281076333cSespie 
291076333cSespie struct line_data *findlines (char *data, int size, int *nlinesp);
301076333cSespie void insert_entry_here (struct spec_entry *entry, int line_number,
311076333cSespie                         struct line_data *dir_lines, int n_entries);
321076333cSespie int compare_section_names (const void *s1, const void *s2);
331076333cSespie int compare_entries_text (const void *e1, const void *e2);
3440248eceSdownsj 
3540248eceSdownsj /* Data structures.  */
3640248eceSdownsj 
3740248eceSdownsj 
38c7c5c1bbSkstailey /* Record info about a single line from a file as read into core.  */
3940248eceSdownsj struct line_data
4040248eceSdownsj {
4140248eceSdownsj   /* The start of the line.  */
4240248eceSdownsj   char *start;
4340248eceSdownsj   /* The number of characters in the line,
4440248eceSdownsj      excluding the terminating newline.  */
4540248eceSdownsj   int size;
4640248eceSdownsj   /* Vector containing pointers to the entries to add before this line.
4740248eceSdownsj      The vector is null-terminated.  */
4840248eceSdownsj   struct spec_entry **add_entries_before;
4940248eceSdownsj   /* 1 means output any needed new sections before this line.  */
5040248eceSdownsj   int add_sections_before;
5140248eceSdownsj   /* 1 means don't output this line.  */
5240248eceSdownsj   int delete;
5340248eceSdownsj };
5440248eceSdownsj 
55c7c5c1bbSkstailey 
5640248eceSdownsj /* This is used for a list of the specified menu section names
5740248eceSdownsj    in which entries should be added.  */
5840248eceSdownsj struct spec_section
5940248eceSdownsj {
6040248eceSdownsj   struct spec_section *next;
6140248eceSdownsj   char *name;
6240248eceSdownsj   /* 1 means we have not yet found an existing section with this name
6340248eceSdownsj      in the dir file--so we will need to add a new section.  */
6440248eceSdownsj   int missing;
6540248eceSdownsj };
6640248eceSdownsj 
6740248eceSdownsj 
68c7c5c1bbSkstailey /* This is used for a list of the entries specified to be added.  */
6940248eceSdownsj struct spec_entry
7040248eceSdownsj {
7140248eceSdownsj   struct spec_entry *next;
7240248eceSdownsj   char *text;
73672dff93Sespie   int text_len;
74672dff93Sespie   /* A pointer to the list of sections to which this entry should be
75672dff93Sespie      added.  */
76672dff93Sespie   struct spec_section *entry_sections;
77672dff93Sespie   /* A pointer to a section that is beyond the end of the chain whose
78672dff93Sespie      head is pointed to by entry_sections.  */
79672dff93Sespie   struct spec_section *entry_sections_tail;
8040248eceSdownsj };
8140248eceSdownsj 
82c7c5c1bbSkstailey 
83c7c5c1bbSkstailey /* This is used for a list of nodes found by parsing the dir file.  */
8440248eceSdownsj struct node
8540248eceSdownsj {
8640248eceSdownsj   struct node *next;
8740248eceSdownsj   /* The node name.  */
8840248eceSdownsj   char *name;
8940248eceSdownsj   /* The line number of the line where the node starts.
9040248eceSdownsj      This is the line that contains control-underscore.  */
9140248eceSdownsj   int start_line;
9240248eceSdownsj   /* The line number of the line where the node ends,
9340248eceSdownsj      which is the end of the file or where the next line starts.  */
9440248eceSdownsj   int end_line;
9540248eceSdownsj   /* Start of first line in this node's menu
9640248eceSdownsj      (the line after the * Menu: line).  */
9740248eceSdownsj   char *menu_start;
9840248eceSdownsj   /* The start of the chain of sections in this node's menu.  */
9940248eceSdownsj   struct menu_section *sections;
10040248eceSdownsj   /* The last menu section in the chain.  */
10140248eceSdownsj   struct menu_section *last_section;
10240248eceSdownsj };
10340248eceSdownsj 
104c7c5c1bbSkstailey 
10540248eceSdownsj /* This is used for a list of sections found in a node's menu.
10640248eceSdownsj    Each  struct node  has such a list in the  sections  field.  */
10740248eceSdownsj struct menu_section
10840248eceSdownsj {
10940248eceSdownsj   struct menu_section *next;
11040248eceSdownsj   char *name;
11140248eceSdownsj   /* Line number of start of section.  */
11240248eceSdownsj   int start_line;
11340248eceSdownsj   /* Line number of end of section.  */
11440248eceSdownsj   int end_line;
11540248eceSdownsj };
11640248eceSdownsj 
117672dff93Sespie /* This table defines all the long-named options, says whether they
118672dff93Sespie    use an argument, and maps them into equivalent single-letter options.  */
119672dff93Sespie 
120672dff93Sespie struct option longopts[] =
121672dff93Sespie {
122672dff93Sespie   { "delete",    no_argument, NULL, 'r' },
123672dff93Sespie   { "dir-file",  required_argument, NULL, 'd' },
124672dff93Sespie   { "entry",     required_argument, NULL, 'e' },
125672dff93Sespie   { "help",      no_argument, NULL, 'h' },
1261076333cSespie   { "infodir",   required_argument, NULL, 'D' },
127672dff93Sespie   { "info-dir",  required_argument, NULL, 'D' },
128672dff93Sespie   { "info-file", required_argument, NULL, 'i' },
129672dff93Sespie   { "item",      required_argument, NULL, 'e' },
130672dff93Sespie   { "quiet",     no_argument, NULL, 'q' },
131672dff93Sespie   { "remove",    no_argument, NULL, 'r' },
132672dff93Sespie   { "section",   required_argument, NULL, 's' },
133672dff93Sespie   { "version",   no_argument, NULL, 'V' },
134672dff93Sespie   { 0 }
135672dff93Sespie };
136672dff93Sespie 
137672dff93Sespie /* Error message functions.  */
138672dff93Sespie 
139672dff93Sespie /* Print error message.  S1 is printf control string, S2 and S3 args for it. */
140672dff93Sespie 
141672dff93Sespie /* VARARGS1 */
142672dff93Sespie void
error(const char * s1,const char * s2,const char * s3)1431076333cSespie error (const char *s1, const char *s2, const char *s3)
144672dff93Sespie {
145672dff93Sespie   fprintf (stderr, "%s: ", progname);
146672dff93Sespie   fprintf (stderr, s1, s2, s3);
147672dff93Sespie   putc ('\n', stderr);
148672dff93Sespie }
149672dff93Sespie 
150672dff93Sespie /* VARARGS1 */
151672dff93Sespie void
warning(const char * s1,const char * s2,const char * s3)1521076333cSespie warning (const char *s1, const char *s2, const char *s3)
153672dff93Sespie {
154672dff93Sespie   fprintf (stderr, _("%s: warning: "), progname);
155672dff93Sespie   fprintf (stderr, s1, s2, s3);
156672dff93Sespie   putc ('\n', stderr);
157672dff93Sespie }
158672dff93Sespie 
159672dff93Sespie /* Print error message and exit.  */
160672dff93Sespie 
161672dff93Sespie void
fatal(const char * s1,const char * s2,const char * s3)1621076333cSespie fatal (const char *s1, const char *s2, const char *s3)
163672dff93Sespie {
164672dff93Sespie   error (s1, s2, s3);
165672dff93Sespie   xexit (1);
166672dff93Sespie }
167672dff93Sespie 
168c7c5c1bbSkstailey /* Return a newly-allocated string
169c7c5c1bbSkstailey    whose contents concatenate those of S1, S2, S3.  */
17040248eceSdownsj char *
concat(const char * s1,const char * s2,const char * s3)1711076333cSespie concat (const char *s1, const char *s2, const char *s3)
17240248eceSdownsj {
17340248eceSdownsj   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
17440248eceSdownsj   char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
17540248eceSdownsj 
17640248eceSdownsj   strcpy (result, s1);
17740248eceSdownsj   strcpy (result + len1, s2);
17840248eceSdownsj   strcpy (result + len1 + len2, s3);
17940248eceSdownsj   *(result + len1 + len2 + len3) = 0;
18040248eceSdownsj 
18140248eceSdownsj   return result;
18240248eceSdownsj }
18340248eceSdownsj 
18440248eceSdownsj /* Return a string containing SIZE characters
18540248eceSdownsj    copied from starting at STRING.  */
18640248eceSdownsj 
18740248eceSdownsj char *
copy_string(const char * string,int size)1881076333cSespie copy_string (const char *string, int size)
18940248eceSdownsj {
19040248eceSdownsj   int i;
19140248eceSdownsj   char *copy = (char *) xmalloc (size + 1);
19240248eceSdownsj   for (i = 0; i < size; i++)
19340248eceSdownsj     copy[i] = string[i];
19440248eceSdownsj   copy[size] = 0;
19540248eceSdownsj   return copy;
19640248eceSdownsj }
19740248eceSdownsj 
19840248eceSdownsj /* Print fatal error message based on errno, with file name NAME.  */
19940248eceSdownsj 
20040248eceSdownsj void
pfatal_with_name(const char * name)2011076333cSespie pfatal_with_name (const char *name)
20240248eceSdownsj {
203c7c5c1bbSkstailey   char *s = concat ("", strerror (errno), _(" for %s"));
2043aa90977Sespie   fatal (s, name, 0);
20540248eceSdownsj }
20640248eceSdownsj 
2071076333cSespie /* Compare the menu item names in LINE1 (line length LEN1)
2081076333cSespie    and LINE2 (line length LEN2).  Return 1 if the item name
2091076333cSespie    in LINE1 is less, 0 otherwise.  */
2101076333cSespie 
2111076333cSespie static int
menu_line_lessp(char * line1,int len1,char * line2,int len2)2121076333cSespie menu_line_lessp (char *line1, int len1, char *line2, int len2)
2131076333cSespie {
2141076333cSespie   int minlen = (len1 < len2 ? len1 : len2);
2151076333cSespie   int i;
2161076333cSespie 
2171076333cSespie   for (i = 0; i < minlen; i++)
2181076333cSespie     {
2191076333cSespie       /* If one item name is a prefix of the other,
2201076333cSespie          the former one is less.  */
2211076333cSespie       if (line1[i] == ':' && line2[i] != ':')
2221076333cSespie         return 1;
2231076333cSespie       if (line2[i] == ':' && line1[i] != ':')
2241076333cSespie         return 0;
2251076333cSespie       /* If they both continue and differ, one is less.  */
2261076333cSespie       if (line1[i] < line2[i])
2271076333cSespie         return 1;
2281076333cSespie       if (line1[i] > line2[i])
2291076333cSespie         return 0;
2301076333cSespie     }
2311076333cSespie   /* With a properly formatted dir file,
2321076333cSespie      we can only get here if the item names are equal.  */
2331076333cSespie   return 0;
2341076333cSespie }
2351076333cSespie 
2361076333cSespie /* Compare the menu item names in LINE1 (line length LEN1)
2371076333cSespie    and LINE2 (line length LEN2).  Return 1 if the item names are equal,
2381076333cSespie    0 otherwise.  */
2391076333cSespie 
2401076333cSespie static int
menu_line_equal(char * line1,int len1,char * line2,int len2)2411076333cSespie menu_line_equal (char *line1, int len1, char *line2, int len2)
2421076333cSespie {
2431076333cSespie   int minlen = (len1 < len2 ? len1 : len2);
2441076333cSespie   int i;
2451076333cSespie 
2461076333cSespie   for (i = 0; i < minlen; i++)
2471076333cSespie     {
2481076333cSespie       /* If both item names end here, they are equal.  */
2491076333cSespie       if (line1[i] == ':' && line2[i] == ':')
2501076333cSespie         return 1;
2511076333cSespie       /* If they both continue and differ, one is less.  */
2521076333cSespie       if (line1[i] != line2[i])
2531076333cSespie         return 0;
2541076333cSespie     }
2551076333cSespie   /* With a properly formatted dir file,
2561076333cSespie      we can only get here if the item names are equal.  */
2571076333cSespie   return 1;
2581076333cSespie }
2591076333cSespie 
2601076333cSespie 
26140248eceSdownsj /* Given the full text of a menu entry, null terminated,
26240248eceSdownsj    return just the menu item name (copied).  */
26340248eceSdownsj 
26440248eceSdownsj char *
extract_menu_item_name(char * item_text)2651076333cSespie extract_menu_item_name (char *item_text)
26640248eceSdownsj {
26740248eceSdownsj   char *p;
26840248eceSdownsj 
26940248eceSdownsj   if (*item_text == '*')
27040248eceSdownsj     item_text++;
27140248eceSdownsj   while (*item_text == ' ')
27240248eceSdownsj     item_text++;
27340248eceSdownsj 
27440248eceSdownsj   p = item_text;
27540248eceSdownsj   while (*p && *p != ':') p++;
27640248eceSdownsj   return copy_string (item_text, p - item_text);
27740248eceSdownsj }
27840248eceSdownsj 
27940248eceSdownsj /* Given the full text of a menu entry, terminated by null or newline,
28040248eceSdownsj    return just the menu item file (copied).  */
28140248eceSdownsj 
28240248eceSdownsj char *
extract_menu_file_name(char * item_text)2831076333cSespie extract_menu_file_name (char *item_text)
28440248eceSdownsj {
28540248eceSdownsj   char *p = item_text;
28640248eceSdownsj 
28740248eceSdownsj   /* If we have text that looks like * ITEM: (FILE)NODE...,
28840248eceSdownsj      extract just FILE.  Otherwise return "(none)".  */
28940248eceSdownsj 
29040248eceSdownsj   if (*p == '*')
29140248eceSdownsj     p++;
29240248eceSdownsj   while (*p == ' ')
29340248eceSdownsj     p++;
29440248eceSdownsj 
29540248eceSdownsj   /* Skip to and past the colon.  */
29640248eceSdownsj   while (*p && *p != '\n' && *p != ':') p++;
29740248eceSdownsj   if (*p == ':') p++;
29840248eceSdownsj 
29940248eceSdownsj   /* Skip past the open-paren.  */
30040248eceSdownsj   while (1)
30140248eceSdownsj     {
30240248eceSdownsj       if (*p == '(')
30340248eceSdownsj         break;
30440248eceSdownsj       else if (*p == ' ' || *p == '\t')
30540248eceSdownsj         p++;
30640248eceSdownsj       else
30740248eceSdownsj         return "(none)";
30840248eceSdownsj     }
30940248eceSdownsj   p++;
31040248eceSdownsj 
31140248eceSdownsj   item_text = p;
31240248eceSdownsj 
31340248eceSdownsj   /* File name ends just before the close-paren.  */
31440248eceSdownsj   while (*p && *p != '\n' && *p != ')') p++;
31540248eceSdownsj   if (*p != ')')
31640248eceSdownsj     return "(none)";
31740248eceSdownsj 
31840248eceSdownsj   return copy_string (item_text, p - item_text);
31940248eceSdownsj }
320672dff93Sespie 
321672dff93Sespie 
322672dff93Sespie 
323672dff93Sespie /* Return FNAME with any [.info][.gz] suffix removed.  */
324672dff93Sespie 
325672dff93Sespie static char *
strip_info_suffix(char * fname)3261076333cSespie strip_info_suffix (char *fname)
327672dff93Sespie {
328672dff93Sespie   char *ret = xstrdup (fname);
329672dff93Sespie   unsigned len = strlen (ret);
330672dff93Sespie 
331672dff93Sespie   if (len > 3 && FILENAME_CMP (ret + len - 3, ".gz") == 0)
332672dff93Sespie     {
333672dff93Sespie       len -= 3;
334672dff93Sespie       ret[len] = 0;
335672dff93Sespie     }
3361076333cSespie   else if (len > 4 && FILENAME_CMP (ret + len - 4, ".bz2") == 0)
3371076333cSespie     {
3381076333cSespie       len -= 4;
3391076333cSespie       ret[len] = 0;
3401076333cSespie     }
341672dff93Sespie 
342672dff93Sespie   if (len > 5 && FILENAME_CMP (ret + len - 5, ".info") == 0)
343672dff93Sespie     {
344672dff93Sespie       len -= 5;
345672dff93Sespie       ret[len] = 0;
346672dff93Sespie     }
347672dff93Sespie   else if (len > 4 && FILENAME_CMP (ret + len - 4, ".inf") == 0)
348672dff93Sespie     {
349672dff93Sespie       len -= 4;
350672dff93Sespie       ret[len] = 0;
351672dff93Sespie     }
352672dff93Sespie #ifdef __MSDOS__
353672dff93Sespie   else if (len > 4 && (FILENAME_CMP (ret + len - 4, ".inz") == 0
354672dff93Sespie                        || FILENAME_CMP (ret + len - 4, ".igz") == 0))
355672dff93Sespie     {
356672dff93Sespie       len -= 4;
357672dff93Sespie       ret[len] = 0;
358672dff93Sespie     }
359672dff93Sespie #endif /* __MSDOS__ */
360672dff93Sespie 
361672dff93Sespie   return ret;
362672dff93Sespie }
363672dff93Sespie 
364672dff93Sespie 
365672dff93Sespie /* Return true if ITEM matches NAME and is followed by TERM_CHAR.  ITEM
366672dff93Sespie    can also be followed by `.gz', `.info.gz', or `.info' (and then
367672dff93Sespie    TERM_CHAR) and still match.  */
368672dff93Sespie 
369672dff93Sespie static int
menu_item_equal(const char * item,char term_char,const char * name)3701076333cSespie menu_item_equal (const char *item, char term_char, const char *name)
371672dff93Sespie {
3721076333cSespie   int ret;
3731076333cSespie   const char *item_basename = item;
374672dff93Sespie   unsigned name_len = strlen (name);
3751076333cSespie 
3761076333cSespie   /* We must compare the basename in ITEM, since we are passed the
3771076333cSespie      basename of the original info file.  Otherwise, a new entry like
3781076333cSespie      "lilypond/lilypond" won't match "lilypond".
3791076333cSespie 
3801076333cSespie      Actually, it seems to me that we should really compare the whole
3811076333cSespie      name, and not just the basename.  Couldn't there be dir1/foo.info
3821076333cSespie      and dir2/foo.info?  Also, it seems like we should be using the
3831076333cSespie      filename from the new dir entries, not the filename on the command
3841076333cSespie      line.  Not worrying about those things right now, though.  --karl,
3851076333cSespie      26mar04.  */
3861076333cSespie   while (*item_basename && !IS_SLASH (*item_basename)
3871076333cSespie 	 && *item_basename != term_char)
3881076333cSespie     item_basename++;
3891076333cSespie   if (! *item_basename || *item_basename == term_char)
3901076333cSespie     item_basename = item;  /* no /, use original */
3911076333cSespie   else
3921076333cSespie     item_basename++;       /* have /, move past it */
3931076333cSespie 
394672dff93Sespie   /* First, ITEM must actually match NAME (usually it won't).  */
3951076333cSespie   ret = strncasecmp (item_basename, name, name_len) == 0;
396672dff93Sespie   if (ret)
397672dff93Sespie     {
398672dff93Sespie       /* Then, `foobar' doesn't match `foo', so be sure we've got all of
399672dff93Sespie          ITEM.  The various suffixes should never actually appear in the
400672dff93Sespie          dir file, but sometimes people put them in.  */
401672dff93Sespie       static char *suffixes[]
402672dff93Sespie         = { "", ".info.gz", ".info", ".inf", ".gz",
403672dff93Sespie #ifdef __MSDOS__
404672dff93Sespie             ".inz", ".igz",
405672dff93Sespie #endif
406672dff93Sespie             NULL };
407672dff93Sespie       unsigned i;
408672dff93Sespie       ret = 0;
409672dff93Sespie       for (i = 0; !ret && suffixes[i]; i++)
410672dff93Sespie         {
411672dff93Sespie           char *suffix = suffixes[i];
412672dff93Sespie           unsigned suffix_len = strlen (suffix);
4131076333cSespie           ret = strncasecmp (item_basename + name_len, suffix, suffix_len) == 0
4141076333cSespie                 && item_basename[name_len + suffix_len] == term_char;
415672dff93Sespie         }
416672dff93Sespie     }
417672dff93Sespie 
418672dff93Sespie   return ret;
419672dff93Sespie }
420672dff93Sespie 
421672dff93Sespie 
42240248eceSdownsj 
42340248eceSdownsj void
suggest_asking_for_help(void)4241076333cSespie suggest_asking_for_help (void)
42540248eceSdownsj {
426c7c5c1bbSkstailey   fprintf (stderr, _("\tTry `%s --help' for a complete list of options.\n"),
42740248eceSdownsj            progname);
428672dff93Sespie   xexit (1);
42940248eceSdownsj }
43040248eceSdownsj 
43140248eceSdownsj void
print_help(void)4321076333cSespie print_help (void)
43340248eceSdownsj {
43412dd3952Sespie   printf (_("Usage: %s [OPTION]... [INFO-FILE [DIR-FILE]]\n\
43512dd3952Sespie \n\
436672dff93Sespie Install or delete dir entries from INFO-FILE in the Info directory file\n\
437672dff93Sespie DIR-FILE.\n\
43840248eceSdownsj \n\
43940248eceSdownsj Options:\n\
440672dff93Sespie  --delete          delete existing entries for INFO-FILE from DIR-FILE;\n\
44140248eceSdownsj                      don't insert any new entries.\n\
442672dff93Sespie  --dir-file=NAME   specify file name of Info directory file.\n\
44340248eceSdownsj                      This is equivalent to using the DIR-FILE argument.\n\
444672dff93Sespie  --entry=TEXT      insert TEXT as an Info directory entry.\n\
44540248eceSdownsj                      TEXT should have the form of an Info menu item line\n\
44640248eceSdownsj                      plus zero or more extra lines starting with whitespace.\n\
44740248eceSdownsj                      If you specify more than one entry, they are all added.\n\
44840248eceSdownsj                      If you don't specify any entries, they are determined\n\
44940248eceSdownsj                      from information in the Info file itself.\n\
450672dff93Sespie  --help            display this help and exit.\n\
451672dff93Sespie  --info-file=FILE  specify Info file to install in the directory.\n\
45240248eceSdownsj                      This is equivalent to using the INFO-FILE argument.\n\
453672dff93Sespie  --info-dir=DIR    same as --dir-file=DIR/dir.\n\
454672dff93Sespie  --item=TEXT       same as --entry TEXT.\n\
45540248eceSdownsj                      An Info directory entry is actually a menu item.\n\
456672dff93Sespie  --quiet           suppress warnings.\n\
457672dff93Sespie  --remove          same as --delete.\n\
458672dff93Sespie  --section=SEC     put this file's entries in section SEC of the directory.\n\
45940248eceSdownsj                      If you specify more than one section, all the entries\n\
46040248eceSdownsj                      are added in each of the sections.\n\
46140248eceSdownsj                      If you don't specify any sections, they are determined\n\
46240248eceSdownsj                      from information in the Info file itself.\n\
463672dff93Sespie  --version         display version information and exit.\n\
4643aa90977Sespie "), progname);
4653aa90977Sespie 
4663aa90977Sespie   puts (_("\n\
467672dff93Sespie Email bug reports to bug-texinfo@gnu.org,\n\
468672dff93Sespie general questions and discussion to help-texinfo@gnu.org.\n\
4693aa90977Sespie Texinfo home page: http://www.gnu.org/software/texinfo/"));
47040248eceSdownsj }
47112dd3952Sespie 
472c7c5c1bbSkstailey 
473c7c5c1bbSkstailey /* If DIRFILE does not exist, create a minimal one (or abort).  If it
474c7c5c1bbSkstailey    already exists, do nothing.  */
47512dd3952Sespie 
476c7c5c1bbSkstailey void
ensure_dirfile_exists(char * dirfile)4771076333cSespie ensure_dirfile_exists (char *dirfile)
47840248eceSdownsj {
479c7c5c1bbSkstailey   int desc = open (dirfile, O_RDONLY);
480c7c5c1bbSkstailey   if (desc < 0 && errno == ENOENT)
481c7c5c1bbSkstailey     {
482c7c5c1bbSkstailey       FILE *f;
483c7c5c1bbSkstailey       char *readerr = strerror (errno);
484c7c5c1bbSkstailey       close (desc);
485c7c5c1bbSkstailey       f = fopen (dirfile, "w");
486c7c5c1bbSkstailey       if (f)
487c7c5c1bbSkstailey         {
488672dff93Sespie           fprintf (f, _("This is the file .../info/dir, which contains the\n\
489c7c5c1bbSkstailey topmost node of the Info hierarchy, called (dir)Top.\n\
490c7c5c1bbSkstailey The first time you invoke Info you start off looking at this node.\n\
4911076333cSespie \x1f\n\
492672dff93Sespie %s\tThis is the top of the INFO tree\n\
493c7c5c1bbSkstailey \n\
494c7c5c1bbSkstailey   This (the Directory node) gives a menu of major topics.\n\
495c7c5c1bbSkstailey   Typing \"q\" exits, \"?\" lists all Info commands, \"d\" returns here,\n\
496c7c5c1bbSkstailey   \"h\" gives a primer for first-timers,\n\
497c7c5c1bbSkstailey   \"mEmacs<Return>\" visits the Emacs manual, etc.\n\
498c7c5c1bbSkstailey \n\
499c7c5c1bbSkstailey   In Emacs, you can click mouse button 2 on a menu item or cross reference\n\
500c7c5c1bbSkstailey   to select it.\n\
501c7c5c1bbSkstailey \n\
5021076333cSespie %s\n\
5031076333cSespie "), "File: dir,\tNode: Top",  /* These keywords must not be translated.  */
5041076333cSespie     "* Menu:"
5051076333cSespie );
506c7c5c1bbSkstailey           if (fclose (f) < 0)
507c7c5c1bbSkstailey             pfatal_with_name (dirfile);
508c7c5c1bbSkstailey         }
509c7c5c1bbSkstailey       else
510c7c5c1bbSkstailey         {
511c7c5c1bbSkstailey           /* Didn't exist, but couldn't open for writing.  */
512c7c5c1bbSkstailey           fprintf (stderr,
513c7c5c1bbSkstailey                    _("%s: could not read (%s) and could not create (%s)\n"),
514c7c5c1bbSkstailey                    dirfile, readerr, strerror (errno));
515672dff93Sespie           xexit (1);
516c7c5c1bbSkstailey         }
517c7c5c1bbSkstailey     }
518c7c5c1bbSkstailey   else
519c7c5c1bbSkstailey     close (desc); /* It already existed, so fine.  */
52040248eceSdownsj }
52140248eceSdownsj 
522672dff93Sespie /* Open FILENAME and return the resulting stream pointer.  If it doesn't
523672dff93Sespie    exist, try FILENAME.gz.  If that doesn't exist either, call
524672dff93Sespie    CREATE_CALLBACK (with FILENAME as arg) to create it, if that is
525672dff93Sespie    non-NULL.  If still no luck, fatal error.
52640248eceSdownsj 
527672dff93Sespie    If we do open it, return the actual name of the file opened in
528672dff93Sespie    OPENED_FILENAME and the compress program to use to (de)compress it in
529672dff93Sespie    COMPRESSION_PROGRAM.  The compression program is determined by the
530672dff93Sespie    magic number, not the filename.  */
531672dff93Sespie 
532672dff93Sespie FILE *
open_possibly_compressed_file(char * filename,void (* create_callback)(char *),char ** opened_filename,char ** compression_program,int * is_pipe)5331076333cSespie open_possibly_compressed_file (char *filename,
5341076333cSespie     void (*create_callback) (char *),
5351076333cSespie     char **opened_filename, char **compression_program, int *is_pipe)
53640248eceSdownsj {
537672dff93Sespie   char *local_opened_filename, *local_compression_program;
538672dff93Sespie   int nread;
539672dff93Sespie   char data[4];
540672dff93Sespie   FILE *f;
54112dd3952Sespie 
542672dff93Sespie   /* We let them pass NULL if they don't want this info, but it's easier
543672dff93Sespie      to always determine it.  */
544672dff93Sespie   if (!opened_filename)
545672dff93Sespie     opened_filename = &local_opened_filename;
546672dff93Sespie 
547672dff93Sespie   *opened_filename = filename;
548672dff93Sespie   f = fopen (*opened_filename, FOPEN_RBIN);
549672dff93Sespie   if (!f)
550672dff93Sespie     {
551672dff93Sespie       *opened_filename = concat (filename, ".gz", "");
552672dff93Sespie       f = fopen (*opened_filename, FOPEN_RBIN);
5531076333cSespie   if (!f)
5541076333cSespie     {
5551076333cSespie       free (*opened_filename);
5561076333cSespie       *opened_filename = concat (filename, ".bz2", "");
5571076333cSespie       f = fopen (*opened_filename, FOPEN_RBIN);
5581076333cSespie     }
5591076333cSespie 
560672dff93Sespie #ifdef __MSDOS__
561672dff93Sespie       if (!f)
562672dff93Sespie         {
563672dff93Sespie           free (*opened_filename);
564672dff93Sespie           *opened_filename = concat (filename, ".igz", "");
565672dff93Sespie           f = fopen (*opened_filename, FOPEN_RBIN);
566672dff93Sespie         }
567672dff93Sespie       if (!f)
568672dff93Sespie         {
569672dff93Sespie           free (*opened_filename);
570672dff93Sespie           *opened_filename = concat (filename, ".inz", "");
571672dff93Sespie           f = fopen (*opened_filename, FOPEN_RBIN);
572672dff93Sespie         }
573672dff93Sespie #endif
574672dff93Sespie       if (!f)
575672dff93Sespie         {
576672dff93Sespie           if (create_callback)
577672dff93Sespie             { /* That didn't work either.  Create the file if we can.  */
578672dff93Sespie               (*create_callback) (filename);
579672dff93Sespie 
580672dff93Sespie               /* And try opening it again.  */
581672dff93Sespie               free (*opened_filename);
582672dff93Sespie               *opened_filename = filename;
583672dff93Sespie               f = fopen (*opened_filename, FOPEN_RBIN);
584672dff93Sespie               if (!f)
585672dff93Sespie                 pfatal_with_name (filename);
586672dff93Sespie             }
587672dff93Sespie           else
588672dff93Sespie             pfatal_with_name (filename);
589672dff93Sespie         }
590672dff93Sespie     }
591672dff93Sespie 
592672dff93Sespie   /* Read first few bytes of file rather than relying on the filename.
593672dff93Sespie      If the file is shorter than this it can't be usable anyway.  */
594672dff93Sespie   nread = fread (data, sizeof (data), 1, f);
595672dff93Sespie   if (nread != 1)
596672dff93Sespie     {
597672dff93Sespie       /* Empty files don't set errno, so we get something like
598672dff93Sespie          "install-info: No error for foo", which is confusing.  */
599672dff93Sespie       if (nread == 0)
6003aa90977Sespie         fatal (_("%s: empty file"), *opened_filename, 0);
601672dff93Sespie       pfatal_with_name (*opened_filename);
602672dff93Sespie     }
603672dff93Sespie 
604672dff93Sespie   if (!compression_program)
605672dff93Sespie     compression_program = &local_compression_program;
606672dff93Sespie 
607672dff93Sespie   if (data[0] == '\x1f' && data[1] == '\x8b')
608672dff93Sespie #if STRIP_DOT_EXE
609672dff93Sespie     /* An explicit .exe yields a better diagnostics from popen below
610672dff93Sespie        if they don't have gzip installed.  */
611672dff93Sespie     *compression_program = "gzip.exe";
612672dff93Sespie #else
613672dff93Sespie     *compression_program = "gzip";
614672dff93Sespie #endif
6151076333cSespie   else if(data[0] == 'B' && data[1] == 'Z' && data[2] == 'h')
6161076333cSespie #ifndef STRIP_DOT_EXE
6171076333cSespie     *compression_program = "bzip2.exe";
6181076333cSespie #else
6191076333cSespie     *compression_program = "bzip2";
6201076333cSespie #endif
6211076333cSespie   else if(data[0] == 'B' && data[1] == 'Z' && data[2] == '0')
6221076333cSespie #ifndef STRIP_DOT_EXE
6231076333cSespie     *compression_program = "bzip.exe";
6241076333cSespie #else
6251076333cSespie     *compression_program = "bzip";
6261076333cSespie #endif
627672dff93Sespie   else
628672dff93Sespie     *compression_program = NULL;
629672dff93Sespie 
630672dff93Sespie   if (*compression_program)
631672dff93Sespie     { /* It's compressed, so fclose the file and then open a pipe.  */
632672dff93Sespie       char *command = concat (*compression_program," -cd <", *opened_filename);
633672dff93Sespie       if (fclose (f) < 0)
634672dff93Sespie         pfatal_with_name (*opened_filename);
635672dff93Sespie       f = popen (command, "r");
636672dff93Sespie       if (f)
637672dff93Sespie         *is_pipe = 1;
638672dff93Sespie       else
639672dff93Sespie         pfatal_with_name (command);
640672dff93Sespie     }
641672dff93Sespie   else
642672dff93Sespie     { /* It's a plain file, seek back over the magic bytes.  */
643672dff93Sespie       if (fseek (f, 0, 0) < 0)
644672dff93Sespie         pfatal_with_name (*opened_filename);
645672dff93Sespie #if O_BINARY
646672dff93Sespie       /* Since this is a text file, and we opened it in binary mode,
647672dff93Sespie          switch back to text mode.  */
648672dff93Sespie       f = freopen (*opened_filename, "r", f);
649672dff93Sespie #endif
650672dff93Sespie       *is_pipe = 0;
651672dff93Sespie     }
652672dff93Sespie 
653672dff93Sespie   return f;
654672dff93Sespie }
65540248eceSdownsj 
656672dff93Sespie /* Read all of file FILENAME into memory and return the address of the
657672dff93Sespie    data.  Store the size of the data into SIZEP.  If need be, uncompress
658672dff93Sespie    (i.e., try FILENAME.gz et al. if FILENAME does not exist) and store
659672dff93Sespie    the actual file name that was opened into OPENED_FILENAME (if it is
660672dff93Sespie    non-NULL), and the companion compression program (if any, else NULL)
661672dff93Sespie    into COMPRESSION_PROGRAM (if that is non-NULL).  If trouble, do
662672dff93Sespie    a fatal error.  */
663672dff93Sespie 
664672dff93Sespie char *
readfile(char * filename,int * sizep,void (* create_callback)(char *),char ** opened_filename,char ** compression_program)6651076333cSespie readfile (char *filename, int *sizep,
6661076333cSespie     void (*create_callback) (char *), char **opened_filename,
6671076333cSespie     char **compression_program)
668672dff93Sespie {
669672dff93Sespie   char *real_name;
670672dff93Sespie   FILE *f;
671672dff93Sespie   int pipe_p;
672672dff93Sespie   int filled = 0;
673672dff93Sespie   int data_size = 8192;
674672dff93Sespie   char *data = xmalloc (data_size);
675672dff93Sespie 
676672dff93Sespie   /* If they passed the space for the file name to return, use it.  */
677672dff93Sespie   f = open_possibly_compressed_file (filename, create_callback,
678672dff93Sespie                                      opened_filename ? opened_filename
679672dff93Sespie                                                      : &real_name,
680672dff93Sespie                                      compression_program, &pipe_p);
681672dff93Sespie 
682672dff93Sespie   for (;;)
683672dff93Sespie     {
684672dff93Sespie       int nread = fread (data + filled, 1, data_size - filled, f);
685672dff93Sespie       if (nread < 0)
686672dff93Sespie         pfatal_with_name (real_name);
687672dff93Sespie       if (nread == 0)
688672dff93Sespie         break;
689672dff93Sespie 
690672dff93Sespie       filled += nread;
691672dff93Sespie       if (filled == data_size)
692672dff93Sespie         {
693672dff93Sespie           data_size += 65536;
694672dff93Sespie           data = xrealloc (data, data_size);
695672dff93Sespie         }
696672dff93Sespie     }
697672dff93Sespie 
698672dff93Sespie   /* We'll end up wasting space if we're not passing the filename back
699672dff93Sespie      and it is not just FILENAME, but so what.  */
700672dff93Sespie   /* We need to close the stream, since on some systems the pipe created
701672dff93Sespie      by popen is simulated by a temporary file which only gets removed
702672dff93Sespie      inside pclose.  */
703672dff93Sespie   if (pipe_p)
704672dff93Sespie     pclose (f);
705672dff93Sespie   else
706672dff93Sespie     fclose (f);
707672dff93Sespie 
708672dff93Sespie   *sizep = filled;
709672dff93Sespie   return data;
710672dff93Sespie }
711672dff93Sespie 
712672dff93Sespie /* Output the old dir file, interpolating the new sections
713672dff93Sespie    and/or new entries where appropriate.  If COMPRESSION_PROGRAM is not
714672dff93Sespie    null, pipe to it to create DIRFILE.  Thus if we read dir.gz on input,
715672dff93Sespie    we'll write dir.gz on output.  */
716672dff93Sespie 
717672dff93Sespie 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)7181076333cSespie output_dirfile (char *dirfile, int dir_nlines, struct line_data *dir_lines,
7191076333cSespie                 int n_entries_to_add, struct spec_entry *entries_to_add,
7201076333cSespie                 struct spec_section *input_sections, char *compression_program)
721672dff93Sespie {
722672dff93Sespie   int i;
723672dff93Sespie   FILE *output;
724672dff93Sespie 
725672dff93Sespie   if (compression_program)
726672dff93Sespie     {
727672dff93Sespie       char *command = concat (compression_program, ">", dirfile);
728672dff93Sespie       output = popen (command, "w");
729672dff93Sespie     }
730672dff93Sespie   else
731672dff93Sespie     output = fopen (dirfile, "w");
732672dff93Sespie 
733672dff93Sespie   if (!output)
734672dff93Sespie     {
735672dff93Sespie       perror (dirfile);
736672dff93Sespie       xexit (1);
737672dff93Sespie     }
738672dff93Sespie 
739672dff93Sespie   for (i = 0; i <= dir_nlines; i++)
740672dff93Sespie     {
741672dff93Sespie       int j;
742672dff93Sespie 
743672dff93Sespie       /* If we decided to output some new entries before this line,
744672dff93Sespie          output them now.  */
745672dff93Sespie       if (dir_lines[i].add_entries_before)
746672dff93Sespie         for (j = 0; j < n_entries_to_add; j++)
747672dff93Sespie           {
748672dff93Sespie             struct spec_entry *this = dir_lines[i].add_entries_before[j];
749672dff93Sespie             if (this == 0)
750672dff93Sespie               break;
751672dff93Sespie             fputs (this->text, output);
752672dff93Sespie           }
753672dff93Sespie       /* If we decided to add some sections here
754672dff93Sespie          because there are no such sections in the file,
755672dff93Sespie          output them now.  */
756672dff93Sespie       if (dir_lines[i].add_sections_before)
757672dff93Sespie         {
758672dff93Sespie           struct spec_section *spec;
759672dff93Sespie           struct spec_section **sections;
760672dff93Sespie           int n_sections = 0;
761672dff93Sespie           struct spec_entry *entry;
762672dff93Sespie           struct spec_entry **entries;
763672dff93Sespie           int n_entries = 0;
764672dff93Sespie 
765672dff93Sespie           /* Count the sections and allocate a vector for all of them.  */
766672dff93Sespie           for (spec = input_sections; spec; spec = spec->next)
767672dff93Sespie             n_sections++;
768672dff93Sespie           sections = ((struct spec_section **)
769672dff93Sespie                       xmalloc (n_sections * sizeof (struct spec_section *)));
770672dff93Sespie 
771672dff93Sespie           /* Fill the vector SECTIONS with pointers to all the sections,
772672dff93Sespie              and sort them.  */
773672dff93Sespie           j = 0;
774672dff93Sespie           for (spec = input_sections; spec; spec = spec->next)
775672dff93Sespie             sections[j++] = spec;
776672dff93Sespie           qsort (sections, n_sections, sizeof (struct spec_section *),
777672dff93Sespie                  compare_section_names);
778672dff93Sespie 
779672dff93Sespie           /* Count the entries and allocate a vector for all of them.  */
780672dff93Sespie           for (entry = entries_to_add; entry; entry = entry->next)
781672dff93Sespie             n_entries++;
782672dff93Sespie           entries = ((struct spec_entry **)
783672dff93Sespie                      xmalloc (n_entries * sizeof (struct spec_entry *)));
784672dff93Sespie 
785672dff93Sespie           /* Fill the vector ENTRIES with pointers to all the sections,
786672dff93Sespie              and sort them.  */
787672dff93Sespie           j = 0;
788672dff93Sespie           for (entry = entries_to_add; entry; entry = entry->next)
789672dff93Sespie             entries[j++] = entry;
790672dff93Sespie           qsort (entries, n_entries, sizeof (struct spec_entry *),
791672dff93Sespie                  compare_entries_text);
792672dff93Sespie 
793672dff93Sespie           /* Generate the new sections in alphabetical order.  In each
794672dff93Sespie              new section, output all of the entries that belong to that
795672dff93Sespie              section, in alphabetical order.  */
796672dff93Sespie           for (j = 0; j < n_sections; j++)
797672dff93Sespie             {
798672dff93Sespie               spec = sections[j];
799672dff93Sespie               if (spec->missing)
800672dff93Sespie                 {
801672dff93Sespie                   int k;
802672dff93Sespie 
803672dff93Sespie                   putc ('\n', output);
804672dff93Sespie                   fputs (spec->name, output);
805672dff93Sespie                   putc ('\n', output);
806672dff93Sespie                   for (k = 0; k < n_entries; k++)
807672dff93Sespie                     {
808672dff93Sespie                       struct spec_section *spec1;
809672dff93Sespie                       /* Did they at all want this entry to be put into
810672dff93Sespie                          this section?  */
811672dff93Sespie                       entry = entries[k];
812672dff93Sespie                       for (spec1 = entry->entry_sections;
813672dff93Sespie                            spec1 && spec1 != entry->entry_sections_tail;
814672dff93Sespie                            spec1 = spec1->next)
815672dff93Sespie                         {
816672dff93Sespie                           if (!strcmp (spec1->name, spec->name))
817672dff93Sespie                             break;
818672dff93Sespie                         }
819672dff93Sespie                       if (spec1 && spec1 != entry->entry_sections_tail)
820672dff93Sespie                         fputs (entry->text, output);
821672dff93Sespie                     }
822672dff93Sespie                 }
823672dff93Sespie             }
824672dff93Sespie 
825672dff93Sespie           free (entries);
826672dff93Sespie           free (sections);
827672dff93Sespie         }
828672dff93Sespie 
829672dff93Sespie       /* Output the original dir lines unless marked for deletion.  */
830672dff93Sespie       if (i < dir_nlines && !dir_lines[i].delete)
831672dff93Sespie         {
832672dff93Sespie           fwrite (dir_lines[i].start, 1, dir_lines[i].size, output);
833672dff93Sespie           putc ('\n', output);
834672dff93Sespie         }
835672dff93Sespie     }
836672dff93Sespie 
837672dff93Sespie   /* Some systems, such as MS-DOS, simulate pipes with temporary files.
838672dff93Sespie      On those systems, the compressor actually gets run inside pclose,
839672dff93Sespie      so we must call pclose.  */
840672dff93Sespie   if (compression_program)
841672dff93Sespie     pclose (output);
842672dff93Sespie   else
843672dff93Sespie     fclose (output);
844672dff93Sespie }
845672dff93Sespie 
846672dff93Sespie /* Parse the input to find the section names and the entry names it
847672dff93Sespie    specifies.  Return the number of entries to add from this file.  */
848672dff93Sespie int
parse_input(const struct line_data * lines,int nlines,struct spec_section ** sections,struct spec_entry ** entries)8491076333cSespie parse_input (const struct line_data *lines, int nlines,
8501076333cSespie              struct spec_section **sections, struct spec_entry **entries)
851672dff93Sespie {
852672dff93Sespie   int n_entries = 0;
853672dff93Sespie   int prefix_length = strlen ("INFO-DIR-SECTION ");
854672dff93Sespie   struct spec_section *head = *sections, *tail = NULL;
855672dff93Sespie   int reset_tail = 0;
856672dff93Sespie   char *start_of_this_entry = 0;
857672dff93Sespie   int ignore_sections = *sections != 0;
858672dff93Sespie   int ignore_entries  = *entries  != 0;
859672dff93Sespie 
860672dff93Sespie   int i;
861672dff93Sespie 
862672dff93Sespie   if (ignore_sections && ignore_entries)
863672dff93Sespie     return 0;
864672dff93Sespie 
865672dff93Sespie   /* Loop here processing lines from the input file.  Each
866672dff93Sespie      INFO-DIR-SECTION entry is added to the SECTIONS linked list.
867672dff93Sespie      Each START-INFO-DIR-ENTRY block is added to the ENTRIES linked
868672dff93Sespie      list, and all its entries inherit the chain of SECTION entries
869672dff93Sespie      defined by the last group of INFO-DIR-SECTION entries we have
870672dff93Sespie      seen until that point.  */
871672dff93Sespie   for (i = 0; i < nlines; i++)
872672dff93Sespie     {
873672dff93Sespie       if (!ignore_sections
874672dff93Sespie           && !strncmp ("INFO-DIR-SECTION ", lines[i].start, prefix_length))
875672dff93Sespie         {
876672dff93Sespie           struct spec_section *next
877672dff93Sespie             = (struct spec_section *) xmalloc (sizeof (struct spec_section));
878672dff93Sespie           next->name = copy_string (lines[i].start + prefix_length,
879672dff93Sespie                                     lines[i].size - prefix_length);
880672dff93Sespie           next->next = *sections;
881672dff93Sespie           next->missing = 1;
882672dff93Sespie           if (reset_tail)
883672dff93Sespie             {
884672dff93Sespie               tail = *sections;
885672dff93Sespie               reset_tail = 0;
886672dff93Sespie             }
887672dff93Sespie           *sections = next;
888672dff93Sespie           head = *sections;
889672dff93Sespie         }
890672dff93Sespie       /* If entries were specified explicitly with command options,
891672dff93Sespie          ignore the entries in the input file.  */
892672dff93Sespie       else if (!ignore_entries)
893672dff93Sespie         {
894672dff93Sespie           if (!strncmp ("START-INFO-DIR-ENTRY", lines[i].start, lines[i].size)
895672dff93Sespie               && sizeof ("START-INFO-DIR-ENTRY") - 1 == lines[i].size)
896672dff93Sespie             {
897672dff93Sespie               if (!*sections)
898672dff93Sespie                 {
899672dff93Sespie                   /* We found an entry, but didn't yet see any sections
900672dff93Sespie                      specified.  Default to section "Miscellaneous".  */
901672dff93Sespie                   *sections = (struct spec_section *)
902672dff93Sespie                     xmalloc (sizeof (struct spec_section));
903672dff93Sespie                   (*sections)->name = "Miscellaneous";
904672dff93Sespie                   (*sections)->next = 0;
905672dff93Sespie                   (*sections)->missing = 1;
906672dff93Sespie                   head = *sections;
907672dff93Sespie                 }
908672dff93Sespie               /* Next time we see INFO-DIR-SECTION, we will reset the
909672dff93Sespie                  tail pointer.  */
910672dff93Sespie               reset_tail = 1;
911672dff93Sespie 
912672dff93Sespie               if (start_of_this_entry != 0)
9133aa90977Sespie                 fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"), 0, 0);
914672dff93Sespie               start_of_this_entry = lines[i + 1].start;
915672dff93Sespie             }
916672dff93Sespie           else if (start_of_this_entry)
917672dff93Sespie             {
918672dff93Sespie               if ((!strncmp ("* ", lines[i].start, 2)
919672dff93Sespie                    && lines[i].start > start_of_this_entry)
920672dff93Sespie                   || (!strncmp ("END-INFO-DIR-ENTRY",
921672dff93Sespie                                 lines[i].start, lines[i].size)
922672dff93Sespie                       && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size))
923672dff93Sespie                 {
924672dff93Sespie                   /* We found an end of this entry.  Allocate another
925672dff93Sespie                      entry, fill its data, and add it to the linked
926672dff93Sespie                      list.  */
927672dff93Sespie                   struct spec_entry *next
928672dff93Sespie                     = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
929672dff93Sespie                   next->text
930672dff93Sespie                     = copy_string (start_of_this_entry,
931672dff93Sespie                                    lines[i].start - start_of_this_entry);
932672dff93Sespie                   next->text_len = lines[i].start - start_of_this_entry;
933672dff93Sespie                   next->entry_sections = head;
934672dff93Sespie                   next->entry_sections_tail = tail;
935672dff93Sespie                   next->next = *entries;
936672dff93Sespie                   *entries = next;
937672dff93Sespie                   n_entries++;
938672dff93Sespie                   if (!strncmp ("END-INFO-DIR-ENTRY",
939672dff93Sespie                                 lines[i].start, lines[i].size)
940672dff93Sespie                       && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size)
941672dff93Sespie                     start_of_this_entry = 0;
942672dff93Sespie                   else
943672dff93Sespie                     start_of_this_entry = lines[i].start;
944672dff93Sespie                 }
945672dff93Sespie               else if (!strncmp ("END-INFO-DIR-ENTRY",
946672dff93Sespie                                  lines[i].start, lines[i].size)
947672dff93Sespie                        && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size)
9483aa90977Sespie                 fatal (_("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY"), 0, 0);
949672dff93Sespie             }
950672dff93Sespie         }
951672dff93Sespie     }
952672dff93Sespie   if (start_of_this_entry != 0)
9533aa90977Sespie     fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"),
9543aa90977Sespie            0, 0);
955672dff93Sespie 
956672dff93Sespie   /* If we ignored the INFO-DIR-ENTRY directives, we need now go back
957672dff93Sespie      and plug the names of all the sections we found into every
958672dff93Sespie      element of the ENTRIES list.  */
959672dff93Sespie   if (ignore_entries && *entries)
960672dff93Sespie     {
961672dff93Sespie       struct spec_entry *entry;
962672dff93Sespie 
963672dff93Sespie       for (entry = *entries; entry; entry = entry->next)
964672dff93Sespie         {
965672dff93Sespie           entry->entry_sections = head;
966672dff93Sespie           entry->entry_sections_tail = tail;
967672dff93Sespie         }
968672dff93Sespie     }
969672dff93Sespie 
970672dff93Sespie   return n_entries;
971672dff93Sespie }
972672dff93Sespie 
973672dff93Sespie /* Parse the dir file whose basename is BASE_NAME.  Find all the
974672dff93Sespie    nodes, and their menus, and the sections of their menus.  */
975672dff93Sespie int
parse_dir_file(struct line_data * lines,int nlines,struct node ** nodes,const char * base_name)9761076333cSespie parse_dir_file (struct line_data *lines, int nlines, struct node **nodes,
9771076333cSespie                 const char *base_name)
978672dff93Sespie {
979672dff93Sespie   int node_header_flag = 0;
980672dff93Sespie   int something_deleted = 0;
981672dff93Sespie   int i;
982672dff93Sespie 
983672dff93Sespie   *nodes = 0;
984672dff93Sespie   for (i = 0; i < nlines; i++)
985672dff93Sespie     {
986672dff93Sespie       /* Parse node header lines.  */
987672dff93Sespie       if (node_header_flag)
988672dff93Sespie         {
989672dff93Sespie           int j, end;
990672dff93Sespie           for (j = 0; j < lines[i].size; j++)
991672dff93Sespie             /* Find the node name and store it in the `struct node'.  */
992672dff93Sespie             if (!strncmp ("Node:", lines[i].start + j, 5))
993672dff93Sespie               {
994672dff93Sespie                 char *line = lines[i].start;
995672dff93Sespie                 /* Find the start of the node name.  */
996672dff93Sespie                 j += 5;
997672dff93Sespie                 while (line[j] == ' ' || line[j] == '\t')
998672dff93Sespie                   j++;
999672dff93Sespie                 /* Find the end of the node name.  */
1000672dff93Sespie                 end = j;
1001672dff93Sespie                 while (line[end] != 0 && line[end] != ',' && line[end] != '\n'
1002672dff93Sespie                        && line[end] != '\t')
1003672dff93Sespie                   end++;
1004672dff93Sespie                 (*nodes)->name = copy_string (line + j, end - j);
1005672dff93Sespie               }
1006672dff93Sespie           node_header_flag = 0;
1007672dff93Sespie         }
1008672dff93Sespie 
1009672dff93Sespie       /* Notice the start of a node.  */
1010672dff93Sespie       if (*lines[i].start == 037)
1011672dff93Sespie         {
1012672dff93Sespie           struct node *next = (struct node *) xmalloc (sizeof (struct node));
1013672dff93Sespie 
1014672dff93Sespie           next->next = *nodes;
1015672dff93Sespie           next->name = NULL;
1016672dff93Sespie           next->start_line = i;
1017672dff93Sespie           next->end_line = 0;
1018672dff93Sespie           next->menu_start = NULL;
1019672dff93Sespie           next->sections = NULL;
1020672dff93Sespie           next->last_section = NULL;
1021672dff93Sespie 
1022672dff93Sespie           if (*nodes != 0)
1023672dff93Sespie             (*nodes)->end_line = i;
1024672dff93Sespie           /* Fill in the end of the last menu section
1025672dff93Sespie              of the previous node.  */
1026672dff93Sespie           if (*nodes != 0 && (*nodes)->last_section != 0)
1027672dff93Sespie             (*nodes)->last_section->end_line = i;
1028672dff93Sespie 
1029672dff93Sespie           *nodes = next;
1030672dff93Sespie 
1031672dff93Sespie           /* The following line is the header of this node;
1032672dff93Sespie              parse it.  */
1033672dff93Sespie           node_header_flag = 1;
1034672dff93Sespie         }
1035672dff93Sespie 
1036672dff93Sespie       /* Notice the lines that start menus.  */
1037672dff93Sespie       if (*nodes != 0 && !strncmp ("* Menu:", lines[i].start, 7))
1038672dff93Sespie         (*nodes)->menu_start = lines[i + 1].start;
1039672dff93Sespie 
1040672dff93Sespie       /* Notice sections in menus.  */
1041672dff93Sespie       if (*nodes != 0
1042672dff93Sespie           && (*nodes)->menu_start != 0
1043672dff93Sespie           && *lines[i].start != '\n'
1044672dff93Sespie           && *lines[i].start != '*'
1045672dff93Sespie           && *lines[i].start != ' '
1046672dff93Sespie           && *lines[i].start != '\t')
1047672dff93Sespie         {
1048672dff93Sespie           /* Add this menu section to the node's list.
1049672dff93Sespie              This list grows in forward order.  */
1050672dff93Sespie           struct menu_section *next
1051672dff93Sespie             = (struct menu_section *) xmalloc (sizeof (struct menu_section));
1052672dff93Sespie 
1053672dff93Sespie           next->start_line = i + 1;
1054672dff93Sespie           next->next = 0;
1055672dff93Sespie           next->end_line = 0;
1056672dff93Sespie           next->name = copy_string (lines[i].start, lines[i].size);
1057672dff93Sespie           if ((*nodes)->sections)
1058672dff93Sespie             {
1059672dff93Sespie               (*nodes)->last_section->next = next;
1060672dff93Sespie               (*nodes)->last_section->end_line = i;
1061672dff93Sespie             }
1062672dff93Sespie           else
1063672dff93Sespie             (*nodes)->sections = next;
1064672dff93Sespie           (*nodes)->last_section = next;
1065672dff93Sespie         }
1066672dff93Sespie 
1067672dff93Sespie       /* Check for an existing entry that should be deleted.
1068672dff93Sespie          Delete all entries which specify this file name.  */
1069672dff93Sespie       if (*lines[i].start == '*')
1070672dff93Sespie         {
1071672dff93Sespie           char *q;
1072672dff93Sespie           char *p = lines[i].start;
1073672dff93Sespie 
1074672dff93Sespie           p++; /* skip * */
1075672dff93Sespie           while (*p == ' ') p++; /* ignore following spaces */
1076672dff93Sespie           q = p; /* remember this, it's the beginning of the menu item.  */
1077672dff93Sespie 
1078672dff93Sespie           /* Read menu item.  */
1079672dff93Sespie           while (*p != 0 && *p != ':')
1080672dff93Sespie             p++;
1081672dff93Sespie           p++; /* skip : */
1082672dff93Sespie 
1083672dff93Sespie           if (*p == ':')
1084672dff93Sespie             { /* XEmacs-style entry, as in * Mew::Messaging.  */
1085672dff93Sespie               if (menu_item_equal (q, ':', base_name))
1086672dff93Sespie                 {
1087672dff93Sespie                   lines[i].delete = 1;
1088672dff93Sespie                   something_deleted = 1;
1089672dff93Sespie                 }
1090672dff93Sespie             }
1091672dff93Sespie           else
1092672dff93Sespie             { /* Emacs-style entry, as in * Emacs: (emacs).  */
1093672dff93Sespie               while (*p == ' ') p++; /* skip spaces after : */
1094672dff93Sespie               if (*p == '(')         /* if at parenthesized (FILENAME) */
1095672dff93Sespie                 {
1096672dff93Sespie                   p++;
1097672dff93Sespie                   if (menu_item_equal (p, ')', base_name))
1098672dff93Sespie                     {
1099672dff93Sespie                       lines[i].delete = 1;
1100672dff93Sespie                       something_deleted = 1;
1101672dff93Sespie                     }
1102672dff93Sespie                 }
1103672dff93Sespie             }
1104672dff93Sespie         }
1105672dff93Sespie 
1106672dff93Sespie       /* Treat lines that start with whitespace
1107672dff93Sespie          as continuations; if we are deleting an entry,
1108672dff93Sespie          delete all its continuations as well.  */
1109672dff93Sespie       else if (i > 0 && (*lines[i].start == ' ' || *lines[i].start == '\t'))
1110672dff93Sespie         {
1111672dff93Sespie           lines[i].delete = lines[i - 1].delete;
1112672dff93Sespie         }
1113672dff93Sespie     }
1114672dff93Sespie 
1115672dff93Sespie   /* Finish the info about the end of the last node.  */
1116672dff93Sespie   if (*nodes != 0)
1117672dff93Sespie     {
1118672dff93Sespie       (*nodes)->end_line = nlines;
1119672dff93Sespie       if ((*nodes)->last_section != 0)
1120672dff93Sespie         (*nodes)->last_section->end_line = nlines;
1121672dff93Sespie     }
1122672dff93Sespie 
1123672dff93Sespie   return something_deleted;
1124672dff93Sespie }
1125672dff93Sespie 
1126c7c5c1bbSkstailey int
main(int argc,char ** argv)11271076333cSespie main (int argc, char **argv)
112840248eceSdownsj {
1129672dff93Sespie   char *opened_dirfilename;
1130672dff93Sespie   char *compression_program;
113140248eceSdownsj   char *infile_sans_info;
1132672dff93Sespie   char *infile = 0, *dirfile = 0;
113340248eceSdownsj 
113440248eceSdownsj   /* Record the text of the Info file, as a sequence of characters
113540248eceSdownsj      and as a sequence of lines.  */
1136672dff93Sespie   char *input_data = NULL;
1137672dff93Sespie   int input_size = 0;
1138672dff93Sespie   struct line_data *input_lines = NULL;
1139672dff93Sespie   int input_nlines = 0;
114040248eceSdownsj 
114140248eceSdownsj   /* Record here the specified section names and directory entries.  */
114240248eceSdownsj   struct spec_section *input_sections = NULL;
114340248eceSdownsj   struct spec_entry *entries_to_add = NULL;
114440248eceSdownsj   int n_entries_to_add = 0;
114540248eceSdownsj 
114640248eceSdownsj   /* Record the old text of the dir file, as plain characters,
114740248eceSdownsj      as lines, and as nodes.  */
114840248eceSdownsj   char *dir_data;
114940248eceSdownsj   int dir_size;
115040248eceSdownsj   int dir_nlines;
115140248eceSdownsj   struct line_data *dir_lines;
115240248eceSdownsj   struct node *dir_nodes;
115340248eceSdownsj 
115440248eceSdownsj   /* Nonzero means --delete was specified (just delete existing entries).  */
115540248eceSdownsj   int delete_flag = 0;
115640248eceSdownsj   int something_deleted = 0;
115740248eceSdownsj   /* Nonzero means -q was specified.  */
115840248eceSdownsj   int quiet_flag = 0;
115940248eceSdownsj 
116040248eceSdownsj   int i;
116140248eceSdownsj 
1162c7c5c1bbSkstailey #ifdef HAVE_SETLOCALE
1163c7c5c1bbSkstailey   /* Set locale via LC_ALL.  */
1164c7c5c1bbSkstailey   setlocale (LC_ALL, "");
1165c7c5c1bbSkstailey #endif
1166c7c5c1bbSkstailey 
1167*964e42ffSderaadt   if (pledge ("stdio rpath wpath cpath proc exec", NULL) == -1)
1168*964e42ffSderaadt     pfatal_with_name ("pledge");
1169*964e42ffSderaadt 
1170c7c5c1bbSkstailey   /* Set the text message domain.  */
1171c7c5c1bbSkstailey   bindtextdomain (PACKAGE, LOCALEDIR);
1172c7c5c1bbSkstailey   textdomain (PACKAGE);
1173c7c5c1bbSkstailey 
117440248eceSdownsj   while (1)
117540248eceSdownsj     {
1176c7c5c1bbSkstailey       int opt = getopt_long (argc, argv, "i:d:e:s:hHr", longopts, 0);
117740248eceSdownsj 
117840248eceSdownsj       if (opt == EOF)
117940248eceSdownsj         break;
118040248eceSdownsj 
118140248eceSdownsj       switch (opt)
118240248eceSdownsj         {
118340248eceSdownsj         case 0:
118440248eceSdownsj           /* If getopt returns 0, then it has already processed a
118540248eceSdownsj              long-named option.  We should do nothing.  */
118640248eceSdownsj           break;
118740248eceSdownsj 
118840248eceSdownsj         case 1:
118940248eceSdownsj           abort ();
119040248eceSdownsj 
119140248eceSdownsj         case 'd':
119240248eceSdownsj           if (dirfile)
119340248eceSdownsj             {
11941076333cSespie               fprintf (stderr, _("%s: already have dir file: %s\n"),
11951076333cSespie                        progname, dirfile);
119640248eceSdownsj               suggest_asking_for_help ();
119740248eceSdownsj             }
119840248eceSdownsj           dirfile = optarg;
119940248eceSdownsj           break;
120040248eceSdownsj 
120140248eceSdownsj         case 'D':
120240248eceSdownsj           if (dirfile)
120340248eceSdownsj             {
12041076333cSespie               fprintf (stderr, _("%s: already have dir file: %s\n"),
12051076333cSespie                        progname, dirfile);
120640248eceSdownsj               suggest_asking_for_help ();
120740248eceSdownsj             }
120840248eceSdownsj           dirfile = concat (optarg, "", "/dir");
120940248eceSdownsj           break;
121040248eceSdownsj 
121140248eceSdownsj         case 'e':
121240248eceSdownsj           {
121340248eceSdownsj             struct spec_entry *next
121440248eceSdownsj               = (struct spec_entry *) xmalloc (sizeof (struct spec_entry));
1215672dff93Sespie             int olen = strlen (optarg);
1216672dff93Sespie             if (! (*optarg != 0 && optarg[olen - 1] == '\n'))
1217672dff93Sespie               {
121840248eceSdownsj                 optarg = concat (optarg, "\n", "");
1219672dff93Sespie                 olen++;
1220672dff93Sespie               }
122140248eceSdownsj             next->text = optarg;
1222672dff93Sespie             next->text_len = olen;
1223672dff93Sespie             next->entry_sections = NULL;
1224672dff93Sespie             next->entry_sections_tail = NULL;
122540248eceSdownsj             next->next = entries_to_add;
122640248eceSdownsj             entries_to_add = next;
122740248eceSdownsj             n_entries_to_add++;
122840248eceSdownsj           }
122940248eceSdownsj           break;
123040248eceSdownsj 
123140248eceSdownsj         case 'h':
123240248eceSdownsj         case 'H':
123340248eceSdownsj           print_help ();
1234672dff93Sespie           xexit (0);
123540248eceSdownsj 
123640248eceSdownsj         case 'i':
123740248eceSdownsj           if (infile)
123840248eceSdownsj             {
1239c7c5c1bbSkstailey               fprintf (stderr, _("%s: Specify the Info file only once.\n"),
124040248eceSdownsj                        progname);
124140248eceSdownsj               suggest_asking_for_help ();
124240248eceSdownsj             }
124340248eceSdownsj           infile = optarg;
124440248eceSdownsj           break;
124540248eceSdownsj 
124640248eceSdownsj         case 'q':
124740248eceSdownsj           quiet_flag = 1;
124840248eceSdownsj           break;
124940248eceSdownsj 
125040248eceSdownsj         case 'r':
125140248eceSdownsj           delete_flag = 1;
125240248eceSdownsj           break;
125340248eceSdownsj 
125440248eceSdownsj         case 's':
125540248eceSdownsj           {
125640248eceSdownsj             struct spec_section *next
125740248eceSdownsj               = (struct spec_section *) xmalloc (sizeof (struct spec_section));
125840248eceSdownsj             next->name = optarg;
125940248eceSdownsj             next->next = input_sections;
126040248eceSdownsj             next->missing = 1;
126140248eceSdownsj             input_sections = next;
126240248eceSdownsj           }
126340248eceSdownsj           break;
126440248eceSdownsj 
126540248eceSdownsj         case 'V':
126612dd3952Sespie           printf ("install-info (GNU %s) %s\n", PACKAGE, VERSION);
1267672dff93Sespie           puts ("");
12681076333cSespie           puts ("Copyright (C) 2004 Free Software Foundation, Inc.");
12691076333cSespie           printf (_("There is NO warranty.  You may redistribute this software\n\
127040248eceSdownsj under the terms of the GNU General Public License.\n\
12711076333cSespie For more information about these matters, see the files named COPYING.\n"));
1272672dff93Sespie           xexit (0);
127340248eceSdownsj 
127440248eceSdownsj         default:
127540248eceSdownsj           suggest_asking_for_help ();
127640248eceSdownsj         }
127740248eceSdownsj     }
127840248eceSdownsj 
127940248eceSdownsj   /* Interpret the non-option arguments as file names.  */
128040248eceSdownsj   for (; optind < argc; ++optind)
128140248eceSdownsj     {
128240248eceSdownsj       if (infile == 0)
128340248eceSdownsj         infile = argv[optind];
128440248eceSdownsj       else if (dirfile == 0)
128540248eceSdownsj         dirfile = argv[optind];
128640248eceSdownsj       else
12873aa90977Sespie         error (_("excess command line argument `%s'"), argv[optind], 0);
128840248eceSdownsj     }
128940248eceSdownsj 
129040248eceSdownsj   if (!infile)
12913aa90977Sespie     fatal (_("No input file specified; try --help for more information."),
12923aa90977Sespie            0, 0);
129340248eceSdownsj   if (!dirfile)
12943aa90977Sespie     fatal (_("No dir file specified; try --help for more information."), 0, 0);
129540248eceSdownsj 
1296672dff93Sespie   /* Read the Info file and parse it into lines, unless we're deleting.  */
1297672dff93Sespie   if (!delete_flag)
1298672dff93Sespie     {
1299672dff93Sespie       input_data = readfile (infile, &input_size, NULL, NULL, NULL);
130040248eceSdownsj       input_lines = findlines (input_data, input_size, &input_nlines);
130140248eceSdownsj     }
130240248eceSdownsj 
1303672dff93Sespie   i = parse_input (input_lines, input_nlines,
1304672dff93Sespie                    &input_sections, &entries_to_add);
1305672dff93Sespie   if (i > n_entries_to_add)
1306672dff93Sespie     n_entries_to_add = i;
130740248eceSdownsj 
130840248eceSdownsj   if (!delete_flag)
1309672dff93Sespie     {
131040248eceSdownsj       if (entries_to_add == 0)
1311672dff93Sespie         { /* No need to abort here, the original info file may not
1312672dff93Sespie              have the requisite Texinfo commands.  This is not
1313672dff93Sespie              something an installer should have to correct (it's a
1314672dff93Sespie              problem for the maintainer), and there's no need to cause
1315672dff93Sespie              subsequent parts of `make install' to fail.  */
13163aa90977Sespie           warning (_("no info dir entry in `%s'"), infile, 0);
1317672dff93Sespie           xexit (0);
1318672dff93Sespie         }
1319672dff93Sespie 
1320672dff93Sespie       /* If the entries came from the command-line arguments, their
1321672dff93Sespie          entry_sections pointers are not yet set.  Walk the chain of
1322672dff93Sespie          the entries and for each entry update entry_sections to point
1323672dff93Sespie          to the head of the list of sections where this entry should
1324672dff93Sespie          be put.  Note that all the entries specified on the command
1325672dff93Sespie          line get put into ALL the sections we've got, either from the
1326672dff93Sespie          Info file, or (under --section) from the command line,
1327672dff93Sespie          because in the loop below every entry inherits the entire
1328672dff93Sespie          chain of sections.  */
1329672dff93Sespie       if (n_entries_to_add > 0 && entries_to_add->entry_sections == NULL)
1330672dff93Sespie         {
1331672dff93Sespie           struct spec_entry *ep;
1332672dff93Sespie 
1333672dff93Sespie           /* If we got no sections, default to "Miscellaneous".  */
1334672dff93Sespie           if (input_sections == NULL)
1335672dff93Sespie             {
1336672dff93Sespie               input_sections = (struct spec_section *)
1337672dff93Sespie                 xmalloc (sizeof (struct spec_section));
1338672dff93Sespie               input_sections->name = "Miscellaneous";
1339672dff93Sespie               input_sections->next = NULL;
1340672dff93Sespie               input_sections->missing = 1;
1341672dff93Sespie             }
1342672dff93Sespie           for (ep = entries_to_add; ep; ep = ep->next)
1343672dff93Sespie             ep->entry_sections = input_sections;
1344672dff93Sespie         }
1345c7c5c1bbSkstailey     }
134640248eceSdownsj 
134740248eceSdownsj   /* Now read in the Info dir file.  */
1348672dff93Sespie   dir_data = readfile (dirfile, &dir_size, ensure_dirfile_exists,
1349672dff93Sespie                        &opened_dirfilename, &compression_program);
135040248eceSdownsj   dir_lines = findlines (dir_data, dir_size, &dir_nlines);
135140248eceSdownsj 
135240248eceSdownsj   /* We will be comparing the entries in the dir file against the
1353672dff93Sespie      current filename, so need to strip off any directory prefix and/or
1354672dff93Sespie      [.info][.gz] suffix.  */
135540248eceSdownsj   {
1356672dff93Sespie     char *infile_basename = infile + strlen (infile);
135740248eceSdownsj 
1358672dff93Sespie     if (HAVE_DRIVE (infile))
1359672dff93Sespie       infile += 2;      /* get past the drive spec X: */
136040248eceSdownsj 
1361672dff93Sespie     while (infile_basename > infile && !IS_SLASH (infile_basename[-1]))
1362672dff93Sespie       infile_basename--;
1363672dff93Sespie 
1364672dff93Sespie     infile_sans_info = strip_info_suffix (infile_basename);
136540248eceSdownsj   }
136640248eceSdownsj 
1367672dff93Sespie   something_deleted
1368672dff93Sespie     = parse_dir_file (dir_lines, dir_nlines, &dir_nodes, infile_sans_info);
136940248eceSdownsj 
137040248eceSdownsj   /* Decide where to add the new entries (unless --delete was used).
137140248eceSdownsj      Find the menu sections to add them in.
137240248eceSdownsj      In each section, find the proper alphabetical place to add
137340248eceSdownsj      each of the entries.  */
137440248eceSdownsj   if (!delete_flag)
137540248eceSdownsj     {
137640248eceSdownsj       struct node *node;
137740248eceSdownsj       struct menu_section *section;
137840248eceSdownsj       struct spec_section *spec;
137940248eceSdownsj 
138040248eceSdownsj       for (node = dir_nodes; node; node = node->next)
138140248eceSdownsj         for (section = node->sections; section; section = section->next)
138240248eceSdownsj           {
138340248eceSdownsj             for (i = section->end_line; i > section->start_line; i--)
138440248eceSdownsj               if (dir_lines[i - 1].size != 0)
138540248eceSdownsj                 break;
138640248eceSdownsj             section->end_line = i;
138740248eceSdownsj 
138840248eceSdownsj             for (spec = input_sections; spec; spec = spec->next)
138940248eceSdownsj               if (!strcmp (spec->name, section->name))
139040248eceSdownsj                 break;
139140248eceSdownsj             if (spec)
139240248eceSdownsj               {
139340248eceSdownsj                 int add_at_line = section->end_line;
139440248eceSdownsj                 struct spec_entry *entry;
139540248eceSdownsj                 /* Say we have found at least one section with this name,
139640248eceSdownsj                    so we need not add such a section.  */
139740248eceSdownsj                 spec->missing = 0;
139840248eceSdownsj                 /* For each entry, find the right place in this section
139940248eceSdownsj                    to add it.  */
140040248eceSdownsj                 for (entry = entries_to_add; entry; entry = entry->next)
140140248eceSdownsj                   {
1402672dff93Sespie                     /* Did they at all want this entry to be put into
1403672dff93Sespie                        this section?  */
1404672dff93Sespie                     for (spec = entry->entry_sections;
1405672dff93Sespie                          spec && spec != entry->entry_sections_tail;
1406672dff93Sespie                          spec = spec->next)
1407672dff93Sespie                       {
1408672dff93Sespie                         if (!strcmp (spec->name, section->name))
1409672dff93Sespie                           break;
1410672dff93Sespie                       }
1411672dff93Sespie                     if (!spec || spec == entry->entry_sections_tail)
1412672dff93Sespie                       continue;
1413672dff93Sespie 
141440248eceSdownsj                     /* Subtract one because dir_lines is zero-based,
141540248eceSdownsj                        but the `end_line' and `start_line' members are
141640248eceSdownsj                        one-based.  */
141740248eceSdownsj                     for (i = section->end_line - 1;
141840248eceSdownsj                          i >= section->start_line - 1; i--)
141940248eceSdownsj                       {
142040248eceSdownsj                         /* If an entry exists with the same name,
142140248eceSdownsj                            and was not marked for deletion
142240248eceSdownsj                            (which means it is for some other file),
142340248eceSdownsj                            we are in trouble.  */
142440248eceSdownsj                         if (dir_lines[i].start[0] == '*'
1425672dff93Sespie                             && menu_line_equal (entry->text, entry->text_len,
142640248eceSdownsj                                                 dir_lines[i].start,
142740248eceSdownsj                                                 dir_lines[i].size)
142840248eceSdownsj                             && !dir_lines[i].delete)
1429c7c5c1bbSkstailey                           fatal (_("menu item `%s' already exists, for file `%s'"),
143040248eceSdownsj                                  extract_menu_item_name (entry->text),
143140248eceSdownsj                                  extract_menu_file_name (dir_lines[i].start));
143240248eceSdownsj                         if (dir_lines[i].start[0] == '*'
1433672dff93Sespie                             && menu_line_lessp (entry->text, entry->text_len,
143440248eceSdownsj                                                 dir_lines[i].start,
143540248eceSdownsj                                                 dir_lines[i].size))
143640248eceSdownsj                           add_at_line = i;
143740248eceSdownsj                       }
143840248eceSdownsj                     insert_entry_here (entry, add_at_line,
143940248eceSdownsj                                        dir_lines, n_entries_to_add);
144040248eceSdownsj                   }
144140248eceSdownsj               }
144240248eceSdownsj           }
144340248eceSdownsj 
144440248eceSdownsj       /* Mark the end of the Top node as the place to add any
144540248eceSdownsj          new sections that are needed.  */
144640248eceSdownsj       for (node = dir_nodes; node; node = node->next)
144740248eceSdownsj         if (node->name && strcmp (node->name, "Top") == 0)
144840248eceSdownsj           dir_lines[node->end_line].add_sections_before = 1;
144940248eceSdownsj     }
145040248eceSdownsj 
145140248eceSdownsj   if (delete_flag && !something_deleted && !quiet_flag)
14523aa90977Sespie     warning (_("no entries found for `%s'; nothing deleted"), infile, 0);
145340248eceSdownsj 
1454672dff93Sespie   output_dirfile (opened_dirfilename, dir_nlines, dir_lines, n_entries_to_add,
1455672dff93Sespie                   entries_to_add, input_sections, compression_program);
145640248eceSdownsj 
1457672dff93Sespie   xexit (0);
14581076333cSespie   return 0; /* Avoid bogus warnings.  */
145940248eceSdownsj }
146040248eceSdownsj 
146140248eceSdownsj /* Divide the text at DATA (of SIZE bytes) into lines.
146240248eceSdownsj    Return a vector of struct line_data describing the lines.
146340248eceSdownsj    Store the length of that vector into *NLINESP.  */
146440248eceSdownsj 
146540248eceSdownsj struct line_data *
findlines(char * data,int size,int * nlinesp)14661076333cSespie findlines (char *data, int size, int *nlinesp)
146740248eceSdownsj {
1468672dff93Sespie   int i;
1469672dff93Sespie   int lineflag = 1;
1470672dff93Sespie   int lines_allocated = 511;
147140248eceSdownsj   int filled = 0;
1472672dff93Sespie   struct line_data *lines
1473672dff93Sespie     = xmalloc ((lines_allocated + 1) * sizeof (struct line_data));
147440248eceSdownsj 
147540248eceSdownsj   for (i = 0; i < size; i++)
147640248eceSdownsj     {
147740248eceSdownsj       if (lineflag)
147840248eceSdownsj         {
147940248eceSdownsj           if (filled == lines_allocated)
148040248eceSdownsj             {
1481672dff93Sespie               /* try to keep things somewhat page-aligned */
1482672dff93Sespie               lines_allocated = ((lines_allocated + 1) * 2) - 1;
1483672dff93Sespie               lines = xrealloc (lines, (lines_allocated + 1)
1484672dff93Sespie                                        * sizeof (struct line_data));
148540248eceSdownsj             }
148640248eceSdownsj           lines[filled].start = &data[i];
148740248eceSdownsj           lines[filled].add_entries_before = 0;
148840248eceSdownsj           lines[filled].add_sections_before = 0;
148940248eceSdownsj           lines[filled].delete = 0;
149040248eceSdownsj           if (filled > 0)
149140248eceSdownsj             lines[filled - 1].size
149240248eceSdownsj               = lines[filled].start - lines[filled - 1].start - 1;
149340248eceSdownsj           filled++;
149440248eceSdownsj         }
149540248eceSdownsj       lineflag = (data[i] == '\n');
149640248eceSdownsj     }
149740248eceSdownsj   if (filled > 0)
149840248eceSdownsj     lines[filled - 1].size = &data[i] - lines[filled - 1].start - lineflag;
149940248eceSdownsj 
150040248eceSdownsj   /* Do not leave garbage in the last element.  */
150140248eceSdownsj   lines[filled].start = NULL;
150240248eceSdownsj   lines[filled].add_entries_before = NULL;
150340248eceSdownsj   lines[filled].add_sections_before = 0;
150440248eceSdownsj   lines[filled].delete = 0;
150540248eceSdownsj   lines[filled].size = 0;
150640248eceSdownsj 
150740248eceSdownsj   *nlinesp = filled;
150840248eceSdownsj   return lines;
150940248eceSdownsj }
151040248eceSdownsj 
15111076333cSespie /* This is the comparison function for qsort for a vector of pointers to
15121076333cSespie    struct spec_section.  (Have to use const void * as the parameter type
15131076333cSespie    to avoid incompatible-with-qsort warnings.)
151440248eceSdownsj    Compare the section names.  */
151540248eceSdownsj 
151640248eceSdownsj int
compare_section_names(const void * p1,const void * p2)15171076333cSespie compare_section_names (const void *p1, const void *p2)
151840248eceSdownsj {
15191076333cSespie   struct spec_section **sec1 = (struct spec_section **) p1;
15201076333cSespie   struct spec_section **sec2 = (struct spec_section **) p2;
152140248eceSdownsj   char *name1 = (*sec1)->name;
152240248eceSdownsj   char *name2 = (*sec2)->name;
152340248eceSdownsj   return strcmp (name1, name2);
152440248eceSdownsj }
152540248eceSdownsj 
1526672dff93Sespie /* This is the comparison function for qsort
1527672dff93Sespie    for a vector of pointers to struct spec_entry.
1528672dff93Sespie    Compare the entries' text.  */
1529672dff93Sespie 
1530672dff93Sespie int
compare_entries_text(const void * p1,const void * p2)15311076333cSespie compare_entries_text (const void *p1, const void *p2)
1532672dff93Sespie {
15331076333cSespie   struct spec_entry **entry1 = (struct spec_entry **) p1;
15341076333cSespie   struct spec_entry **entry2 = (struct spec_entry **) p2;
1535672dff93Sespie   char *text1 = (*entry1)->text;
1536672dff93Sespie   char *text2 = (*entry2)->text;
1537672dff93Sespie   char *colon1 = strchr (text1, ':');
1538672dff93Sespie   char *colon2 = strchr (text2, ':');
1539672dff93Sespie   int len1, len2;
1540672dff93Sespie 
1541672dff93Sespie   if (!colon1)
1542672dff93Sespie     len1 = strlen (text1);
1543672dff93Sespie   else
1544672dff93Sespie     len1 = colon1 - text1;
1545672dff93Sespie   if (!colon2)
1546672dff93Sespie     len2 = strlen (text2);
1547672dff93Sespie   else
1548672dff93Sespie     len2 = colon2 - text2;
1549672dff93Sespie   return strncmp (text1, text2, len1 <= len2 ? len1 : len2);
1550672dff93Sespie }
1551672dff93Sespie 
155240248eceSdownsj /* Insert ENTRY into the add_entries_before vector
155340248eceSdownsj    for line number LINE_NUMBER of the dir file.
155440248eceSdownsj    DIR_LINES and N_ENTRIES carry information from like-named variables
155540248eceSdownsj    in main.  */
155640248eceSdownsj 
155740248eceSdownsj void
insert_entry_here(struct spec_entry * entry,int line_number,struct line_data * dir_lines,int n_entries)15581076333cSespie insert_entry_here (struct spec_entry *entry, int line_number,
15591076333cSespie                    struct line_data *dir_lines, int n_entries)
156040248eceSdownsj {
1561672dff93Sespie   int i, j;
156240248eceSdownsj 
156340248eceSdownsj   if (dir_lines[line_number].add_entries_before == 0)
156440248eceSdownsj     {
156540248eceSdownsj       dir_lines[line_number].add_entries_before
156640248eceSdownsj         = (struct spec_entry **) xmalloc (n_entries * sizeof (struct spec_entry *));
156740248eceSdownsj       for (i = 0; i < n_entries; i++)
156840248eceSdownsj         dir_lines[line_number].add_entries_before[i] = 0;
156940248eceSdownsj     }
157040248eceSdownsj 
1571672dff93Sespie   /* Find the place where this entry belongs.  If there are already
1572672dff93Sespie      several entries to add before LINE_NUMBER, make sure they are in
1573672dff93Sespie      alphabetical order.  */
157440248eceSdownsj   for (i = 0; i < n_entries; i++)
1575672dff93Sespie     if (dir_lines[line_number].add_entries_before[i] == 0
1576672dff93Sespie         || menu_line_lessp (entry->text, strlen (entry->text),
1577672dff93Sespie                             dir_lines[line_number].add_entries_before[i]->text,
1578672dff93Sespie                             strlen (dir_lines[line_number].add_entries_before[i]->text)))
157940248eceSdownsj       break;
158040248eceSdownsj 
158140248eceSdownsj   if (i == n_entries)
158240248eceSdownsj     abort ();
158340248eceSdownsj 
1584672dff93Sespie   /* If we need to plug ENTRY into the middle of the
1585672dff93Sespie      ADD_ENTRIES_BEFORE array, move the entries which should be output
1586672dff93Sespie      after this one down one notch, before adding a new one.  */
1587672dff93Sespie   if (dir_lines[line_number].add_entries_before[i] != 0)
1588672dff93Sespie     for (j = n_entries - 1; j > i; j--)
1589672dff93Sespie       dir_lines[line_number].add_entries_before[j]
1590672dff93Sespie         = dir_lines[line_number].add_entries_before[j - 1];
1591672dff93Sespie 
159240248eceSdownsj   dir_lines[line_number].add_entries_before[i] = entry;
159340248eceSdownsj }
1594