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