1 | /* toc.c -- table of contents handling.
|
---|
2 | $Id: toc.c,v 1.6 2004/04/11 17:56:47 karl Exp $
|
---|
3 |
|
---|
4 | Copyright (C) 1999, 2000, 2001, 2002, 2003 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
|
---|
18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
---|
19 |
|
---|
20 | Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
|
---|
21 |
|
---|
22 | #include "system.h"
|
---|
23 | #include "makeinfo.h"
|
---|
24 | #include "cmds.h"
|
---|
25 | #include "files.h"
|
---|
26 | #include "macro.h"
|
---|
27 | #include "node.h"
|
---|
28 | #include "html.h"
|
---|
29 | #include "lang.h"
|
---|
30 | #include "makeinfo.h"
|
---|
31 | #include "sectioning.h"
|
---|
32 | #include "toc.h"
|
---|
33 | #include "xml.h"
|
---|
34 |
|
---|
35 | /* array of toc entries */
|
---|
36 | static TOC_ENTRY_ELT **toc_entry_alist = NULL;
|
---|
37 |
|
---|
38 | /* toc_counter start from 0 ... n for every @chapter, @section ... */
|
---|
39 | static int toc_counter = 0;
|
---|
40 | |
---|
41 |
|
---|
42 | /* Routine to add an entry to the table of contents */
|
---|
43 | int
|
---|
44 | toc_add_entry (char *tocname, int level, char *node_name, char *anchor)
|
---|
45 | {
|
---|
46 | char *tocname_and_node, *expanded_node, *d;
|
---|
47 | char *s = NULL;
|
---|
48 | char *filename = NULL;
|
---|
49 |
|
---|
50 | if (!node_name)
|
---|
51 | node_name = "";
|
---|
52 |
|
---|
53 | /* I assume that xrealloc behaves like xmalloc if toc_entry_alist is
|
---|
54 | NULL */
|
---|
55 | toc_entry_alist = xrealloc (toc_entry_alist,
|
---|
56 | (toc_counter + 1) * sizeof (TOC_ENTRY_ELT *));
|
---|
57 |
|
---|
58 | toc_entry_alist[toc_counter] = xmalloc (sizeof (TOC_ENTRY_ELT));
|
---|
59 |
|
---|
60 | if (html)
|
---|
61 | {
|
---|
62 | /* We need to insert the expanded node name into the toc, so
|
---|
63 | that when we eventually output the toc, its <a ref= link will
|
---|
64 | point to the <a name= tag created by cm_node in the navigation
|
---|
65 | bar. We cannot expand the containing_node member, for the
|
---|
66 | reasons explained in the WARNING below. We also cannot wait
|
---|
67 | with the node name expansion until the toc is actually output,
|
---|
68 | since by that time the macro definitions may have been changed.
|
---|
69 | So instead we store in the tocname member the expanded node
|
---|
70 | name and the toc name concatenated together (with the necessary
|
---|
71 | html markup), since that's how they are output. */
|
---|
72 | if (!anchor)
|
---|
73 | s = expanded_node = expand_node_name (node_name);
|
---|
74 | else
|
---|
75 | expanded_node = anchor;
|
---|
76 | if (splitting)
|
---|
77 | {
|
---|
78 | if (!anchor)
|
---|
79 | filename = nodename_to_filename (expanded_node);
|
---|
80 | else
|
---|
81 | filename = filename_part (current_output_filename);
|
---|
82 | }
|
---|
83 | /* Sigh... Need to HTML-escape the expanded node name like
|
---|
84 | add_anchor_name does, except that we are not writing this to
|
---|
85 | the output, so can't use add_anchor_name... */
|
---|
86 | /* The factor 5 in the next allocation is because the maximum
|
---|
87 | expansion of HTML-escaping is for the & character, which is
|
---|
88 | output as "&". 2 is for "> that separates node from tocname. */
|
---|
89 | d = tocname_and_node = (char *)xmalloc (2 + 5 * strlen (expanded_node)
|
---|
90 | + strlen (tocname) + 1);
|
---|
91 | if (!anchor)
|
---|
92 | {
|
---|
93 | for (; *s; s++)
|
---|
94 | {
|
---|
95 | if (cr_or_whitespace (*s))
|
---|
96 | *d++ = '-';
|
---|
97 | else if (! URL_SAFE_CHAR (*s))
|
---|
98 | {
|
---|
99 | sprintf (d, "_00%x", (unsigned char) *s);
|
---|
100 | /* do this manually since sprintf returns char * on
|
---|
101 | SunOS 4 and other old systems. */
|
---|
102 | while (*d)
|
---|
103 | d++;
|
---|
104 | }
|
---|
105 | else
|
---|
106 | *d++ = *s;
|
---|
107 | }
|
---|
108 | strcpy (d, "\">");
|
---|
109 | }
|
---|
110 | else
|
---|
111 | /* Section outside any node, they provided explicit anchor. */
|
---|
112 | strcpy (d, anchor);
|
---|
113 | strcat (d, tocname);
|
---|
114 | free (tocname); /* it was malloc'ed by substring() */
|
---|
115 | free (expanded_node);
|
---|
116 | toc_entry_alist[toc_counter]->name = tocname_and_node;
|
---|
117 | }
|
---|
118 | else
|
---|
119 | toc_entry_alist[toc_counter]->name = tocname;
|
---|
120 | /* WARNING! The node name saved in containing_node member must
|
---|
121 | be the node name with _only_ macros expanded (the macros in
|
---|
122 | the node name are expanded by cm_node when it grabs the name
|
---|
123 | from the @node directive). Non-macros, like @value, @@ and
|
---|
124 | other @-commands must NOT be expanded in containing_node,
|
---|
125 | because toc_find_section_of_node looks up the node name where
|
---|
126 | they are also unexpanded. You *have* been warned! */
|
---|
127 | toc_entry_alist[toc_counter]->containing_node = xstrdup (node_name);
|
---|
128 | toc_entry_alist[toc_counter]->level = level;
|
---|
129 | toc_entry_alist[toc_counter]->number = toc_counter;
|
---|
130 | toc_entry_alist[toc_counter]->html_file = filename;
|
---|
131 |
|
---|
132 | /* have to be done at least */
|
---|
133 | return toc_counter++;
|
---|
134 | }
|
---|
135 |
|
---|
136 | /* Return the name of a chapter/section/subsection etc. that
|
---|
137 | corresponds to the node NODE. If the node isn't found,
|
---|
138 | return NULL.
|
---|
139 |
|
---|
140 | WARNING! This function relies on NODE being unexpanded
|
---|
141 | except for macros (i.e., @value, @@, and other non-macros
|
---|
142 | should NOT be expanded), because the containing_node member
|
---|
143 | stores unexpanded node names.
|
---|
144 |
|
---|
145 | Note that this function returns the first section whose
|
---|
146 | containing node is NODE. Thus, they will lose if they use
|
---|
147 | more than a single chapter structioning command in a node,
|
---|
148 | or if they have a node without any structuring commands. */
|
---|
149 | char *
|
---|
150 | toc_find_section_of_node (char *node)
|
---|
151 | {
|
---|
152 | int i;
|
---|
153 |
|
---|
154 | if (!node)
|
---|
155 | node = "";
|
---|
156 | for (i = 0; i < toc_counter; i++)
|
---|
157 | if (STREQ (node, toc_entry_alist[i]->containing_node))
|
---|
158 | return toc_entry_alist[i]->name;
|
---|
159 |
|
---|
160 | return NULL;
|
---|
161 | }
|
---|
162 |
|
---|
163 | /* free up memory used by toc entries */
|
---|
164 | void
|
---|
165 | toc_free (void)
|
---|
166 | {
|
---|
167 | int i;
|
---|
168 |
|
---|
169 | if (toc_counter)
|
---|
170 | {
|
---|
171 | for (i = 0; i < toc_counter; i++)
|
---|
172 | {
|
---|
173 | free (toc_entry_alist[i]->name);
|
---|
174 | free (toc_entry_alist[i]->containing_node);
|
---|
175 | free (toc_entry_alist[i]);
|
---|
176 | }
|
---|
177 |
|
---|
178 | free (toc_entry_alist);
|
---|
179 | toc_entry_alist = NULL; /* to be sure ;-) */
|
---|
180 | toc_counter = 0; /* to be absolutley sure ;-) */
|
---|
181 | }
|
---|
182 | }
|
---|
183 | |
---|
184 |
|
---|
185 | /* Print table of contents in HTML. */
|
---|
186 |
|
---|
187 | static void
|
---|
188 | contents_update_html (void)
|
---|
189 | {
|
---|
190 | int i;
|
---|
191 | int k;
|
---|
192 | int last_level;
|
---|
193 |
|
---|
194 | /* does exist any toc? */
|
---|
195 | if (!toc_counter)
|
---|
196 | /* no, so return to sender ;-) */
|
---|
197 | return;
|
---|
198 |
|
---|
199 | add_html_block_elt_args ("\n<div class=\"contents\">\n<h2>%s</h2>\n<ul>\n", _("Table of Contents"));
|
---|
200 |
|
---|
201 | last_level = toc_entry_alist[0]->level;
|
---|
202 |
|
---|
203 | for (i = 0; i < toc_counter; i++)
|
---|
204 | {
|
---|
205 | if (toc_entry_alist[i]->level > last_level)
|
---|
206 | {
|
---|
207 | /* unusual, but it is possible
|
---|
208 | @chapter ...
|
---|
209 | @subsubsection ... ? */
|
---|
210 | for (k = 0; k < (toc_entry_alist[i]->level-last_level); k++)
|
---|
211 | add_html_block_elt ("<ul>\n");
|
---|
212 | }
|
---|
213 | else if (toc_entry_alist[i]->level < last_level)
|
---|
214 | {
|
---|
215 | /* @subsubsection ...
|
---|
216 | @chapter ... this IS usual.*/
|
---|
217 | for (k = 0; k < (last_level-toc_entry_alist[i]->level); k++)
|
---|
218 | add_word ("</li></ul>\n");
|
---|
219 | }
|
---|
220 |
|
---|
221 | /* No double entries in TOC. */
|
---|
222 | if (!(i && strcmp (toc_entry_alist[i]->name,
|
---|
223 | toc_entry_alist[i-1]->name) == 0))
|
---|
224 | {
|
---|
225 | /* each toc entry is a list item. */
|
---|
226 | add_word ("<li>");
|
---|
227 |
|
---|
228 | /* Insert link -- to an external file if splitting, or
|
---|
229 | within the current document if not splitting. */
|
---|
230 | add_word ("<a ");
|
---|
231 | /* For chapters (only), insert an anchor that the short contents
|
---|
232 | will link to. */
|
---|
233 | if (toc_entry_alist[i]->level == 0)
|
---|
234 | {
|
---|
235 | char *p = toc_entry_alist[i]->name;
|
---|
236 |
|
---|
237 | /* toc_entry_alist[i]->name has the form `foo">bar',
|
---|
238 | that is, it includes both the node name and anchor
|
---|
239 | text. We need to find where `foo', the node name,
|
---|
240 | ends, and use that in toc_FOO. */
|
---|
241 | while (*p && *p != '"')
|
---|
242 | p++;
|
---|
243 | add_word_args ("name=\"toc_%.*s\" ",
|
---|
244 | p - toc_entry_alist[i]->name, toc_entry_alist[i]->name);
|
---|
245 | }
|
---|
246 | add_word_args ("href=\"%s#%s</a>\n",
|
---|
247 | splitting ? toc_entry_alist[i]->html_file : "",
|
---|
248 | toc_entry_alist[i]->name);
|
---|
249 | }
|
---|
250 |
|
---|
251 | last_level = toc_entry_alist[i]->level;
|
---|
252 | }
|
---|
253 |
|
---|
254 | /* Go back to start level. */
|
---|
255 | if (toc_entry_alist[0]->level < last_level)
|
---|
256 | for (k = 0; k < (last_level-toc_entry_alist[0]->level); k++)
|
---|
257 | add_word ("</li></ul>\n");
|
---|
258 |
|
---|
259 | add_word ("</li></ul>\n</div>\n\n");
|
---|
260 | }
|
---|
261 |
|
---|
262 | /* print table of contents in ASCII (--no-headers)
|
---|
263 | May be we should create a new command line switch --ascii ? */
|
---|
264 | static void
|
---|
265 | contents_update_info (void)
|
---|
266 | {
|
---|
267 | int i;
|
---|
268 | int k;
|
---|
269 |
|
---|
270 | if (!toc_counter)
|
---|
271 | return;
|
---|
272 |
|
---|
273 | insert_string ((char *) _("Table of Contents"));
|
---|
274 | insert ('\n');
|
---|
275 | for (i = 0; i < strlen (_("Table of Contents")); i++)
|
---|
276 | insert ('*');
|
---|
277 | insert_string ("\n\n");
|
---|
278 |
|
---|
279 | for (i = 0; i < toc_counter; i++)
|
---|
280 | {
|
---|
281 | if (toc_entry_alist[i]->level == 0)
|
---|
282 | add_char ('\n');
|
---|
283 |
|
---|
284 | /* indention with two spaces per level, should this
|
---|
285 | changed? */
|
---|
286 | for (k = 0; k < toc_entry_alist[i]->level; k++)
|
---|
287 | insert_string (" ");
|
---|
288 |
|
---|
289 | insert_string (toc_entry_alist[i]->name);
|
---|
290 | insert ('\n');
|
---|
291 | }
|
---|
292 | insert_string ("\n\n");
|
---|
293 | }
|
---|
294 |
|
---|
295 | /* shortcontents in HTML; Should this produce a standalone file? */
|
---|
296 | static void
|
---|
297 | shortcontents_update_html (char *contents_filename)
|
---|
298 | {
|
---|
299 | int i;
|
---|
300 | char *toc_file = NULL;
|
---|
301 |
|
---|
302 | /* does exist any toc? */
|
---|
303 | if (!toc_counter)
|
---|
304 | return;
|
---|
305 |
|
---|
306 | add_html_block_elt_args ("\n<div class=\"shortcontents\">\n<h2>%s</h2>\n<ul>\n", _("Short Contents"));
|
---|
307 |
|
---|
308 | if (contents_filename)
|
---|
309 | toc_file = filename_part (contents_filename);
|
---|
310 |
|
---|
311 | for (i = 0; i < toc_counter; i++)
|
---|
312 | {
|
---|
313 | char *name = toc_entry_alist[i]->name;
|
---|
314 |
|
---|
315 | if (toc_entry_alist[i]->level == 0)
|
---|
316 | {
|
---|
317 | if (contents_filename)
|
---|
318 | add_word_args ("<li><a href=\"%s#toc_%s</a></li>\n",
|
---|
319 | splitting ? toc_file : "", name);
|
---|
320 | else
|
---|
321 | add_word_args ("<a href=\"%s#%s</a>\n",
|
---|
322 | splitting ? toc_entry_alist[i]->html_file : "", name);
|
---|
323 | }
|
---|
324 | }
|
---|
325 | add_word ("</ul>\n</div>\n\n");
|
---|
326 | if (contents_filename)
|
---|
327 | free (toc_file);
|
---|
328 | }
|
---|
329 |
|
---|
330 | /* short contents in ASCII (--no-headers). */
|
---|
331 | static void
|
---|
332 | shortcontents_update_info (void)
|
---|
333 | {
|
---|
334 | int i;
|
---|
335 |
|
---|
336 | if (!toc_counter)
|
---|
337 | return;
|
---|
338 |
|
---|
339 | insert_string ((char *) _("Short Contents"));
|
---|
340 | insert ('\n');
|
---|
341 | for (i = 0; i < strlen (_("Short Contents")); i++)
|
---|
342 | insert ('*');
|
---|
343 | insert_string ("\n\n");
|
---|
344 |
|
---|
345 | for (i = 0; i < toc_counter; i++)
|
---|
346 | {
|
---|
347 | if (toc_entry_alist[i]->level == 0)
|
---|
348 | {
|
---|
349 | insert_string (toc_entry_alist[i]->name);
|
---|
350 | insert ('\n');
|
---|
351 | }
|
---|
352 | }
|
---|
353 | insert_string ("\n\n");
|
---|
354 | }
|
---|
355 |
|
---|
356 | void
|
---|
357 | cm_contents (int arg)
|
---|
358 | {
|
---|
359 | /* the file where we found the @contents directive */
|
---|
360 | static char *contents_filename;
|
---|
361 |
|
---|
362 | /* No need to mess with delayed stuff for XML and Docbook. */
|
---|
363 | if (xml)
|
---|
364 | {
|
---|
365 | if (arg == START)
|
---|
366 | {
|
---|
367 | int elt = STREQ (command, "contents") ? CONTENTS : SHORTCONTENTS;
|
---|
368 | xml_insert_element (elt, START);
|
---|
369 | xml_insert_element (elt, END);
|
---|
370 | }
|
---|
371 | }
|
---|
372 | else if (!handling_delayed_writes)
|
---|
373 | {
|
---|
374 | register_delayed_write (STREQ (command, "contents")
|
---|
375 | ? "@contents" : "@shortcontents");
|
---|
376 |
|
---|
377 | if (html && STREQ (command, "contents"))
|
---|
378 | {
|
---|
379 | if (contents_filename)
|
---|
380 | free (contents_filename);
|
---|
381 | contents_filename = xstrdup (current_output_filename);
|
---|
382 | }
|
---|
383 | }
|
---|
384 | else if (html)
|
---|
385 | STREQ (command, "contents")
|
---|
386 | ? contents_update_html () : shortcontents_update_html (contents_filename);
|
---|
387 | else if (no_headers)
|
---|
388 | STREQ (command, "contents")
|
---|
389 | ? contents_update_info () : shortcontents_update_info ();
|
---|
390 | }
|
---|