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