No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / texinfo / makeinfo / toc.c
blob1dbf973e0dd25064eae9bec914b3c5edba73df12
1 /* $NetBSD$ */
3 /* toc.c -- table of contents handling.
4 Id: toc.c,v 1.6 2004/04/11 17:56:47 karl Exp
6 Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
11 any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
24 #include "system.h"
25 #include "makeinfo.h"
26 #include "cmds.h"
27 #include "files.h"
28 #include "macro.h"
29 #include "node.h"
30 #include "html.h"
31 #include "lang.h"
32 #include "makeinfo.h"
33 #include "sectioning.h"
34 #include "toc.h"
35 #include "xml.h"
37 /* array of toc entries */
38 static TOC_ENTRY_ELT **toc_entry_alist = NULL;
40 /* toc_counter start from 0 ... n for every @chapter, @section ... */
41 static int toc_counter = 0;
43 /* Routine to add an entry to the table of contents */
44 int
45 toc_add_entry (char *tocname, int level, char *node_name, char *anchor)
47 char *tocname_and_node, *expanded_node, *d;
48 char *s = NULL;
49 char *filename = NULL;
51 if (!node_name)
52 node_name = "";
54 /* I assume that xrealloc behaves like xmalloc if toc_entry_alist is
55 NULL */
56 toc_entry_alist = xrealloc (toc_entry_alist,
57 (toc_counter + 1) * sizeof (TOC_ENTRY_ELT *));
59 toc_entry_alist[toc_counter] = xmalloc (sizeof (TOC_ENTRY_ELT));
61 if (html)
63 /* We need to insert the expanded node name into the toc, so
64 that when we eventually output the toc, its <a ref= link will
65 point to the <a name= tag created by cm_node in the navigation
66 bar. We cannot expand the containing_node member, for the
67 reasons explained in the WARNING below. We also cannot wait
68 with the node name expansion until the toc is actually output,
69 since by that time the macro definitions may have been changed.
70 So instead we store in the tocname member the expanded node
71 name and the toc name concatenated together (with the necessary
72 html markup), since that's how they are output. */
73 if (!anchor)
74 s = expanded_node = expand_node_name (node_name);
75 else
76 expanded_node = anchor;
77 if (splitting)
79 if (!anchor)
80 filename = nodename_to_filename (expanded_node);
81 else
82 filename = filename_part (current_output_filename);
84 /* Sigh... Need to HTML-escape the expanded node name like
85 add_anchor_name does, except that we are not writing this to
86 the output, so can't use add_anchor_name... */
87 /* The factor 5 in the next allocation is because the maximum
88 expansion of HTML-escaping is for the & character, which is
89 output as "&amp;". 2 is for "> that separates node from tocname. */
90 d = tocname_and_node = (char *)xmalloc (2 + 5 * strlen (expanded_node)
91 + strlen (tocname) + 1);
92 if (!anchor)
94 for (; *s; s++)
96 if (cr_or_whitespace (*s))
97 *d++ = '-';
98 else if (! URL_SAFE_CHAR (*s))
100 sprintf (d, "_00%x", (unsigned char) *s);
101 /* do this manually since sprintf returns char * on
102 SunOS 4 and other old systems. */
103 while (*d)
104 d++;
106 else
107 *d++ = *s;
109 strcpy (d, "\">");
111 else
112 /* Section outside any node, they provided explicit anchor. */
113 strcpy (d, anchor);
114 strcat (d, tocname);
115 free (tocname); /* it was malloc'ed by substring() */
116 free (expanded_node);
117 toc_entry_alist[toc_counter]->name = tocname_and_node;
119 else
120 toc_entry_alist[toc_counter]->name = tocname;
121 /* WARNING! The node name saved in containing_node member must
122 be the node name with _only_ macros expanded (the macros in
123 the node name are expanded by cm_node when it grabs the name
124 from the @node directive). Non-macros, like @value, @@ and
125 other @-commands must NOT be expanded in containing_node,
126 because toc_find_section_of_node looks up the node name where
127 they are also unexpanded. You *have* been warned! */
128 toc_entry_alist[toc_counter]->containing_node = xstrdup (node_name);
129 toc_entry_alist[toc_counter]->level = level;
130 toc_entry_alist[toc_counter]->number = toc_counter;
131 toc_entry_alist[toc_counter]->html_file = filename;
133 /* have to be done at least */
134 return toc_counter++;
137 /* Return the name of a chapter/section/subsection etc. that
138 corresponds to the node NODE. If the node isn't found,
139 return NULL.
141 WARNING! This function relies on NODE being unexpanded
142 except for macros (i.e., @value, @@, and other non-macros
143 should NOT be expanded), because the containing_node member
144 stores unexpanded node names.
146 Note that this function returns the first section whose
147 containing node is NODE. Thus, they will lose if they use
148 more than a single chapter structioning command in a node,
149 or if they have a node without any structuring commands. */
150 char *
151 toc_find_section_of_node (char *node)
153 int i;
155 if (!node)
156 node = "";
157 for (i = 0; i < toc_counter; i++)
158 if (STREQ (node, toc_entry_alist[i]->containing_node))
159 return toc_entry_alist[i]->name;
161 return NULL;
164 /* free up memory used by toc entries */
165 void
166 toc_free (void)
168 int i;
170 if (toc_counter)
172 for (i = 0; i < toc_counter; i++)
174 free (toc_entry_alist[i]->name);
175 free (toc_entry_alist[i]->containing_node);
176 free (toc_entry_alist[i]);
179 free (toc_entry_alist);
180 toc_entry_alist = NULL; /* to be sure ;-) */
181 toc_counter = 0; /* to be absolutley sure ;-) */
185 /* Print table of contents in HTML. */
187 static void
188 contents_update_html (void)
190 int i;
191 int k;
192 int last_level;
194 /* does exist any toc? */
195 if (!toc_counter)
196 /* no, so return to sender ;-) */
197 return;
199 add_html_block_elt_args ("\n<div class=\"contents\">\n<h2>%s</h2>\n<ul>\n", _("Table of Contents"));
201 last_level = toc_entry_alist[0]->level;
203 for (i = 0; i < toc_counter; i++)
205 if (toc_entry_alist[i]->level > last_level)
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");
213 else if (toc_entry_alist[i]->level < last_level)
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");
221 /* No double entries in TOC. */
222 if (!(i && strcmp (toc_entry_alist[i]->name,
223 toc_entry_alist[i-1]->name) == 0))
225 /* each toc entry is a list item. */
226 add_word ("<li>");
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)
235 char *p = toc_entry_alist[i]->name;
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);
246 add_word_args ("href=\"%s#%s</a>\n",
247 splitting ? toc_entry_alist[i]->html_file : "",
248 toc_entry_alist[i]->name);
251 last_level = toc_entry_alist[i]->level;
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");
259 add_word ("</li></ul>\n</div>\n\n");
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)
267 int i;
268 int k;
270 if (!toc_counter)
271 return;
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");
279 for (i = 0; i < toc_counter; i++)
281 if (toc_entry_alist[i]->level == 0)
282 add_char ('\n');
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 (" ");
289 insert_string (toc_entry_alist[i]->name);
290 insert ('\n');
292 insert_string ("\n\n");
295 /* shortcontents in HTML; Should this produce a standalone file? */
296 static void
297 shortcontents_update_html (char *contents_filename)
299 int i;
300 char *toc_file = NULL;
302 /* does exist any toc? */
303 if (!toc_counter)
304 return;
306 add_html_block_elt_args ("\n<div class=\"shortcontents\">\n<h2>%s</h2>\n<ul>\n", _("Short Contents"));
308 if (contents_filename)
309 toc_file = filename_part (contents_filename);
311 for (i = 0; i < toc_counter; i++)
313 char *name = toc_entry_alist[i]->name;
315 if (toc_entry_alist[i]->level == 0)
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);
325 add_word ("</ul>\n</div>\n\n");
326 if (contents_filename)
327 free (toc_file);
330 /* short contents in ASCII (--no-headers). */
331 static void
332 shortcontents_update_info (void)
334 int i;
336 if (!toc_counter)
337 return;
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");
345 for (i = 0; i < toc_counter; i++)
347 if (toc_entry_alist[i]->level == 0)
349 insert_string (toc_entry_alist[i]->name);
350 insert ('\n');
353 insert_string ("\n\n");
356 void
357 cm_contents (int arg)
359 /* the file where we found the @contents directive */
360 static char *contents_filename;
362 /* No need to mess with delayed stuff for XML and Docbook. */
363 if (xml)
365 if (arg == START)
367 int elt = STREQ (command, "contents") ? CONTENTS : SHORTCONTENTS;
368 xml_insert_element (elt, START);
369 xml_insert_element (elt, END);
372 else if (!handling_delayed_writes)
374 register_delayed_write (STREQ (command, "contents")
375 ? "@contents" : "@shortcontents");
377 if (html && STREQ (command, "contents"))
379 if (contents_filename)
380 free (contents_filename);
381 contents_filename = xstrdup (current_output_filename);
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 ();