1 /* install-info -- create Info directory entry(ies) for an Info file.
2 Copyright (C) 1996 Free Software Foundation, Inc.
4 $Id: install-info.c,v 1.12 1996/10/03 23:13:36 karl Exp $
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. */
20 #define INSTALL_INFO_VERSION_STRING "GNU install-info (Texinfo 3.9) 1.2"
25 #include <sys/types.h>
28 #ifdef HAVE_SYS_FCNTL_H
29 #include <sys/fcntl.h>
32 #endif /* !HAVE_SYS_FCNTL_H */
33 #ifdef HAVE_SYS_FILE_H
37 /* Name this program was invoked with. */
41 struct line_data
*findlines ();
44 void insert_entry_here ();
45 int compare_section_names ();
49 /* Data structures. */
51 /* Record info about a single line from a file
56 /* The start of the line. */
58 /* The number of characters in the line,
59 excluding the terminating newline. */
61 /* Vector containing pointers to the entries to add before this line.
62 The vector is null-terminated. */
63 struct spec_entry
**add_entries_before
;
64 /* 1 means output any needed new sections before this line. */
65 int add_sections_before
;
66 /* 1 means don't output this line. */
70 /* This is used for a list of the specified menu section names
71 in which entries should be added. */
75 struct spec_section
*next
;
77 /* 1 means we have not yet found an existing section with this name
78 in the dir file--so we will need to add a new section. */
82 /* This is used for a list of the entries specified to be added. */
86 struct spec_entry
*next
;
90 /* This is used for a list of nodes found by parsing the dir file. */
97 /* The line number of the line where the node starts.
98 This is the line that contains control-underscore. */
100 /* The line number of the line where the node ends,
101 which is the end of the file or where the next line starts. */
103 /* Start of first line in this node's menu
104 (the line after the * Menu: line). */
106 /* The start of the chain of sections in this node's menu. */
107 struct menu_section
*sections
;
108 /* The last menu section in the chain. */
109 struct menu_section
*last_section
;
112 /* This is used for a list of sections found in a node's menu.
113 Each struct node has such a list in the sections field. */
117 struct menu_section
*next
;
119 /* Line number of start of section. */
121 /* Line number of end of section. */
125 /* Memory allocation and string operations. */
127 /* Like malloc but get fatal error if memory is exhausted. */
133 extern void *malloc ();
134 void *result
= malloc (size
);
136 fatal ("virtual memory exhausted", 0);
140 /* Like malloc but get fatal error if memory is exhausted. */
147 extern void *realloc ();
148 void *result
= realloc (obj
, size
);
150 fatal ("virtual memory exhausted", 0);
154 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
160 int len1
= strlen (s1
), len2
= strlen (s2
), len3
= strlen (s3
);
161 char *result
= (char *) xmalloc (len1
+ len2
+ len3
+ 1);
164 strcpy (result
+ len1
, s2
);
165 strcpy (result
+ len1
+ len2
, s3
);
166 *(result
+ len1
+ len2
+ len3
) = 0;
171 /* Return a string containing SIZE characters
172 copied from starting at STRING. */
175 copy_string (string
, size
)
180 char *copy
= (char *) xmalloc (size
+ 1);
181 for (i
= 0; i
< size
; i
++)
187 /* Error message functions. */
189 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
196 fprintf (stderr
, "%s: ", progname
);
197 fprintf (stderr
, s1
, s2
, s3
);
198 fprintf (stderr
, "\n");
206 fprintf (stderr
, "%s: Warning: ", progname
);
207 fprintf (stderr
, s1
, s2
, s3
);
208 fprintf (stderr
, "\n");
211 /* Print error message and exit. */
221 /* Print fatal error message based on errno, with file name NAME. */
224 pfatal_with_name (name
)
227 char *s
= concat ("", my_strerror (errno
), " for %s");
231 /* Given the full text of a menu entry, null terminated,
232 return just the menu item name (copied). */
235 extract_menu_item_name (item_text
)
240 if (*item_text
== '*')
242 while (*item_text
== ' ')
246 while (*p
&& *p
!= ':') p
++;
247 return copy_string (item_text
, p
- item_text
);
250 /* Given the full text of a menu entry, terminated by null or newline,
251 return just the menu item file (copied). */
254 extract_menu_file_name (item_text
)
259 /* If we have text that looks like * ITEM: (FILE)NODE...,
260 extract just FILE. Otherwise return "(none)". */
267 /* Skip to and past the colon. */
268 while (*p
&& *p
!= '\n' && *p
!= ':') p
++;
271 /* Skip past the open-paren. */
276 else if (*p
== ' ' || *p
== '\t')
285 /* File name ends just before the close-paren. */
286 while (*p
&& *p
!= '\n' && *p
!= ')') p
++;
290 return copy_string (item_text
, p
- item_text
);
294 suggest_asking_for_help ()
296 fprintf (stderr
, "\tTry `%s --help' for a complete list of options.\n",
304 printf ("%s [OPTION]... [INFO-FILE [DIR-FILE]]\n\
305 Install INFO-FILE in the Info directory file DIR-FILE.\n\
308 --delete Delete existing entries in INFO-FILE;\n\
309 don't insert any new entries.\n\
310 --dir-file=NAME Specify file name of Info directory file.\n\
311 This is equivalent to using the DIR-FILE argument.\n\
312 --entry=TEXT Insert TEXT as an Info directory entry.\n\
313 TEXT should have the form of an Info menu item line\n\
314 plus zero or more extra lines starting with whitespace.\n\
315 If you specify more than one entry, they are all added.\n\
316 If you don't specify any entries, they are determined\n\
317 from information in the Info file itself.\n\
318 --help Display this help and exit.\n\
319 --info-file=FILE Specify Info file to install in the directory.\n\
320 This is equivalent to using the INFO-FILE argument.\n\
321 --info-dir=DIR Same as --dir-file=DIR/dir.\n\
322 --item=TEXT Same as --entry TEXT.\n\
323 An Info directory entry is actually a menu item.\n\
324 --quiet Suppress warnings.\n\
325 --remove Same as --delete.\n\
326 --section=SEC Put this file's entries in section SEC of the directory.\n\
327 If you specify more than one section, all the entries\n\
328 are added in each of the sections.\n\
329 If you don't specify any sections, they are determined\n\
330 from information in the Info file itself.\n\
331 --version Display version information and exit.\n\
333 Email bug reports to bug-texinfo@prep.ai.mit.edu.\n\
337 /* Convert an errno value into a string describing the error.
338 We define this function here rather than using strerror
339 because not all systems have strerror. */
345 extern char *sys_errlist
[];
348 if (errnum
>= 0 && errnum
< sys_nerr
)
349 return sys_errlist
[errnum
];
350 return (char *) "Unknown error";
353 /* This table defines all the long-named options, says whether they
354 use an argument, and maps them into equivalent single-letter options. */
356 struct option longopts
[] =
358 { "delete", no_argument
, NULL
, 'r' },
359 { "dir-file", required_argument
, NULL
, 'd' },
360 { "entry", required_argument
, NULL
, 'e' },
361 { "help", no_argument
, NULL
, 'h' },
362 { "info-dir", required_argument
, NULL
, 'D' },
363 { "info-file", required_argument
, NULL
, 'i' },
364 { "item", required_argument
, NULL
, 'e' },
365 { "quiet", no_argument
, NULL
, 'q' },
366 { "remove", no_argument
, NULL
, 'r' },
367 { "section", required_argument
, NULL
, 's' },
368 { "version", no_argument
, NULL
, 'V' },
376 char *infile
= 0, *dirfile
= 0;
377 char *infile_sans_info
;
378 unsigned infilelen_sans_info
;
381 /* Record the text of the Info file, as a sequence of characters
382 and as a sequence of lines. */
385 struct line_data
*input_lines
;
388 /* Record here the specified section names and directory entries. */
389 struct spec_section
*input_sections
= NULL
;
390 struct spec_entry
*entries_to_add
= NULL
;
391 int n_entries_to_add
= 0;
393 /* Record the old text of the dir file, as plain characters,
394 as lines, and as nodes. */
398 struct line_data
*dir_lines
;
399 struct node
*dir_nodes
;
401 /* Nonzero means --delete was specified (just delete existing entries). */
403 int something_deleted
= 0;
404 /* Nonzero means -q was specified. */
407 int node_header_flag
;
415 int opt
= getopt_long (argc
, argv
, "i:d:e:s:hHr", longopts
, 0);
423 /* If getopt returns 0, then it has already processed a
424 long-named option. We should do nothing. */
433 fprintf (stderr
, "%s: Specify the Info directory only once.\n",
435 suggest_asking_for_help ();
443 fprintf (stderr
, "%s: Specify the Info directory only once.\n",
445 suggest_asking_for_help ();
447 dirfile
= concat (optarg
, "", "/dir");
452 struct spec_entry
*next
453 = (struct spec_entry
*) xmalloc (sizeof (struct spec_entry
));
454 if (! (*optarg
!= 0 && optarg
[strlen (optarg
) - 1] == '\n'))
455 optarg
= concat (optarg
, "\n", "");
457 next
->next
= entries_to_add
;
458 entries_to_add
= next
;
471 fprintf (stderr
, "%s: Specify the Info file only once.\n",
473 suggest_asking_for_help ();
488 struct spec_section
*next
489 = (struct spec_section
*) xmalloc (sizeof (struct spec_section
));
491 next
->next
= input_sections
;
493 input_sections
= next
;
498 puts (INSTALL_INFO_VERSION_STRING
);
499 puts ("Copyright (C) 1996 Free Software Foundation, Inc.\n\
500 There is NO warranty. You may redistribute this software\n\
501 under the terms of the GNU General Public License.\n\
502 For more information about these matters, see the files named COPYING.");
506 suggest_asking_for_help ();
510 /* Interpret the non-option arguments as file names. */
511 for (; optind
< argc
; ++optind
)
514 infile
= argv
[optind
];
515 else if (dirfile
== 0)
516 dirfile
= argv
[optind
];
518 error ("excess command line argument `%s'", argv
[optind
]);
522 fatal ("No input file specified");
524 fatal ("No dir file specified");
526 /* Read the Info file and parse it into lines. */
528 input_data
= readfile (infile
, &input_size
);
529 input_lines
= findlines (input_data
, input_size
, &input_nlines
);
531 /* Parse the input file to find the section names it specifies. */
533 if (input_sections
== 0)
535 prefix_length
= strlen ("INFO-DIR-SECTION ");
536 for (i
= 0; i
< input_nlines
; i
++)
538 if (!strncmp ("INFO-DIR-SECTION ", input_lines
[i
].start
,
541 struct spec_section
*next
542 = (struct spec_section
*) xmalloc (sizeof (struct spec_section
));
543 next
->name
= copy_string (input_lines
[i
].start
+ prefix_length
,
544 input_lines
[i
].size
- prefix_length
);
545 next
->next
= input_sections
;
547 input_sections
= next
;
552 /* Default to section "Miscellaneous" if no sections specified. */
553 if (input_sections
== 0)
556 = (struct spec_section
*) xmalloc (sizeof (struct spec_section
));
557 input_sections
->name
= "Miscellaneous";
558 input_sections
->next
= 0;
559 input_sections
->missing
= 1;
562 /* Now find the directory entries specified in the file
563 and put them on entries_to_add. But not if entries
564 were specified explicitly with command options. */
566 if (entries_to_add
== 0)
568 char *start_of_this_entry
= 0;
569 for (i
= 0; i
< input_nlines
; i
++)
571 if (!strncmp ("START-INFO-DIR-ENTRY", input_lines
[i
].start
,
573 && sizeof ("START-INFO-DIR-ENTRY") - 1 == input_lines
[i
].size
)
575 if (start_of_this_entry
!= 0)
576 fatal ("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY");
577 start_of_this_entry
= input_lines
[i
+ 1].start
;
579 if (!strncmp ("END-INFO-DIR-ENTRY", input_lines
[i
].start
,
581 && sizeof ("END-INFO-DIR-ENTRY") - 1 == input_lines
[i
].size
)
583 if (start_of_this_entry
!= 0)
585 struct spec_entry
*next
586 = (struct spec_entry
*) xmalloc (sizeof (struct spec_entry
));
587 next
->text
= copy_string (start_of_this_entry
,
588 input_lines
[i
].start
- start_of_this_entry
);
589 next
->next
= entries_to_add
;
590 entries_to_add
= next
;
592 start_of_this_entry
= 0;
595 fatal ("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY");
598 if (start_of_this_entry
!= 0)
599 fatal ("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY");
603 if (entries_to_add
== 0)
604 fatal ("no info dir entry in `%s'", infile
);
606 /* Now read in the Info dir file. */
607 dir_data
= readfile (dirfile
, &dir_size
);
608 dir_lines
= findlines (dir_data
, dir_size
, &dir_nlines
);
610 /* We will be comparing the entries in the dir file against the
611 current filename, so need to strip off any directory prefix and any
614 unsigned basename_len
;
615 extern char *strrchr ();
616 char *infile_basename
= strrchr (infile
, '/');
620 infile_basename
= infile
;
622 basename_len
= strlen (infile_basename
);
624 = (strlen (infile_basename
) > 5
625 && strcmp (infile_basename
+ basename_len
- 5, ".info") == 0)
626 ? copy_string (infile_basename
, basename_len
- 5)
629 infilelen_sans_info
= strlen (infile_sans_info
);
632 /* Parse the dir file. Find all the nodes, and their menus,
633 and the sections of their menus. */
636 node_header_flag
= 0;
637 for (i
= 0; i
< dir_nlines
; i
++)
639 /* Parse node header lines. */
640 if (node_header_flag
)
643 for (j
= 0; j
< dir_lines
[i
].size
; j
++)
644 /* Find the node name and store it in the `struct node'. */
645 if (!strncmp ("Node:", dir_lines
[i
].start
+ j
, 5))
647 char *line
= dir_lines
[i
].start
;
648 /* Find the start of the node name. */
650 while (line
[j
] == ' ' || line
[j
] == '\t')
652 /* Find the end of the node name. */
654 while (line
[end
] != 0 && line
[end
] != ',' && line
[end
] != '\n'
655 && line
[end
] != '\t')
657 dir_nodes
->name
= copy_string (line
+ j
, end
- j
);
659 node_header_flag
= 0;
662 /* Notice the start of a node. */
663 if (*dir_lines
[i
].start
== 037)
666 = (struct node
*) xmalloc (sizeof (struct node
));
667 next
->next
= dir_nodes
;
669 next
->start_line
= i
;
671 next
->menu_start
= NULL
;
672 next
->sections
= NULL
;
673 next
->last_section
= NULL
;
676 dir_nodes
->end_line
= i
;
677 /* Fill in the end of the last menu section
678 of the previous node. */
679 if (dir_nodes
!= 0 && dir_nodes
->last_section
!= 0)
680 dir_nodes
->last_section
->end_line
= i
;
684 /* The following line is the header of this node;
686 node_header_flag
= 1;
689 /* Notice the lines that start menus. */
691 && !strncmp ("* Menu:", dir_lines
[i
].start
, 7))
692 dir_nodes
->menu_start
= dir_lines
[i
+ 1].start
;
694 /* Notice sections in menus. */
696 && dir_nodes
->menu_start
!= 0
697 && *dir_lines
[i
].start
!= '\n'
698 && *dir_lines
[i
].start
!= '*'
699 && *dir_lines
[i
].start
!= ' '
700 && *dir_lines
[i
].start
!= '\t')
702 /* Add this menu section to the node's list.
703 This list grows in forward order. */
704 struct menu_section
*next
705 = (struct menu_section
*) xmalloc (sizeof (struct menu_section
));
706 next
->start_line
= i
+ 1;
709 next
->name
= copy_string (dir_lines
[i
].start
, dir_lines
[i
].size
);
710 if (dir_nodes
->sections
)
712 dir_nodes
->last_section
->next
= next
;
713 dir_nodes
->last_section
->end_line
= i
;
716 dir_nodes
->sections
= next
;
717 dir_nodes
->last_section
= next
;
720 /* Check for an existing entry that should be deleted.
721 Delete all entries which specify this file name. */
722 if (*dir_lines
[i
].start
== '*')
724 char *p
= dir_lines
[i
].start
;
726 while (*p
!= 0 && *p
!= ':')
729 while (*p
== ' ') p
++;
733 if ((dir_lines
[i
].size
734 > (p
- dir_lines
[i
].start
+ infilelen_sans_info
))
735 && !strncmp (p
, infile_sans_info
, infilelen_sans_info
)
736 && p
[infilelen_sans_info
] == ')')
737 dir_lines
[i
].delete = 1;
740 /* Treat lines that start with whitespace
741 as continuations; if we are deleting an entry,
742 delete all its continuations as well. */
744 && (*dir_lines
[i
].start
== ' '
745 || *dir_lines
[i
].start
== '\t'))
747 dir_lines
[i
].delete = dir_lines
[i
- 1].delete;
748 something_deleted
= 1;
752 /* Finish the info about the end of the last node. */
755 dir_nodes
->end_line
= dir_nlines
;
756 if (dir_nodes
->last_section
!= 0)
757 dir_nodes
->last_section
->end_line
= dir_nlines
;
760 /* Decide where to add the new entries (unless --delete was used).
761 Find the menu sections to add them in.
762 In each section, find the proper alphabetical place to add
763 each of the entries. */
768 struct menu_section
*section
;
769 struct spec_section
*spec
;
771 for (node
= dir_nodes
; node
; node
= node
->next
)
772 for (section
= node
->sections
; section
; section
= section
->next
)
774 for (i
= section
->end_line
; i
> section
->start_line
; i
--)
775 if (dir_lines
[i
- 1].size
!= 0)
777 section
->end_line
= i
;
779 for (spec
= input_sections
; spec
; spec
= spec
->next
)
780 if (!strcmp (spec
->name
, section
->name
))
784 int add_at_line
= section
->end_line
;
785 struct spec_entry
*entry
;
786 /* Say we have found at least one section with this name,
787 so we need not add such a section. */
789 /* For each entry, find the right place in this section
791 for (entry
= entries_to_add
; entry
; entry
= entry
->next
)
793 int textlen
= strlen (entry
->text
);
794 /* Subtract one because dir_lines is zero-based,
795 but the `end_line' and `start_line' members are
797 for (i
= section
->end_line
- 1;
798 i
>= section
->start_line
- 1; i
--)
800 /* If an entry exists with the same name,
801 and was not marked for deletion
802 (which means it is for some other file),
803 we are in trouble. */
804 if (dir_lines
[i
].start
[0] == '*'
805 && menu_line_equal (entry
->text
, textlen
,
808 && !dir_lines
[i
].delete)
809 fatal ("menu item `%s' already exists, for file `%s'",
810 extract_menu_item_name (entry
->text
),
811 extract_menu_file_name (dir_lines
[i
].start
));
812 if (dir_lines
[i
].start
[0] == '*'
813 && menu_line_lessp (entry
->text
, textlen
,
818 insert_entry_here (entry
, add_at_line
,
819 dir_lines
, n_entries_to_add
);
824 /* Mark the end of the Top node as the place to add any
825 new sections that are needed. */
826 for (node
= dir_nodes
; node
; node
= node
->next
)
827 if (node
->name
&& strcmp (node
->name
, "Top") == 0)
828 dir_lines
[node
->end_line
].add_sections_before
= 1;
831 if (delete_flag
&& !something_deleted
&& !quiet_flag
)
832 warning ("no entries found for `%s'; nothing deleted", infile
);
834 /* Output the old dir file, interpolating the new sections
835 and/or new entries where appropriate. */
837 output
= fopen (dirfile
, "w");
844 for (i
= 0; i
<= dir_nlines
; i
++)
848 /* If we decided to output some new entries before this line,
850 if (dir_lines
[i
].add_entries_before
)
851 for (j
= 0; j
< n_entries_to_add
; j
++)
853 struct spec_entry
*this = dir_lines
[i
].add_entries_before
[j
];
856 fputs (this->text
, output
);
858 /* If we decided to add some sections here
859 because there are no such sections in the file,
861 if (dir_lines
[i
].add_sections_before
)
863 struct spec_section
*spec
;
864 struct spec_section
**sections
;
867 /* Count the sections and allocate a vector for all of them. */
868 for (spec
= input_sections
; spec
; spec
= spec
->next
)
870 sections
= ((struct spec_section
**)
871 xmalloc (n_sections
* sizeof (struct spec_section
*)));
873 /* Fill the vector SECTIONS with pointers to all the sections,
876 for (spec
= input_sections
; spec
; spec
= spec
->next
)
877 sections
[j
++] = spec
;
878 qsort (sections
, n_sections
, sizeof (struct spec_section
*),
879 compare_section_names
);
881 /* Generate the new sections in alphabetical order.
882 In each new section, output all of our entries. */
883 for (j
= 0; j
< n_sections
; j
++)
888 struct spec_entry
*entry
;
891 fputs (spec
->name
, output
);
893 for (entry
= entries_to_add
; entry
; entry
= entry
->next
)
894 fputs (entry
->text
, output
);
901 /* Output the original dir lines unless marked for deletion. */
902 if (i
< dir_nlines
&& !dir_lines
[i
].delete)
904 fwrite (dir_lines
[i
].start
, 1, dir_lines
[i
].size
, output
);
914 /* Read all of file FILNAME into memory
915 and return the address of the data.
916 Store the size into SIZEP.
917 If there is trouble, do a fatal error. */
920 readfile (filename
, sizep
)
924 int data_size
= 1024;
925 char *data
= (char *) xmalloc (data_size
);
929 int desc
= open (filename
, O_RDONLY
);
932 pfatal_with_name (filename
);
936 nread
= read (desc
, data
+ filled
, data_size
- filled
);
938 pfatal_with_name (filename
);
943 if (filled
== data_size
)
946 data
= (char *) xrealloc (data
, data_size
);
954 /* Divide the text at DATA (of SIZE bytes) into lines.
955 Return a vector of struct line_data describing the lines.
956 Store the length of that vector into *NLINESP. */
959 findlines (data
, size
, nlinesp
)
964 struct line_data
*lines
;
965 int lines_allocated
= 512;
970 lines
= (struct line_data
*) xmalloc (lines_allocated
* sizeof (struct line_data
));
973 for (i
= 0; i
< size
; i
++)
977 if (filled
== lines_allocated
)
979 lines_allocated
*= 2;
980 lines
= (struct line_data
*) xrealloc (lines
, lines_allocated
* sizeof (struct line_data
));
982 lines
[filled
].start
= &data
[i
];
983 lines
[filled
].add_entries_before
= 0;
984 lines
[filled
].add_sections_before
= 0;
985 lines
[filled
].delete = 0;
987 lines
[filled
- 1].size
988 = lines
[filled
].start
- lines
[filled
- 1].start
- 1;
991 lineflag
= (data
[i
] == '\n');
994 lines
[filled
- 1].size
= &data
[i
] - lines
[filled
- 1].start
- lineflag
;
996 /* Do not leave garbage in the last element. */
997 lines
[filled
].start
= NULL
;
998 lines
[filled
].add_entries_before
= NULL
;
999 lines
[filled
].add_sections_before
= 0;
1000 lines
[filled
].delete = 0;
1001 lines
[filled
].size
= 0;
1007 /* Compare the menu item names in LINE1 (line length LEN1)
1008 and LINE2 (line length LEN2). Return 1 if the item name
1009 in LINE1 is less, 0 otherwise. */
1012 menu_line_lessp (line1
, len1
, line2
, len2
)
1018 int minlen
= (len1
< len2
? len1
: len2
);
1021 for (i
= 0; i
< minlen
; i
++)
1023 /* If one item name is a prefix of the other,
1024 the former one is less. */
1025 if (line1
[i
] == ':' && line2
[i
] != ':')
1027 if (line2
[i
] == ':' && line1
[i
] != ':')
1029 /* If they both continue and differ, one is less. */
1030 if (line1
[i
] < line2
[i
])
1032 if (line1
[i
] > line2
[i
])
1035 /* With a properly formatted dir file,
1036 we can only get here if the item names are equal. */
1040 /* Compare the menu item names in LINE1 (line length LEN1)
1041 and LINE2 (line length LEN2). Return 1 if the item names are equal,
1045 menu_line_equal (line1
, len1
, line2
, len2
)
1051 int minlen
= (len1
< len2
? len1
: len2
);
1054 for (i
= 0; i
< minlen
; i
++)
1056 /* If both item names end here, they are equal. */
1057 if (line1
[i
] == ':' && line2
[i
] == ':')
1059 /* If they both continue and differ, one is less. */
1060 if (line1
[i
] != line2
[i
])
1063 /* With a properly formatted dir file,
1064 we can only get here if the item names are equal. */
1068 /* This is the comparison function for qsort
1069 for a vector of pointers to struct spec_section.
1070 Compare the section names. */
1073 compare_section_names (sec1
, sec2
)
1074 struct spec_section
**sec1
, **sec2
;
1076 char *name1
= (*sec1
)->name
;
1077 char *name2
= (*sec2
)->name
;
1078 return strcmp (name1
, name2
);
1081 /* Insert ENTRY into the add_entries_before vector
1082 for line number LINE_NUMBER of the dir file.
1083 DIR_LINES and N_ENTRIES carry information from like-named variables
1087 insert_entry_here (entry
, line_number
, dir_lines
, n_entries
)
1088 struct spec_entry
*entry
;
1090 struct line_data
*dir_lines
;
1095 if (dir_lines
[line_number
].add_entries_before
== 0)
1097 dir_lines
[line_number
].add_entries_before
1098 = (struct spec_entry
**) xmalloc (n_entries
* sizeof (struct spec_entry
*));
1099 for (i
= 0; i
< n_entries
; i
++)
1100 dir_lines
[line_number
].add_entries_before
[i
] = 0;
1103 for (i
= 0; i
< n_entries
; i
++)
1104 if (dir_lines
[line_number
].add_entries_before
[i
] == 0)
1110 dir_lines
[line_number
].add_entries_before
[i
] = entry
;