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.1.1.1 1996/12/15 21:39:32 downsj Exp $ 5 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. 10 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. 15 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. */ 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. */ 38 char *progname; 39 40 char *readfile (); 41 struct line_data *findlines (); 42 char *my_strerror (); 43 void fatal (); 44 void insert_entry_here (); 45 int compare_section_names (); 46 47 struct spec_entry; 48 49 /* Data structures. */ 50 51 /* Record info about a single line from a file 52 as read into core. */ 53 54 struct line_data 55 { 56 /* The start of the line. */ 57 char *start; 58 /* The number of characters in the line, 59 excluding the terminating newline. */ 60 int size; 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. */ 67 int delete; 68 }; 69 70 /* This is used for a list of the specified menu section names 71 in which entries should be added. */ 72 73 struct spec_section 74 { 75 struct spec_section *next; 76 char *name; 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. */ 79 int missing; 80 }; 81 82 /* This is used for a list of the entries specified to be added. */ 83 84 struct spec_entry 85 { 86 struct spec_entry *next; 87 char *text; 88 }; 89 90 /* This is used for a list of nodes found by parsing the dir file. */ 91 92 struct node 93 { 94 struct node *next; 95 /* The node name. */ 96 char *name; 97 /* The line number of the line where the node starts. 98 This is the line that contains control-underscore. */ 99 int start_line; 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. */ 102 int end_line; 103 /* Start of first line in this node's menu 104 (the line after the * Menu: line). */ 105 char *menu_start; 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; 110 }; 111 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. */ 114 115 struct menu_section 116 { 117 struct menu_section *next; 118 char *name; 119 /* Line number of start of section. */ 120 int start_line; 121 /* Line number of end of section. */ 122 int end_line; 123 }; 124 125 /* Memory allocation and string operations. */ 126 127 /* Like malloc but get fatal error if memory is exhausted. */ 128 129 void * 130 xmalloc (size) 131 unsigned int size; 132 { 133 extern void *malloc (); 134 void *result = malloc (size); 135 if (result == NULL) 136 fatal ("virtual memory exhausted", 0); 137 return result; 138 } 139 140 /* Like malloc but get fatal error if memory is exhausted. */ 141 142 void * 143 xrealloc (obj, size) 144 void *obj; 145 unsigned int size; 146 { 147 extern void *realloc (); 148 void *result = realloc (obj, size); 149 if (result == NULL) 150 fatal ("virtual memory exhausted", 0); 151 return result; 152 } 153 154 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */ 155 156 char * 157 concat (s1, s2, s3) 158 char *s1, *s2, *s3; 159 { 160 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); 161 char *result = (char *) xmalloc (len1 + len2 + len3 + 1); 162 163 strcpy (result, s1); 164 strcpy (result + len1, s2); 165 strcpy (result + len1 + len2, s3); 166 *(result + len1 + len2 + len3) = 0; 167 168 return result; 169 } 170 171 /* Return a string containing SIZE characters 172 copied from starting at STRING. */ 173 174 char * 175 copy_string (string, size) 176 char *string; 177 int size; 178 { 179 int i; 180 char *copy = (char *) xmalloc (size + 1); 181 for (i = 0; i < size; i++) 182 copy[i] = string[i]; 183 copy[size] = 0; 184 return copy; 185 } 186 187 /* Error message functions. */ 188 189 /* Print error message. `s1' is printf control string, `s2' is arg for it. */ 190 191 /* VARARGS1 */ 192 void 193 error (s1, s2, s3) 194 char *s1, *s2, *s3; 195 { 196 fprintf (stderr, "%s: ", progname); 197 fprintf (stderr, s1, s2, s3); 198 fprintf (stderr, "\n"); 199 } 200 201 /* VARARGS1 */ 202 void 203 warning (s1, s2, s3) 204 char *s1, *s2, *s3; 205 { 206 fprintf (stderr, "%s: Warning: ", progname); 207 fprintf (stderr, s1, s2, s3); 208 fprintf (stderr, "\n"); 209 } 210 211 /* Print error message and exit. */ 212 213 void 214 fatal (s1, s2, s3) 215 char *s1, *s2, *s3; 216 { 217 error (s1, s2, s3); 218 exit (1); 219 } 220 221 /* Print fatal error message based on errno, with file name NAME. */ 222 223 void 224 pfatal_with_name (name) 225 char *name; 226 { 227 char *s = concat ("", my_strerror (errno), " for %s"); 228 fatal (s, name); 229 } 230 231 /* Given the full text of a menu entry, null terminated, 232 return just the menu item name (copied). */ 233 234 char * 235 extract_menu_item_name (item_text) 236 char *item_text; 237 { 238 char *p; 239 240 if (*item_text == '*') 241 item_text++; 242 while (*item_text == ' ') 243 item_text++; 244 245 p = item_text; 246 while (*p && *p != ':') p++; 247 return copy_string (item_text, p - item_text); 248 } 249 250 /* Given the full text of a menu entry, terminated by null or newline, 251 return just the menu item file (copied). */ 252 253 char * 254 extract_menu_file_name (item_text) 255 char *item_text; 256 { 257 char *p = item_text; 258 259 /* If we have text that looks like * ITEM: (FILE)NODE..., 260 extract just FILE. Otherwise return "(none)". */ 261 262 if (*p == '*') 263 p++; 264 while (*p == ' ') 265 p++; 266 267 /* Skip to and past the colon. */ 268 while (*p && *p != '\n' && *p != ':') p++; 269 if (*p == ':') p++; 270 271 /* Skip past the open-paren. */ 272 while (1) 273 { 274 if (*p == '(') 275 break; 276 else if (*p == ' ' || *p == '\t') 277 p++; 278 else 279 return "(none)"; 280 } 281 p++; 282 283 item_text = p; 284 285 /* File name ends just before the close-paren. */ 286 while (*p && *p != '\n' && *p != ')') p++; 287 if (*p != ')') 288 return "(none)"; 289 290 return copy_string (item_text, p - item_text); 291 } 292 293 void 294 suggest_asking_for_help () 295 { 296 fprintf (stderr, "\tTry `%s --help' for a complete list of options.\n", 297 progname); 298 exit (1); 299 } 300 301 void 302 print_help () 303 { 304 printf ("%s [OPTION]... [INFO-FILE [DIR-FILE]]\n\ 305 Install INFO-FILE in the Info directory file DIR-FILE.\n\ 306 \n\ 307 Options:\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\ 332 \n\ 333 Email bug reports to bug-texinfo@prep.ai.mit.edu.\n\ 334 ", progname); 335 } 336 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. */ 340 341 char * 342 my_strerror (errnum) 343 int errnum; 344 { 345 extern char *sys_errlist[]; 346 extern int sys_nerr; 347 348 if (errnum >= 0 && errnum < sys_nerr) 349 return sys_errlist[errnum]; 350 return (char *) "Unknown error"; 351 } 352 353 /* This table defines all the long-named options, says whether they 354 use an argument, and maps them into equivalent single-letter options. */ 355 356 struct option longopts[] = 357 { 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' }, 369 { 0 } 370 }; 371 372 main (argc, argv) 373 int argc; 374 char **argv; 375 { 376 char *infile = 0, *dirfile = 0; 377 char *infile_sans_info; 378 unsigned infilelen_sans_info; 379 FILE *output; 380 381 /* Record the text of the Info file, as a sequence of characters 382 and as a sequence of lines. */ 383 char *input_data; 384 int input_size; 385 struct line_data *input_lines; 386 int input_nlines; 387 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; 392 393 /* Record the old text of the dir file, as plain characters, 394 as lines, and as nodes. */ 395 char *dir_data; 396 int dir_size; 397 int dir_nlines; 398 struct line_data *dir_lines; 399 struct node *dir_nodes; 400 401 /* Nonzero means --delete was specified (just delete existing entries). */ 402 int delete_flag = 0; 403 int something_deleted = 0; 404 /* Nonzero means -q was specified. */ 405 int quiet_flag = 0; 406 407 int node_header_flag; 408 int prefix_length; 409 int i; 410 411 progname = argv[0]; 412 413 while (1) 414 { 415 int opt = getopt_long (argc, argv, "i:d:e:s:hHr", longopts, 0); 416 417 if (opt == EOF) 418 break; 419 420 switch (opt) 421 { 422 case 0: 423 /* If getopt returns 0, then it has already processed a 424 long-named option. We should do nothing. */ 425 break; 426 427 case 1: 428 abort (); 429 430 case 'd': 431 if (dirfile) 432 { 433 fprintf (stderr, "%s: Specify the Info directory only once.\n", 434 progname); 435 suggest_asking_for_help (); 436 } 437 dirfile = optarg; 438 break; 439 440 case 'D': 441 if (dirfile) 442 { 443 fprintf (stderr, "%s: Specify the Info directory only once.\n", 444 progname); 445 suggest_asking_for_help (); 446 } 447 dirfile = concat (optarg, "", "/dir"); 448 break; 449 450 case 'e': 451 { 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", ""); 456 next->text = optarg; 457 next->next = entries_to_add; 458 entries_to_add = next; 459 n_entries_to_add++; 460 } 461 break; 462 463 case 'h': 464 case 'H': 465 print_help (); 466 exit (0); 467 468 case 'i': 469 if (infile) 470 { 471 fprintf (stderr, "%s: Specify the Info file only once.\n", 472 progname); 473 suggest_asking_for_help (); 474 } 475 infile = optarg; 476 break; 477 478 case 'q': 479 quiet_flag = 1; 480 break; 481 482 case 'r': 483 delete_flag = 1; 484 break; 485 486 case 's': 487 { 488 struct spec_section *next 489 = (struct spec_section *) xmalloc (sizeof (struct spec_section)); 490 next->name = optarg; 491 next->next = input_sections; 492 next->missing = 1; 493 input_sections = next; 494 } 495 break; 496 497 case 'V': 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."); 503 exit (0); 504 505 default: 506 suggest_asking_for_help (); 507 } 508 } 509 510 /* Interpret the non-option arguments as file names. */ 511 for (; optind < argc; ++optind) 512 { 513 if (infile == 0) 514 infile = argv[optind]; 515 else if (dirfile == 0) 516 dirfile = argv[optind]; 517 else 518 error ("excess command line argument `%s'", argv[optind]); 519 } 520 521 if (!infile) 522 fatal ("No input file specified"); 523 if (!dirfile) 524 fatal ("No dir file specified"); 525 526 /* Read the Info file and parse it into lines. */ 527 528 input_data = readfile (infile, &input_size); 529 input_lines = findlines (input_data, input_size, &input_nlines); 530 531 /* Parse the input file to find the section names it specifies. */ 532 533 if (input_sections == 0) 534 { 535 prefix_length = strlen ("INFO-DIR-SECTION "); 536 for (i = 0; i < input_nlines; i++) 537 { 538 if (!strncmp ("INFO-DIR-SECTION ", input_lines[i].start, 539 prefix_length)) 540 { 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; 546 next->missing = 1; 547 input_sections = next; 548 } 549 } 550 } 551 552 /* Default to section "Miscellaneous" if no sections specified. */ 553 if (input_sections == 0) 554 { 555 input_sections 556 = (struct spec_section *) xmalloc (sizeof (struct spec_section)); 557 input_sections->name = "Miscellaneous"; 558 input_sections->next = 0; 559 input_sections->missing = 1; 560 } 561 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. */ 565 566 if (entries_to_add == 0) 567 { 568 char *start_of_this_entry = 0; 569 for (i = 0; i < input_nlines; i++) 570 { 571 if (!strncmp ("START-INFO-DIR-ENTRY", input_lines[i].start, 572 input_lines[i].size) 573 && sizeof ("START-INFO-DIR-ENTRY") - 1 == input_lines[i].size) 574 { 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; 578 } 579 if (!strncmp ("END-INFO-DIR-ENTRY", input_lines[i].start, 580 input_lines[i].size) 581 && sizeof ("END-INFO-DIR-ENTRY") - 1 == input_lines[i].size) 582 { 583 if (start_of_this_entry != 0) 584 { 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; 591 n_entries_to_add++; 592 start_of_this_entry = 0; 593 } 594 else 595 fatal ("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY"); 596 } 597 } 598 if (start_of_this_entry != 0) 599 fatal ("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"); 600 } 601 602 if (!delete_flag) 603 if (entries_to_add == 0) 604 fatal ("no info dir entry in `%s'", infile); 605 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); 609 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 612 .info suffix. */ 613 { 614 unsigned basename_len; 615 extern char *strrchr (); 616 char *infile_basename = strrchr (infile, '/'); 617 if (infile_basename) 618 infile_basename++; 619 else 620 infile_basename = infile; 621 622 basename_len = strlen (infile_basename); 623 infile_sans_info 624 = (strlen (infile_basename) > 5 625 && strcmp (infile_basename + basename_len - 5, ".info") == 0) 626 ? copy_string (infile_basename, basename_len - 5) 627 : infile_basename; 628 629 infilelen_sans_info = strlen (infile_sans_info); 630 } 631 632 /* Parse the dir file. Find all the nodes, and their menus, 633 and the sections of their menus. */ 634 635 dir_nodes = 0; 636 node_header_flag = 0; 637 for (i = 0; i < dir_nlines; i++) 638 { 639 /* Parse node header lines. */ 640 if (node_header_flag) 641 { 642 int j, end; 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)) 646 { 647 char *line = dir_lines[i].start; 648 /* Find the start of the node name. */ 649 j += 5; 650 while (line[j] == ' ' || line[j] == '\t') 651 j++; 652 /* Find the end of the node name. */ 653 end = j; 654 while (line[end] != 0 && line[end] != ',' && line[end] != '\n' 655 && line[end] != '\t') 656 end++; 657 dir_nodes->name = copy_string (line + j, end - j); 658 } 659 node_header_flag = 0; 660 } 661 662 /* Notice the start of a node. */ 663 if (*dir_lines[i].start == 037) 664 { 665 struct node *next 666 = (struct node *) xmalloc (sizeof (struct node)); 667 next->next = dir_nodes; 668 next->name = NULL; 669 next->start_line = i; 670 next->end_line = 0; 671 next->menu_start = NULL; 672 next->sections = NULL; 673 next->last_section = NULL; 674 675 if (dir_nodes != 0) 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; 681 682 dir_nodes = next; 683 684 /* The following line is the header of this node; 685 parse it. */ 686 node_header_flag = 1; 687 } 688 689 /* Notice the lines that start menus. */ 690 if (dir_nodes != 0 691 && !strncmp ("* Menu:", dir_lines[i].start, 7)) 692 dir_nodes->menu_start = dir_lines[i + 1].start; 693 694 /* Notice sections in menus. */ 695 if (dir_nodes != 0 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') 701 { 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; 707 next->next = 0; 708 next->end_line = 0; 709 next->name = copy_string (dir_lines[i].start, dir_lines[i].size); 710 if (dir_nodes->sections) 711 { 712 dir_nodes->last_section->next = next; 713 dir_nodes->last_section->end_line = i; 714 } 715 else 716 dir_nodes->sections = next; 717 dir_nodes->last_section = next; 718 } 719 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 == '*') 723 { 724 char *p = dir_lines[i].start; 725 726 while (*p != 0 && *p != ':') 727 p++; 728 p++; 729 while (*p == ' ') p++; 730 if (*p == '(') 731 { 732 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; 738 } 739 } 740 /* Treat lines that start with whitespace 741 as continuations; if we are deleting an entry, 742 delete all its continuations as well. */ 743 else if (i > 0 744 && (*dir_lines[i].start == ' ' 745 || *dir_lines[i].start == '\t')) 746 { 747 dir_lines[i].delete = dir_lines[i - 1].delete; 748 something_deleted = 1; 749 } 750 } 751 752 /* Finish the info about the end of the last node. */ 753 if (dir_nodes != 0) 754 { 755 dir_nodes->end_line = dir_nlines; 756 if (dir_nodes->last_section != 0) 757 dir_nodes->last_section->end_line = dir_nlines; 758 } 759 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. */ 764 765 if (!delete_flag) 766 { 767 struct node *node; 768 struct menu_section *section; 769 struct spec_section *spec; 770 771 for (node = dir_nodes; node; node = node->next) 772 for (section = node->sections; section; section = section->next) 773 { 774 for (i = section->end_line; i > section->start_line; i--) 775 if (dir_lines[i - 1].size != 0) 776 break; 777 section->end_line = i; 778 779 for (spec = input_sections; spec; spec = spec->next) 780 if (!strcmp (spec->name, section->name)) 781 break; 782 if (spec) 783 { 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. */ 788 spec->missing = 0; 789 /* For each entry, find the right place in this section 790 to add it. */ 791 for (entry = entries_to_add; entry; entry = entry->next) 792 { 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 796 one-based. */ 797 for (i = section->end_line - 1; 798 i >= section->start_line - 1; i--) 799 { 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, 806 dir_lines[i].start, 807 dir_lines[i].size) 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, 814 dir_lines[i].start, 815 dir_lines[i].size)) 816 add_at_line = i; 817 } 818 insert_entry_here (entry, add_at_line, 819 dir_lines, n_entries_to_add); 820 } 821 } 822 } 823 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; 829 } 830 831 if (delete_flag && !something_deleted && !quiet_flag) 832 warning ("no entries found for `%s'; nothing deleted", infile); 833 834 /* Output the old dir file, interpolating the new sections 835 and/or new entries where appropriate. */ 836 837 output = fopen (dirfile, "w"); 838 if (!output) 839 { 840 perror (dirfile); 841 exit (1); 842 } 843 844 for (i = 0; i <= dir_nlines; i++) 845 { 846 int j; 847 848 /* If we decided to output some new entries before this line, 849 output them now. */ 850 if (dir_lines[i].add_entries_before) 851 for (j = 0; j < n_entries_to_add; j++) 852 { 853 struct spec_entry *this = dir_lines[i].add_entries_before[j]; 854 if (this == 0) 855 break; 856 fputs (this->text, output); 857 } 858 /* If we decided to add some sections here 859 because there are no such sections in the file, 860 output them now. */ 861 if (dir_lines[i].add_sections_before) 862 { 863 struct spec_section *spec; 864 struct spec_section **sections; 865 int n_sections = 0; 866 867 /* Count the sections and allocate a vector for all of them. */ 868 for (spec = input_sections; spec; spec = spec->next) 869 n_sections++; 870 sections = ((struct spec_section **) 871 xmalloc (n_sections * sizeof (struct spec_section *))); 872 873 /* Fill the vector SECTIONS with pointers to all the sections, 874 and sort them. */ 875 j = 0; 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); 880 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++) 884 { 885 spec = sections[j]; 886 if (spec->missing) 887 { 888 struct spec_entry *entry; 889 890 putc ('\n', output); 891 fputs (spec->name, output); 892 putc ('\n', output); 893 for (entry = entries_to_add; entry; entry = entry->next) 894 fputs (entry->text, output); 895 } 896 } 897 898 free (sections); 899 } 900 901 /* Output the original dir lines unless marked for deletion. */ 902 if (i < dir_nlines && !dir_lines[i].delete) 903 { 904 fwrite (dir_lines[i].start, 1, dir_lines[i].size, output); 905 putc ('\n', output); 906 } 907 } 908 909 fclose (output); 910 911 exit (0); 912 } 913 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. */ 918 919 char * 920 readfile (filename, sizep) 921 char *filename; 922 int *sizep; 923 { 924 int data_size = 1024; 925 char *data = (char *) xmalloc (data_size); 926 int filled = 0; 927 int nread = 0; 928 929 int desc = open (filename, O_RDONLY); 930 931 if (desc < 0) 932 pfatal_with_name (filename); 933 934 while (1) 935 { 936 nread = read (desc, data + filled, data_size - filled); 937 if (nread < 0) 938 pfatal_with_name (filename); 939 if (nread == 0) 940 break; 941 942 filled += nread; 943 if (filled == data_size) 944 { 945 data_size *= 2; 946 data = (char *) xrealloc (data, data_size); 947 } 948 } 949 950 *sizep = filled; 951 return data; 952 } 953 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. */ 957 958 struct line_data * 959 findlines (data, size, nlinesp) 960 char *data; 961 int size; 962 int *nlinesp; 963 { 964 struct line_data *lines; 965 int lines_allocated = 512; 966 int filled = 0; 967 int i = 0; 968 int lineflag; 969 970 lines = (struct line_data *) xmalloc (lines_allocated * sizeof (struct line_data)); 971 972 lineflag = 1; 973 for (i = 0; i < size; i++) 974 { 975 if (lineflag) 976 { 977 if (filled == lines_allocated) 978 { 979 lines_allocated *= 2; 980 lines = (struct line_data *) xrealloc (lines, lines_allocated * sizeof (struct line_data)); 981 } 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; 986 if (filled > 0) 987 lines[filled - 1].size 988 = lines[filled].start - lines[filled - 1].start - 1; 989 filled++; 990 } 991 lineflag = (data[i] == '\n'); 992 } 993 if (filled > 0) 994 lines[filled - 1].size = &data[i] - lines[filled - 1].start - lineflag; 995 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; 1002 1003 *nlinesp = filled; 1004 return lines; 1005 } 1006 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. */ 1010 1011 int 1012 menu_line_lessp (line1, len1, line2, len2) 1013 char *line1; 1014 int len1; 1015 char *line2; 1016 int len2; 1017 { 1018 int minlen = (len1 < len2 ? len1 : len2); 1019 int i; 1020 1021 for (i = 0; i < minlen; i++) 1022 { 1023 /* If one item name is a prefix of the other, 1024 the former one is less. */ 1025 if (line1[i] == ':' && line2[i] != ':') 1026 return 1; 1027 if (line2[i] == ':' && line1[i] != ':') 1028 return 0; 1029 /* If they both continue and differ, one is less. */ 1030 if (line1[i] < line2[i]) 1031 return 1; 1032 if (line1[i] > line2[i]) 1033 return 0; 1034 } 1035 /* With a properly formatted dir file, 1036 we can only get here if the item names are equal. */ 1037 return 0; 1038 } 1039 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, 1042 0 otherwise. */ 1043 1044 int 1045 menu_line_equal (line1, len1, line2, len2) 1046 char *line1; 1047 int len1; 1048 char *line2; 1049 int len2; 1050 { 1051 int minlen = (len1 < len2 ? len1 : len2); 1052 int i; 1053 1054 for (i = 0; i < minlen; i++) 1055 { 1056 /* If both item names end here, they are equal. */ 1057 if (line1[i] == ':' && line2[i] == ':') 1058 return 1; 1059 /* If they both continue and differ, one is less. */ 1060 if (line1[i] != line2[i]) 1061 return 0; 1062 } 1063 /* With a properly formatted dir file, 1064 we can only get here if the item names are equal. */ 1065 return 1; 1066 } 1067 1068 /* This is the comparison function for qsort 1069 for a vector of pointers to struct spec_section. 1070 Compare the section names. */ 1071 1072 int 1073 compare_section_names (sec1, sec2) 1074 struct spec_section **sec1, **sec2; 1075 { 1076 char *name1 = (*sec1)->name; 1077 char *name2 = (*sec2)->name; 1078 return strcmp (name1, name2); 1079 } 1080 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 1084 in main. */ 1085 1086 void 1087 insert_entry_here (entry, line_number, dir_lines, n_entries) 1088 struct spec_entry *entry; 1089 int line_number; 1090 struct line_data *dir_lines; 1091 int n_entries; 1092 { 1093 int i; 1094 1095 if (dir_lines[line_number].add_entries_before == 0) 1096 { 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; 1101 } 1102 1103 for (i = 0; i < n_entries; i++) 1104 if (dir_lines[line_number].add_entries_before[i] == 0) 1105 break; 1106 1107 if (i == n_entries) 1108 abort (); 1109 1110 dir_lines[line_number].add_entries_before[i] = entry; 1111 } 1112