1 /* install-info -- create Info directory entry(ies) for an Info file. 2 $Id: install-info.c,v 1.6 2000/02/09 02:18:42 espie Exp $ 3 4 Copyright (C) 1996, 97, 98, 99 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); 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); 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); 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 \n\ 420 Email bug reports to bug-texinfo@gnu.org,\n\ 421 general questions and discussion to help-texinfo@gnu.org.\n\ 422 "), progname); 423 } 424 425 426 /* If DIRFILE does not exist, create a minimal one (or abort). If it 427 already exists, do nothing. */ 428 429 void 430 ensure_dirfile_exists (dirfile) 431 char *dirfile; 432 { 433 int desc = open (dirfile, O_RDONLY); 434 if (desc < 0 && errno == ENOENT) 435 { 436 FILE *f; 437 char *readerr = strerror (errno); 438 close (desc); 439 f = fopen (dirfile, "w"); 440 if (f) 441 { 442 fprintf (f, _("This is the file .../info/dir, which contains the\n\ 443 topmost node of the Info hierarchy, called (dir)Top.\n\ 444 The first time you invoke Info you start off looking at this node.\n\ 445 \n\ 446 %s\tThis is the top of the INFO tree\n\ 447 \n\ 448 This (the Directory node) gives a menu of major topics.\n\ 449 Typing \"q\" exits, \"?\" lists all Info commands, \"d\" returns here,\n\ 450 \"h\" gives a primer for first-timers,\n\ 451 \"mEmacs<Return>\" visits the Emacs manual, etc.\n\ 452 \n\ 453 In Emacs, you can click mouse button 2 on a menu item or cross reference\n\ 454 to select it.\n\ 455 \n\ 456 * Menu:\n\ 457 "), "File: dir,\tNode: Top"); /* This part must not be translated. */ 458 if (fclose (f) < 0) 459 pfatal_with_name (dirfile); 460 } 461 else 462 { 463 /* Didn't exist, but couldn't open for writing. */ 464 fprintf (stderr, 465 _("%s: could not read (%s) and could not create (%s)\n"), 466 dirfile, readerr, strerror (errno)); 467 xexit (1); 468 } 469 } 470 else 471 close (desc); /* It already existed, so fine. */ 472 } 473 474 /* Open FILENAME and return the resulting stream pointer. If it doesn't 475 exist, try FILENAME.gz. If that doesn't exist either, call 476 CREATE_CALLBACK (with FILENAME as arg) to create it, if that is 477 non-NULL. If still no luck, fatal error. 478 479 If we do open it, return the actual name of the file opened in 480 OPENED_FILENAME and the compress program to use to (de)compress it in 481 COMPRESSION_PROGRAM. The compression program is determined by the 482 magic number, not the filename. */ 483 484 FILE * 485 open_possibly_compressed_file (filename, create_callback, 486 opened_filename, compression_program, is_pipe) 487 char *filename; 488 void (*create_callback) (); 489 char **opened_filename; 490 char **compression_program; 491 int *is_pipe; 492 { 493 char *local_opened_filename, *local_compression_program; 494 int nread; 495 char data[4]; 496 FILE *f; 497 498 /* We let them pass NULL if they don't want this info, but it's easier 499 to always determine it. */ 500 if (!opened_filename) 501 opened_filename = &local_opened_filename; 502 503 *opened_filename = filename; 504 f = fopen (*opened_filename, FOPEN_RBIN); 505 if (!f) 506 { 507 *opened_filename = concat (filename, ".gz", ""); 508 f = fopen (*opened_filename, FOPEN_RBIN); 509 #ifdef __MSDOS__ 510 if (!f) 511 { 512 free (*opened_filename); 513 *opened_filename = concat (filename, ".igz", ""); 514 f = fopen (*opened_filename, FOPEN_RBIN); 515 } 516 if (!f) 517 { 518 free (*opened_filename); 519 *opened_filename = concat (filename, ".inz", ""); 520 f = fopen (*opened_filename, FOPEN_RBIN); 521 } 522 #endif 523 if (!f) 524 { 525 if (create_callback) 526 { /* That didn't work either. Create the file if we can. */ 527 (*create_callback) (filename); 528 529 /* And try opening it again. */ 530 free (*opened_filename); 531 *opened_filename = filename; 532 f = fopen (*opened_filename, FOPEN_RBIN); 533 if (!f) 534 pfatal_with_name (filename); 535 } 536 else 537 pfatal_with_name (filename); 538 } 539 } 540 541 /* Read first few bytes of file rather than relying on the filename. 542 If the file is shorter than this it can't be usable anyway. */ 543 nread = fread (data, sizeof (data), 1, f); 544 if (nread != 1) 545 { 546 /* Empty files don't set errno, so we get something like 547 "install-info: No error for foo", which is confusing. */ 548 if (nread == 0) 549 fatal (_("%s: empty file"), *opened_filename); 550 pfatal_with_name (*opened_filename); 551 } 552 553 if (!compression_program) 554 compression_program = &local_compression_program; 555 556 if (data[0] == '\x1f' && data[1] == '\x8b') 557 #if STRIP_DOT_EXE 558 /* An explicit .exe yields a better diagnostics from popen below 559 if they don't have gzip installed. */ 560 *compression_program = "gzip.exe"; 561 #else 562 *compression_program = "gzip"; 563 #endif 564 else 565 *compression_program = NULL; 566 567 if (*compression_program) 568 { /* It's compressed, so fclose the file and then open a pipe. */ 569 char *command = concat (*compression_program," -cd <", *opened_filename); 570 if (fclose (f) < 0) 571 pfatal_with_name (*opened_filename); 572 f = popen (command, "r"); 573 if (f) 574 *is_pipe = 1; 575 else 576 pfatal_with_name (command); 577 } 578 else 579 { /* It's a plain file, seek back over the magic bytes. */ 580 if (fseek (f, 0, 0) < 0) 581 pfatal_with_name (*opened_filename); 582 #if O_BINARY 583 /* Since this is a text file, and we opened it in binary mode, 584 switch back to text mode. */ 585 f = freopen (*opened_filename, "r", f); 586 #endif 587 *is_pipe = 0; 588 } 589 590 return f; 591 } 592 593 /* Read all of file FILENAME into memory and return the address of the 594 data. Store the size of the data into SIZEP. If need be, uncompress 595 (i.e., try FILENAME.gz et al. if FILENAME does not exist) and store 596 the actual file name that was opened into OPENED_FILENAME (if it is 597 non-NULL), and the companion compression program (if any, else NULL) 598 into COMPRESSION_PROGRAM (if that is non-NULL). If trouble, do 599 a fatal error. */ 600 601 char * 602 readfile (filename, sizep, create_callback, 603 opened_filename, compression_program) 604 char *filename; 605 int *sizep; 606 void (*create_callback) (); 607 char **opened_filename; 608 char **compression_program; 609 { 610 char *real_name; 611 FILE *f; 612 int pipe_p; 613 int filled = 0; 614 int data_size = 8192; 615 char *data = xmalloc (data_size); 616 617 /* If they passed the space for the file name to return, use it. */ 618 f = open_possibly_compressed_file (filename, create_callback, 619 opened_filename ? opened_filename 620 : &real_name, 621 compression_program, &pipe_p); 622 623 for (;;) 624 { 625 int nread = fread (data + filled, 1, data_size - filled, f); 626 if (nread < 0) 627 pfatal_with_name (real_name); 628 if (nread == 0) 629 break; 630 631 filled += nread; 632 if (filled == data_size) 633 { 634 data_size += 65536; 635 data = xrealloc (data, data_size); 636 } 637 } 638 639 /* We'll end up wasting space if we're not passing the filename back 640 and it is not just FILENAME, but so what. */ 641 /* We need to close the stream, since on some systems the pipe created 642 by popen is simulated by a temporary file which only gets removed 643 inside pclose. */ 644 if (pipe_p) 645 pclose (f); 646 else 647 fclose (f); 648 649 *sizep = filled; 650 return data; 651 } 652 653 /* Output the old dir file, interpolating the new sections 654 and/or new entries where appropriate. If COMPRESSION_PROGRAM is not 655 null, pipe to it to create DIRFILE. Thus if we read dir.gz on input, 656 we'll write dir.gz on output. */ 657 658 static void 659 output_dirfile (dirfile, dir_nlines, dir_lines, 660 n_entries_to_add, entries_to_add, input_sections, 661 compression_program) 662 char *dirfile; 663 int dir_nlines; 664 struct line_data *dir_lines; 665 int n_entries_to_add; 666 struct spec_entry *entries_to_add; 667 struct spec_section *input_sections; 668 char *compression_program; 669 { 670 int i; 671 FILE *output; 672 673 if (compression_program) 674 { 675 char *command = concat (compression_program, ">", dirfile); 676 output = popen (command, "w"); 677 } 678 else 679 output = fopen (dirfile, "w"); 680 681 if (!output) 682 { 683 perror (dirfile); 684 xexit (1); 685 } 686 687 for (i = 0; i <= dir_nlines; i++) 688 { 689 int j; 690 691 /* If we decided to output some new entries before this line, 692 output them now. */ 693 if (dir_lines[i].add_entries_before) 694 for (j = 0; j < n_entries_to_add; j++) 695 { 696 struct spec_entry *this = dir_lines[i].add_entries_before[j]; 697 if (this == 0) 698 break; 699 fputs (this->text, output); 700 } 701 /* If we decided to add some sections here 702 because there are no such sections in the file, 703 output them now. */ 704 if (dir_lines[i].add_sections_before) 705 { 706 struct spec_section *spec; 707 struct spec_section **sections; 708 int n_sections = 0; 709 struct spec_entry *entry; 710 struct spec_entry **entries; 711 int n_entries = 0; 712 713 /* Count the sections and allocate a vector for all of them. */ 714 for (spec = input_sections; spec; spec = spec->next) 715 n_sections++; 716 sections = ((struct spec_section **) 717 xmalloc (n_sections * sizeof (struct spec_section *))); 718 719 /* Fill the vector SECTIONS with pointers to all the sections, 720 and sort them. */ 721 j = 0; 722 for (spec = input_sections; spec; spec = spec->next) 723 sections[j++] = spec; 724 qsort (sections, n_sections, sizeof (struct spec_section *), 725 compare_section_names); 726 727 /* Count the entries and allocate a vector for all of them. */ 728 for (entry = entries_to_add; entry; entry = entry->next) 729 n_entries++; 730 entries = ((struct spec_entry **) 731 xmalloc (n_entries * sizeof (struct spec_entry *))); 732 733 /* Fill the vector ENTRIES with pointers to all the sections, 734 and sort them. */ 735 j = 0; 736 for (entry = entries_to_add; entry; entry = entry->next) 737 entries[j++] = entry; 738 qsort (entries, n_entries, sizeof (struct spec_entry *), 739 compare_entries_text); 740 741 /* Generate the new sections in alphabetical order. In each 742 new section, output all of the entries that belong to that 743 section, in alphabetical order. */ 744 for (j = 0; j < n_sections; j++) 745 { 746 spec = sections[j]; 747 if (spec->missing) 748 { 749 int k; 750 751 putc ('\n', output); 752 fputs (spec->name, output); 753 putc ('\n', output); 754 for (k = 0; k < n_entries; k++) 755 { 756 struct spec_section *spec1; 757 /* Did they at all want this entry to be put into 758 this section? */ 759 entry = entries[k]; 760 for (spec1 = entry->entry_sections; 761 spec1 && spec1 != entry->entry_sections_tail; 762 spec1 = spec1->next) 763 { 764 if (!strcmp (spec1->name, spec->name)) 765 break; 766 } 767 if (spec1 && spec1 != entry->entry_sections_tail) 768 fputs (entry->text, output); 769 } 770 } 771 } 772 773 free (entries); 774 free (sections); 775 } 776 777 /* Output the original dir lines unless marked for deletion. */ 778 if (i < dir_nlines && !dir_lines[i].delete) 779 { 780 fwrite (dir_lines[i].start, 1, dir_lines[i].size, output); 781 putc ('\n', output); 782 } 783 } 784 785 /* Some systems, such as MS-DOS, simulate pipes with temporary files. 786 On those systems, the compressor actually gets run inside pclose, 787 so we must call pclose. */ 788 if (compression_program) 789 pclose (output); 790 else 791 fclose (output); 792 } 793 794 /* Parse the input to find the section names and the entry names it 795 specifies. Return the number of entries to add from this file. */ 796 int 797 parse_input (lines, nlines, sections, entries) 798 const struct line_data *lines; 799 int nlines; 800 struct spec_section **sections; 801 struct spec_entry **entries; 802 { 803 int n_entries = 0; 804 int prefix_length = strlen ("INFO-DIR-SECTION "); 805 struct spec_section *head = *sections, *tail = NULL; 806 int reset_tail = 0; 807 char *start_of_this_entry = 0; 808 int ignore_sections = *sections != 0; 809 int ignore_entries = *entries != 0; 810 811 int i; 812 813 if (ignore_sections && ignore_entries) 814 return 0; 815 816 /* Loop here processing lines from the input file. Each 817 INFO-DIR-SECTION entry is added to the SECTIONS linked list. 818 Each START-INFO-DIR-ENTRY block is added to the ENTRIES linked 819 list, and all its entries inherit the chain of SECTION entries 820 defined by the last group of INFO-DIR-SECTION entries we have 821 seen until that point. */ 822 for (i = 0; i < nlines; i++) 823 { 824 if (!ignore_sections 825 && !strncmp ("INFO-DIR-SECTION ", lines[i].start, prefix_length)) 826 { 827 struct spec_section *next 828 = (struct spec_section *) xmalloc (sizeof (struct spec_section)); 829 next->name = copy_string (lines[i].start + prefix_length, 830 lines[i].size - prefix_length); 831 next->next = *sections; 832 next->missing = 1; 833 if (reset_tail) 834 { 835 tail = *sections; 836 reset_tail = 0; 837 } 838 *sections = next; 839 head = *sections; 840 } 841 /* If entries were specified explicitly with command options, 842 ignore the entries in the input file. */ 843 else if (!ignore_entries) 844 { 845 if (!strncmp ("START-INFO-DIR-ENTRY", lines[i].start, lines[i].size) 846 && sizeof ("START-INFO-DIR-ENTRY") - 1 == lines[i].size) 847 { 848 if (!*sections) 849 { 850 /* We found an entry, but didn't yet see any sections 851 specified. Default to section "Miscellaneous". */ 852 *sections = (struct spec_section *) 853 xmalloc (sizeof (struct spec_section)); 854 (*sections)->name = "Miscellaneous"; 855 (*sections)->next = 0; 856 (*sections)->missing = 1; 857 head = *sections; 858 } 859 /* Next time we see INFO-DIR-SECTION, we will reset the 860 tail pointer. */ 861 reset_tail = 1; 862 863 if (start_of_this_entry != 0) 864 fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY")); 865 start_of_this_entry = lines[i + 1].start; 866 } 867 else if (start_of_this_entry) 868 { 869 if ((!strncmp ("* ", lines[i].start, 2) 870 && lines[i].start > start_of_this_entry) 871 || (!strncmp ("END-INFO-DIR-ENTRY", 872 lines[i].start, lines[i].size) 873 && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size)) 874 { 875 /* We found an end of this entry. Allocate another 876 entry, fill its data, and add it to the linked 877 list. */ 878 struct spec_entry *next 879 = (struct spec_entry *) xmalloc (sizeof (struct spec_entry)); 880 next->text 881 = copy_string (start_of_this_entry, 882 lines[i].start - start_of_this_entry); 883 next->text_len = lines[i].start - start_of_this_entry; 884 next->entry_sections = head; 885 next->entry_sections_tail = tail; 886 next->next = *entries; 887 *entries = next; 888 n_entries++; 889 if (!strncmp ("END-INFO-DIR-ENTRY", 890 lines[i].start, lines[i].size) 891 && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size) 892 start_of_this_entry = 0; 893 else 894 start_of_this_entry = lines[i].start; 895 } 896 else if (!strncmp ("END-INFO-DIR-ENTRY", 897 lines[i].start, lines[i].size) 898 && sizeof ("END-INFO-DIR-ENTRY") - 1 == lines[i].size) 899 fatal (_("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY")); 900 } 901 } 902 } 903 if (start_of_this_entry != 0) 904 fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY")); 905 906 /* If we ignored the INFO-DIR-ENTRY directives, we need now go back 907 and plug the names of all the sections we found into every 908 element of the ENTRIES list. */ 909 if (ignore_entries && *entries) 910 { 911 struct spec_entry *entry; 912 913 for (entry = *entries; entry; entry = entry->next) 914 { 915 entry->entry_sections = head; 916 entry->entry_sections_tail = tail; 917 } 918 } 919 920 return n_entries; 921 } 922 923 /* Parse the dir file whose basename is BASE_NAME. Find all the 924 nodes, and their menus, and the sections of their menus. */ 925 int 926 parse_dir_file (lines, nlines, nodes, base_name) 927 struct line_data *lines; 928 int nlines; 929 struct node **nodes; 930 const char *base_name; 931 { 932 int node_header_flag = 0; 933 int something_deleted = 0; 934 int i; 935 936 *nodes = 0; 937 for (i = 0; i < nlines; i++) 938 { 939 /* Parse node header lines. */ 940 if (node_header_flag) 941 { 942 int j, end; 943 for (j = 0; j < lines[i].size; j++) 944 /* Find the node name and store it in the `struct node'. */ 945 if (!strncmp ("Node:", lines[i].start + j, 5)) 946 { 947 char *line = lines[i].start; 948 /* Find the start of the node name. */ 949 j += 5; 950 while (line[j] == ' ' || line[j] == '\t') 951 j++; 952 /* Find the end of the node name. */ 953 end = j; 954 while (line[end] != 0 && line[end] != ',' && line[end] != '\n' 955 && line[end] != '\t') 956 end++; 957 (*nodes)->name = copy_string (line + j, end - j); 958 } 959 node_header_flag = 0; 960 } 961 962 /* Notice the start of a node. */ 963 if (*lines[i].start == 037) 964 { 965 struct node *next = (struct node *) xmalloc (sizeof (struct node)); 966 967 next->next = *nodes; 968 next->name = NULL; 969 next->start_line = i; 970 next->end_line = 0; 971 next->menu_start = NULL; 972 next->sections = NULL; 973 next->last_section = NULL; 974 975 if (*nodes != 0) 976 (*nodes)->end_line = i; 977 /* Fill in the end of the last menu section 978 of the previous node. */ 979 if (*nodes != 0 && (*nodes)->last_section != 0) 980 (*nodes)->last_section->end_line = i; 981 982 *nodes = next; 983 984 /* The following line is the header of this node; 985 parse it. */ 986 node_header_flag = 1; 987 } 988 989 /* Notice the lines that start menus. */ 990 if (*nodes != 0 && !strncmp ("* Menu:", lines[i].start, 7)) 991 (*nodes)->menu_start = lines[i + 1].start; 992 993 /* Notice sections in menus. */ 994 if (*nodes != 0 995 && (*nodes)->menu_start != 0 996 && *lines[i].start != '\n' 997 && *lines[i].start != '*' 998 && *lines[i].start != ' ' 999 && *lines[i].start != '\t') 1000 { 1001 /* Add this menu section to the node's list. 1002 This list grows in forward order. */ 1003 struct menu_section *next 1004 = (struct menu_section *) xmalloc (sizeof (struct menu_section)); 1005 1006 next->start_line = i + 1; 1007 next->next = 0; 1008 next->end_line = 0; 1009 next->name = copy_string (lines[i].start, lines[i].size); 1010 if ((*nodes)->sections) 1011 { 1012 (*nodes)->last_section->next = next; 1013 (*nodes)->last_section->end_line = i; 1014 } 1015 else 1016 (*nodes)->sections = next; 1017 (*nodes)->last_section = next; 1018 } 1019 1020 /* Check for an existing entry that should be deleted. 1021 Delete all entries which specify this file name. */ 1022 if (*lines[i].start == '*') 1023 { 1024 char *q; 1025 char *p = lines[i].start; 1026 1027 p++; /* skip * */ 1028 while (*p == ' ') p++; /* ignore following spaces */ 1029 q = p; /* remember this, it's the beginning of the menu item. */ 1030 1031 /* Read menu item. */ 1032 while (*p != 0 && *p != ':') 1033 p++; 1034 p++; /* skip : */ 1035 1036 if (*p == ':') 1037 { /* XEmacs-style entry, as in * Mew::Messaging. */ 1038 if (menu_item_equal (q, ':', base_name)) 1039 { 1040 lines[i].delete = 1; 1041 something_deleted = 1; 1042 } 1043 } 1044 else 1045 { /* Emacs-style entry, as in * Emacs: (emacs). */ 1046 while (*p == ' ') p++; /* skip spaces after : */ 1047 if (*p == '(') /* if at parenthesized (FILENAME) */ 1048 { 1049 p++; 1050 if (menu_item_equal (p, ')', base_name)) 1051 { 1052 lines[i].delete = 1; 1053 something_deleted = 1; 1054 } 1055 } 1056 } 1057 } 1058 1059 /* Treat lines that start with whitespace 1060 as continuations; if we are deleting an entry, 1061 delete all its continuations as well. */ 1062 else if (i > 0 && (*lines[i].start == ' ' || *lines[i].start == '\t')) 1063 { 1064 lines[i].delete = lines[i - 1].delete; 1065 } 1066 } 1067 1068 /* Finish the info about the end of the last node. */ 1069 if (*nodes != 0) 1070 { 1071 (*nodes)->end_line = nlines; 1072 if ((*nodes)->last_section != 0) 1073 (*nodes)->last_section->end_line = nlines; 1074 } 1075 1076 return something_deleted; 1077 } 1078 1079 int 1080 main (argc, argv) 1081 int argc; 1082 char **argv; 1083 { 1084 char *opened_dirfilename; 1085 char *compression_program; 1086 char *infile_sans_info; 1087 char *infile = 0, *dirfile = 0; 1088 unsigned infilelen_sans_info; 1089 1090 /* Record the text of the Info file, as a sequence of characters 1091 and as a sequence of lines. */ 1092 char *input_data = NULL; 1093 int input_size = 0; 1094 struct line_data *input_lines = NULL; 1095 int input_nlines = 0; 1096 1097 /* Record here the specified section names and directory entries. */ 1098 struct spec_section *input_sections = NULL; 1099 struct spec_entry *entries_to_add = NULL; 1100 int n_entries_to_add = 0; 1101 1102 /* Record the old text of the dir file, as plain characters, 1103 as lines, and as nodes. */ 1104 char *dir_data; 1105 int dir_size; 1106 int dir_nlines; 1107 struct line_data *dir_lines; 1108 struct node *dir_nodes; 1109 1110 /* Nonzero means --delete was specified (just delete existing entries). */ 1111 int delete_flag = 0; 1112 int something_deleted = 0; 1113 /* Nonzero means -q was specified. */ 1114 int quiet_flag = 0; 1115 1116 int i; 1117 1118 #ifdef HAVE_SETLOCALE 1119 /* Set locale via LC_ALL. */ 1120 setlocale (LC_ALL, ""); 1121 #endif 1122 1123 /* Set the text message domain. */ 1124 bindtextdomain (PACKAGE, LOCALEDIR); 1125 textdomain (PACKAGE); 1126 1127 while (1) 1128 { 1129 int opt = getopt_long (argc, argv, "i:d:e:s:hHr", longopts, 0); 1130 1131 if (opt == EOF) 1132 break; 1133 1134 switch (opt) 1135 { 1136 case 0: 1137 /* If getopt returns 0, then it has already processed a 1138 long-named option. We should do nothing. */ 1139 break; 1140 1141 case 1: 1142 abort (); 1143 1144 case 'd': 1145 if (dirfile) 1146 { 1147 fprintf (stderr, _("%s: Specify the Info directory only once.\n"), 1148 progname); 1149 suggest_asking_for_help (); 1150 } 1151 dirfile = optarg; 1152 break; 1153 1154 case 'D': 1155 if (dirfile) 1156 { 1157 fprintf (stderr, _("%s: Specify the Info directory only once.\n"), 1158 progname); 1159 suggest_asking_for_help (); 1160 } 1161 dirfile = concat (optarg, "", "/dir"); 1162 break; 1163 1164 case 'e': 1165 { 1166 struct spec_entry *next 1167 = (struct spec_entry *) xmalloc (sizeof (struct spec_entry)); 1168 int olen = strlen (optarg); 1169 if (! (*optarg != 0 && optarg[olen - 1] == '\n')) 1170 { 1171 optarg = concat (optarg, "\n", ""); 1172 olen++; 1173 } 1174 next->text = optarg; 1175 next->text_len = olen; 1176 next->entry_sections = NULL; 1177 next->entry_sections_tail = NULL; 1178 next->next = entries_to_add; 1179 entries_to_add = next; 1180 n_entries_to_add++; 1181 } 1182 break; 1183 1184 case 'h': 1185 case 'H': 1186 print_help (); 1187 xexit (0); 1188 1189 case 'i': 1190 if (infile) 1191 { 1192 fprintf (stderr, _("%s: Specify the Info file only once.\n"), 1193 progname); 1194 suggest_asking_for_help (); 1195 } 1196 infile = optarg; 1197 break; 1198 1199 case 'q': 1200 quiet_flag = 1; 1201 break; 1202 1203 case 'r': 1204 delete_flag = 1; 1205 break; 1206 1207 case 's': 1208 { 1209 struct spec_section *next 1210 = (struct spec_section *) xmalloc (sizeof (struct spec_section)); 1211 next->name = optarg; 1212 next->next = input_sections; 1213 next->missing = 1; 1214 input_sections = next; 1215 } 1216 break; 1217 1218 case 'V': 1219 printf ("install-info (GNU %s) %s\n", PACKAGE, VERSION); 1220 puts (""); 1221 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\ 1222 There is NO warranty. You may redistribute this software\n\ 1223 under the terms of the GNU General Public License.\n\ 1224 For more information about these matters, see the files named COPYING.\n"), 1225 "1999"); 1226 xexit (0); 1227 1228 default: 1229 suggest_asking_for_help (); 1230 } 1231 } 1232 1233 /* Interpret the non-option arguments as file names. */ 1234 for (; optind < argc; ++optind) 1235 { 1236 if (infile == 0) 1237 infile = argv[optind]; 1238 else if (dirfile == 0) 1239 dirfile = argv[optind]; 1240 else 1241 error (_("excess command line argument `%s'"), argv[optind]); 1242 } 1243 1244 if (!infile) 1245 fatal (_("No input file specified; try --help for more information.")); 1246 if (!dirfile) 1247 fatal (_("No dir file specified; try --help for more information.")); 1248 1249 /* Read the Info file and parse it into lines, unless we're deleting. */ 1250 if (!delete_flag) 1251 { 1252 input_data = readfile (infile, &input_size, NULL, NULL, NULL); 1253 input_lines = findlines (input_data, input_size, &input_nlines); 1254 } 1255 1256 i = parse_input (input_lines, input_nlines, 1257 &input_sections, &entries_to_add); 1258 if (i > n_entries_to_add) 1259 n_entries_to_add = i; 1260 1261 if (!delete_flag) 1262 { 1263 if (entries_to_add == 0) 1264 { /* No need to abort here, the original info file may not 1265 have the requisite Texinfo commands. This is not 1266 something an installer should have to correct (it's a 1267 problem for the maintainer), and there's no need to cause 1268 subsequent parts of `make install' to fail. */ 1269 warning (_("no info dir entry in `%s'"), infile); 1270 xexit (0); 1271 } 1272 1273 /* If the entries came from the command-line arguments, their 1274 entry_sections pointers are not yet set. Walk the chain of 1275 the entries and for each entry update entry_sections to point 1276 to the head of the list of sections where this entry should 1277 be put. Note that all the entries specified on the command 1278 line get put into ALL the sections we've got, either from the 1279 Info file, or (under --section) from the command line, 1280 because in the loop below every entry inherits the entire 1281 chain of sections. */ 1282 if (n_entries_to_add > 0 && entries_to_add->entry_sections == NULL) 1283 { 1284 struct spec_entry *ep; 1285 1286 /* If we got no sections, default to "Miscellaneous". */ 1287 if (input_sections == NULL) 1288 { 1289 input_sections = (struct spec_section *) 1290 xmalloc (sizeof (struct spec_section)); 1291 input_sections->name = "Miscellaneous"; 1292 input_sections->next = NULL; 1293 input_sections->missing = 1; 1294 } 1295 for (ep = entries_to_add; ep; ep = ep->next) 1296 ep->entry_sections = input_sections; 1297 } 1298 } 1299 1300 /* Now read in the Info dir file. */ 1301 dir_data = readfile (dirfile, &dir_size, ensure_dirfile_exists, 1302 &opened_dirfilename, &compression_program); 1303 dir_lines = findlines (dir_data, dir_size, &dir_nlines); 1304 1305 /* We will be comparing the entries in the dir file against the 1306 current filename, so need to strip off any directory prefix and/or 1307 [.info][.gz] suffix. */ 1308 { 1309 char *infile_basename = infile + strlen (infile); 1310 1311 if (HAVE_DRIVE (infile)) 1312 infile += 2; /* get past the drive spec X: */ 1313 1314 while (infile_basename > infile && !IS_SLASH (infile_basename[-1])) 1315 infile_basename--; 1316 1317 infile_sans_info = strip_info_suffix (infile_basename); 1318 infilelen_sans_info = strlen (infile_sans_info); 1319 } 1320 1321 something_deleted 1322 = parse_dir_file (dir_lines, dir_nlines, &dir_nodes, infile_sans_info); 1323 1324 /* Decide where to add the new entries (unless --delete was used). 1325 Find the menu sections to add them in. 1326 In each section, find the proper alphabetical place to add 1327 each of the entries. */ 1328 1329 if (!delete_flag) 1330 { 1331 struct node *node; 1332 struct menu_section *section; 1333 struct spec_section *spec; 1334 1335 for (node = dir_nodes; node; node = node->next) 1336 for (section = node->sections; section; section = section->next) 1337 { 1338 for (i = section->end_line; i > section->start_line; i--) 1339 if (dir_lines[i - 1].size != 0) 1340 break; 1341 section->end_line = i; 1342 1343 for (spec = input_sections; spec; spec = spec->next) 1344 if (!strcmp (spec->name, section->name)) 1345 break; 1346 if (spec) 1347 { 1348 int add_at_line = section->end_line; 1349 struct spec_entry *entry; 1350 /* Say we have found at least one section with this name, 1351 so we need not add such a section. */ 1352 spec->missing = 0; 1353 /* For each entry, find the right place in this section 1354 to add it. */ 1355 for (entry = entries_to_add; entry; entry = entry->next) 1356 { 1357 /* Did they at all want this entry to be put into 1358 this section? */ 1359 for (spec = entry->entry_sections; 1360 spec && spec != entry->entry_sections_tail; 1361 spec = spec->next) 1362 { 1363 if (!strcmp (spec->name, section->name)) 1364 break; 1365 } 1366 if (!spec || spec == entry->entry_sections_tail) 1367 continue; 1368 1369 /* Subtract one because dir_lines is zero-based, 1370 but the `end_line' and `start_line' members are 1371 one-based. */ 1372 for (i = section->end_line - 1; 1373 i >= section->start_line - 1; i--) 1374 { 1375 /* If an entry exists with the same name, 1376 and was not marked for deletion 1377 (which means it is for some other file), 1378 we are in trouble. */ 1379 if (dir_lines[i].start[0] == '*' 1380 && menu_line_equal (entry->text, entry->text_len, 1381 dir_lines[i].start, 1382 dir_lines[i].size) 1383 && !dir_lines[i].delete) 1384 fatal (_("menu item `%s' already exists, for file `%s'"), 1385 extract_menu_item_name (entry->text), 1386 extract_menu_file_name (dir_lines[i].start)); 1387 if (dir_lines[i].start[0] == '*' 1388 && menu_line_lessp (entry->text, entry->text_len, 1389 dir_lines[i].start, 1390 dir_lines[i].size)) 1391 add_at_line = i; 1392 } 1393 insert_entry_here (entry, add_at_line, 1394 dir_lines, n_entries_to_add); 1395 } 1396 } 1397 } 1398 1399 /* Mark the end of the Top node as the place to add any 1400 new sections that are needed. */ 1401 for (node = dir_nodes; node; node = node->next) 1402 if (node->name && strcmp (node->name, "Top") == 0) 1403 dir_lines[node->end_line].add_sections_before = 1; 1404 } 1405 1406 if (delete_flag && !something_deleted && !quiet_flag) 1407 warning (_("no entries found for `%s'; nothing deleted"), infile); 1408 1409 output_dirfile (opened_dirfilename, dir_nlines, dir_lines, n_entries_to_add, 1410 entries_to_add, input_sections, compression_program); 1411 1412 xexit (0); 1413 } 1414 1415 /* Divide the text at DATA (of SIZE bytes) into lines. 1416 Return a vector of struct line_data describing the lines. 1417 Store the length of that vector into *NLINESP. */ 1418 1419 struct line_data * 1420 findlines (data, size, nlinesp) 1421 char *data; 1422 int size; 1423 int *nlinesp; 1424 { 1425 int i; 1426 int lineflag = 1; 1427 int lines_allocated = 511; 1428 int filled = 0; 1429 struct line_data *lines 1430 = xmalloc ((lines_allocated + 1) * sizeof (struct line_data)); 1431 1432 for (i = 0; i < size; i++) 1433 { 1434 if (lineflag) 1435 { 1436 if (filled == lines_allocated) 1437 { 1438 /* try to keep things somewhat page-aligned */ 1439 lines_allocated = ((lines_allocated + 1) * 2) - 1; 1440 lines = xrealloc (lines, (lines_allocated + 1) 1441 * sizeof (struct line_data)); 1442 } 1443 lines[filled].start = &data[i]; 1444 lines[filled].add_entries_before = 0; 1445 lines[filled].add_sections_before = 0; 1446 lines[filled].delete = 0; 1447 if (filled > 0) 1448 lines[filled - 1].size 1449 = lines[filled].start - lines[filled - 1].start - 1; 1450 filled++; 1451 } 1452 lineflag = (data[i] == '\n'); 1453 } 1454 if (filled > 0) 1455 lines[filled - 1].size = &data[i] - lines[filled - 1].start - lineflag; 1456 1457 /* Do not leave garbage in the last element. */ 1458 lines[filled].start = NULL; 1459 lines[filled].add_entries_before = NULL; 1460 lines[filled].add_sections_before = 0; 1461 lines[filled].delete = 0; 1462 lines[filled].size = 0; 1463 1464 *nlinesp = filled; 1465 return lines; 1466 } 1467 1468 /* Compare the menu item names in LINE1 (line length LEN1) 1469 and LINE2 (line length LEN2). Return 1 if the item name 1470 in LINE1 is less, 0 otherwise. */ 1471 1472 int 1473 menu_line_lessp (line1, len1, line2, len2) 1474 char *line1; 1475 int len1; 1476 char *line2; 1477 int len2; 1478 { 1479 int minlen = (len1 < len2 ? len1 : len2); 1480 int i; 1481 1482 for (i = 0; i < minlen; i++) 1483 { 1484 /* If one item name is a prefix of the other, 1485 the former one is less. */ 1486 if (line1[i] == ':' && line2[i] != ':') 1487 return 1; 1488 if (line2[i] == ':' && line1[i] != ':') 1489 return 0; 1490 /* If they both continue and differ, one is less. */ 1491 if (line1[i] < line2[i]) 1492 return 1; 1493 if (line1[i] > line2[i]) 1494 return 0; 1495 } 1496 /* With a properly formatted dir file, 1497 we can only get here if the item names are equal. */ 1498 return 0; 1499 } 1500 1501 /* Compare the menu item names in LINE1 (line length LEN1) 1502 and LINE2 (line length LEN2). Return 1 if the item names are equal, 1503 0 otherwise. */ 1504 1505 int 1506 menu_line_equal (line1, len1, line2, len2) 1507 char *line1; 1508 int len1; 1509 char *line2; 1510 int len2; 1511 { 1512 int minlen = (len1 < len2 ? len1 : len2); 1513 int i; 1514 1515 for (i = 0; i < minlen; i++) 1516 { 1517 /* If both item names end here, they are equal. */ 1518 if (line1[i] == ':' && line2[i] == ':') 1519 return 1; 1520 /* If they both continue and differ, one is less. */ 1521 if (line1[i] != line2[i]) 1522 return 0; 1523 } 1524 /* With a properly formatted dir file, 1525 we can only get here if the item names are equal. */ 1526 return 1; 1527 } 1528 1529 /* This is the comparison function for qsort 1530 for a vector of pointers to struct spec_section. 1531 Compare the section names. */ 1532 1533 int 1534 compare_section_names (sec1, sec2) 1535 struct spec_section **sec1, **sec2; 1536 { 1537 char *name1 = (*sec1)->name; 1538 char *name2 = (*sec2)->name; 1539 return strcmp (name1, name2); 1540 } 1541 1542 /* This is the comparison function for qsort 1543 for a vector of pointers to struct spec_entry. 1544 Compare the entries' text. */ 1545 1546 int 1547 compare_entries_text (entry1, entry2) 1548 struct spec_entry **entry1, **entry2; 1549 { 1550 char *text1 = (*entry1)->text; 1551 char *text2 = (*entry2)->text; 1552 char *colon1 = strchr (text1, ':'); 1553 char *colon2 = strchr (text2, ':'); 1554 int len1, len2; 1555 1556 if (!colon1) 1557 len1 = strlen (text1); 1558 else 1559 len1 = colon1 - text1; 1560 if (!colon2) 1561 len2 = strlen (text2); 1562 else 1563 len2 = colon2 - text2; 1564 return strncmp (text1, text2, len1 <= len2 ? len1 : len2); 1565 } 1566 1567 /* Insert ENTRY into the add_entries_before vector 1568 for line number LINE_NUMBER of the dir file. 1569 DIR_LINES and N_ENTRIES carry information from like-named variables 1570 in main. */ 1571 1572 void 1573 insert_entry_here (entry, line_number, dir_lines, n_entries) 1574 struct spec_entry *entry; 1575 int line_number; 1576 struct line_data *dir_lines; 1577 int n_entries; 1578 { 1579 int i, j; 1580 1581 if (dir_lines[line_number].add_entries_before == 0) 1582 { 1583 dir_lines[line_number].add_entries_before 1584 = (struct spec_entry **) xmalloc (n_entries * sizeof (struct spec_entry *)); 1585 for (i = 0; i < n_entries; i++) 1586 dir_lines[line_number].add_entries_before[i] = 0; 1587 } 1588 1589 /* Find the place where this entry belongs. If there are already 1590 several entries to add before LINE_NUMBER, make sure they are in 1591 alphabetical order. */ 1592 for (i = 0; i < n_entries; i++) 1593 if (dir_lines[line_number].add_entries_before[i] == 0 1594 || menu_line_lessp (entry->text, strlen (entry->text), 1595 dir_lines[line_number].add_entries_before[i]->text, 1596 strlen (dir_lines[line_number].add_entries_before[i]->text))) 1597 break; 1598 1599 if (i == n_entries) 1600 abort (); 1601 1602 /* If we need to plug ENTRY into the middle of the 1603 ADD_ENTRIES_BEFORE array, move the entries which should be output 1604 after this one down one notch, before adding a new one. */ 1605 if (dir_lines[line_number].add_entries_before[i] != 0) 1606 for (j = n_entries - 1; j > i; j--) 1607 dir_lines[line_number].add_entries_before[j] 1608 = dir_lines[line_number].add_entries_before[j - 1]; 1609 1610 dir_lines[line_number].add_entries_before[i] = entry; 1611 } 1612