install-info.c revision 27060
1/* install-info -- create Info directory entry(ies) for an Info file. 2 Copyright (C) 1996 Free Software Foundation, Inc. 3 4$Id: install-info.c,v 1.12 1996/10/03 23:13:36 karl Exp $ 5 6This program is free software; you can redistribute it and/or modify 7it under the terms of the GNU General Public License as published by 8the Free Software Foundation; either version 2 of the License, or 9(at your option) any later version. 10 11This program is distributed in the hope that it will be useful, 12but WITHOUT ANY WARRANTY; without even the implied warranty of 13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14GNU General Public License for more details. 15 16You should have received a copy of the GNU General Public License 17along with this program; if not, write to the Free Software 18Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 19 20#define INSTALL_INFO_VERSION_STRING "GNU install-info (Texinfo 3.9) 1.2" 21 22#include <stdio.h> 23#include <errno.h> 24#include <getopt.h> 25#include <sys/types.h> 26 27/* Get O_RDONLY. */ 28#ifdef HAVE_SYS_FCNTL_H 29#include <sys/fcntl.h> 30#else 31#include <fcntl.h> 32#endif /* !HAVE_SYS_FCNTL_H */ 33#ifdef HAVE_SYS_FILE_H 34#include <sys/file.h> 35#endif 36 37/* Name this program was invoked with. */ 38char *progname; 39 40char *readfile (); 41struct line_data *findlines (); 42void fatal (); 43void insert_entry_here (); 44int compare_section_names (); 45 46struct spec_entry; 47 48/* Data structures. */ 49 50/* Record info about a single line from a file 51 as read into core. */ 52 53struct line_data 54{ 55 /* The start of the line. */ 56 char *start; 57 /* The number of characters in the line, 58 excluding the terminating newline. */ 59 int size; 60 /* Vector containing pointers to the entries to add before this line. 61 The vector is null-terminated. */ 62 struct spec_entry **add_entries_before; 63 /* 1 means output any needed new sections before this line. */ 64 int add_sections_before; 65 /* 1 means don't output this line. */ 66 int delete; 67}; 68 69/* This is used for a list of the specified menu section names 70 in which entries should be added. */ 71 72struct spec_section 73{ 74 struct spec_section *next; 75 char *name; 76 /* 1 means we have not yet found an existing section with this name 77 in the dir file--so we will need to add a new section. */ 78 int missing; 79}; 80 81/* This is used for a list of the entries specified to be added. */ 82 83struct spec_entry 84{ 85 struct spec_entry *next; 86 char *text; 87}; 88 89/* This is used for a list of nodes found by parsing the dir file. */ 90 91struct node 92{ 93 struct node *next; 94 /* The node name. */ 95 char *name; 96 /* The line number of the line where the node starts. 97 This is the line that contains control-underscore. */ 98 int start_line; 99 /* The line number of the line where the node ends, 100 which is the end of the file or where the next line starts. */ 101 int end_line; 102 /* Start of first line in this node's menu 103 (the line after the * Menu: line). */ 104 char *menu_start; 105 /* The start of the chain of sections in this node's menu. */ 106 struct menu_section *sections; 107 /* The last menu section in the chain. */ 108 struct menu_section *last_section; 109}; 110 111/* This is used for a list of sections found in a node's menu. 112 Each struct node has such a list in the sections field. */ 113 114struct menu_section 115{ 116 struct menu_section *next; 117 char *name; 118 /* Line number of start of section. */ 119 int start_line; 120 /* Line number of end of section. */ 121 int end_line; 122}; 123 124/* Memory allocation and string operations. */ 125 126/* Like malloc but get fatal error if memory is exhausted. */ 127 128void * 129xmalloc (size) 130 unsigned int size; 131{ 132 extern void *malloc (); 133 void *result = malloc (size); 134 if (result == NULL) 135 fatal ("virtual memory exhausted", 0); 136 return result; 137} 138 139/* Like malloc but get fatal error if memory is exhausted. */ 140 141void * 142xrealloc (obj, size) 143 void *obj; 144 unsigned int size; 145{ 146 extern void *realloc (); 147 void *result = realloc (obj, size); 148 if (result == NULL) 149 fatal ("virtual memory exhausted", 0); 150 return result; 151} 152 153/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */ 154 155char * 156concat (s1, s2, s3) 157 char *s1, *s2, *s3; 158{ 159 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); 160 char *result = (char *) xmalloc (len1 + len2 + len3 + 1); 161 162 strcpy (result, s1); 163 strcpy (result + len1, s2); 164 strcpy (result + len1 + len2, s3); 165 *(result + len1 + len2 + len3) = 0; 166 167 return result; 168} 169 170/* Return a string containing SIZE characters 171 copied from starting at STRING. */ 172 173char * 174copy_string (string, size) 175 char *string; 176 int size; 177{ 178 int i; 179 char *copy = (char *) xmalloc (size + 1); 180 for (i = 0; i < size; i++) 181 copy[i] = string[i]; 182 copy[size] = 0; 183 return copy; 184} 185 186/* Error message functions. */ 187 188/* Print error message. `s1' is printf control string, `s2' is arg for it. */ 189 190/* VARARGS1 */ 191void 192error (s1, s2, s3) 193 char *s1, *s2, *s3; 194{ 195 fprintf (stderr, "%s: ", progname); 196 fprintf (stderr, s1, s2, s3); 197 fprintf (stderr, "\n"); 198} 199 200/* VARARGS1 */ 201void 202warning (s1, s2, s3) 203 char *s1, *s2, *s3; 204{ 205 fprintf (stderr, "%s: Warning: ", progname); 206 fprintf (stderr, s1, s2, s3); 207 fprintf (stderr, "\n"); 208} 209 210/* Print error message and exit. */ 211 212void 213fatal (s1, s2, s3) 214 char *s1, *s2, *s3; 215{ 216 error (s1, s2, s3); 217 exit (1); 218} 219 220/* Print fatal error message based on errno, with file name NAME. */ 221 222void 223pfatal_with_name (name) 224 char *name; 225{ 226 char *s = concat ("", strerror (errno), " for %s"); 227 fatal (s, name); 228} 229 230/* Given the full text of a menu entry, null terminated, 231 return just the menu item name (copied). */ 232 233char * 234extract_menu_item_name (item_text) 235 char *item_text; 236{ 237 char *p; 238 239 if (*item_text == '*') 240 item_text++; 241 while (*item_text == ' ') 242 item_text++; 243 244 p = item_text; 245 while (*p && *p != ':') p++; 246 return copy_string (item_text, p - item_text); 247} 248 249/* Given the full text of a menu entry, terminated by null or newline, 250 return just the menu item file (copied). */ 251 252char * 253extract_menu_file_name (item_text) 254 char *item_text; 255{ 256 char *p = item_text; 257 258 /* If we have text that looks like * ITEM: (FILE)NODE..., 259 extract just FILE. Otherwise return "(none)". */ 260 261 if (*p == '*') 262 p++; 263 while (*p == ' ') 264 p++; 265 266 /* Skip to and past the colon. */ 267 while (*p && *p != '\n' && *p != ':') p++; 268 if (*p == ':') p++; 269 270 /* Skip past the open-paren. */ 271 while (1) 272 { 273 if (*p == '(') 274 break; 275 else if (*p == ' ' || *p == '\t') 276 p++; 277 else 278 return "(none)"; 279 } 280 p++; 281 282 item_text = p; 283 284 /* File name ends just before the close-paren. */ 285 while (*p && *p != '\n' && *p != ')') p++; 286 if (*p != ')') 287 return "(none)"; 288 289 return copy_string (item_text, p - item_text); 290} 291 292void 293suggest_asking_for_help () 294{ 295 fprintf (stderr, "\tTry `%s --help' for a complete list of options.\n", 296 progname); 297 exit (1); 298} 299 300void 301print_help () 302{ 303 printf ("%s [OPTION]... [INFO-FILE [DIR-FILE]]\n\ 304 Install INFO-FILE in the Info directory file DIR-FILE.\n\ 305\n\ 306Options:\n\ 307--delete Delete existing entries in INFO-FILE;\n\ 308 don't insert any new entries.\n\ 309--defentry=TEXT Like --entry, but only use TEXT if an entry\n\ 310 is not present in INFO-FILE.\n\ 311--defsection=TEXT Like --section, but only use TEXT if a section\n\ 312 is not present in INFO-FILE.\n\ 313--dir-file=NAME Specify file name of Info directory file.\n\ 314 This is equivalent to using the DIR-FILE argument.\n\ 315--entry=TEXT Insert TEXT as an Info directory entry.\n\ 316 TEXT should have the form of an Info menu item line\n\ 317 plus zero or more extra lines starting with whitespace.\n\ 318 If you specify more than one entry, they are all added.\n\ 319 If you don't specify any entries, they are determined\n\ 320 from information in the Info file itself.\n\ 321--forceentry=TEXT Like --entry, but ignore any entry in INFO-FILE.\n\ 322--help Display this help and exit.\n\ 323--info-file=FILE Specify Info file to install in the directory.\n\ 324 This is equivalent to using the INFO-FILE argument.\n\ 325--info-dir=DIR Same as --dir-file=DIR/dir.\n\ 326--item=TEXT Same as --entry TEXT.\n\ 327 An Info directory entry is actually a menu item.\n\ 328--quiet Suppress warnings.\n\ 329--remove Same as --delete.\n\ 330--section=SEC Put this file's entries in section SEC of the directory.\n\ 331 If you specify more than one section, all the entries\n\ 332 are added in each of the sections.\n\ 333 If you don't specify any sections, they are determined\n\ 334 from information in the Info file itself.\n\ 335--version Display version information and exit.\n\ 336\n\ 337Email bug reports to bug-texinfo@prep.ai.mit.edu.\n\ 338", progname); 339} 340 341 342/* This table defines all the long-named options, says whether they 343 use an argument, and maps them into equivalent single-letter options. */ 344 345struct option longopts[] = 346{ 347 { "delete", no_argument, NULL, 'r' }, 348 { "defentry", required_argument, NULL, 'E' }, 349 { "defsection", required_argument, NULL, 'S' }, 350 { "dir-file", required_argument, NULL, 'd' }, 351 { "entry", required_argument, NULL, 'e' }, 352 { "forceentry", required_argument, NULL, 'f' }, 353 { "help", no_argument, NULL, 'h' }, 354 { "info-dir", required_argument, NULL, 'D' }, 355 { "info-file", required_argument, NULL, 'i' }, 356 { "item", required_argument, NULL, 'e' }, 357 { "quiet", no_argument, NULL, 'q' }, 358 { "remove", no_argument, NULL, 'r' }, 359 { "section", required_argument, NULL, 's' }, 360 { "version", no_argument, NULL, 'V' }, 361 { 0 } 362}; 363 364main (argc, argv) 365 int argc; 366 char **argv; 367{ 368 char *infile = 0, *dirfile = 0; 369 char *infile_sans_info; 370 unsigned infilelen_sans_info; 371 FILE *output; 372 373 /* Record the text of the Info file, as a sequence of characters 374 and as a sequence of lines. */ 375 char *input_data; 376 int input_size; 377 struct line_data *input_lines; 378 int input_nlines; 379 380 /* Record here the specified section names and directory entries. */ 381 struct spec_section *input_sections = NULL; 382 struct spec_entry *entries_to_add = NULL; 383 int n_entries_to_add = 0; 384 385 /* Record the old text of the dir file, as plain characters, 386 as lines, and as nodes. */ 387 char *dir_data; 388 int dir_size; 389 int dir_nlines; 390 struct line_data *dir_lines; 391 struct node *dir_nodes; 392 393 /* Nonzero means --delete was specified (just delete existing entries). */ 394 int delete_flag = 0; 395 int something_deleted = 0; 396 /* Nonzero means -q was specified. */ 397 int quiet_flag = 0; 398 399 int node_header_flag; 400 int prefix_length; 401 int i; 402 403 /* Nonzero means only use if not present in info file. */ 404 int entry_default = 0; 405 int entry_force = 0; 406 int section_default = 0; 407 408 progname = argv[0]; 409 410 while (1) 411 { 412 int opt = getopt_long (argc, argv, "i:d:e:s:hHr", longopts, 0); 413 414 if (opt == EOF) 415 break; 416 417 switch (opt) 418 { 419 case 0: 420 /* If getopt returns 0, then it has already processed a 421 long-named option. We should do nothing. */ 422 break; 423 424 case 1: 425 abort (); 426 427 case 'd': 428 if (dirfile) 429 { 430 fprintf (stderr, "%s: Specify the Info directory only once.\n", 431 progname); 432 suggest_asking_for_help (); 433 } 434 dirfile = optarg; 435 break; 436 437 case 'D': 438 if (dirfile) 439 { 440 fprintf (stderr, "%s: Specify the Info directory only once.\n", 441 progname); 442 suggest_asking_for_help (); 443 } 444 dirfile = concat (optarg, "", "/dir"); 445 break; 446 447 case 'f': 448 entry_force = 1; 449 if (!optarg[0]) 450 { 451 fprintf (stderr, "%s: Must provide entry name.\n", progname); 452 suggest_asking_for_help (); 453 } 454 case 'E': 455 entry_default = 1; 456 if (!optarg[0]) 457 break; 458 case 'e': 459 { 460 struct spec_entry *next 461 = (struct spec_entry *) xmalloc (sizeof (struct spec_entry)); 462 if (! (*optarg != 0 && optarg[strlen (optarg) - 1] == '\n')) 463 optarg = concat (optarg, "\n", ""); 464 next->text = optarg; 465 next->next = entries_to_add; 466 entries_to_add = next; 467 n_entries_to_add++; 468 } 469 break; 470 471 case 'h': 472 case 'H': 473 print_help (); 474 exit (0); 475 476 case 'i': 477 if (infile) 478 { 479 fprintf (stderr, "%s: Specify the Info file only once.\n", 480 progname); 481 suggest_asking_for_help (); 482 } 483 infile = optarg; 484 break; 485 486 case 'q': 487 quiet_flag = 1; 488 break; 489 490 case 'r': 491 delete_flag = 1; 492 break; 493 494 case 'S': 495 section_default = 1; 496 if (!optarg[0]) 497 break; 498 case 's': 499 { 500 struct spec_section *next 501 = (struct spec_section *) xmalloc (sizeof (struct spec_section)); 502 next->name = optarg; 503 next->next = input_sections; 504 next->missing = 1; 505 input_sections = next; 506 } 507 break; 508 509 case 'V': 510 puts (INSTALL_INFO_VERSION_STRING); 511puts ("Copyright (C) 1996 Free Software Foundation, Inc.\n\ 512There is NO warranty. You may redistribute this software\n\ 513under the terms of the GNU General Public License.\n\ 514For more information about these matters, see the files named COPYING."); 515 exit (0); 516 517 default: 518 suggest_asking_for_help (); 519 } 520 } 521 522 if (entry_force) 523 entry_default = 0; 524 525 /* Interpret the non-option arguments as file names. */ 526 for (; optind < argc; ++optind) 527 { 528 if (infile == 0) 529 infile = argv[optind]; 530 else if (dirfile == 0) 531 dirfile = argv[optind]; 532 else 533 error ("excess command line argument `%s'", argv[optind]); 534 } 535 536 if (!infile) 537 fatal ("No input file specified"); 538 if (!dirfile) 539 fatal ("No dir file specified"); 540 541 /* Read the Info file and parse it into lines. */ 542 543 input_data = readfile (infile, &input_size); 544 input_lines = findlines (input_data, input_size, &input_nlines); 545 546 /* Parse the input file to find the section names it specifies. */ 547 548 if (input_sections == 0 || section_default) 549 { 550 prefix_length = strlen ("INFO-DIR-SECTION "); 551 for (i = 0; i < input_nlines; i++) 552 { 553 if (!strncmp ("INFO-DIR-SECTION ", input_lines[i].start, 554 prefix_length)) 555 { 556 struct spec_section *next 557 = (struct spec_section *) xmalloc (sizeof (struct spec_section)); 558 559 if (section_default) 560 { 561 input_sections = NULL; /* This leaks. */ 562 section_default = 0; 563 } 564 565 next->name = copy_string (input_lines[i].start + prefix_length, 566 input_lines[i].size - prefix_length); 567 next->next = input_sections; 568 next->missing = 1; 569 input_sections = next; 570 } 571 } 572 } 573 574 /* Default to section "Miscellaneous" if no sections specified. */ 575 if (input_sections == 0) 576 { 577 input_sections 578 = (struct spec_section *) xmalloc (sizeof (struct spec_section)); 579 input_sections->name = "Miscellaneous"; 580 input_sections->next = 0; 581 input_sections->missing = 1; 582 } 583 584 /* Now find the directory entries specified in the file 585 and put them on entries_to_add. But not if entries 586 were specified explicitly with command options. */ 587 588 if ( !entry_force && (entries_to_add == 0 || entry_default) ) 589 { 590 char *start_of_this_entry = 0; 591 for (i = 0; i < input_nlines; i++) 592 { 593 if (!strncmp ("START-INFO-DIR-ENTRY", input_lines[i].start, 594 input_lines[i].size) 595 && sizeof ("START-INFO-DIR-ENTRY") - 1 == input_lines[i].size) 596 { 597 if (start_of_this_entry != 0) 598 fatal ("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"); 599 start_of_this_entry = input_lines[i + 1].start; 600 } 601 if (!strncmp ("END-INFO-DIR-ENTRY", input_lines[i].start, 602 input_lines[i].size) 603 && sizeof ("END-INFO-DIR-ENTRY") - 1 == input_lines[i].size) 604 { 605 if (start_of_this_entry != 0) 606 { 607 struct spec_entry *next 608 = (struct spec_entry *) xmalloc (sizeof (struct spec_entry)); 609 610 if (entry_default) 611 { 612 entries_to_add = NULL; 613 entry_default = 0; 614 } 615 616 next->text = copy_string (start_of_this_entry, 617 input_lines[i].start - start_of_this_entry); 618 next->next = entries_to_add; 619 entries_to_add = next; 620 n_entries_to_add++; 621 start_of_this_entry = 0; 622 } 623 else 624 fatal ("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY"); 625 } 626 } 627 if (start_of_this_entry != 0) 628 fatal ("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"); 629 } 630 631 if (!delete_flag) 632 if (entries_to_add == 0) 633 fatal ("no info dir entry in `%s'", infile); 634 635 /* Now read in the Info dir file. */ 636 dir_data = readfile (dirfile, &dir_size); 637 dir_lines = findlines (dir_data, dir_size, &dir_nlines); 638 639 /* We will be comparing the entries in the dir file against the 640 current filename, so need to strip off any directory prefix and any 641 .info suffix. */ 642 { 643 unsigned basename_len; 644 extern char *strrchr (); 645 char *infile_basename = strrchr (infile, '/'); 646 if (infile_basename) 647 infile_basename++; 648 else 649 infile_basename = infile; 650 651 basename_len = strlen (infile_basename); 652 infile_sans_info 653 = (strlen (infile_basename) > 5 654 && strcmp (infile_basename + basename_len - 5, ".info") == 0) 655 ? copy_string (infile_basename, basename_len - 5) 656 : infile_basename; 657 658 infilelen_sans_info = strlen (infile_sans_info); 659 } 660 661 /* Parse the dir file. Find all the nodes, and their menus, 662 and the sections of their menus. */ 663 664 dir_nodes = 0; 665 node_header_flag = 0; 666 for (i = 0; i < dir_nlines; i++) 667 { 668 /* Parse node header lines. */ 669 if (node_header_flag) 670 { 671 int j, end; 672 for (j = 0; j < dir_lines[i].size; j++) 673 /* Find the node name and store it in the `struct node'. */ 674 if (!strncmp ("Node:", dir_lines[i].start + j, 5)) 675 { 676 char *line = dir_lines[i].start; 677 /* Find the start of the node name. */ 678 j += 5; 679 while (line[j] == ' ' || line[j] == '\t') 680 j++; 681 /* Find the end of the node name. */ 682 end = j; 683 while (line[end] != 0 && line[end] != ',' && line[end] != '\n' 684 && line[end] != '\t') 685 end++; 686 dir_nodes->name = copy_string (line + j, end - j); 687 } 688 node_header_flag = 0; 689 } 690 691 /* Notice the start of a node. */ 692 if (*dir_lines[i].start == 037) 693 { 694 struct node *next 695 = (struct node *) xmalloc (sizeof (struct node)); 696 next->next = dir_nodes; 697 next->name = NULL; 698 next->start_line = i; 699 next->end_line = 0; 700 next->menu_start = NULL; 701 next->sections = NULL; 702 next->last_section = NULL; 703 704 if (dir_nodes != 0) 705 dir_nodes->end_line = i; 706 /* Fill in the end of the last menu section 707 of the previous node. */ 708 if (dir_nodes != 0 && dir_nodes->last_section != 0) 709 dir_nodes->last_section->end_line = i; 710 711 dir_nodes = next; 712 713 /* The following line is the header of this node; 714 parse it. */ 715 node_header_flag = 1; 716 } 717 718 /* Notice the lines that start menus. */ 719 if (dir_nodes != 0 720 && !strncmp ("* Menu:", dir_lines[i].start, 7)) 721 dir_nodes->menu_start = dir_lines[i + 1].start; 722 723 /* Notice sections in menus. */ 724 if (dir_nodes != 0 725 && dir_nodes->menu_start != 0 726 && *dir_lines[i].start != '\n' 727 && *dir_lines[i].start != '*' 728 && *dir_lines[i].start != ' ' 729 && *dir_lines[i].start != '\t') 730 { 731 /* Add this menu section to the node's list. 732 This list grows in forward order. */ 733 struct menu_section *next 734 = (struct menu_section *) xmalloc (sizeof (struct menu_section)); 735 next->start_line = i + 1; 736 next->next = 0; 737 next->end_line = 0; 738 next->name = copy_string (dir_lines[i].start, dir_lines[i].size); 739 if (dir_nodes->sections) 740 { 741 dir_nodes->last_section->next = next; 742 dir_nodes->last_section->end_line = i; 743 } 744 else 745 dir_nodes->sections = next; 746 dir_nodes->last_section = next; 747 } 748 749 /* Check for an existing entry that should be deleted. 750 Delete all entries which specify this file name. */ 751 if (*dir_lines[i].start == '*') 752 { 753 char *p = dir_lines[i].start; 754 755 while (*p != 0 && *p != ':') 756 p++; 757 p++; 758 while (*p == ' ') p++; 759 if (*p == '(') 760 { 761 p++; 762 if ((dir_lines[i].size 763 > (p - dir_lines[i].start + infilelen_sans_info)) 764 && !strncmp(p, infile_sans_info, infilelen_sans_info) 765 && ( p[infilelen_sans_info] == ')' || 766 strncmp (p + infilelen_sans_info, ".info)", 6) == 0)) 767 dir_lines[i].delete = 1; 768 } 769 } 770 /* Treat lines that start with whitespace 771 as continuations; if we are deleting an entry, 772 delete all its continuations as well. */ 773 else if (i > 0 774 && (*dir_lines[i].start == ' ' 775 || *dir_lines[i].start == '\t')) 776 { 777 dir_lines[i].delete = dir_lines[i - 1].delete; 778 something_deleted = 1; 779 } 780 } 781 782 /* Finish the info about the end of the last node. */ 783 if (dir_nodes != 0) 784 { 785 dir_nodes->end_line = dir_nlines; 786 if (dir_nodes->last_section != 0) 787 dir_nodes->last_section->end_line = dir_nlines; 788 } 789 790 /* Decide where to add the new entries (unless --delete was used). 791 Find the menu sections to add them in. 792 In each section, find the proper alphabetical place to add 793 each of the entries. */ 794 795 if (!delete_flag) 796 { 797 struct node *node; 798 struct menu_section *section; 799 struct spec_section *spec; 800 801 for (node = dir_nodes; node; node = node->next) 802 for (section = node->sections; section; section = section->next) 803 { 804 for (i = section->end_line; i > section->start_line; i--) 805 if (dir_lines[i - 1].size != 0) 806 break; 807 section->end_line = i; 808 809 for (spec = input_sections; spec; spec = spec->next) 810 if (!strcmp (spec->name, section->name)) 811 break; 812 if (spec) 813 { 814 int add_at_line = section->end_line; 815 struct spec_entry *entry; 816 /* Say we have found at least one section with this name, 817 so we need not add such a section. */ 818 spec->missing = 0; 819 /* For each entry, find the right place in this section 820 to add it. */ 821 for (entry = entries_to_add; entry; entry = entry->next) 822 { 823 int textlen = strlen (entry->text); 824 /* Subtract one because dir_lines is zero-based, 825 but the `end_line' and `start_line' members are 826 one-based. */ 827 for (i = section->end_line - 1; 828 i >= section->start_line - 1; i--) 829 { 830 /* If an entry exists with the same name, 831 and was not marked for deletion 832 (which means it is for some other file), 833 we are in trouble. */ 834 if (dir_lines[i].start[0] == '*' 835 && menu_line_equal (entry->text, textlen, 836 dir_lines[i].start, 837 dir_lines[i].size) 838 && !dir_lines[i].delete) 839 fatal ("menu item `%s' already exists, for file `%s'", 840 extract_menu_item_name (entry->text), 841 extract_menu_file_name (dir_lines[i].start)); 842 if (dir_lines[i].start[0] == '*' 843 && menu_line_lessp (entry->text, textlen, 844 dir_lines[i].start, 845 dir_lines[i].size)) 846 add_at_line = i; 847 } 848 insert_entry_here (entry, add_at_line, 849 dir_lines, n_entries_to_add); 850 } 851 } 852 } 853 854 /* Mark the end of the Top node as the place to add any 855 new sections that are needed. */ 856 for (node = dir_nodes; node; node = node->next) 857 if (node->name && strcmp (node->name, "Top") == 0) 858 dir_lines[node->end_line].add_sections_before = 1; 859 } 860 861 if (delete_flag && !something_deleted && !quiet_flag) 862 warning ("no entries found for `%s'; nothing deleted", infile); 863 864 /* Output the old dir file, interpolating the new sections 865 and/or new entries where appropriate. */ 866 867 output = fopen (dirfile, "w"); 868 if (!output) 869 { 870 perror (dirfile); 871 exit (1); 872 } 873 874 for (i = 0; i <= dir_nlines; i++) 875 { 876 int j; 877 878 /* If we decided to output some new entries before this line, 879 output them now. */ 880 if (dir_lines[i].add_entries_before) 881 for (j = 0; j < n_entries_to_add; j++) 882 { 883 struct spec_entry *this = dir_lines[i].add_entries_before[j]; 884 if (this == 0) 885 break; 886 fputs (this->text, output); 887 } 888 /* If we decided to add some sections here 889 because there are no such sections in the file, 890 output them now. */ 891 if (dir_lines[i].add_sections_before) 892 { 893 struct spec_section *spec; 894 struct spec_section **sections; 895 int n_sections = 0; 896 897 /* Count the sections and allocate a vector for all of them. */ 898 for (spec = input_sections; spec; spec = spec->next) 899 n_sections++; 900 sections = ((struct spec_section **) 901 xmalloc (n_sections * sizeof (struct spec_section *))); 902 903 /* Fill the vector SECTIONS with pointers to all the sections, 904 and sort them. */ 905 j = 0; 906 for (spec = input_sections; spec; spec = spec->next) 907 sections[j++] = spec; 908 qsort (sections, n_sections, sizeof (struct spec_section *), 909 compare_section_names); 910 911 /* Generate the new sections in alphabetical order. 912 In each new section, output all of our entries. */ 913 for (j = 0; j < n_sections; j++) 914 { 915 spec = sections[j]; 916 if (spec->missing) 917 { 918 struct spec_entry *entry; 919 920 putc ('\n', output); 921 fputs (spec->name, output); 922 putc ('\n', output); 923 for (entry = entries_to_add; entry; entry = entry->next) 924 fputs (entry->text, output); 925 } 926 } 927 928 free (sections); 929 } 930 931 /* Output the original dir lines unless marked for deletion. */ 932 if (i < dir_nlines && !dir_lines[i].delete) 933 { 934 fwrite (dir_lines[i].start, 1, dir_lines[i].size, output); 935 putc ('\n', output); 936 } 937 } 938 939 fclose (output); 940 941 exit (0); 942} 943 944/* Read all of file FILNAME into memory 945 and return the address of the data. 946 Store the size into SIZEP. 947 If there is trouble, do a fatal error. */ 948 949char * 950readfile (filename, sizep) 951 char *filename; 952 int *sizep; 953{ 954 int data_size = 1024; 955 char *data = (char *) xmalloc (data_size); 956 int filled = 0; 957 int nread = 0; 958 959 int desc = open (filename, O_RDONLY); 960 961 if (desc < 0) 962 pfatal_with_name (filename); 963 964 while (1) 965 { 966 nread = read (desc, data + filled, data_size - filled); 967 if (nread < 0) 968 pfatal_with_name (filename); 969 if (nread == 0) 970 break; 971 972 filled += nread; 973 if (filled == data_size) 974 { 975 data_size *= 2; 976 data = (char *) xrealloc (data, data_size); 977 } 978 } 979 980 *sizep = filled; 981 return data; 982} 983 984/* Divide the text at DATA (of SIZE bytes) into lines. 985 Return a vector of struct line_data describing the lines. 986 Store the length of that vector into *NLINESP. */ 987 988struct line_data * 989findlines (data, size, nlinesp) 990 char *data; 991 int size; 992 int *nlinesp; 993{ 994 struct line_data *lines; 995 int lines_allocated = 512; 996 int filled = 0; 997 int i = 0; 998 int lineflag; 999 1000 lines = (struct line_data *) xmalloc (lines_allocated * sizeof (struct line_data)); 1001 1002 lineflag = 1; 1003 for (i = 0; i < size; i++) 1004 { 1005 if (lineflag) 1006 { 1007 if (filled == lines_allocated) 1008 { 1009 lines_allocated *= 2; 1010 lines = (struct line_data *) xrealloc (lines, lines_allocated * sizeof (struct line_data)); 1011 } 1012 lines[filled].start = &data[i]; 1013 lines[filled].add_entries_before = 0; 1014 lines[filled].add_sections_before = 0; 1015 lines[filled].delete = 0; 1016 if (filled > 0) 1017 lines[filled - 1].size 1018 = lines[filled].start - lines[filled - 1].start - 1; 1019 filled++; 1020 } 1021 lineflag = (data[i] == '\n'); 1022 } 1023 if (filled > 0) 1024 lines[filled - 1].size = &data[i] - lines[filled - 1].start - lineflag; 1025 1026 /* Do not leave garbage in the last element. */ 1027 lines[filled].start = NULL; 1028 lines[filled].add_entries_before = NULL; 1029 lines[filled].add_sections_before = 0; 1030 lines[filled].delete = 0; 1031 lines[filled].size = 0; 1032 1033 *nlinesp = filled; 1034 return lines; 1035} 1036 1037/* Compare the menu item names in LINE1 (line length LEN1) 1038 and LINE2 (line length LEN2). Return 1 if the item name 1039 in LINE1 is less, 0 otherwise. */ 1040 1041int 1042menu_line_lessp (line1, len1, line2, len2) 1043 char *line1; 1044 int len1; 1045 char *line2; 1046 int len2; 1047{ 1048 int minlen = (len1 < len2 ? len1 : len2); 1049 int i; 1050 1051 for (i = 0; i < minlen; i++) 1052 { 1053 /* If one item name is a prefix of the other, 1054 the former one is less. */ 1055 if (line1[i] == ':' && line2[i] != ':') 1056 return 1; 1057 if (line2[i] == ':' && line1[i] != ':') 1058 return 0; 1059 /* If they both continue and differ, one is less. */ 1060 if (line1[i] < line2[i]) 1061 return 1; 1062 if (line1[i] > line2[i]) 1063 return 0; 1064 } 1065 /* With a properly formatted dir file, 1066 we can only get here if the item names are equal. */ 1067 return 0; 1068} 1069 1070/* Compare the menu item names in LINE1 (line length LEN1) 1071 and LINE2 (line length LEN2). Return 1 if the item names are equal, 1072 0 otherwise. */ 1073 1074int 1075menu_line_equal (line1, len1, line2, len2) 1076 char *line1; 1077 int len1; 1078 char *line2; 1079 int len2; 1080{ 1081 int minlen = (len1 < len2 ? len1 : len2); 1082 int i; 1083 1084 for (i = 0; i < minlen; i++) 1085 { 1086 /* If both item names end here, they are equal. */ 1087 if (line1[i] == ':' && line2[i] == ':') 1088 return 1; 1089 /* If they both continue and differ, one is less. */ 1090 if (line1[i] != line2[i]) 1091 return 0; 1092 } 1093 /* With a properly formatted dir file, 1094 we can only get here if the item names are equal. */ 1095 return 1; 1096} 1097 1098/* This is the comparison function for qsort 1099 for a vector of pointers to struct spec_section. 1100 Compare the section names. */ 1101 1102int 1103compare_section_names (sec1, sec2) 1104 struct spec_section **sec1, **sec2; 1105{ 1106 char *name1 = (*sec1)->name; 1107 char *name2 = (*sec2)->name; 1108 return strcmp (name1, name2); 1109} 1110 1111/* Insert ENTRY into the add_entries_before vector 1112 for line number LINE_NUMBER of the dir file. 1113 DIR_LINES and N_ENTRIES carry information from like-named variables 1114 in main. */ 1115 1116void 1117insert_entry_here (entry, line_number, dir_lines, n_entries) 1118 struct spec_entry *entry; 1119 int line_number; 1120 struct line_data *dir_lines; 1121 int n_entries; 1122{ 1123 int i; 1124 1125 if (dir_lines[line_number].add_entries_before == 0) 1126 { 1127 dir_lines[line_number].add_entries_before 1128 = (struct spec_entry **) xmalloc (n_entries * sizeof (struct spec_entry *)); 1129 for (i = 0; i < n_entries; i++) 1130 dir_lines[line_number].add_entries_before[i] = 0; 1131 } 1132 1133 for (i = 0; i < n_entries; i++) 1134 if (dir_lines[line_number].add_entries_before[i] == 0) 1135 break; 1136 1137 if (i == n_entries) 1138 abort (); 1139 1140 dir_lines[line_number].add_entries_before[i] = entry; 1141} 1142