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