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üurgen. We can't
832 expand and then escape since we'll end up with
833 J&uuml;rgen. We can't escape and then expand
834 because then `expansion' will see J@"urgen, and
835 @"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