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