156160Sru/* index.c -- indexing for Texinfo. 2146515Sru $Id: index.c,v 1.17 2004/11/30 02:03:23 karl Exp $ 356160Sru 4146515Sru Copyright (C) 1998, 1999, 2002, 2003, 2004 Free Software Foundation, 5146515Sru Inc. 656160Sru 756160Sru This program is free software; you can redistribute it and/or modify 856160Sru it under the terms of the GNU General Public License as published by 956160Sru the Free Software Foundation; either version 2, or (at your option) 1056160Sru any later version. 1156160Sru 1256160Sru This program is distributed in the hope that it will be useful, 1356160Sru but WITHOUT ANY WARRANTY; without even the implied warranty of 1456160Sru MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1556160Sru GNU General Public License for more details. 1656160Sru 1756160Sru You should have received a copy of the GNU General Public License 1856160Sru along with this program; if not, write to the Free Software Foundation, 1956160Sru Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 2056160Sru 2156160Sru#include "system.h" 22146515Sru#include "files.h" 23146515Sru#include "footnote.h" 24146515Sru#include "html.h" 2556160Sru#include "index.h" 2656160Sru#include "lang.h" 2756160Sru#include "macro.h" 28146515Sru#include "sectioning.h" 2956160Sru#include "toc.h" 3093139Sru#include "xml.h" 3156160Sru 3256160SruINDEX_ALIST **name_index_alist = NULL; 3356160Sru 3456160Sru/* An array of pointers. Each one is for a different index. The 3556160Sru "synindex" command changes which array slot is pointed to by a 3656160Sru given "index". */ 3756160SruINDEX_ELT **the_indices = NULL; 3856160Sru 3956160Sru/* The number of defined indices. */ 4056160Sruint defined_indices = 0; 4156160Sru 42146515Sru/* This is the order of the index. */ 43146515Sruint index_counter = 0; 44146515Sru 4556160Sru/* Stuff for defining commands on the fly. */ 4656160SruCOMMAND **user_command_array = NULL; 4756160Sruint user_command_array_len = 0; 4856160Sru 4956160Sru/* How to compare index entries for sorting. May be set to strcoll. */ 50146515Sruint (*index_compare_fn) (const char *a, const char *b) = strcasecmp; 51146515Sru 52146515Sru/* Function to compare index entries for sorting. (Calls 53146515Sru `index_compare_fn' above.) */ 54146515Sruint index_element_compare (const void *element1, const void *element2); 5556160Sru 5656160Sru/* Find which element in the known list of indices has this name. 5756160Sru Returns -1 if NAME isn't found. */ 5856160Srustatic int 59146515Srufind_index_offset (char *name) 6056160Sru{ 6156160Sru int i; 6256160Sru for (i = 0; i < defined_indices; i++) 6356160Sru if (name_index_alist[i] && STREQ (name, name_index_alist[i]->name)) 6456160Sru return i; 6556160Sru return -1; 6656160Sru} 6756160Sru 6856160Sru/* Return a pointer to the entry of (name . index) for this name. 6956160Sru Return NULL if the index doesn't exist. */ 70146515Srustatic INDEX_ALIST * 71146515Srufind_index (char *name) 7256160Sru{ 7356160Sru int offset = find_index_offset (name); 7456160Sru if (offset > -1) 7556160Sru return name_index_alist[offset]; 7656160Sru else 7756160Sru return NULL; 7856160Sru} 7956160Sru 8056160Sru/* User-defined commands, which happens only from user-defined indexes. 8156160Sru Used to initialize the builtin indices, too. */ 82146515Srustatic void 83146515Srudefine_user_command (char *name, COMMAND_FUNCTION (*proc), int needs_braces_p) 8456160Sru{ 8556160Sru int slot = user_command_array_len; 8656160Sru user_command_array_len++; 8756160Sru 8856160Sru if (!user_command_array) 8956160Sru user_command_array = xmalloc (1 * sizeof (COMMAND *)); 9056160Sru 9156160Sru user_command_array = xrealloc (user_command_array, 9256160Sru (1 + user_command_array_len) * sizeof (COMMAND *)); 9356160Sru 9456160Sru user_command_array[slot] = xmalloc (sizeof (COMMAND)); 9556160Sru user_command_array[slot]->name = xstrdup (name); 9656160Sru user_command_array[slot]->proc = proc; 9756160Sru user_command_array[slot]->argument_in_braces = needs_braces_p; 9856160Sru} 9956160Sru 10056160Sru/* Please release me, let me go... */ 10156160Srustatic void 102146515Srufree_index (INDEX_ELT *index) 10356160Sru{ 10456160Sru INDEX_ELT *temp; 10556160Sru 10656160Sru while ((temp = index)) 10756160Sru { 10856160Sru free (temp->entry); 10956160Sru free (temp->entry_text); 11056160Sru /* Do not free the node, because we already freed the tag table, 11156160Sru which freed all the node names. */ 11256160Sru /* free (temp->node); */ 11356160Sru index = index->next; 11456160Sru free (temp); 11556160Sru } 11656160Sru} 11756160Sru 11856160Sru/* Flush an index by name. This will delete the list of entries that 11956160Sru would be written by a @printindex command for this index. */ 12056160Srustatic void 121146515Sruundefindex (char *name) 12256160Sru{ 12356160Sru int i; 12456160Sru int which = find_index_offset (name); 12556160Sru 12656160Sru /* The index might have already been freed if this was the target of 12756160Sru an @synindex. */ 12856160Sru if (which < 0 || !name_index_alist[which]) 12956160Sru return; 13056160Sru 13156160Sru i = name_index_alist[which]->read_index; 13256160Sru 13356160Sru free_index (the_indices[i]); 13456160Sru the_indices[i] = NULL; 13556160Sru 13656160Sru free (name_index_alist[which]->name); 13756160Sru free (name_index_alist[which]); 13856160Sru name_index_alist[which] = NULL; 13956160Sru} 14056160Sru 141146515Sru/* Add the arguments to the current index command to the index NAME. */ 14256160Srustatic void 143146515Sruindex_add_arg (char *name) 14456160Sru{ 14556160Sru int which; 14656160Sru char *index_entry; 14756160Sru INDEX_ALIST *tem; 14856160Sru 14956160Sru tem = find_index (name); 15056160Sru 15156160Sru which = tem ? tem->write_index : -1; 15256160Sru 15356160Sru if (macro_expansion_output_stream && !executing_string) 15456160Sru append_to_expansion_output (input_text_offset + 1); 15556160Sru 15656160Sru get_rest_of_line (0, &index_entry); 15756160Sru ignore_blank_line (); 15856160Sru 15956160Sru if (macro_expansion_output_stream && !executing_string) 16056160Sru { 16156160Sru char *index_line = xmalloc (strlen (index_entry) + 2); 16256160Sru sprintf (index_line, "%s\n", index_entry); 16356160Sru me_execute_string_keep_state (index_line, NULL); 16456160Sru free (index_line); 16556160Sru } 16656160Sru 16756160Sru if (which < 0) 16856160Sru { 16956160Sru line_error (_("Unknown index `%s'"), name); 17056160Sru free (index_entry); 17156160Sru } 17256160Sru else 17356160Sru { 17456160Sru INDEX_ELT *new = xmalloc (sizeof (INDEX_ELT)); 175146515Sru 176146515Sru index_counter++; 177146515Sru 178146515Sru /* Get output line number updated before doing anything. */ 179146515Sru if (!html && !xml) 180146515Sru flush_output (); 181146515Sru 18256160Sru new->next = the_indices[which]; 183146515Sru new->entry = NULL; 18456160Sru new->entry_text = index_entry; 185146515Sru /* Since footnotes are handled at the very end of the document, 186146515Sru node name in the non-split HTML outputs always show the last 187146515Sru node. We artificially make it ``Footnotes''. */ 188146515Sru if (html && !splitting && already_outputting_pending_notes) 189146515Sru new->node = xstrdup (_("Footnotes")); 190146515Sru else 191146515Sru new->node = current_node ? current_node : xstrdup (""); 192146515Sru if (!html && !xml && no_headers) 193146515Sru { 194146515Sru new->section = current_sectioning_number (); 195146515Sru if (strlen (new->section) == 0) 196146515Sru new->section_name = current_sectioning_name (); 197146515Sru else 198146515Sru new->section_name = ""; 199146515Sru } 200146515Sru else 201146515Sru { 202146515Sru new->section = NULL; 203146515Sru new->section_name = NULL; 204146515Sru } 20556160Sru new->code = tem->code; 20656160Sru new->defining_line = line_number - 1; 207146515Sru new->output_line = no_headers ? output_line_number : node_line_number; 20856160Sru /* We need to make a copy since input_filename may point to 20956160Sru something that goes away, for example, inside a macro. 21056160Sru (see the findexerr test). */ 21156160Sru new->defining_file = xstrdup (input_filename); 212146515Sru 213146515Sru if (html && splitting) 214146515Sru { 215146515Sru if (current_output_filename && *current_output_filename) 216146515Sru new->output_file = filename_part (current_output_filename); 217146515Sru else 218146515Sru new->output_file = xstrdup (""); 219146515Sru } 220146515Sru else 221146515Sru new->output_file = NULL; 222146515Sru 223146515Sru new->entry_number = index_counter; 22456160Sru the_indices[which] = new; 225146515Sru 226100513Sru#if 0 227100513Sru /* The index breaks if there are colons in the entry. 228100513Sru -- This is true, but it's too painful to force changing index 229100513Sru entries to use `colon', and too confusing for users. The real 230100513Sru fix is to change Info support to support arbitrary characters 231100513Sru in node names, and we're not ready to do that. --karl, 232100513Sru 19mar02. */ 23393139Sru if (strchr (new->entry_text, ':')) 23493139Sru warning (_("Info cannot handle `:' in index entry `%s'"), 23593139Sru new->entry_text); 236100513Sru#endif 237146515Sru 238146515Sru if (html) 239146515Sru { 240146515Sru /* Anchor. */ 241146515Sru int removed_empty_elt = 0; 242146515Sru 243146515Sru /* We must put the anchor outside the <dl> and <ul> blocks. */ 244146515Sru if (rollback_empty_tag ("dl")) 245146515Sru removed_empty_elt = 1; 246146515Sru else if (rollback_empty_tag ("ul")) 247146515Sru removed_empty_elt = 2; 248146515Sru 249146515Sru add_word ("<a name=\"index-"); 250146515Sru add_escaped_anchor_name (index_entry, 0); 251146515Sru add_word_args ("-%d\"></a>", index_counter); 252146515Sru 253146515Sru if (removed_empty_elt == 1) 254146515Sru add_html_block_elt_args ("\n<dl>"); 255146515Sru else if (removed_empty_elt == 2) 256146515Sru add_html_block_elt_args ("\n<ul>"); 257146515Sru } 25856160Sru } 259146515Sru 26093139Sru if (xml) 26193139Sru xml_insert_indexterm (index_entry, name); 26256160Sru} 26356160Sru 26456160Sru/* The function which user defined index commands call. */ 26556160Srustatic void 266146515Srugen_index (void) 26756160Sru{ 26856160Sru char *name = xstrdup (command); 26956160Sru if (strlen (name) >= strlen ("index")) 27056160Sru name[strlen (name) - strlen ("index")] = 0; 27156160Sru index_add_arg (name); 27256160Sru free (name); 27356160Sru} 27456160Sru 27556160Sru/* Define an index known as NAME. We assign the slot number. 27656160Sru If CODE is nonzero, make this a code index. */ 27756160Srustatic void 278146515Srudefindex (char *name, int code) 27956160Sru{ 28056160Sru int i, slot; 28156160Sru 28256160Sru /* If it already exists, flush it. */ 28356160Sru undefindex (name); 28456160Sru 28556160Sru /* Try to find an empty slot. */ 28656160Sru slot = -1; 28756160Sru for (i = 0; i < defined_indices; i++) 28856160Sru if (!name_index_alist[i]) 28956160Sru { 29056160Sru slot = i; 29156160Sru break; 29256160Sru } 29356160Sru 29456160Sru if (slot < 0) 29556160Sru { /* No such luck. Make space for another index. */ 29656160Sru slot = defined_indices; 29756160Sru defined_indices++; 29856160Sru 29956160Sru name_index_alist = (INDEX_ALIST **) 30056160Sru xrealloc (name_index_alist, (1 + defined_indices) 30156160Sru * sizeof (INDEX_ALIST *)); 30256160Sru the_indices = (INDEX_ELT **) 30356160Sru xrealloc (the_indices, (1 + defined_indices) * sizeof (INDEX_ELT *)); 30456160Sru } 30556160Sru 30656160Sru /* We have a slot. Start assigning. */ 30756160Sru name_index_alist[slot] = xmalloc (sizeof (INDEX_ALIST)); 30856160Sru name_index_alist[slot]->name = xstrdup (name); 30956160Sru name_index_alist[slot]->read_index = slot; 31056160Sru name_index_alist[slot]->write_index = slot; 31156160Sru name_index_alist[slot]->code = code; 31256160Sru 31356160Sru the_indices[slot] = NULL; 31456160Sru} 31556160Sru 31656160Sru/* Define an index NAME, implicitly @code if CODE is nonzero. */ 31756160Srustatic void 318146515Srutop_defindex (char *name, int code) 31956160Sru{ 32056160Sru char *temp; 32156160Sru 32256160Sru temp = xmalloc (1 + strlen (name) + strlen ("index")); 32356160Sru sprintf (temp, "%sindex", name); 32456160Sru define_user_command (temp, gen_index, 0); 32556160Sru defindex (name, code); 32656160Sru free (temp); 32756160Sru} 32856160Sru 32956160Sru/* Set up predefined indices. */ 33056160Sruvoid 331146515Sruinit_indices (void) 33256160Sru{ 33356160Sru int i; 33456160Sru 33556160Sru /* Create the default data structures. */ 33656160Sru 33756160Sru /* Initialize data space. */ 33856160Sru if (!the_indices) 33956160Sru { 34056160Sru the_indices = xmalloc ((1 + defined_indices) * sizeof (INDEX_ELT *)); 34156160Sru the_indices[defined_indices] = NULL; 34256160Sru 34356160Sru name_index_alist = xmalloc ((1 + defined_indices) 34456160Sru * sizeof (INDEX_ALIST *)); 34556160Sru name_index_alist[defined_indices] = NULL; 34656160Sru } 34756160Sru 34856160Sru /* If there were existing indices, get rid of them now. */ 34956160Sru for (i = 0; i < defined_indices; i++) 35056160Sru { 35156160Sru if (name_index_alist[i]) 35256160Sru { /* Suppose we're called with two input files, and the first 35356160Sru does a @synindex pg cp. Then, when we get here to start 35456160Sru the second file, the "pg" element won't get freed by 35556160Sru undefindex (because it's pointing to "cp"). So free it 35656160Sru here; otherwise, when we try to define the pg index again 35756160Sru just below, it will still point to cp. */ 358114472Sru undefindex (name_index_alist[i]->name); 359116525Sru 360116525Sru /* undefindex sets all this to null in some cases. */ 361116525Sru if (name_index_alist[i]) 362116525Sru { 363116525Sru free (name_index_alist[i]->name); 364116525Sru free (name_index_alist[i]); 365116525Sru name_index_alist[i] = NULL; 366116525Sru } 36756160Sru } 36856160Sru } 36956160Sru 37056160Sru /* Add the default indices. */ 37156160Sru top_defindex ("cp", 0); /* cp is the only non-code index. */ 37256160Sru top_defindex ("fn", 1); 37356160Sru top_defindex ("ky", 1); 37456160Sru top_defindex ("pg", 1); 37556160Sru top_defindex ("tp", 1); 37656160Sru top_defindex ("vr", 1); 37756160Sru} 37856160Sru 37956160Sru/* Given an index name, return the offset in the_indices of this index, 38056160Sru or -1 if there is no such index. */ 381146515Srustatic int 382146515Srutranslate_index (char *name) 38356160Sru{ 38456160Sru INDEX_ALIST *which = find_index (name); 38556160Sru 38656160Sru if (which) 38756160Sru return which->read_index; 38856160Sru else 38956160Sru return -1; 39056160Sru} 39156160Sru 39256160Sru/* Return the index list which belongs to NAME. */ 39356160SruINDEX_ELT * 394146515Sruindex_list (char *name) 39556160Sru{ 39656160Sru int which = translate_index (name); 39756160Sru if (which < 0) 39856160Sru return (INDEX_ELT *) -1; 39956160Sru else 40056160Sru return the_indices[which]; 40156160Sru} 40256160Sru 40356160Sru/* Define a new index command. Arg is name of index. */ 40456160Srustatic void 405146515Srugen_defindex (int code) 40656160Sru{ 40756160Sru char *name; 40856160Sru get_rest_of_line (0, &name); 40956160Sru 41056160Sru if (find_index (name)) 41156160Sru { 41256160Sru line_error (_("Index `%s' already exists"), name); 41356160Sru } 41456160Sru else 41556160Sru { 41656160Sru char *temp = xmalloc (strlen (name) + sizeof ("index")); 41756160Sru sprintf (temp, "%sindex", name); 41856160Sru define_user_command (temp, gen_index, 0); 41956160Sru defindex (name, code); 42056160Sru free (temp); 42156160Sru } 42256160Sru 42356160Sru free (name); 42456160Sru} 42556160Sru 42656160Sruvoid 427146515Srucm_defindex (void) 42856160Sru{ 42956160Sru gen_defindex (0); 43056160Sru} 43156160Sru 43256160Sruvoid 433146515Srucm_defcodeindex (void) 43456160Sru{ 43556160Sru gen_defindex (1); 43656160Sru} 43756160Sru 43856160Sru/* Expects 2 args, on the same line. Both are index abbreviations. 43956160Sru Make the first one be a synonym for the second one, i.e. make the 44056160Sru first one have the same index as the second one. */ 44156160Sruvoid 442146515Srucm_synindex (void) 44356160Sru{ 44456160Sru int source, target; 44556160Sru char *abbrev1, *abbrev2; 44656160Sru 44756160Sru skip_whitespace (); 44856160Sru get_until_in_line (0, " ", &abbrev1); 44956160Sru target = find_index_offset (abbrev1); 45056160Sru skip_whitespace (); 45156160Sru get_until_in_line (0, " ", &abbrev2); 45256160Sru source = find_index_offset (abbrev2); 45356160Sru if (source < 0 || target < 0) 45456160Sru { 45556160Sru line_error (_("Unknown index `%s' and/or `%s' in @synindex"), 45656160Sru abbrev1, abbrev2); 45756160Sru } 45856160Sru else 45956160Sru { 460146515Sru if (xml && !docbook) 461146515Sru xml_synindex (abbrev1, abbrev2); 462146515Sru else 463146515Sru name_index_alist[target]->write_index 464146515Sru = name_index_alist[source]->write_index; 46556160Sru } 46656160Sru 46756160Sru free (abbrev1); 46856160Sru free (abbrev2); 46956160Sru} 47056160Sru 47156160Sruvoid 472146515Srucm_pindex (void) /* Pinhead index. */ 47356160Sru{ 47456160Sru index_add_arg ("pg"); 47556160Sru} 47656160Sru 47756160Sruvoid 478146515Srucm_vindex (void) /* Variable index. */ 47956160Sru{ 48056160Sru index_add_arg ("vr"); 48156160Sru} 48256160Sru 48356160Sruvoid 484146515Srucm_kindex (void) /* Key index. */ 48556160Sru{ 48656160Sru index_add_arg ("ky"); 48756160Sru} 48856160Sru 48956160Sruvoid 490146515Srucm_cindex (void) /* Concept index. */ 49156160Sru{ 49256160Sru index_add_arg ("cp"); 49356160Sru} 49456160Sru 49556160Sruvoid 496146515Srucm_findex (void) /* Function index. */ 49756160Sru{ 49856160Sru index_add_arg ("fn"); 49956160Sru} 50056160Sru 50156160Sruvoid 502146515Srucm_tindex (void) /* Data Type index. */ 50356160Sru{ 50456160Sru index_add_arg ("tp"); 50556160Sru} 50656160Sru 50756160Sruint 508146515Sruindex_element_compare (const void *element1, const void *element2) 50956160Sru{ 510146515Sru INDEX_ELT **elt1 = (INDEX_ELT **) element1; 511146515Sru INDEX_ELT **elt2 = (INDEX_ELT **) element2; 512146515Sru 513146515Sru return index_compare_fn ((*elt1)->entry, (*elt2)->entry); 51456160Sru} 51556160Sru 51656160Sru/* Force all index entries to be unique. */ 517146515Srustatic void 518146515Srumake_index_entries_unique (INDEX_ELT **array, int count) 51956160Sru{ 52056160Sru int i, j; 52156160Sru INDEX_ELT **copy; 52256160Sru int counter = 1; 52356160Sru 52456160Sru copy = xmalloc ((1 + count) * sizeof (INDEX_ELT *)); 52556160Sru 52656160Sru for (i = 0, j = 0; i < count; i++) 52756160Sru { 52856160Sru if (i == (count - 1) 52956160Sru || array[i]->node != array[i + 1]->node 53056160Sru || !STREQ (array[i]->entry, array[i + 1]->entry)) 53156160Sru copy[j++] = array[i]; 53256160Sru else 53356160Sru { 53456160Sru free (array[i]->entry); 53556160Sru free (array[i]->entry_text); 53656160Sru free (array[i]); 53756160Sru } 53856160Sru } 53956160Sru copy[j] = NULL; 54056160Sru 54156160Sru /* Now COPY contains only unique entries. Duplicated entries in the 54256160Sru original array have been freed. Replace the current array with 54356160Sru the copy, fixing the NEXT pointers. */ 54456160Sru for (i = 0; copy[i]; i++) 54556160Sru { 54656160Sru copy[i]->next = copy[i + 1]; 54756160Sru 54856160Sru /* Fix entry names which are the same. They point to different nodes, 54956160Sru so we make the entry name unique. */ 55056160Sru if (copy[i+1] 55156160Sru && STREQ (copy[i]->entry, copy[i + 1]->entry) 55256160Sru && !html) 55356160Sru { 55456160Sru char *new_entry_name; 55556160Sru 55656160Sru new_entry_name = xmalloc (10 + strlen (copy[i]->entry)); 55756160Sru sprintf (new_entry_name, "%s <%d>", copy[i]->entry, counter); 55856160Sru free (copy[i]->entry); 55956160Sru copy[i]->entry = new_entry_name; 56056160Sru counter++; 56156160Sru } 56256160Sru else 56356160Sru counter = 1; 56456160Sru 56556160Sru array[i] = copy[i]; 56656160Sru } 56756160Sru array[i] = NULL; 56856160Sru 56956160Sru /* Free the storage used only by COPY. */ 57056160Sru free (copy); 57156160Sru} 57256160Sru 573116525Sru 574116525Sru/* Sort the index passed in INDEX, returning an array of pointers to 575116525Sru elements. The array is terminated with a NULL pointer. */ 576116525Sru 577146515Srustatic INDEX_ELT ** 578146515Srusort_index (INDEX_ELT *index) 57956160Sru{ 58056160Sru INDEX_ELT **array; 581116525Sru INDEX_ELT *temp; 58256160Sru int count = 0; 58356160Sru int save_line_number = line_number; 58456160Sru char *save_input_filename = input_filename; 58556160Sru int save_html = html; 58656160Sru 58756160Sru /* Pretend we are in non-HTML mode, for the purpose of getting the 58856160Sru expanded index entry that lacks any markup and other HTML escape 58956160Sru characters which could produce a wrong sort order. */ 59056160Sru /* fixme: html: this still causes some markup, such as non-ASCII 59156160Sru characters @AE{} etc., to sort incorrectly. */ 59256160Sru html = 0; 59356160Sru 594116525Sru for (temp = index, count = 0; temp; temp = temp->next, count++) 595116525Sru ; 596116525Sru /* We have the length, now we can allocate an array. */ 59756160Sru array = xmalloc ((count + 1) * sizeof (INDEX_ELT *)); 59856160Sru 599116525Sru for (temp = index, count = 0; temp; temp = temp->next, count++) 60056160Sru { 601116525Sru /* Allocate new memory for the return array, since parts of the 602116525Sru original INDEX get freed. Otherwise, if the document calls 603116525Sru @printindex twice on the same index, with duplicate entries, 604116525Sru we'll have garbage the second time. There are cleaner ways to 605116525Sru deal, but this will suffice for now. */ 606116525Sru array[count] = xmalloc (sizeof (INDEX_ELT)); 607116525Sru *(array[count]) = *(temp); /* struct assignment, hope it's ok */ 60856160Sru 609116525Sru /* Adjust next pointers to use the new memory. */ 610116525Sru if (count > 0) 611116525Sru array[count-1]->next = array[count]; 612116525Sru 61356160Sru /* Set line number and input filename to the source line for this 61456160Sru index entry, as this expansion finds any errors. */ 615116525Sru line_number = array[count]->defining_line; 616116525Sru input_filename = array[count]->defining_file; 61756160Sru 61856160Sru /* If this particular entry should be printed as a "code" index, 619116525Sru then expand it as @code{entry}, i.e., as in fixed-width font. */ 620116525Sru array[count]->entry = expansion (temp->entry_text, array[count]->code); 62156160Sru } 62256160Sru array[count] = NULL; /* terminate the array. */ 623116525Sru 62456160Sru line_number = save_line_number; 62556160Sru input_filename = save_input_filename; 62656160Sru html = save_html; 62756160Sru 62856160Sru#ifdef HAVE_STRCOLL 62956160Sru /* This is not perfect. We should set (then restore) the locale to the 63056160Sru documentlanguage, so strcoll operates according to the document's 63156160Sru locale, not the user's. For now, I'm just going to assume that 63256160Sru those few new documents which use @documentlanguage will be 63356160Sru processed in the appropriate locale. In any case, don't use 63456160Sru strcoll in the C (aka POSIX) locale, that is the ASCII ordering. */ 63556160Sru if (language_code != en) 63656160Sru { 63756160Sru char *lang_env = getenv ("LANG"); 63856160Sru if (lang_env && !STREQ (lang_env, "C") && !STREQ (lang_env, "POSIX")) 63956160Sru index_compare_fn = strcoll; 64056160Sru } 641116525Sru#endif /* HAVE_STRCOLL */ 64256160Sru 64356160Sru /* Sort the array. */ 64456160Sru qsort (array, count, sizeof (INDEX_ELT *), index_element_compare); 645116525Sru 646116525Sru /* Remove duplicate entries. */ 64756160Sru make_index_entries_unique (array, count); 648116525Sru 649116525Sru /* Replace the original index with the sorted one, in case the 650116525Sru document wants to print it again. If the index wasn't empty. */ 651116525Sru if (index) 652116525Sru *index = **array; 653116525Sru 65456160Sru return array; 65556160Sru} 65656160Sru 657146515Srustatic void 658146515Sruinsert_index_output_line_no (int line_number, int output_line_number_len) 659146515Sru{ 660146515Sru int last_column; 661146515Sru int str_size = output_line_number_len + strlen (_("(line )")) 662146515Sru + sizeof (NULL); 663146515Sru char *out_line_no_str = (char *) xmalloc (str_size + 1); 664146515Sru 665146515Sru /* Do not translate ``(line NNN)'' below for !no_headers case (Info output), 666146515Sru because it's something like the ``* Menu'' strings. For plaintext output 667146515Sru it should be translated though. */ 668146515Sru sprintf (out_line_no_str, 669146515Sru no_headers ? _("(line %*d)") : "(line %*d)", 670146515Sru output_line_number_len, line_number); 671146515Sru 672146515Sru { 673146515Sru int i = output_paragraph_offset; 674146515Sru while (0 < i && output_paragraph[i-1] != '\n') 675146515Sru i--; 676146515Sru last_column = output_paragraph_offset - i; 677146515Sru } 678146515Sru 679146515Sru if (last_column + strlen (out_line_no_str) > fill_column) 680146515Sru { 681146515Sru insert ('\n'); 682146515Sru last_column = 0; 683146515Sru } 684146515Sru 685146515Sru while (last_column + strlen (out_line_no_str) < fill_column) 686146515Sru { 687146515Sru insert (' '); 688146515Sru last_column++; 689146515Sru } 690146515Sru 691146515Sru insert_string (out_line_no_str); 692146515Sru insert ('\n'); 693146515Sru 694146515Sru free (out_line_no_str); 695146515Sru} 696146515Sru 69756160Sru/* Nonzero means that we are in the middle of printing an index. */ 69856160Sruint printing_index = 0; 69956160Sru 70056160Sru/* Takes one arg, a short name of an index to print. 70156160Sru Outputs a menu of the sorted elements of the index. */ 70256160Sruvoid 703146515Srucm_printindex (void) 70456160Sru{ 705146515Sru char *index_name; 706146515Sru get_rest_of_line (0, &index_name); 707146515Sru 708146515Sru /* get_rest_of_line increments the line number by one, 709146515Sru so to make warnings/errors point to the correct line, 710146515Sru we decrement the line_number again. */ 711146515Sru if (!handling_delayed_writes) 712146515Sru line_number--; 713146515Sru 71493139Sru if (xml && !docbook) 71556160Sru { 71693139Sru xml_insert_element (PRINTINDEX, START); 71793139Sru insert_string (index_name); 71893139Sru xml_insert_element (PRINTINDEX, END); 71956160Sru } 720146515Sru else if (!handling_delayed_writes) 721146515Sru { 722146515Sru int command_len = sizeof ("@ ") + strlen (command) + strlen (index_name); 723146515Sru char *index_command = xmalloc (command_len + 1); 724146515Sru 725146515Sru close_paragraph (); 726146515Sru if (docbook) 727146515Sru xml_begin_index (); 728146515Sru 729146515Sru sprintf (index_command, "@%s %s", command, index_name); 730146515Sru register_delayed_write (index_command); 731146515Sru free (index_command); 732146515Sru } 73393139Sru else 73456160Sru { 73593139Sru int item; 73693139Sru INDEX_ELT *index; 73793139Sru INDEX_ELT *last_index = 0; 73893139Sru INDEX_ELT **array; 73993139Sru unsigned line_length; 74093139Sru char *line; 74193139Sru int saved_inhibit_paragraph_indentation = inhibit_paragraph_indentation; 74293139Sru int saved_filling_enabled = filling_enabled; 74393139Sru int saved_line_number = line_number; 74493139Sru char *saved_input_filename = input_filename; 745146515Sru unsigned output_line_number_len; 74656160Sru 74793139Sru index = index_list (index_name); 74893139Sru if (index == (INDEX_ELT *)-1) 749114472Sru { 750114472Sru line_error (_("Unknown index `%s' in @printindex"), index_name); 751114472Sru free (index_name); 752114472Sru return; 753114472Sru } 754116525Sru 75593139Sru /* Do this before sorting, so execute_string is in the good environment */ 75693139Sru if (xml && docbook) 757114472Sru xml_begin_index (); 758116525Sru 75993139Sru /* Do this before sorting, so execute_string in index_element_compare 760114472Sru will give the same results as when we actually print. */ 76193139Sru printing_index = 1; 76293139Sru filling_enabled = 0; 76393139Sru inhibit_paragraph_indentation = 1; 76493139Sru xml_sort_index = 1; 76593139Sru array = sort_index (index); 76693139Sru xml_sort_index = 0; 76793139Sru close_paragraph (); 76856160Sru if (html) 769146515Sru add_html_block_elt_args ("<ul class=\"index-%s\" compact>", 770146515Sru index_name); 77193139Sru else if (!no_headers && !docbook) 772146515Sru { /* Info. Add magic cookie for info readers (to treat this 773146515Sru menu differently), and the usual start-of-menu. */ 774146515Sru add_char ('\0'); 775146515Sru add_word ("\010[index"); 776146515Sru add_char ('\0'); 777146515Sru add_word ("\010]\n"); 778146515Sru add_word ("* Menu:\n\n"); 779146515Sru } 780116525Sru 78193139Sru me_inhibit_expansion++; 782116525Sru 78393139Sru /* This will probably be enough. */ 78493139Sru line_length = 100; 78593139Sru line = xmalloc (line_length); 786116525Sru 787146515Sru { 788146515Sru char *max_output_line_number = (char *) xmalloc (25 * sizeof (char)); 789146515Sru 790146515Sru if (no_headers) 791146515Sru sprintf (max_output_line_number, "%d", output_line_number); 792146515Sru else 793146515Sru { 794146515Sru INDEX_ELT *tmp_entry = index; 795146515Sru unsigned tmp = 0; 796146515Sru for (tmp_entry = index; tmp_entry; tmp_entry = tmp_entry->next) 797146515Sru tmp = tmp_entry->output_line > tmp ? tmp_entry->output_line : tmp; 798146515Sru sprintf (max_output_line_number, "%d", tmp); 799146515Sru } 800146515Sru 801146515Sru output_line_number_len = strlen (max_output_line_number); 802146515Sru free (max_output_line_number); 803146515Sru } 804146515Sru 80593139Sru for (item = 0; (index = array[item]); item++) 806114472Sru { 807114472Sru /* A pathological document might have an index entry outside of any 808114472Sru node. Don't crash; try using the section name instead. */ 809146515Sru char *index_node = index->node; 810116525Sru 811114472Sru line_number = index->defining_line; 812114472Sru input_filename = index->defining_file; 813116525Sru 814114472Sru if ((!index_node || !*index_node) && html) 815114472Sru index_node = toc_find_section_of_node (index_node); 816116525Sru 817114472Sru if (!index_node || !*index_node) 818114472Sru { 819114472Sru line_error (_("Entry for index `%s' outside of any node"), 820114472Sru index_name); 821114472Sru if (html || !no_headers) 822146515Sru index_node = (char *) _("(outside of any node)"); 823114472Sru } 824116525Sru 825114472Sru if (html) 826114472Sru { 827146515Sru /* For HTML, we need to expand and HTML-escape the 828146515Sru original entry text, at the same time. Consider 829146515Sru @cindex J@"urgen. We want Jüurgen. We can't 830146515Sru expand and then escape since we'll end up with 831146515Sru J&uuml;rgen. We can't escape and then expand 832146515Sru because then `expansion' will see J@"urgen, and 833146515Sru @"urgen is not a command. */ 834146515Sru char *html_entry = 835146515Sru maybe_escaped_expansion (index->entry_text, index->code, 1); 836116525Sru 837146515Sru add_html_block_elt_args ("\n<li><a href=\"%s#index-", 838146515Sru (splitting && index->output_file) ? index->output_file : ""); 839146515Sru add_escaped_anchor_name (index->entry_text, 0); 840146515Sru add_word_args ("-%d\">%s</a>: ", index->entry_number, 841146515Sru html_entry); 842146515Sru free (html_entry); 843146515Sru 844114472Sru add_word ("<a href=\""); 845114472Sru if (index->node && *index->node) 846114472Sru { 847146515Sru /* Ensure any non-macros in the node name are expanded. */ 848116525Sru char *expanded_index; 849116525Sru 850114472Sru in_fixed_width_font++; 851116525Sru expanded_index = expansion (index_node, 0); 852114472Sru in_fixed_width_font--; 853116525Sru add_anchor_name (expanded_index, 1); 854146515Sru expanded_index = escape_string (expanded_index); 855116525Sru add_word_args ("\">%s</a>", expanded_index); 856116525Sru free (expanded_index); 857114472Sru } 858114472Sru else if (STREQ (index_node, _("(outside of any node)"))) 859114472Sru { 860114472Sru add_anchor_name (index_node, 1); 861114472Sru add_word_args ("\">%s</a>", index_node); 862114472Sru } 863114472Sru else 864114472Sru /* If we use the section instead of the (missing) node, then 865114472Sru index_node already includes all we need except the #. */ 866114472Sru add_word_args ("#%s</a>", index_node); 867146515Sru 868146515Sru add_html_block_elt ("</li>"); 869114472Sru } 870114472Sru else if (xml && docbook) 871114472Sru { 872114472Sru /* In the DocBook case, the expanded index entry is not 873114472Sru good for us, since it was expanded for non-DocBook mode 874114472Sru inside sort_index. So we send the original entry text 875114472Sru to be used with execute_string. */ 876114472Sru xml_insert_indexentry (index->entry_text, index_node); 877114472Sru } 878114472Sru else 879114472Sru { 880114472Sru unsigned new_length = strlen (index->entry); 881116525Sru 882114472Sru if (new_length < 50) /* minimum length used below */ 883114472Sru new_length = 50; 884114472Sru new_length += strlen (index_node) + 7; /* * : .\n\0 */ 885116525Sru 886114472Sru if (new_length > line_length) 887114472Sru { 888114472Sru line_length = new_length; 889114472Sru line = xrealloc (line, line_length); 890114472Sru } 891114472Sru /* Print the entry, nicely formatted. We've already 892114472Sru expanded any commands in index->entry, including any 893114472Sru implicit @code. Thus, can't call execute_string, since 894114472Sru @@ has turned into @. */ 895114472Sru if (!no_headers) 896114472Sru { 897114472Sru sprintf (line, "* %-37s ", index->entry); 898114472Sru line[2 + strlen (index->entry)] = ':'; 899114472Sru insert_string (line); 900114472Sru /* Make sure any non-macros in the node name are expanded. */ 901114472Sru in_fixed_width_font++; 902146515Sru execute_string ("%s. ", index_node); 903146515Sru insert_index_output_line_no (index->output_line, 904146515Sru output_line_number_len); 905114472Sru in_fixed_width_font--; 906114472Sru } 907114472Sru else 908114472Sru { 909114472Sru /* With --no-headers, the @node lines are gone, so 910114472Sru there's little sense in referring to them in the 911114472Sru index. Instead, output the number or name of the 912114472Sru section that corresponds to that node. */ 913146515Sru sprintf (line, "%-*s ", number_sections ? 46 : 1, index->entry); 914114472Sru line[strlen (index->entry)] = ':'; 915114472Sru insert_string (line); 916116525Sru 917146515Sru if (strlen (index->section) > 0) 918146515Sru { /* We got your number. */ 919146515Sru insert_string ((char *) _("See ")); 920146515Sru insert_string (index->section); 921114472Sru } 922114472Sru else 923146515Sru { /* Sigh, index in an @unnumbered. :-\ */ 924146515Sru insert_string ("\n "); 925146515Sru insert_string ((char *) _("See ")); 926146515Sru insert_string ("``"); 927146515Sru insert_string (expansion (index->section_name, 0)); 928146515Sru insert_string ("''"); 929114472Sru } 930146515Sru 931146515Sru insert_string (". "); 932146515Sru insert_index_output_line_no (index->output_line, 933146515Sru output_line_number_len); 934114472Sru } 935114472Sru } 936116525Sru 937114472Sru /* Prevent `output_paragraph' from growing to the size of the 938114472Sru whole index. */ 939114472Sru flush_output (); 940114472Sru last_index = index; 941114472Sru } 94256160Sru 94393139Sru free (line); 944116525Sru 94593139Sru me_inhibit_expansion--; 94693139Sru printing_index = 0; 947116525Sru 94893139Sru close_single_paragraph (); 94993139Sru filling_enabled = saved_filling_enabled; 95093139Sru inhibit_paragraph_indentation = saved_inhibit_paragraph_indentation; 95193139Sru input_filename = saved_input_filename; 95293139Sru line_number = saved_line_number; 953116525Sru 95493139Sru if (html) 955146515Sru add_html_block_elt ("</ul>"); 95693139Sru else if (xml && docbook) 957114472Sru xml_end_index (); 95856160Sru } 959146515Sru 960146515Sru free (index_name); 961146515Sru /* Re-increment the line number, because get_rest_of_line 962146515Sru left us looking at the next line after the command. */ 963146515Sru line_number++; 96456160Sru} 965