1 /* $NetBSD: index.c,v 1.6 2025/12/31 22:18:50 oster Exp $ */ 2 3 /* index.c -- indexing for Texinfo. 4 Id: index.c,v 1.17 2004/11/30 02:03:23 karl Exp 5 6 Copyright (C) 1998, 1999, 2002, 2003, 2004 Free Software Foundation, 7 Inc. 8 9 This program is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 2, or (at your option) 12 any later version. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program; if not, write to the Free Software Foundation, 21 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 22 23 #include "system.h" 24 #include "files.h" 25 #include "footnote.h" 26 #include "html.h" 27 #include "index.h" 28 #include "lang.h" 29 #include "macro.h" 30 #include "sectioning.h" 31 #include "toc.h" 32 #include "xml.h" 33 34 INDEX_ALIST **name_index_alist = NULL; 35 36 /* An array of pointers. Each one is for a different index. The 37 "synindex" command changes which array slot is pointed to by a 38 given "index". */ 39 INDEX_ELT **the_indices = NULL; 40 41 /* The number of defined indices. */ 42 int defined_indices = 0; 43 44 /* This is the order of the index. */ 45 int index_counter = 0; 46 47 /* Stuff for defining commands on the fly. */ 48 COMMAND **user_command_array = NULL; 49 int user_command_array_len = 0; 50 51 /* How to compare index entries for sorting. May be set to strcoll. */ 52 int (*index_compare_fn) (const char *a, const char *b) = strcasecmp; 53 54 /* Function to compare index entries for sorting. (Calls 55 `index_compare_fn' above.) */ 56 int index_element_compare (const void *element1, const void *element2); 57 58 /* Find which element in the known list of indices has this name. 60 Returns -1 if NAME isn't found. */ 61 static int 62 find_index_offset (char *name) 63 { 64 int i; 65 for (i = 0; i < defined_indices; i++) 66 if (name_index_alist[i] && STREQ (name, name_index_alist[i]->name)) 67 return i; 68 return -1; 69 } 70 71 /* Return a pointer to the entry of (name . index) for this name. 72 Return NULL if the index doesn't exist. */ 73 static INDEX_ALIST * 74 find_index (char *name) 75 { 76 int offset = find_index_offset (name); 77 if (offset > -1) 78 return name_index_alist[offset]; 79 else 80 return NULL; 81 } 82 83 /* User-defined commands, which happens only from user-defined indexes. 85 Used to initialize the builtin indices, too. */ 86 static void 87 define_user_command (char *name, COMMAND_FUNCTION (*proc), int needs_braces_p) 88 { 89 int slot = user_command_array_len; 90 user_command_array_len++; 91 92 if (!user_command_array) 93 user_command_array = xmalloc (1 * sizeof (COMMAND *)); 94 95 user_command_array = xrealloc (user_command_array, 96 (1 + user_command_array_len) * sizeof (COMMAND *)); 97 98 user_command_array[slot] = xmalloc (sizeof (COMMAND)); 99 user_command_array[slot]->name = xstrdup (name); 100 user_command_array[slot]->proc = proc; 101 user_command_array[slot]->argument_in_braces = needs_braces_p; 102 } 103 104 /* Please release me, let me go... */ 106 static void 107 free_index (INDEX_ELT *index) 108 { 109 INDEX_ELT *temp; 110 111 while ((temp = index)) 112 { 113 free (temp->entry); 114 free (temp->entry_text); 115 /* Do not free the node, because we already freed the tag table, 116 which freed all the node names. */ 117 /* free (temp->node); */ 118 index = index->next; 119 free (temp); 120 } 121 } 122 123 /* Flush an index by name. This will delete the list of entries that 124 would be written by a @printindex command for this index. */ 125 static void 126 undefindex (char *name) 127 { 128 int i; 129 int which = find_index_offset (name); 130 131 /* The index might have already been freed if this was the target of 132 an @synindex. */ 133 if (which < 0 || !name_index_alist[which]) 134 return; 135 136 i = name_index_alist[which]->read_index; 137 138 free_index (the_indices[i]); 139 the_indices[i] = NULL; 140 141 free (name_index_alist[which]->name); 142 free (name_index_alist[which]); 143 name_index_alist[which] = NULL; 144 } 145 146 /* Add the arguments to the current index command to the index NAME. */ 148 static void 149 index_add_arg (char *name) 150 { 151 int which; 152 char *index_entry; 153 INDEX_ALIST *tem; 154 155 tem = find_index (name); 156 157 which = tem ? tem->write_index : -1; 158 159 if (macro_expansion_output_stream && !executing_string) 160 append_to_expansion_output (input_text_offset + 1); 161 162 get_rest_of_line (0, &index_entry); 163 ignore_blank_line (); 164 165 if (macro_expansion_output_stream && !executing_string) 166 { 167 char *index_line = xmalloc (strlen (index_entry) + 2); 168 sprintf (index_line, "%s\n", index_entry); 169 me_execute_string_keep_state (index_line, NULL); 170 free (index_line); 171 } 172 173 if (which < 0) 174 { 175 line_error (_("Unknown index `%s'"), name); 176 free (index_entry); 177 } 178 else 179 { 180 INDEX_ELT *new = xmalloc (sizeof (INDEX_ELT)); 181 182 index_counter++; 183 184 /* Get output line number updated before doing anything. */ 185 if (!html && !xml) 186 flush_output (); 187 188 new->next = the_indices[which]; 189 new->entry = NULL; 190 new->entry_text = index_entry; 191 /* Since footnotes are handled at the very end of the document, 192 node name in the non-split HTML outputs always show the last 193 node. We artificially make it ``Footnotes''. */ 194 if (html && !splitting && already_outputting_pending_notes) 195 new->node = xstrdup (_("Footnotes")); 196 else 197 new->node = current_node ? current_node : xstrdup (""); 198 if (!html && !xml && no_headers) 199 { 200 new->section = current_sectioning_number (); 201 if (strlen (new->section) == 0) 202 new->section_name = current_sectioning_name (); 203 else 204 new->section_name = ""; 205 } 206 else 207 { 208 new->section = NULL; 209 new->section_name = NULL; 210 } 211 new->code = tem->code; 212 new->defining_line = line_number - 1; 213 new->output_line = no_headers ? output_line_number : node_line_number; 214 /* We need to make a copy since input_filename may point to 215 something that goes away, for example, inside a macro. 216 (see the findexerr test). */ 217 new->defining_file = xstrdup (input_filename); 218 219 if (html && splitting) 220 { 221 if (current_output_filename && *current_output_filename) 222 new->output_file = filename_part (current_output_filename); 223 else 224 new->output_file = xstrdup (""); 225 } 226 else 227 new->output_file = NULL; 228 229 new->entry_number = index_counter; 230 the_indices[which] = new; 231 232 #if 0 233 /* The index breaks if there are colons in the entry. 234 -- This is true, but it's too painful to force changing index 235 entries to use `colon', and too confusing for users. The real 236 fix is to change Info support to support arbitrary characters 237 in node names, and we're not ready to do that. --karl, 238 19mar02. */ 239 if (strchr (new->entry_text, ':')) 240 warning (_("Info cannot handle `:' in index entry `%s'"), 241 new->entry_text); 242 #endif 243 244 if (html) 245 { 246 /* Anchor. */ 247 int removed_empty_elt = 0; 248 249 /* We must put the anchor outside the <dl> and <ul> blocks. */ 250 if (rollback_empty_tag ("dl")) 251 removed_empty_elt = 1; 252 else if (rollback_empty_tag ("ul")) 253 removed_empty_elt = 2; 254 255 add_word ("<a name=\"index-"); 256 add_escaped_anchor_name (index_entry, 0); 257 add_word_args ("-%d\"></a>", index_counter); 258 259 if (removed_empty_elt == 1) 260 add_html_block_elt_args ("\n<dl>"); 261 else if (removed_empty_elt == 2) 262 add_html_block_elt_args ("\n<ul>"); 263 } 264 } 265 266 if (xml) 267 xml_insert_indexterm (index_entry, name); 268 } 269 270 /* The function which user defined index commands call. */ 271 static void 272 gen_index (int arg, int arg2, int arg3) 273 { 274 char *name = xstrdup (command); 275 if (strlen (name) >= strlen ("index")) 276 name[strlen (name) - strlen ("index")] = 0; 277 index_add_arg (name); 278 free (name); 279 } 280 281 /* Define an index known as NAME. We assign the slot number. 283 If CODE is nonzero, make this a code index. */ 284 static void 285 defindex (char *name, int code) 286 { 287 int i, slot; 288 289 /* If it already exists, flush it. */ 290 undefindex (name); 291 292 /* Try to find an empty slot. */ 293 slot = -1; 294 for (i = 0; i < defined_indices; i++) 295 if (!name_index_alist[i]) 296 { 297 slot = i; 298 break; 299 } 300 301 if (slot < 0) 302 { /* No such luck. Make space for another index. */ 303 slot = defined_indices; 304 defined_indices++; 305 306 name_index_alist = (INDEX_ALIST **) 307 xrealloc (name_index_alist, (1 + defined_indices) 308 * sizeof (INDEX_ALIST *)); 309 the_indices = (INDEX_ELT **) 310 xrealloc (the_indices, (1 + defined_indices) * sizeof (INDEX_ELT *)); 311 } 312 313 /* We have a slot. Start assigning. */ 314 name_index_alist[slot] = xmalloc (sizeof (INDEX_ALIST)); 315 name_index_alist[slot]->name = xstrdup (name); 316 name_index_alist[slot]->read_index = slot; 317 name_index_alist[slot]->write_index = slot; 318 name_index_alist[slot]->code = code; 319 320 the_indices[slot] = NULL; 321 } 322 323 /* Define an index NAME, implicitly @code if CODE is nonzero. */ 324 static void 325 top_defindex (char *name, int code) 326 { 327 char *temp; 328 329 temp = xmalloc (1 + strlen (name) + strlen ("index")); 330 sprintf (temp, "%sindex", name); 331 define_user_command (temp, gen_index, 0); 332 defindex (name, code); 333 free (temp); 334 } 335 336 /* Set up predefined indices. */ 338 void 339 init_indices (void) 340 { 341 int i; 342 343 /* Create the default data structures. */ 344 345 /* Initialize data space. */ 346 if (!the_indices) 347 { 348 the_indices = xmalloc ((1 + defined_indices) * sizeof (INDEX_ELT *)); 349 the_indices[defined_indices] = NULL; 350 351 name_index_alist = xmalloc ((1 + defined_indices) 352 * sizeof (INDEX_ALIST *)); 353 name_index_alist[defined_indices] = NULL; 354 } 355 356 /* If there were existing indices, get rid of them now. */ 357 for (i = 0; i < defined_indices; i++) 358 { 359 if (name_index_alist[i]) 360 { /* Suppose we're called with two input files, and the first 361 does a @synindex pg cp. Then, when we get here to start 362 the second file, the "pg" element won't get freed by 363 undefindex (because it's pointing to "cp"). So free it 364 here; otherwise, when we try to define the pg index again 365 just below, it will still point to cp. */ 366 undefindex (name_index_alist[i]->name); 367 368 /* undefindex sets all this to null in some cases. */ 369 if (name_index_alist[i]) 370 { 371 free (name_index_alist[i]->name); 372 free (name_index_alist[i]); 373 name_index_alist[i] = NULL; 374 } 375 } 376 } 377 378 /* Add the default indices. */ 379 top_defindex ("cp", 0); /* cp is the only non-code index. */ 380 top_defindex ("fn", 1); 381 top_defindex ("ky", 1); 382 top_defindex ("pg", 1); 383 top_defindex ("tp", 1); 384 top_defindex ("vr", 1); 385 } 386 387 /* Given an index name, return the offset in the_indices of this index, 388 or -1 if there is no such index. */ 389 static int 390 translate_index (char *name) 391 { 392 INDEX_ALIST *which = find_index (name); 393 394 if (which) 395 return which->read_index; 396 else 397 return -1; 398 } 399 400 /* Return the index list which belongs to NAME. */ 401 INDEX_ELT * 402 index_list (char *name) 403 { 404 int which = translate_index (name); 405 if (which < 0) 406 return (INDEX_ELT *) -1; 407 else 408 return the_indices[which]; 409 } 410 411 /* Define a new index command. Arg is name of index. */ 412 static void 413 gen_defindex (int code) 414 { 415 char *name; 416 get_rest_of_line (0, &name); 417 418 if (find_index (name)) 419 { 420 line_error (_("Index `%s' already exists"), name); 421 } 422 else 423 { 424 char *temp = xmalloc (strlen (name) + sizeof ("index")); 425 sprintf (temp, "%sindex", name); 426 define_user_command (temp, gen_index, 0); 427 defindex (name, code); 428 free (temp); 429 } 430 431 free (name); 432 } 433 434 void 435 cm_defindex (int arg, int arg2, int arg3) 436 { 437 gen_defindex (0); 438 } 439 440 void 441 cm_defcodeindex (int arg, int arg2, int arg3) 442 { 443 gen_defindex (1); 444 } 445 446 /* Expects 2 args, on the same line. Both are index abbreviations. 447 Make the first one be a synonym for the second one, i.e. make the 448 first one have the same index as the second one. */ 449 void 450 cm_synindex (int arg, int arg2, int arg3) 451 { 452 int source, target; 453 char *abbrev1, *abbrev2; 454 455 skip_whitespace (); 456 get_until_in_line (0, " ", &abbrev1); 457 target = find_index_offset (abbrev1); 458 skip_whitespace (); 459 get_until_in_line (0, " ", &abbrev2); 460 source = find_index_offset (abbrev2); 461 if (source < 0 || target < 0) 462 { 463 line_error (_("Unknown index `%s' and/or `%s' in @synindex"), 464 abbrev1, abbrev2); 465 } 466 else 467 { 468 if (xml && !docbook) 469 xml_synindex (abbrev1, abbrev2); 470 else 471 name_index_alist[target]->write_index 472 = name_index_alist[source]->write_index; 473 } 474 475 free (abbrev1); 476 free (abbrev2); 477 } 478 479 void 480 cm_pindex (int arg, int arg2, int arg3) /* Pinhead index. */ 481 { 482 index_add_arg ("pg"); 483 } 484 485 void 486 cm_vindex (int arg, int arg2, int arg3) /* Variable index. */ 487 { 488 index_add_arg ("vr"); 489 } 490 491 void 492 cm_kindex (int arg, int arg2, int arg3) /* Key index. */ 493 { 494 index_add_arg ("ky"); 495 } 496 497 void 498 cm_cindex (int arg, int arg2, int arg3) /* Concept index. */ 499 { 500 index_add_arg ("cp"); 501 } 502 503 void 504 cm_findex (int arg, int arg2, int arg3) /* Function index. */ 505 { 506 index_add_arg ("fn"); 507 } 508 509 void 510 cm_tindex (int arg, int arg2, int arg3) /* Data Type index. */ 511 { 512 index_add_arg ("tp"); 513 } 514 515 int 516 index_element_compare (const void *element1, const void *element2) 517 { 518 INDEX_ELT **elt1 = (INDEX_ELT **) element1; 519 INDEX_ELT **elt2 = (INDEX_ELT **) element2; 520 int ret = 0; 521 522 /* Find a stable sort order. */ 523 if (ret == 0) 524 ret = index_compare_fn ((*elt1)->entry, (*elt2)->entry); 525 if (ret == 0) 526 ret = strcmp ((*elt1)->defining_file, (*elt2)->defining_file); 527 if (ret == 0) 528 ret = strcmp ((*elt1)->node, (*elt2)->node); 529 if (ret == 0) { 530 if ((*elt1)->defining_line < (*elt2)->defining_line) 531 ret = -1; 532 else if ((*elt1)->defining_line > (*elt2)->defining_line) 533 ret = 1; 534 } 535 if (ret == 0) { 536 if ((*elt1)->entry_number < (*elt2)->entry_number) 537 ret = -1; 538 else if ((*elt1)->entry_number > (*elt2)->entry_number) 539 ret = 1; 540 } 541 if (ret == 0) { 542 abort (); 543 } 544 545 return ret; 546 } 547 548 /* Force all index entries to be unique. */ 549 static void 550 make_index_entries_unique (INDEX_ELT **array, int count) 551 { 552 int i, j; 553 INDEX_ELT **copy; 554 int counter = 1; 555 556 copy = xmalloc ((1 + count) * sizeof (INDEX_ELT *)); 557 558 for (i = 0, j = 0; i < count; i++) 559 { 560 if (i == (count - 1) 561 || array[i]->node != array[i + 1]->node 562 || !STREQ (array[i]->entry, array[i + 1]->entry)) 563 copy[j++] = array[i]; 564 else 565 { 566 free (array[i]->entry); 567 free (array[i]->entry_text); 568 free (array[i]); 569 } 570 } 571 copy[j] = NULL; 572 573 /* Now COPY contains only unique entries. Duplicated entries in the 574 original array have been freed. Replace the current array with 575 the copy, fixing the NEXT pointers. */ 576 for (i = 0; copy[i]; i++) 577 { 578 copy[i]->next = copy[i + 1]; 579 580 /* Fix entry names which are the same. They point to different nodes, 581 so we make the entry name unique. */ 582 if (copy[i+1] 583 && STREQ (copy[i]->entry, copy[i + 1]->entry) 584 && !html) 585 { 586 char *new_entry_name; 587 588 new_entry_name = xmalloc (10 + strlen (copy[i]->entry)); 589 sprintf (new_entry_name, "%s <%d>", copy[i]->entry, counter); 590 free (copy[i]->entry); 591 copy[i]->entry = new_entry_name; 592 counter++; 593 } 594 else 595 counter = 1; 596 597 array[i] = copy[i]; 598 } 599 array[i] = NULL; 600 601 /* Free the storage used only by COPY. */ 602 free (copy); 603 } 604 605 606 /* Sort the index passed in INDEX, returning an array of pointers to 607 elements. The array is terminated with a NULL pointer. */ 608 609 static INDEX_ELT ** 610 sort_index (INDEX_ELT *index) 611 { 612 INDEX_ELT **array; 613 INDEX_ELT *temp; 614 int count = 0; 615 int save_line_number = line_number; 616 char *save_input_filename = input_filename; 617 int save_html = html; 618 619 /* Pretend we are in non-HTML mode, for the purpose of getting the 620 expanded index entry that lacks any markup and other HTML escape 621 characters which could produce a wrong sort order. */ 622 /* fixme: html: this still causes some markup, such as non-ASCII 623 characters @AE{} etc., to sort incorrectly. */ 624 html = 0; 625 626 for (temp = index, count = 0; temp; temp = temp->next, count++) 627 ; 628 /* We have the length, now we can allocate an array. */ 629 array = xmalloc ((count + 1) * sizeof (INDEX_ELT *)); 630 631 for (temp = index, count = 0; temp; temp = temp->next, count++) 632 { 633 /* Allocate new memory for the return array, since parts of the 634 original INDEX get freed. Otherwise, if the document calls 635 @printindex twice on the same index, with duplicate entries, 636 we'll have garbage the second time. There are cleaner ways to 637 deal, but this will suffice for now. */ 638 array[count] = xmalloc (sizeof (INDEX_ELT)); 639 *(array[count]) = *(temp); /* struct assignment, hope it's ok */ 640 641 /* Adjust next pointers to use the new memory. */ 642 if (count > 0) 643 array[count-1]->next = array[count]; 644 645 /* Set line number and input filename to the source line for this 646 index entry, as this expansion finds any errors. */ 647 line_number = array[count]->defining_line; 648 input_filename = array[count]->defining_file; 649 650 /* If this particular entry should be printed as a "code" index, 651 then expand it as @code{entry}, i.e., as in fixed-width font. */ 652 array[count]->entry = expansion (temp->entry_text, array[count]->code); 653 } 654 array[count] = NULL; /* terminate the array. */ 655 656 line_number = save_line_number; 657 input_filename = save_input_filename; 658 html = save_html; 659 660 #ifdef HAVE_STRCOLL 661 /* This is not perfect. We should set (then restore) the locale to the 662 documentlanguage, so strcoll operates according to the document's 663 locale, not the user's. For now, I'm just going to assume that 664 those few new documents which use @documentlanguage will be 665 processed in the appropriate locale. In any case, don't use 666 strcoll in the C (aka POSIX) locale, that is the ASCII ordering. */ 667 if (language_code != en) 668 { 669 char *lang_env = getenv ("LANG"); 670 if (lang_env && !STREQ (lang_env, "C") && !STREQ (lang_env, "POSIX")) 671 index_compare_fn = strcoll; 672 } 673 #endif /* HAVE_STRCOLL */ 674 675 /* Sort the array. */ 676 qsort (array, count, sizeof (INDEX_ELT *), index_element_compare); 677 678 /* Remove duplicate entries. */ 679 make_index_entries_unique (array, count); 680 681 /* Replace the original index with the sorted one, in case the 682 document wants to print it again. If the index wasn't empty. */ 683 if (index) 684 *index = **array; 685 686 return array; 687 } 688 689 static void 690 insert_index_output_line_no (int line_number, int output_line_number_len) 691 { 692 int last_column; 693 int str_size = output_line_number_len + strlen (_("(line )")) 694 + sizeof (NULL); 695 char *out_line_no_str = (char *) xmalloc (str_size + 1); 696 697 /* Do not translate ``(line NNN)'' below for !no_headers case (Info output), 698 because it's something like the ``* Menu'' strings. For plaintext output 699 it should be translated though. */ 700 sprintf (out_line_no_str, 701 no_headers ? _("(line %*d)") : "(line %*d)", 702 output_line_number_len, line_number); 703 704 { 705 int i = output_paragraph_offset; 706 while (0 < i && output_paragraph[i-1] != '\n') 707 i--; 708 last_column = output_paragraph_offset - i; 709 } 710 711 if (last_column + strlen (out_line_no_str) > fill_column) 712 { 713 insert ('\n'); 714 last_column = 0; 715 } 716 717 while (last_column + strlen (out_line_no_str) < fill_column) 718 { 719 insert (' '); 720 last_column++; 721 } 722 723 insert_string (out_line_no_str); 724 insert ('\n'); 725 726 free (out_line_no_str); 727 } 728 729 /* Nonzero means that we are in the middle of printing an index. */ 730 int printing_index = 0; 731 732 /* Takes one arg, a short name of an index to print. 733 Outputs a menu of the sorted elements of the index. */ 734 void 735 cm_printindex (int arg, int arg2, int arg3) 736 { 737 char *index_name; 738 get_rest_of_line (0, &index_name); 739 740 /* get_rest_of_line increments the line number by one, 741 so to make warnings/errors point to the correct line, 742 we decrement the line_number again. */ 743 if (!handling_delayed_writes) 744 line_number--; 745 746 if (xml && !docbook) 747 { 748 xml_insert_element (PRINTINDEX, START); 749 insert_string (index_name); 750 xml_insert_element (PRINTINDEX, END); 751 } 752 else if (!handling_delayed_writes) 753 { 754 int command_len = sizeof ("@ ") + strlen (command) + strlen (index_name); 755 char *index_command = xmalloc (command_len + 1); 756 757 close_paragraph (); 758 if (docbook) 759 xml_begin_index (); 760 761 sprintf (index_command, "@%s %s", command, index_name); 762 register_delayed_write (index_command); 763 free (index_command); 764 } 765 else 766 { 767 int item; 768 INDEX_ELT *index; 769 INDEX_ELT *last_index = 0; 770 INDEX_ELT **array; 771 unsigned line_length; 772 char *line; 773 int saved_inhibit_paragraph_indentation = inhibit_paragraph_indentation; 774 int saved_filling_enabled = filling_enabled; 775 int saved_line_number = line_number; 776 char *saved_input_filename = input_filename; 777 unsigned output_line_number_len; 778 779 index = index_list (index_name); 780 if (index == (INDEX_ELT *)-1) 781 { 782 line_error (_("Unknown index `%s' in @printindex"), index_name); 783 free (index_name); 784 return; 785 } 786 787 /* Do this before sorting, so execute_string is in the good environment */ 788 if (xml && docbook) 789 xml_begin_index (); 790 791 /* Do this before sorting, so execute_string in index_element_compare 792 will give the same results as when we actually print. */ 793 printing_index = 1; 794 filling_enabled = 0; 795 inhibit_paragraph_indentation = 1; 796 xml_sort_index = 1; 797 array = sort_index (index); 798 xml_sort_index = 0; 799 close_paragraph (); 800 if (html) 801 add_html_block_elt_args ("<ul class=\"index-%s\" compact>", 802 index_name); 803 else if (!no_headers && !docbook) 804 { /* Info. Add magic cookie for info readers (to treat this 805 menu differently), and the usual start-of-menu. */ 806 add_char ('\0'); 807 add_word ("\010[index"); 808 add_char ('\0'); 809 add_word ("\010]\n"); 810 add_word ("* Menu:\n\n"); 811 } 812 813 me_inhibit_expansion++; 814 815 /* This will probably be enough. */ 816 line_length = 100; 817 line = xmalloc (line_length); 818 819 { 820 char *max_output_line_number = (char *) xmalloc (25 * sizeof (char)); 821 822 if (no_headers) 823 sprintf (max_output_line_number, "%d", output_line_number); 824 else 825 { 826 INDEX_ELT *tmp_entry = index; 827 unsigned tmp = 0; 828 for (tmp_entry = index; tmp_entry; tmp_entry = tmp_entry->next) 829 tmp = tmp_entry->output_line > tmp ? tmp_entry->output_line : tmp; 830 sprintf (max_output_line_number, "%d", tmp); 831 } 832 833 output_line_number_len = strlen (max_output_line_number); 834 free (max_output_line_number); 835 } 836 837 for (item = 0; (index = array[item]); item++) 838 { 839 /* A pathological document might have an index entry outside of any 840 node. Don't crash; try using the section name instead. */ 841 char *index_node = index->node; 842 843 line_number = index->defining_line; 844 input_filename = index->defining_file; 845 846 if ((!index_node || !*index_node) && html) 847 index_node = toc_find_section_of_node (index_node); 848 849 if (!index_node || !*index_node) 850 { 851 line_error (_("Entry for index `%s' outside of any node"), 852 index_name); 853 if (html || !no_headers) 854 index_node = (char *) _("(outside of any node)"); 855 } 856 857 if (html) 858 { 859 /* For HTML, we need to expand and HTML-escape the 860 original entry text, at the same time. Consider 861 @cindex J@"urgen. We want Jüurgen. We can't 862 expand and then escape since we'll end up with 863 J&uuml;rgen. We can't escape and then expand 864 because then `expansion' will see J@"urgen, and 865 @"urgen is not a command. */ 866 char *html_entry = 867 maybe_escaped_expansion (index->entry_text, index->code, 1); 868 869 add_html_block_elt_args ("\n<li><a href=\"%s#index-", 870 (splitting && index->output_file) ? index->output_file : ""); 871 add_escaped_anchor_name (index->entry_text, 0); 872 add_word_args ("-%d\">%s</a>: ", index->entry_number, 873 html_entry); 874 free (html_entry); 875 876 add_word ("<a href=\""); 877 if (index->node && *index->node) 878 { 879 /* Ensure any non-macros in the node name are expanded. */ 880 char *expanded_index; 881 882 in_fixed_width_font++; 883 expanded_index = expansion (index_node, 0); 884 in_fixed_width_font--; 885 add_anchor_name (expanded_index, 1); 886 expanded_index = escape_string (expanded_index); 887 add_word_args ("\">%s</a>", expanded_index); 888 free (expanded_index); 889 } 890 else if (STREQ (index_node, _("(outside of any node)"))) 891 { 892 add_anchor_name (index_node, 1); 893 add_word_args ("\">%s</a>", index_node); 894 } 895 else 896 /* If we use the section instead of the (missing) node, then 897 index_node already includes all we need except the #. */ 898 add_word_args ("#%s</a>", index_node); 899 900 add_html_block_elt ("</li>"); 901 } 902 else if (xml && docbook) 903 { 904 /* In the DocBook case, the expanded index entry is not 905 good for us, since it was expanded for non-DocBook mode 906 inside sort_index. So we send the original entry text 907 to be used with execute_string. */ 908 xml_insert_indexentry (index->entry_text, index_node); 909 } 910 else 911 { 912 unsigned new_length = strlen (index->entry); 913 914 if (new_length < 50) /* minimum length used below */ 915 new_length = 50; 916 new_length += strlen (index_node) + 7; /* * : .\n\0 */ 917 918 if (new_length > line_length) 919 { 920 line_length = new_length; 921 line = xrealloc (line, line_length); 922 } 923 /* Print the entry, nicely formatted. We've already 924 expanded any commands in index->entry, including any 925 implicit @code. Thus, can't call execute_string, since 926 @@ has turned into @. */ 927 if (!no_headers) 928 { 929 sprintf (line, "* %-37s ", index->entry); 930 line[2 + strlen (index->entry)] = ':'; 931 insert_string (line); 932 /* Make sure any non-macros in the node name are expanded. */ 933 in_fixed_width_font++; 934 execute_string ("%s. ", index_node); 935 insert_index_output_line_no (index->output_line, 936 output_line_number_len); 937 in_fixed_width_font--; 938 } 939 else 940 { 941 /* With --no-headers, the @node lines are gone, so 942 there's little sense in referring to them in the 943 index. Instead, output the number or name of the 944 section that corresponds to that node. */ 945 sprintf (line, "%-*s ", number_sections ? 46 : 1, index->entry); 946 line[strlen (index->entry)] = ':'; 947 insert_string (line); 948 949 if (strlen (index->section) > 0) 950 { /* We got your number. */ 951 insert_string ((char *) _("See ")); 952 insert_string (index->section); 953 } 954 else 955 { /* Sigh, index in an @unnumbered. :-\ */ 956 insert_string ("\n "); 957 insert_string ((char *) _("See ")); 958 insert_string ("``"); 959 insert_string (expansion (index->section_name, 0)); 960 insert_string ("''"); 961 } 962 963 insert_string (". "); 964 insert_index_output_line_no (index->output_line, 965 output_line_number_len); 966 } 967 } 968 969 /* Prevent `output_paragraph' from growing to the size of the 970 whole index. */ 971 flush_output (); 972 last_index = index; 973 } 974 975 free (line); 976 977 me_inhibit_expansion--; 978 printing_index = 0; 979 980 close_single_paragraph (); 981 filling_enabled = saved_filling_enabled; 982 inhibit_paragraph_indentation = saved_inhibit_paragraph_indentation; 983 input_filename = saved_input_filename; 984 line_number = saved_line_number; 985 986 if (html) 987 add_html_block_elt ("</ul>"); 988 else if (xml && docbook) 989 xml_end_index (); 990 } 991 992 free (index_name); 993 /* Re-increment the line number, because get_rest_of_line 994 left us looking at the next line after the command. */ 995 line_number++; 996 } 997