source: trunk/essentials/sys-apps/texinfo/makeinfo/toc.c

Last change on this file was 2617, checked in by bird, 19 years ago

GNU Texinfo 4.8

File size: 11.6 KB
Line 
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 */
36static TOC_ENTRY_ELT **toc_entry_alist = NULL;
37
38/* toc_counter start from 0 ... n for every @chapter, @section ... */
39static int toc_counter = 0;
40
41
42/* Routine to add an entry to the table of contents */
43int
44toc_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 "&amp;". 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. */
149char *
150toc_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 */
164void
165toc_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
187static void
188contents_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 ? */
264static void
265contents_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? */
296static void
297shortcontents_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). */
331static void
332shortcontents_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
356void
357cm_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}
Note: See TracBrowser for help on using the repository browser.