1 /* install-info -- create Info directory entry(ies) for an Info file.
2 $Id: install-info.c,v 1.1.1.3 1998/03/24 18:20:30 law Exp $
4 Copyright (C) 1996, 97, 98 Free Software Foundation, Inc.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/
27 /* Name this program was invoked with. */
31 struct line_data
*findlines ();
33 void insert_entry_here ();
34 int compare_section_names ();
38 /* Data structures. */
41 /* Record info about a single line from a file as read into core. */
44 /* The start of the line. */
46 /* The number of characters in the line,
47 excluding the terminating newline. */
49 /* Vector containing pointers to the entries to add before this line.
50 The vector is null-terminated. */
51 struct spec_entry
**add_entries_before
;
52 /* 1 means output any needed new sections before this line. */
53 int add_sections_before
;
54 /* 1 means don't output this line. */
59 /* This is used for a list of the specified menu section names
60 in which entries should be added. */
63 struct spec_section
*next
;
65 /* 1 means we have not yet found an existing section with this name
66 in the dir file--so we will need to add a new section. */
71 /* This is used for a list of the entries specified to be added. */
74 struct spec_entry
*next
;
79 /* This is used for a list of nodes found by parsing the dir file. */
85 /* The line number of the line where the node starts.
86 This is the line that contains control-underscore. */
88 /* The line number of the line where the node ends,
89 which is the end of the file or where the next line starts. */
91 /* Start of first line in this node's menu
92 (the line after the * Menu: line). */
94 /* The start of the chain of sections in this node's menu. */
95 struct menu_section
*sections
;
96 /* The last menu section in the chain. */
97 struct menu_section
*last_section
;
101 /* This is used for a list of sections found in a node's menu.
102 Each struct node has such a list in the sections field. */
105 struct menu_section
*next
;
107 /* Line number of start of section. */
109 /* Line number of end of section. */
113 /* Memory allocation and string operations. */
115 /* Like malloc but get fatal error if memory is exhausted. */
120 extern void *malloc ();
121 void *result
= malloc (size
);
123 fatal (_("virtual memory exhausted"), 0);
127 /* Like realloc but get fatal error if memory is exhausted. */
133 extern void *realloc ();
134 void *result
= realloc (obj
, size
);
136 fatal (_("virtual memory exhausted"), 0);
140 /* Return a newly-allocated string
141 whose contents concatenate those of S1, S2, S3. */
146 int len1
= strlen (s1
), len2
= strlen (s2
), len3
= strlen (s3
);
147 char *result
= (char *) xmalloc (len1
+ len2
+ len3
+ 1);
150 strcpy (result
+ len1
, s2
);
151 strcpy (result
+ len1
+ len2
, s3
);
152 *(result
+ len1
+ len2
+ len3
) = 0;
157 /* Return a string containing SIZE characters
158 copied from starting at STRING. */
161 copy_string (string
, size
)
166 char *copy
= (char *) xmalloc (size
+ 1);
167 for (i
= 0; i
< size
; i
++)
173 /* Error message functions. */
175 /* Print error message. S1 is printf control string, S2 and S3 args for it. */
182 fprintf (stderr
, "%s: ", progname
);
183 fprintf (stderr
, s1
, s2
, s3
);
192 fprintf (stderr
, _("%s: warning: "), progname
);
193 fprintf (stderr
, s1
, s2
, s3
);
197 /* Print error message and exit. */
207 /* Print fatal error message based on errno, with file name NAME. */
210 pfatal_with_name (name
)
213 char *s
= concat ("", strerror (errno
), _(" for %s"));
217 /* Given the full text of a menu entry, null terminated,
218 return just the menu item name (copied). */
221 extract_menu_item_name (item_text
)
226 if (*item_text
== '*')
228 while (*item_text
== ' ')
232 while (*p
&& *p
!= ':') p
++;
233 return copy_string (item_text
, p
- item_text
);
236 /* Given the full text of a menu entry, terminated by null or newline,
237 return just the menu item file (copied). */
240 extract_menu_file_name (item_text
)
245 /* If we have text that looks like * ITEM: (FILE)NODE...,
246 extract just FILE. Otherwise return "(none)". */
253 /* Skip to and past the colon. */
254 while (*p
&& *p
!= '\n' && *p
!= ':') p
++;
257 /* Skip past the open-paren. */
262 else if (*p
== ' ' || *p
== '\t')
271 /* File name ends just before the close-paren. */
272 while (*p
&& *p
!= '\n' && *p
!= ')') p
++;
276 return copy_string (item_text
, p
- item_text
);
280 suggest_asking_for_help ()
282 fprintf (stderr
, _("\tTry `%s --help' for a complete list of options.\n"),
290 printf (_("Usage: %s [OPTION]... [INFO-FILE [DIR-FILE]]\n\
292 Install INFO-FILE in the Info directory file DIR-FILE.\n\
295 --delete Delete existing entries in INFO-FILE;\n\
296 don't insert any new entries.\n\
297 --dir-file=NAME Specify file name of Info directory file.\n\
298 This is equivalent to using the DIR-FILE argument.\n\
299 --entry=TEXT Insert TEXT as an Info directory entry.\n\
300 TEXT should have the form of an Info menu item line\n\
301 plus zero or more extra lines starting with whitespace.\n\
302 If you specify more than one entry, they are all added.\n\
303 If you don't specify any entries, they are determined\n\
304 from information in the Info file itself.\n\
305 --help Display this help and exit.\n\
306 --info-file=FILE Specify Info file to install in the directory.\n\
307 This is equivalent to using the INFO-FILE argument.\n\
308 --info-dir=DIR Same as --dir-file=DIR/dir.\n\
309 --item=TEXT Same as --entry TEXT.\n\
310 An Info directory entry is actually a menu item.\n\
311 --quiet Suppress warnings.\n\
312 --remove Same as --delete.\n\
313 --section=SEC Put this file's entries in section SEC of the directory.\n\
314 If you specify more than one section, all the entries\n\
315 are added in each of the sections.\n\
316 If you don't specify any sections, they are determined\n\
317 from information in the Info file itself.\n\
318 --version Display version information and exit.\n\
320 Email bug reports to bug-texinfo@gnu.org.\n\
325 /* If DIRFILE does not exist, create a minimal one (or abort). If it
326 already exists, do nothing. */
329 ensure_dirfile_exists (dirfile
)
332 int desc
= open (dirfile
, O_RDONLY
);
333 if (desc
< 0 && errno
== ENOENT
)
336 char *readerr
= strerror (errno
);
338 f
= fopen (dirfile
, "w");
341 fputs (_("This is the file .../info/dir, which contains the\n\
342 topmost node of the Info hierarchy, called (dir)Top.\n\
343 The first time you invoke Info you start off looking at this node.\n\
345 File: dir,\tNode: Top,\tThis is the top of the INFO tree\n\
347 This (the Directory node) gives a menu of major topics.\n\
348 Typing \"q\" exits, \"?\" lists all Info commands, \"d\" returns here,\n\
349 \"h\" gives a primer for first-timers,\n\
350 \"mEmacs<Return>\" visits the Emacs manual, etc.\n\
352 In Emacs, you can click mouse button 2 on a menu item or cross reference\n\
358 pfatal_with_name (dirfile
);
362 /* Didn't exist, but couldn't open for writing. */
364 _("%s: could not read (%s) and could not create (%s)\n"),
365 dirfile
, readerr
, strerror (errno
));
370 close (desc
); /* It already existed, so fine. */
373 /* This table defines all the long-named options, says whether they
374 use an argument, and maps them into equivalent single-letter options. */
376 struct option longopts
[] =
378 { "delete", no_argument
, NULL
, 'r' },
379 { "dir-file", required_argument
, NULL
, 'd' },
380 { "entry", required_argument
, NULL
, 'e' },
381 { "help", no_argument
, NULL
, 'h' },
382 { "info-dir", required_argument
, NULL
, 'D' },
383 { "info-file", required_argument
, NULL
, 'i' },
384 { "item", required_argument
, NULL
, 'e' },
385 { "quiet", no_argument
, NULL
, 'q' },
386 { "remove", no_argument
, NULL
, 'r' },
387 { "section", required_argument
, NULL
, 's' },
388 { "version", no_argument
, NULL
, 'V' },
398 char *infile
= 0, *dirfile
= 0;
399 char *infile_sans_info
;
400 unsigned infilelen_sans_info
;
403 /* Record the text of the Info file, as a sequence of characters
404 and as a sequence of lines. */
407 struct line_data
*input_lines
;
410 /* Record here the specified section names and directory entries. */
411 struct spec_section
*input_sections
= NULL
;
412 struct spec_entry
*entries_to_add
= NULL
;
413 int n_entries_to_add
= 0;
415 /* Record the old text of the dir file, as plain characters,
416 as lines, and as nodes. */
420 struct line_data
*dir_lines
;
421 struct node
*dir_nodes
;
423 /* Nonzero means --delete was specified (just delete existing entries). */
425 int something_deleted
= 0;
426 /* Nonzero means -q was specified. */
429 int node_header_flag
;
435 #ifdef HAVE_SETLOCALE
436 /* Set locale via LC_ALL. */
437 setlocale (LC_ALL
, "");
440 /* Set the text message domain. */
441 bindtextdomain (PACKAGE
, LOCALEDIR
);
442 textdomain (PACKAGE
);
446 int opt
= getopt_long (argc
, argv
, "i:d:e:s:hHr", longopts
, 0);
454 /* If getopt returns 0, then it has already processed a
455 long-named option. We should do nothing. */
464 fprintf (stderr
, _("%s: Specify the Info directory only once.\n"),
466 suggest_asking_for_help ();
474 fprintf (stderr
, _("%s: Specify the Info directory only once.\n"),
476 suggest_asking_for_help ();
478 dirfile
= concat (optarg
, "", "/dir");
483 struct spec_entry
*next
484 = (struct spec_entry
*) xmalloc (sizeof (struct spec_entry
));
485 if (! (*optarg
!= 0 && optarg
[strlen (optarg
) - 1] == '\n'))
486 optarg
= concat (optarg
, "\n", "");
488 next
->next
= entries_to_add
;
489 entries_to_add
= next
;
502 fprintf (stderr
, _("%s: Specify the Info file only once.\n"),
504 suggest_asking_for_help ();
519 struct spec_section
*next
520 = (struct spec_section
*) xmalloc (sizeof (struct spec_section
));
522 next
->next
= input_sections
;
524 input_sections
= next
;
529 printf ("install-info (GNU %s) %s\n", PACKAGE
, VERSION
);
530 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
531 There is NO warranty. You may redistribute this software\n\
532 under the terms of the GNU General Public License.\n\
533 For more information about these matters, see the files named COPYING.\n"),
538 suggest_asking_for_help ();
542 /* Interpret the non-option arguments as file names. */
543 for (; optind
< argc
; ++optind
)
546 infile
= argv
[optind
];
547 else if (dirfile
== 0)
548 dirfile
= argv
[optind
];
550 error (_("excess command line argument `%s'"), argv
[optind
]);
554 fatal (_("No input file specified; try --help for more information."));
556 fatal (_("No dir file specified; try --help for more information."));
558 /* Read the Info file and parse it into lines. */
560 input_data
= readfile (infile
, &input_size
);
561 input_lines
= findlines (input_data
, input_size
, &input_nlines
);
563 /* Parse the input file to find the section names it specifies. */
565 if (input_sections
== 0)
567 prefix_length
= strlen ("INFO-DIR-SECTION ");
568 for (i
= 0; i
< input_nlines
; i
++)
570 if (!strncmp ("INFO-DIR-SECTION ", input_lines
[i
].start
,
573 struct spec_section
*next
574 = (struct spec_section
*) xmalloc (sizeof (struct spec_section
));
575 next
->name
= copy_string (input_lines
[i
].start
+ prefix_length
,
576 input_lines
[i
].size
- prefix_length
);
577 next
->next
= input_sections
;
579 input_sections
= next
;
584 /* Default to section "Miscellaneous" if no sections specified. */
585 if (input_sections
== 0)
588 = (struct spec_section
*) xmalloc (sizeof (struct spec_section
));
589 input_sections
->name
= "Miscellaneous";
590 input_sections
->next
= 0;
591 input_sections
->missing
= 1;
594 /* Now find the directory entries specified in the file
595 and put them on entries_to_add. But not if entries
596 were specified explicitly with command options. */
598 if (entries_to_add
== 0)
600 char *start_of_this_entry
= 0;
601 for (i
= 0; i
< input_nlines
; i
++)
603 if (!strncmp ("START-INFO-DIR-ENTRY", input_lines
[i
].start
,
605 && sizeof ("START-INFO-DIR-ENTRY") - 1 == input_lines
[i
].size
)
607 if (start_of_this_entry
!= 0)
608 fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"));
609 start_of_this_entry
= input_lines
[i
+ 1].start
;
611 if (!strncmp ("END-INFO-DIR-ENTRY", input_lines
[i
].start
,
613 && sizeof ("END-INFO-DIR-ENTRY") - 1 == input_lines
[i
].size
)
615 if (start_of_this_entry
!= 0)
617 struct spec_entry
*next
618 = (struct spec_entry
*) xmalloc (sizeof (struct spec_entry
));
619 next
->text
= copy_string (start_of_this_entry
,
620 input_lines
[i
].start
- start_of_this_entry
);
621 next
->next
= entries_to_add
;
622 entries_to_add
= next
;
624 start_of_this_entry
= 0;
627 fatal (_("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY"));
630 if (start_of_this_entry
!= 0)
631 fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"));
635 if (entries_to_add
== 0)
636 { /* No need to abort here, the original info file may not have
637 the requisite Texinfo commands. This is not something an
638 installer should have to correct (it's a problem for the
639 maintainer), and there's no need to cause subsequent parts of
640 `make install' to fail. */
641 warning (_("no info dir entry in `%s'"), infile
);
645 /* Now read in the Info dir file. */
646 ensure_dirfile_exists (dirfile
);
647 dir_data
= readfile (dirfile
, &dir_size
);
648 dir_lines
= findlines (dir_data
, dir_size
, &dir_nlines
);
650 /* We will be comparing the entries in the dir file against the
651 current filename, so need to strip off any directory prefix and any
654 unsigned basename_len
;
655 char *infile_basename
= strrchr (infile
, '/');
659 infile_basename
= infile
;
661 basename_len
= strlen (infile_basename
);
663 = (strlen (infile_basename
) > 5
664 && strcmp (infile_basename
+ basename_len
- 5, ".info") == 0)
665 ? copy_string (infile_basename
, basename_len
- 5)
668 infilelen_sans_info
= strlen (infile_sans_info
);
671 /* Parse the dir file. Find all the nodes, and their menus,
672 and the sections of their menus. */
675 node_header_flag
= 0;
676 for (i
= 0; i
< dir_nlines
; i
++)
678 /* Parse node header lines. */
679 if (node_header_flag
)
682 for (j
= 0; j
< dir_lines
[i
].size
; j
++)
683 /* Find the node name and store it in the `struct node'. */
684 if (!strncmp ("Node:", dir_lines
[i
].start
+ j
, 5))
686 char *line
= dir_lines
[i
].start
;
687 /* Find the start of the node name. */
689 while (line
[j
] == ' ' || line
[j
] == '\t')
691 /* Find the end of the node name. */
693 while (line
[end
] != 0 && line
[end
] != ',' && line
[end
] != '\n'
694 && line
[end
] != '\t')
696 dir_nodes
->name
= copy_string (line
+ j
, end
- j
);
698 node_header_flag
= 0;
701 /* Notice the start of a node. */
702 if (*dir_lines
[i
].start
== 037)
705 = (struct node
*) xmalloc (sizeof (struct node
));
706 next
->next
= dir_nodes
;
708 next
->start_line
= i
;
710 next
->menu_start
= NULL
;
711 next
->sections
= NULL
;
712 next
->last_section
= NULL
;
715 dir_nodes
->end_line
= i
;
716 /* Fill in the end of the last menu section
717 of the previous node. */
718 if (dir_nodes
!= 0 && dir_nodes
->last_section
!= 0)
719 dir_nodes
->last_section
->end_line
= i
;
723 /* The following line is the header of this node;
725 node_header_flag
= 1;
728 /* Notice the lines that start menus. */
730 && !strncmp ("* Menu:", dir_lines
[i
].start
, 7))
731 dir_nodes
->menu_start
= dir_lines
[i
+ 1].start
;
733 /* Notice sections in menus. */
735 && dir_nodes
->menu_start
!= 0
736 && *dir_lines
[i
].start
!= '\n'
737 && *dir_lines
[i
].start
!= '*'
738 && *dir_lines
[i
].start
!= ' '
739 && *dir_lines
[i
].start
!= '\t')
741 /* Add this menu section to the node's list.
742 This list grows in forward order. */
743 struct menu_section
*next
744 = (struct menu_section
*) xmalloc (sizeof (struct menu_section
));
745 next
->start_line
= i
+ 1;
748 next
->name
= copy_string (dir_lines
[i
].start
, dir_lines
[i
].size
);
749 if (dir_nodes
->sections
)
751 dir_nodes
->last_section
->next
= next
;
752 dir_nodes
->last_section
->end_line
= i
;
755 dir_nodes
->sections
= next
;
756 dir_nodes
->last_section
= next
;
759 /* Check for an existing entry that should be deleted.
760 Delete all entries which specify this file name. */
761 if (*dir_lines
[i
].start
== '*')
763 char *p
= dir_lines
[i
].start
;
765 while (*p
!= 0 && *p
!= ':')
768 while (*p
== ' ') p
++;
772 if ((dir_lines
[i
].size
773 > (p
- dir_lines
[i
].start
+ infilelen_sans_info
))
774 && !strncmp (p
, infile_sans_info
, infilelen_sans_info
)
775 && (p
[infilelen_sans_info
] == ')'
776 || !strncmp (p
+ infilelen_sans_info
, ".info)", 6)))
778 dir_lines
[i
].delete = 1;
779 something_deleted
= 1;
783 /* Treat lines that start with whitespace
784 as continuations; if we are deleting an entry,
785 delete all its continuations as well. */
787 && (*dir_lines
[i
].start
== ' '
788 || *dir_lines
[i
].start
== '\t'))
790 dir_lines
[i
].delete = dir_lines
[i
- 1].delete;
791 something_deleted
= 1;
795 /* Finish the info about the end of the last node. */
798 dir_nodes
->end_line
= dir_nlines
;
799 if (dir_nodes
->last_section
!= 0)
800 dir_nodes
->last_section
->end_line
= dir_nlines
;
803 /* Decide where to add the new entries (unless --delete was used).
804 Find the menu sections to add them in.
805 In each section, find the proper alphabetical place to add
806 each of the entries. */
811 struct menu_section
*section
;
812 struct spec_section
*spec
;
814 for (node
= dir_nodes
; node
; node
= node
->next
)
815 for (section
= node
->sections
; section
; section
= section
->next
)
817 for (i
= section
->end_line
; i
> section
->start_line
; i
--)
818 if (dir_lines
[i
- 1].size
!= 0)
820 section
->end_line
= i
;
822 for (spec
= input_sections
; spec
; spec
= spec
->next
)
823 if (!strcmp (spec
->name
, section
->name
))
827 int add_at_line
= section
->end_line
;
828 struct spec_entry
*entry
;
829 /* Say we have found at least one section with this name,
830 so we need not add such a section. */
832 /* For each entry, find the right place in this section
834 for (entry
= entries_to_add
; entry
; entry
= entry
->next
)
836 int textlen
= strlen (entry
->text
);
837 /* Subtract one because dir_lines is zero-based,
838 but the `end_line' and `start_line' members are
840 for (i
= section
->end_line
- 1;
841 i
>= section
->start_line
- 1; i
--)
843 /* If an entry exists with the same name,
844 and was not marked for deletion
845 (which means it is for some other file),
846 we are in trouble. */
847 if (dir_lines
[i
].start
[0] == '*'
848 && menu_line_equal (entry
->text
, textlen
,
851 && !dir_lines
[i
].delete)
852 fatal (_("menu item `%s' already exists, for file `%s'"),
853 extract_menu_item_name (entry
->text
),
854 extract_menu_file_name (dir_lines
[i
].start
));
855 if (dir_lines
[i
].start
[0] == '*'
856 && menu_line_lessp (entry
->text
, textlen
,
861 insert_entry_here (entry
, add_at_line
,
862 dir_lines
, n_entries_to_add
);
867 /* Mark the end of the Top node as the place to add any
868 new sections that are needed. */
869 for (node
= dir_nodes
; node
; node
= node
->next
)
870 if (node
->name
&& strcmp (node
->name
, "Top") == 0)
871 dir_lines
[node
->end_line
].add_sections_before
= 1;
874 if (delete_flag
&& !something_deleted
&& !quiet_flag
)
875 warning (_("no entries found for `%s'; nothing deleted"), infile
);
877 /* Output the old dir file, interpolating the new sections
878 and/or new entries where appropriate. */
880 output
= fopen (dirfile
, "w");
887 for (i
= 0; i
<= dir_nlines
; i
++)
891 /* If we decided to output some new entries before this line,
893 if (dir_lines
[i
].add_entries_before
)
894 for (j
= 0; j
< n_entries_to_add
; j
++)
896 struct spec_entry
*this = dir_lines
[i
].add_entries_before
[j
];
899 fputs (this->text
, output
);
901 /* If we decided to add some sections here
902 because there are no such sections in the file,
904 if (dir_lines
[i
].add_sections_before
)
906 struct spec_section
*spec
;
907 struct spec_section
**sections
;
910 /* Count the sections and allocate a vector for all of them. */
911 for (spec
= input_sections
; spec
; spec
= spec
->next
)
913 sections
= ((struct spec_section
**)
914 xmalloc (n_sections
* sizeof (struct spec_section
*)));
916 /* Fill the vector SECTIONS with pointers to all the sections,
919 for (spec
= input_sections
; spec
; spec
= spec
->next
)
920 sections
[j
++] = spec
;
921 qsort (sections
, n_sections
, sizeof (struct spec_section
*),
922 compare_section_names
);
924 /* Generate the new sections in alphabetical order.
925 In each new section, output all of our entries. */
926 for (j
= 0; j
< n_sections
; j
++)
931 struct spec_entry
*entry
;
934 fputs (spec
->name
, output
);
936 for (entry
= entries_to_add
; entry
; entry
= entry
->next
)
937 fputs (entry
->text
, output
);
944 /* Output the original dir lines unless marked for deletion. */
945 if (i
< dir_nlines
&& !dir_lines
[i
].delete)
947 fwrite (dir_lines
[i
].start
, 1, dir_lines
[i
].size
, output
);
957 /* Read all of file FILNAME into memory
958 and return the address of the data.
959 Store the size into SIZEP.
960 If there is trouble, do a fatal error. */
963 readfile (filename
, sizep
)
968 int data_size
= 1024;
969 char *data
= (char *) xmalloc (data_size
);
977 desc
= open (filename
, O_RDONLY
);
979 pfatal_with_name (filename
);
982 /* The file should always be two bytes long. */
983 if (read (desc
, data
, 2) != 2)
984 pfatal_with_name (filename
);
986 /* Undo that read. */
987 lseek (desc
, 0, SEEK_SET
);
989 /* If we see gzip magic, use gzdopen. */
990 if (data
[0] == '\x1f' && data
[1] == '\x8b')
993 zdesc
= gzdopen (desc
, "r");
996 pfatal_with_name (filename
);
999 #endif /* HAVE_LIBZ */
1005 nread
= gzread (zdesc
, data
+ filled
, data_size
- filled
);
1008 nread
= read (desc
, data
+ filled
, data_size
- filled
);
1011 pfatal_with_name (filename
);
1016 if (filled
== data_size
)
1019 data
= (char *) xrealloc (data
, data_size
);
1035 /* Divide the text at DATA (of SIZE bytes) into lines.
1036 Return a vector of struct line_data describing the lines.
1037 Store the length of that vector into *NLINESP. */
1040 findlines (data
, size
, nlinesp
)
1045 struct line_data
*lines
;
1046 int lines_allocated
= 512;
1051 lines
= (struct line_data
*) xmalloc (lines_allocated
* sizeof (struct line_data
));
1054 for (i
= 0; i
< size
; i
++)
1058 if (filled
== lines_allocated
)
1060 lines_allocated
*= 2;
1061 lines
= (struct line_data
*) xrealloc (lines
, lines_allocated
* sizeof (struct line_data
));
1063 lines
[filled
].start
= &data
[i
];
1064 lines
[filled
].add_entries_before
= 0;
1065 lines
[filled
].add_sections_before
= 0;
1066 lines
[filled
].delete = 0;
1068 lines
[filled
- 1].size
1069 = lines
[filled
].start
- lines
[filled
- 1].start
- 1;
1072 lineflag
= (data
[i
] == '\n');
1075 lines
[filled
- 1].size
= &data
[i
] - lines
[filled
- 1].start
- lineflag
;
1077 /* Do not leave garbage in the last element. */
1078 lines
[filled
].start
= NULL
;
1079 lines
[filled
].add_entries_before
= NULL
;
1080 lines
[filled
].add_sections_before
= 0;
1081 lines
[filled
].delete = 0;
1082 lines
[filled
].size
= 0;
1088 /* Compare the menu item names in LINE1 (line length LEN1)
1089 and LINE2 (line length LEN2). Return 1 if the item name
1090 in LINE1 is less, 0 otherwise. */
1093 menu_line_lessp (line1
, len1
, line2
, len2
)
1099 int minlen
= (len1
< len2
? len1
: len2
);
1102 for (i
= 0; i
< minlen
; i
++)
1104 /* If one item name is a prefix of the other,
1105 the former one is less. */
1106 if (line1
[i
] == ':' && line2
[i
] != ':')
1108 if (line2
[i
] == ':' && line1
[i
] != ':')
1110 /* If they both continue and differ, one is less. */
1111 if (line1
[i
] < line2
[i
])
1113 if (line1
[i
] > line2
[i
])
1116 /* With a properly formatted dir file,
1117 we can only get here if the item names are equal. */
1121 /* Compare the menu item names in LINE1 (line length LEN1)
1122 and LINE2 (line length LEN2). Return 1 if the item names are equal,
1126 menu_line_equal (line1
, len1
, line2
, len2
)
1132 int minlen
= (len1
< len2
? len1
: len2
);
1135 for (i
= 0; i
< minlen
; i
++)
1137 /* If both item names end here, they are equal. */
1138 if (line1
[i
] == ':' && line2
[i
] == ':')
1140 /* If they both continue and differ, one is less. */
1141 if (line1
[i
] != line2
[i
])
1144 /* With a properly formatted dir file,
1145 we can only get here if the item names are equal. */
1149 /* This is the comparison function for qsort
1150 for a vector of pointers to struct spec_section.
1151 Compare the section names. */
1154 compare_section_names (sec1
, sec2
)
1155 struct spec_section
**sec1
, **sec2
;
1157 char *name1
= (*sec1
)->name
;
1158 char *name2
= (*sec2
)->name
;
1159 return strcmp (name1
, name2
);
1162 /* Insert ENTRY into the add_entries_before vector
1163 for line number LINE_NUMBER of the dir file.
1164 DIR_LINES and N_ENTRIES carry information from like-named variables
1168 insert_entry_here (entry
, line_number
, dir_lines
, n_entries
)
1169 struct spec_entry
*entry
;
1171 struct line_data
*dir_lines
;
1176 if (dir_lines
[line_number
].add_entries_before
== 0)
1178 dir_lines
[line_number
].add_entries_before
1179 = (struct spec_entry
**) xmalloc (n_entries
* sizeof (struct spec_entry
*));
1180 for (i
= 0; i
< n_entries
; i
++)
1181 dir_lines
[line_number
].add_entries_before
[i
] = 0;
1184 for (i
= 0; i
< n_entries
; i
++)
1185 if (dir_lines
[line_number
].add_entries_before
[i
] == 0)
1191 dir_lines
[line_number
].add_entries_before
[i
] = entry
;