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