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