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