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