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