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&uuml;urgen.  We can't
830146515Sru                 expand and then escape since we'll end up with
831146515Sru                 J&amp;uuml;rgen.  We can't escape and then expand
832146515Sru                 because then `expansion' will see J@&quot;urgen, and
833146515Sru                 @&quot;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