Bug Summary

File:src/gnu/usr.bin/texinfo/makeinfo/index.c
Warning:line 261, column 5
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

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