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