1 /* $NetBSD: index.c,v 1.3 2024/05/05 15:26:20 riastradh 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 int ret = 0;
515
516 /* Find a stable sort order. */
517 if (ret == 0)
518 ret = index_compare_fn ((*elt1)->entry, (*elt2)->entry);
519 if (ret == 0)
520 ret = strcmp ((*elt1)->defining_file, (*elt2)->defining_file);
521 if (ret == 0)
522 ret = strcmp ((*elt1)->node, (*elt2)->node);
523 if (ret == 0) {
524 if ((*elt1)->defining_line < (*elt2)->defining_line)
525 ret = -1;
526 else if ((*elt1)->defining_line > (*elt2)->defining_line)
527 ret = 1;
528 }
529 if (ret == 0) {
530 if ((*elt1)->entry_number < (*elt2)->entry_number)
531 ret = -1;
532 else if ((*elt1)->entry_number > (*elt2)->entry_number)
533 ret = 1;
534 }
535 if (ret == 0) {
536 abort ();
537 }
538
539 return ret;
540 }
541
542 /* Force all index entries to be unique. */
543 static void
make_index_entries_unique(INDEX_ELT ** array,int count)544 make_index_entries_unique (INDEX_ELT **array, int count)
545 {
546 int i, j;
547 INDEX_ELT **copy;
548 int counter = 1;
549
550 copy = xmalloc ((1 + count) * sizeof (INDEX_ELT *));
551
552 for (i = 0, j = 0; i < count; i++)
553 {
554 if (i == (count - 1)
555 || array[i]->node != array[i + 1]->node
556 || !STREQ (array[i]->entry, array[i + 1]->entry))
557 copy[j++] = array[i];
558 else
559 {
560 free (array[i]->entry);
561 free (array[i]->entry_text);
562 free (array[i]);
563 }
564 }
565 copy[j] = NULL;
566
567 /* Now COPY contains only unique entries. Duplicated entries in the
568 original array have been freed. Replace the current array with
569 the copy, fixing the NEXT pointers. */
570 for (i = 0; copy[i]; i++)
571 {
572 copy[i]->next = copy[i + 1];
573
574 /* Fix entry names which are the same. They point to different nodes,
575 so we make the entry name unique. */
576 if (copy[i+1]
577 && STREQ (copy[i]->entry, copy[i + 1]->entry)
578 && !html)
579 {
580 char *new_entry_name;
581
582 new_entry_name = xmalloc (10 + strlen (copy[i]->entry));
583 sprintf (new_entry_name, "%s <%d>", copy[i]->entry, counter);
584 free (copy[i]->entry);
585 copy[i]->entry = new_entry_name;
586 counter++;
587 }
588 else
589 counter = 1;
590
591 array[i] = copy[i];
592 }
593 array[i] = NULL;
594
595 /* Free the storage used only by COPY. */
596 free (copy);
597 }
598
599
600 /* Sort the index passed in INDEX, returning an array of pointers to
601 elements. The array is terminated with a NULL pointer. */
602
603 static INDEX_ELT **
sort_index(INDEX_ELT * index)604 sort_index (INDEX_ELT *index)
605 {
606 INDEX_ELT **array;
607 INDEX_ELT *temp;
608 int count = 0;
609 int save_line_number = line_number;
610 char *save_input_filename = input_filename;
611 int save_html = html;
612
613 /* Pretend we are in non-HTML mode, for the purpose of getting the
614 expanded index entry that lacks any markup and other HTML escape
615 characters which could produce a wrong sort order. */
616 /* fixme: html: this still causes some markup, such as non-ASCII
617 characters @AE{} etc., to sort incorrectly. */
618 html = 0;
619
620 for (temp = index, count = 0; temp; temp = temp->next, count++)
621 ;
622 /* We have the length, now we can allocate an array. */
623 array = xmalloc ((count + 1) * sizeof (INDEX_ELT *));
624
625 for (temp = index, count = 0; temp; temp = temp->next, count++)
626 {
627 /* Allocate new memory for the return array, since parts of the
628 original INDEX get freed. Otherwise, if the document calls
629 @printindex twice on the same index, with duplicate entries,
630 we'll have garbage the second time. There are cleaner ways to
631 deal, but this will suffice for now. */
632 array[count] = xmalloc (sizeof (INDEX_ELT));
633 *(array[count]) = *(temp); /* struct assignment, hope it's ok */
634
635 /* Adjust next pointers to use the new memory. */
636 if (count > 0)
637 array[count-1]->next = array[count];
638
639 /* Set line number and input filename to the source line for this
640 index entry, as this expansion finds any errors. */
641 line_number = array[count]->defining_line;
642 input_filename = array[count]->defining_file;
643
644 /* If this particular entry should be printed as a "code" index,
645 then expand it as @code{entry}, i.e., as in fixed-width font. */
646 array[count]->entry = expansion (temp->entry_text, array[count]->code);
647 }
648 array[count] = NULL; /* terminate the array. */
649
650 line_number = save_line_number;
651 input_filename = save_input_filename;
652 html = save_html;
653
654 #ifdef HAVE_STRCOLL
655 /* This is not perfect. We should set (then restore) the locale to the
656 documentlanguage, so strcoll operates according to the document's
657 locale, not the user's. For now, I'm just going to assume that
658 those few new documents which use @documentlanguage will be
659 processed in the appropriate locale. In any case, don't use
660 strcoll in the C (aka POSIX) locale, that is the ASCII ordering. */
661 if (language_code != en)
662 {
663 char *lang_env = getenv ("LANG");
664 if (lang_env && !STREQ (lang_env, "C") && !STREQ (lang_env, "POSIX"))
665 index_compare_fn = strcoll;
666 }
667 #endif /* HAVE_STRCOLL */
668
669 /* Sort the array. */
670 qsort (array, count, sizeof (INDEX_ELT *), index_element_compare);
671
672 /* Remove duplicate entries. */
673 make_index_entries_unique (array, count);
674
675 /* Replace the original index with the sorted one, in case the
676 document wants to print it again. If the index wasn't empty. */
677 if (index)
678 *index = **array;
679
680 return array;
681 }
682
683 static void
insert_index_output_line_no(int line_number,int output_line_number_len)684 insert_index_output_line_no (int line_number, int output_line_number_len)
685 {
686 int last_column;
687 int str_size = output_line_number_len + strlen (_("(line )"))
688 + sizeof (NULL);
689 char *out_line_no_str = (char *) xmalloc (str_size + 1);
690
691 /* Do not translate ``(line NNN)'' below for !no_headers case (Info output),
692 because it's something like the ``* Menu'' strings. For plaintext output
693 it should be translated though. */
694 sprintf (out_line_no_str,
695 no_headers ? _("(line %*d)") : "(line %*d)",
696 output_line_number_len, line_number);
697
698 {
699 int i = output_paragraph_offset;
700 while (0 < i && output_paragraph[i-1] != '\n')
701 i--;
702 last_column = output_paragraph_offset - i;
703 }
704
705 if (last_column + strlen (out_line_no_str) > fill_column)
706 {
707 insert ('\n');
708 last_column = 0;
709 }
710
711 while (last_column + strlen (out_line_no_str) < fill_column)
712 {
713 insert (' ');
714 last_column++;
715 }
716
717 insert_string (out_line_no_str);
718 insert ('\n');
719
720 free (out_line_no_str);
721 }
722
723 /* Nonzero means that we are in the middle of printing an index. */
724 int printing_index = 0;
725
726 /* Takes one arg, a short name of an index to print.
727 Outputs a menu of the sorted elements of the index. */
728 void
cm_printindex(void)729 cm_printindex (void)
730 {
731 char *index_name;
732 get_rest_of_line (0, &index_name);
733
734 /* get_rest_of_line increments the line number by one,
735 so to make warnings/errors point to the correct line,
736 we decrement the line_number again. */
737 if (!handling_delayed_writes)
738 line_number--;
739
740 if (xml && !docbook)
741 {
742 xml_insert_element (PRINTINDEX, START);
743 insert_string (index_name);
744 xml_insert_element (PRINTINDEX, END);
745 }
746 else if (!handling_delayed_writes)
747 {
748 int command_len = sizeof ("@ ") + strlen (command) + strlen (index_name);
749 char *index_command = xmalloc (command_len + 1);
750
751 close_paragraph ();
752 if (docbook)
753 xml_begin_index ();
754
755 sprintf (index_command, "@%s %s", command, index_name);
756 register_delayed_write (index_command);
757 free (index_command);
758 }
759 else
760 {
761 int item;
762 INDEX_ELT *index;
763 INDEX_ELT *last_index = 0;
764 INDEX_ELT **array;
765 unsigned line_length;
766 char *line;
767 int saved_inhibit_paragraph_indentation = inhibit_paragraph_indentation;
768 int saved_filling_enabled = filling_enabled;
769 int saved_line_number = line_number;
770 char *saved_input_filename = input_filename;
771 unsigned output_line_number_len;
772
773 index = index_list (index_name);
774 if (index == (INDEX_ELT *)-1)
775 {
776 line_error (_("Unknown index `%s' in @printindex"), index_name);
777 free (index_name);
778 return;
779 }
780
781 /* Do this before sorting, so execute_string is in the good environment */
782 if (xml && docbook)
783 xml_begin_index ();
784
785 /* Do this before sorting, so execute_string in index_element_compare
786 will give the same results as when we actually print. */
787 printing_index = 1;
788 filling_enabled = 0;
789 inhibit_paragraph_indentation = 1;
790 xml_sort_index = 1;
791 array = sort_index (index);
792 xml_sort_index = 0;
793 close_paragraph ();
794 if (html)
795 add_html_block_elt_args ("<ul class=\"index-%s\" compact>",
796 index_name);
797 else if (!no_headers && !docbook)
798 { /* Info. Add magic cookie for info readers (to treat this
799 menu differently), and the usual start-of-menu. */
800 add_char ('\0');
801 add_word ("\010[index");
802 add_char ('\0');
803 add_word ("\010]\n");
804 add_word ("* Menu:\n\n");
805 }
806
807 me_inhibit_expansion++;
808
809 /* This will probably be enough. */
810 line_length = 100;
811 line = xmalloc (line_length);
812
813 {
814 char *max_output_line_number = (char *) xmalloc (25 * sizeof (char));
815
816 if (no_headers)
817 sprintf (max_output_line_number, "%d", output_line_number);
818 else
819 {
820 INDEX_ELT *tmp_entry = index;
821 unsigned tmp = 0;
822 for (tmp_entry = index; tmp_entry; tmp_entry = tmp_entry->next)
823 tmp = tmp_entry->output_line > tmp ? tmp_entry->output_line : tmp;
824 sprintf (max_output_line_number, "%d", tmp);
825 }
826
827 output_line_number_len = strlen (max_output_line_number);
828 free (max_output_line_number);
829 }
830
831 for (item = 0; (index = array[item]); item++)
832 {
833 /* A pathological document might have an index entry outside of any
834 node. Don't crash; try using the section name instead. */
835 char *index_node = index->node;
836
837 line_number = index->defining_line;
838 input_filename = index->defining_file;
839
840 if ((!index_node || !*index_node) && html)
841 index_node = toc_find_section_of_node (index_node);
842
843 if (!index_node || !*index_node)
844 {
845 line_error (_("Entry for index `%s' outside of any node"),
846 index_name);
847 if (html || !no_headers)
848 index_node = (char *) _("(outside of any node)");
849 }
850
851 if (html)
852 {
853 /* For HTML, we need to expand and HTML-escape the
854 original entry text, at the same time. Consider
855 @cindex J@"urgen. We want Jüurgen. We can't
856 expand and then escape since we'll end up with
857 J&uuml;rgen. We can't escape and then expand
858 because then `expansion' will see J@"urgen, and
859 @"urgen is not a command. */
860 char *html_entry =
861 maybe_escaped_expansion (index->entry_text, index->code, 1);
862
863 add_html_block_elt_args ("\n<li><a href=\"%s#index-",
864 (splitting && index->output_file) ? index->output_file : "");
865 add_escaped_anchor_name (index->entry_text, 0);
866 add_word_args ("-%d\">%s</a>: ", index->entry_number,
867 html_entry);
868 free (html_entry);
869
870 add_word ("<a href=\"");
871 if (index->node && *index->node)
872 {
873 /* Ensure any non-macros in the node name are expanded. */
874 char *expanded_index;
875
876 in_fixed_width_font++;
877 expanded_index = expansion (index_node, 0);
878 in_fixed_width_font--;
879 add_anchor_name (expanded_index, 1);
880 expanded_index = escape_string (expanded_index);
881 add_word_args ("\">%s</a>", expanded_index);
882 free (expanded_index);
883 }
884 else if (STREQ (index_node, _("(outside of any node)")))
885 {
886 add_anchor_name (index_node, 1);
887 add_word_args ("\">%s</a>", index_node);
888 }
889 else
890 /* If we use the section instead of the (missing) node, then
891 index_node already includes all we need except the #. */
892 add_word_args ("#%s</a>", index_node);
893
894 add_html_block_elt ("</li>");
895 }
896 else if (xml && docbook)
897 {
898 /* In the DocBook case, the expanded index entry is not
899 good for us, since it was expanded for non-DocBook mode
900 inside sort_index. So we send the original entry text
901 to be used with execute_string. */
902 xml_insert_indexentry (index->entry_text, index_node);
903 }
904 else
905 {
906 unsigned new_length = strlen (index->entry);
907
908 if (new_length < 50) /* minimum length used below */
909 new_length = 50;
910 new_length += strlen (index_node) + 7; /* * : .\n\0 */
911
912 if (new_length > line_length)
913 {
914 line_length = new_length;
915 line = xrealloc (line, line_length);
916 }
917 /* Print the entry, nicely formatted. We've already
918 expanded any commands in index->entry, including any
919 implicit @code. Thus, can't call execute_string, since
920 @@ has turned into @. */
921 if (!no_headers)
922 {
923 sprintf (line, "* %-37s ", index->entry);
924 line[2 + strlen (index->entry)] = ':';
925 insert_string (line);
926 /* Make sure any non-macros in the node name are expanded. */
927 in_fixed_width_font++;
928 execute_string ("%s. ", index_node);
929 insert_index_output_line_no (index->output_line,
930 output_line_number_len);
931 in_fixed_width_font--;
932 }
933 else
934 {
935 /* With --no-headers, the @node lines are gone, so
936 there's little sense in referring to them in the
937 index. Instead, output the number or name of the
938 section that corresponds to that node. */
939 sprintf (line, "%-*s ", number_sections ? 46 : 1, index->entry);
940 line[strlen (index->entry)] = ':';
941 insert_string (line);
942
943 if (strlen (index->section) > 0)
944 { /* We got your number. */
945 insert_string ((char *) _("See "));
946 insert_string (index->section);
947 }
948 else
949 { /* Sigh, index in an @unnumbered. :-\ */
950 insert_string ("\n ");
951 insert_string ((char *) _("See "));
952 insert_string ("``");
953 insert_string (expansion (index->section_name, 0));
954 insert_string ("''");
955 }
956
957 insert_string (". ");
958 insert_index_output_line_no (index->output_line,
959 output_line_number_len);
960 }
961 }
962
963 /* Prevent `output_paragraph' from growing to the size of the
964 whole index. */
965 flush_output ();
966 last_index = index;
967 }
968
969 free (line);
970
971 me_inhibit_expansion--;
972 printing_index = 0;
973
974 close_single_paragraph ();
975 filling_enabled = saved_filling_enabled;
976 inhibit_paragraph_indentation = saved_inhibit_paragraph_indentation;
977 input_filename = saved_input_filename;
978 line_number = saved_line_number;
979
980 if (html)
981 add_html_block_elt ("</ul>");
982 else if (xml && docbook)
983 xml_end_index ();
984 }
985
986 free (index_name);
987 /* Re-increment the line number, because get_rest_of_line
988 left us looking at the next line after the command. */
989 line_number++;
990 }
991