xref: /openbsd/gnu/usr.bin/texinfo/makeinfo/index.c (revision a1acfa9b)
1 /* index.c -- indexing for Texinfo.
2    $Id: index.c,v 1.1.1.3 2006/07/17 16:03:46 espie 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 
32 INDEX_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". */
37 INDEX_ELT **the_indices = NULL;
38 
39 /* The number of defined indices. */
40 int defined_indices = 0;
41 
42 /* This is the order of the index.  */
43 int index_counter = 0;
44 
45 /* Stuff for defining commands on the fly. */
46 COMMAND **user_command_array = NULL;
47 int user_command_array_len = 0;
48 
49 /* How to compare index entries for sorting.  May be set to strcoll.  */
50 int (*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.)  */
54 int 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. */
58 static int
find_index_offset(char * name)59 find_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. */
70 static INDEX_ALIST *
find_index(char * name)71 find_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.  */
82 static void
define_user_command(char * name,COMMAND_FUNCTION (* proc),int needs_braces_p)83 define_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... */
101 static void
free_index(INDEX_ELT * index)102 free_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. */
120 static void
undefindex(char * name)121 undefindex (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.  */
142 static void
index_add_arg(char * name)143 index_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. */
265 static void
gen_index(void)266 gen_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. */
277 static void
defindex(char * name,int code)278 defindex (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.  */
317 static void
top_defindex(char * name,int code)318 top_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.  */
330 void
init_indices(void)331 init_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. */
381 static int
translate_index(char * name)382 translate_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. */
393 INDEX_ELT *
index_list(char * name)394 index_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. */
404 static void
gen_defindex(int code)405 gen_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 
426 void
cm_defindex(void)427 cm_defindex (void)
428 {
429   gen_defindex (0);
430 }
431 
432 void
cm_defcodeindex(void)433 cm_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. */
441 void
cm_synindex(void)442 cm_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 
471 void
cm_pindex(void)472 cm_pindex (void)                    /* Pinhead index. */
473 {
474   index_add_arg ("pg");
475 }
476 
477 void
cm_vindex(void)478 cm_vindex (void)                    /* Variable index. */
479 {
480   index_add_arg ("vr");
481 }
482 
483 void
cm_kindex(void)484 cm_kindex (void)                    /* Key index. */
485 {
486   index_add_arg ("ky");
487 }
488 
489 void
cm_cindex(void)490 cm_cindex (void)                    /* Concept index. */
491 {
492   index_add_arg ("cp");
493 }
494 
495 void
cm_findex(void)496 cm_findex (void)                    /* Function index. */
497 {
498   index_add_arg ("fn");
499 }
500 
501 void
cm_tindex(void)502 cm_tindex (void)                    /* Data Type index. */
503 {
504   index_add_arg ("tp");
505 }
506 
507 int
index_element_compare(const void * element1,const void * element2)508 index_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. */
517 static void
make_index_entries_unique(INDEX_ELT ** array,int count)518 make_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 
577 static INDEX_ELT **
sort_index(INDEX_ELT * index)578 sort_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 
657 static void
insert_index_output_line_no(int line_number,int output_line_number_len)658 insert_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. */
698 int 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. */
702 void
cm_printindex(void)703 cm_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