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