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