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