source: trunk/texinfo/util/install-info.c@ 2754

Last change on this file since 2754 was 2617, checked in by bird, 19 years ago

GNU Texinfo 4.8

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