1 /* toc.c -- table of contents handling.
2 $Id: toc.c,v 1.12 2008/05/19 18:26:48 karl Exp $
4 Copyright (C) 1999, 2000, 2001, 2002, 2003, 2007, 2008
5 Free Software Foundation, Inc.
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 3 of the License, or
10 (at your option) any later version.
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.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
31 #include "sectioning.h"
35 /* array of toc entries */
36 static TOC_ENTRY_ELT **toc_entry_alist = NULL;
38 /* toc_counter start from 0 ... n for every @chapter, @section ... */
39 static int toc_counter = 0;
41 /* Routine to add an entry to the table of contents */
43 toc_add_entry (char *tocname, int level, char *node_name, char *anchor)
45 char *expanded_node, *d;
47 char *filename = NULL;
52 /* I assume that xrealloc behaves like xmalloc if toc_entry_alist is
54 toc_entry_alist = xrealloc (toc_entry_alist,
55 (toc_counter + 1) * sizeof (TOC_ENTRY_ELT *));
57 toc_entry_alist[toc_counter] = xmalloc (sizeof (TOC_ENTRY_ELT));
61 /* We need to insert the expanded node name into the toc, so
62 that when we eventually output the toc, its <a ref= link will
63 point to the <a name= tag created by cm_node in the navigation
64 bar. We cannot expand the containing_node member, for the
65 reasons explained in the WARNING below. We also cannot wait
66 with the node name expansion until the toc is actually output,
67 since by that time the macro definitions may have been changed.
68 So instead we store in the tocname member the expanded node
69 name and the toc name concatenated together (with the necessary
70 html markup), since that's how they are output. */
72 s = expanded_node = expand_node_name (node_name);
74 expanded_node = anchor;
78 filename = nodename_to_filename (expanded_node);
80 filename = filename_part (current_output_filename);
83 /* Need to HTML-escape the expanded node name like
84 add_anchor_name does... */
85 d = escaped_anchor_name (expanded_node);
87 /* Section outside any node, they provided explicit anchor. */
90 /* Add space for the "> which may be needed, and the tocname */
91 d = xrealloc (d, strlen (d) + strlen (tocname) + 3);
95 free (tocname); /* it was malloc'ed by substring() */
97 toc_entry_alist[toc_counter]->name = d;
100 toc_entry_alist[toc_counter]->name = tocname;
101 /* WARNING! The node name saved in containing_node member must
102 be the node name with _only_ macros expanded (the macros in
103 the node name are expanded by cm_node when it grabs the name
104 from the @node directive). Non-macros, like @value, @@ and
105 other @-commands must NOT be expanded in containing_node,
106 because toc_find_section_of_node looks up the node name where
107 they are also unexpanded. You *have* been warned! */
108 toc_entry_alist[toc_counter]->containing_node = xstrdup (node_name);
109 toc_entry_alist[toc_counter]->level = level;
110 toc_entry_alist[toc_counter]->number = toc_counter;
111 toc_entry_alist[toc_counter]->html_file = filename;
113 /* have to be done at least */
114 return toc_counter++;
117 /* Return the name of a chapter/section/subsection etc. that
118 corresponds to the node NODE. If the node isn't found,
121 WARNING! This function relies on NODE being unexpanded
122 except for macros (i.e., @value, @@, and other non-macros
123 should NOT be expanded), because the containing_node member
124 stores unexpanded node names.
126 Note that this function returns the first section whose
127 containing node is NODE. Thus, they will lose if they use
128 more than a single chapter structioning command in a node,
129 or if they have a node without any structuring commands. */
131 toc_find_section_of_node (char *node)
137 for (i = 0; i < toc_counter; i++)
138 if (STREQ (node, toc_entry_alist[i]->containing_node))
139 return toc_entry_alist[i]->name;
144 /* free up memory used by toc entries */
152 for (i = 0; i < toc_counter; i++)
154 free (toc_entry_alist[i]->name);
155 free (toc_entry_alist[i]->containing_node);
156 free (toc_entry_alist[i]);
159 free (toc_entry_alist);
160 toc_entry_alist = NULL; /* to be sure ;-) */
161 toc_counter = 0; /* to be absolutley sure ;-) */
165 /* Print table of contents in HTML. */
168 contents_update_html (void)
174 /* does exist any toc? */
176 /* no, so return to sender ;-) */
179 add_html_block_elt_args ("\n<div class=\"contents\">\n<h2>%s</h2>\n<ul>\n", gdt("Table of Contents"));
181 last_level = toc_entry_alist[0]->level;
183 for (i = 0; i < toc_counter; i++)
185 if (toc_entry_alist[i]->level > last_level)
187 /* unusual, but it is possible
189 @subsubsection ... ? */
190 for (k = 0; k < (toc_entry_alist[i]->level-last_level); k++)
191 add_html_block_elt ("<ul>\n");
193 else if (toc_entry_alist[i]->level < last_level)
195 /* @subsubsection ...
196 @chapter ... this IS usual.*/
197 for (k = 0; k < (last_level-toc_entry_alist[i]->level); k++)
198 add_word ("</li></ul>\n");
201 /* No double entries in TOC. */
202 if (!(i && strcmp (toc_entry_alist[i]->name,
203 toc_entry_alist[i-1]->name) == 0))
205 /* each toc entry is a list item. */
208 /* Insert link -- to an external file if splitting, or
209 within the current document if not splitting. */
211 /* For chapters (only), insert an anchor that the short contents
213 if (toc_entry_alist[i]->level == 0)
215 char *p = toc_entry_alist[i]->name;
217 /* toc_entry_alist[i]->name has the form `foo">bar',
218 that is, it includes both the node name and anchor
219 text. We need to find where `foo', the node name,
220 ends, and use that in toc_FOO. */
221 while (*p && *p != '"')
223 add_word_args ("name=\"toc_%.*s\" ",
224 p - toc_entry_alist[i]->name, toc_entry_alist[i]->name);
225 /* save the link if necessary */
226 if (internal_links_stream)
228 fprintf (internal_links_stream, "%s#toc_%.*s\ttoc\t%s\n",
229 splitting ? toc_entry_alist[i]->html_file : "",
230 p - toc_entry_alist[i]->name, toc_entry_alist[i]->name,
234 add_word_args ("href=\"%s#%s</a>\n",
235 splitting ? toc_entry_alist[i]->html_file : "",
236 toc_entry_alist[i]->name);
239 last_level = toc_entry_alist[i]->level;
242 /* Go back to start level. */
243 if (toc_entry_alist[0]->level < last_level)
244 for (k = 0; k < (last_level-toc_entry_alist[0]->level); k++)
245 add_word ("</li></ul>\n");
247 add_word ("</li></ul>\n</div>\n\n");
250 /* print table of contents in ASCII (--no-headers)
251 May be we should create a new command line switch --ascii ? */
253 contents_update_info (void)
261 insert_string ((char *) gdt("Table of Contents"));
263 for (i = 0; i < strlen (gdt("Table of Contents")); i++)
265 insert_string ("\n\n");
267 for (i = 0; i < toc_counter; i++)
269 if (toc_entry_alist[i]->level == 0)
272 /* indention with two spaces per level, should this
274 for (k = 0; k < toc_entry_alist[i]->level; k++)
277 insert_string (toc_entry_alist[i]->name);
280 insert_string ("\n\n");
283 /* shortcontents in HTML; Should this produce a standalone file? */
285 shortcontents_update_html (char *contents_filename)
288 char *toc_file = NULL;
290 /* does exist any toc? */
294 add_html_block_elt_args ("\n<div class=\"shortcontents\">\n<h2>%s</h2>\n<ul>\n", gdt("Short Contents"));
296 if (contents_filename)
297 toc_file = filename_part (contents_filename);
299 for (i = 0; i < toc_counter; i++)
301 char *name = toc_entry_alist[i]->name;
303 if (toc_entry_alist[i]->level == 0)
305 if (contents_filename)
306 add_word_args ("<li><a href=\"%s#toc_%s</a></li>\n",
307 splitting ? toc_file : "", name);
309 add_word_args ("<a href=\"%s#%s</a>\n",
310 splitting ? toc_entry_alist[i]->html_file : "", name);
313 add_word ("</ul>\n</div>\n\n");
314 if (contents_filename)
318 /* short contents in ASCII (--no-headers). */
320 shortcontents_update_info (void)
327 insert_string ((char *) gdt("Short Contents"));
329 for (i = 0; i < strlen (gdt("Short Contents")); i++)
331 insert_string ("\n\n");
333 for (i = 0; i < toc_counter; i++)
335 if (toc_entry_alist[i]->level == 0)
337 insert_string (toc_entry_alist[i]->name);
341 insert_string ("\n\n");
345 cm_contents (int arg)
347 /* the file where we found the @contents directive */
348 static char *contents_filename;
350 /* No need to mess with delayed stuff for XML and Docbook. */
355 int elt = STREQ (command, "contents") ? CONTENTS : SHORTCONTENTS;
356 xml_insert_element (elt, START);
357 xml_insert_element (elt, END);
360 else if (!handling_delayed_writes)
362 register_delayed_write (STREQ (command, "contents")
363 ? "@contents" : "@shortcontents");
365 if (html && STREQ (command, "contents"))
367 if (contents_filename)
368 free (contents_filename);
369 contents_filename = xstrdup (current_output_filename);
373 STREQ (command, "contents")
374 ? contents_update_html () : shortcontents_update_html (contents_filename);
376 STREQ (command, "contents")
377 ? contents_update_info () : shortcontents_update_info ();