1 /* toc.c -- table of contents handling.
2 $Id: toc.c,v 1.22 2002/04/01 14:07:11 karl Exp $
4 Copyright (C) 1999, 2000, 01, 02 Free Software Foundation, Inc.
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)
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.
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.
20 Written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
31 #include "sectioning.h"
36 /* array of toc entries */
37 static TOC_ENTRY_ELT **toc_entry_alist = NULL;
39 /* toc_counter start from 0 ... n for every @chapter, @section ... */
40 static int toc_counter = 0;
42 /* the file where we found the @contents directive */
43 char *contents_filename;
45 /* the file where we found the @shortcontents directive */
46 char *shortcontents_filename;
48 static const char contents_placebo[] = "\n...Table of Contents...\n";
49 static const char shortcontents_placebo[] = "\n...Short Contents...\n";
50 static const char lots_of_stars[] =
51 "***************************************************************************";
54 /* Routine to add an entry to the table of contents */
56 toc_add_entry (tocname, level, node_name, anchor)
62 char *tocname_and_node, *expanded_node, *s, *d;
63 char *filename = NULL;
68 /* I assume that xrealloc behaves like xmalloc if toc_entry_alist is
70 toc_entry_alist = xrealloc (toc_entry_alist,
71 (toc_counter + 1) * sizeof (TOC_ENTRY_ELT *));
73 toc_entry_alist[toc_counter] = xmalloc (sizeof (TOC_ENTRY_ELT));
77 /* We need to insert the expanded node name into the toc, so
78 that when we eventually output the toc, its <a ref= link will
79 point to the <a name= tag created by cm_node in the navigation
80 bar. We cannot expand the containing_node member, for the
81 reasons explained in the WARNING below. We also cannot wait
82 with the node name expansion until the toc is actually output,
83 since by that time the macro definitions may have been changed.
84 So instead we store in the tocname member the expanded node
85 name and the toc name concatenated together (with the necessary
86 html markup), since that's how they are output. */
88 s = expanded_node = expand_node_name (node_name);
90 expanded_node = anchor;
94 filename = nodename_to_filename (expanded_node);
96 filename = filename_part (current_output_filename);
98 /* Sigh... Need to HTML-escape the expanded node name like
99 add_anchor_name does, except that we are not writing this to
100 the output, so can't use add_anchor_name... */
101 /* The factor 5 in the next allocation is because the maximum
102 expansion of HTML-escaping is for the & character, which is
103 output as "&". 2 is for "> that separates node from tocname. */
104 d = tocname_and_node = (char *)xmalloc (2 + 5 * strlen (expanded_node)
105 + strlen (tocname) + 1);
115 else if (! URL_SAFE_CHAR (*s))
117 sprintf (d, "%%%x", (unsigned char) *s);
118 /* do this manually since sprintf returns char * on
119 SunOS 4 and other old systems. */
129 /* Section outside any node, they provided explicit anchor. */
132 free (tocname); /* it was malloc'ed by substring() */
133 free (expanded_node);
134 toc_entry_alist[toc_counter]->name = tocname_and_node;
137 toc_entry_alist[toc_counter]->name = tocname;
138 /* WARNING! The node name saved in containing_node member must
139 be the node name with _only_ macros expanded (the macros in
140 the node name are expanded by cm_node when it grabs the name
141 from the @node directive). Non-macros, like @value, @@ and
142 other @-commands must NOT be expanded in containing_node,
143 because toc_find_section_of_node looks up the node name where
144 they are also unexpanded. You *have* been warned! */
145 toc_entry_alist[toc_counter]->containing_node = xstrdup (node_name);
146 toc_entry_alist[toc_counter]->level = level;
147 toc_entry_alist[toc_counter]->number = toc_counter;
148 toc_entry_alist[toc_counter]->html_file = filename;
150 /* have to be done at least */
151 return toc_counter++;
154 /* Return the name of a chapter/section/subsection etc. that
155 corresponds to the node NODE. If the node isn't found,
158 WARNING! This function relies on NODE being unexpanded
159 except for macros (i.e., @value, @@, and other non-macros
160 should NOT be expanded), because the containing_node member
161 stores unexpanded node names.
163 Note that this function returns the first section whose
164 containing node is NODE. Thus, they will lose if they use
165 more than a single chapter structioning command in a node,
166 or if they have a node without any structuring commands. */
168 toc_find_section_of_node (node)
175 for (i = 0; i < toc_counter; i++)
176 if (STREQ (node, toc_entry_alist[i]->containing_node))
177 return toc_entry_alist[i]->name;
182 /* free up memory used by toc entries */
190 for (i = 0; i < toc_counter; i++)
192 free (toc_entry_alist[i]->name);
193 free (toc_entry_alist[i]->containing_node);
194 free (toc_entry_alist[i]);
197 free (toc_entry_alist);
198 toc_entry_alist = NULL; /* to be sure ;-) */
199 toc_counter = 0; /* to be absolutley sure ;-) */
204 /* Print table of contents in HTML. */
207 contents_update_html (fp)
214 /* does exist any toc? */
216 /* no, so return to sender ;-) */
219 flush_output (); /* in case we are writing stdout */
221 fprintf (fp, "\n<h2>%s</h2>\n<ul>\n", _("Table of Contents"));
223 last_level = toc_entry_alist[0]->level;
225 for (i = 0; i < toc_counter; i++)
227 if (toc_entry_alist[i]->level > last_level)
229 /* unusual, but it is possible
231 @subsubsection ... ? */
232 for (k = 0; k < (toc_entry_alist[i]->level-last_level); k++)
233 fputs ("<ul>\n", fp);
235 else if (toc_entry_alist[i]->level < last_level)
237 /* @subsubsection ...
238 @chapter ... this IS usual.*/
239 for (k = 0; k < (last_level-toc_entry_alist[i]->level); k++)
240 fputs ("</ul>\n", fp);
243 /* No double entries in TOC. */
244 if (!(i && strcmp (toc_entry_alist[i]->name,
245 toc_entry_alist[i-1]->name) == 0))
247 /* each toc entry is a list item. */
250 /* For chapters (only), insert an anchor that the short contents
252 if (toc_entry_alist[i]->level == 0)
254 char *p = toc_entry_alist[i]->name;
256 /* toc_entry_alist[i]->name has the form `foo">bar',
257 that is, it includes both the node name and anchor
258 text. We need to find where `foo', the node name,
259 ends, and use that in toc_FOO. */
260 while (*p && *p != '"')
262 fprintf (fp, "<a name=\"toc_%.*s\"></a>\n ",
263 p - toc_entry_alist[i]->name, toc_entry_alist[i]->name);
266 /* Insert link -- to an external file if splitting, or
267 within the current document if not splitting. */
268 fprintf (fp, "<a href=\"%s#%s</a>\n",
269 splitting ? toc_entry_alist[i]->html_file : "",
270 toc_entry_alist[i]->name);
273 last_level = toc_entry_alist[i]->level;
276 /* Go back to start level. */
277 if (toc_entry_alist[0]->level < last_level)
278 for (k = 0; k < (last_level-toc_entry_alist[0]->level); k++)
279 fputs ("</ul>\n", fp);
281 fputs ("</ul>\n\n", fp);
284 /* print table of contents in ASCII (--no-headers)
285 May be we should create a new command line switch --ascii ? */
287 contents_update_info (fp)
296 flush_output (); /* in case we are writing stdout */
298 fprintf (fp, "%s\n%.*s\n\n", _("Table of Contents"),
299 (int) strlen (_("Table of Contents")), lots_of_stars);
301 for (i = 0; i < toc_counter; i++)
303 if (toc_entry_alist[i]->level == 0)
306 /* indention with two spaces per level, should this
308 for (k = 0; k < toc_entry_alist[i]->level; k++)
311 fprintf (fp, "%s\n", toc_entry_alist[i]->name);
316 /* shortcontents in HTML; Should this produce a standalone file? */
318 shortcontents_update_html (fp)
324 /* does exist any toc? */
328 flush_output (); /* in case we are writing stdout */
330 fprintf (fp, "\n<h2>%s</h2>\n<ul>\n", _("Short Contents"));
332 if (contents_filename)
333 toc_file = filename_part (contents_filename);
335 for (i = 0; i < toc_counter; i++)
337 char *name = toc_entry_alist[i]->name;
339 if (toc_entry_alist[i]->level == 0)
341 if (contents_filename)
342 fprintf (fp, "<li><a href=\"%s#toc_%s</a>\n",
343 splitting ? toc_file : "", name);
345 fprintf (fp, "<a href=\"%s#%s</a>\n",
346 splitting ? toc_entry_alist[i]->html_file : "", name);
349 fputs ("</ul>\n\n", fp);
350 if (contents_filename)
354 /* short contents in ASCII (--no-headers). */
356 shortcontents_update_info (fp)
364 flush_output (); /* in case we are writing stdout */
366 fprintf (fp, "%s\n%.*s\n\n", _("Short Contents"),
367 (int) strlen (_("Short Contents")), lots_of_stars);
369 for (i = 0; i < toc_counter; i++)
371 if (toc_entry_alist[i]->level == 0)
372 fprintf (fp, "%s\n", toc_entry_alist[i]->name);
379 static char *toc_buf;
382 rewrite_top (fname, placebo)
383 const char *fname, *placebo;
387 /* Can't rewrite standard output or the null device. No point in
389 if (STREQ (fname, "-")
390 || FILENAME_CMP (fname, NULL_DEVICE) == 0
391 || FILENAME_CMP (fname, ALSO_NULL_DEVICE) == 0)
394 toc_buf = find_and_load (fname);
402 idx = search_forward (placebo, 0);
406 error (_("%s: TOC should be here, but it was not found"), fname);
410 toc_fp = fopen (fname, "w");
417 if (fwrite (toc_buf, 1, idx, toc_fp) != idx)
423 return idx + strlen (placebo);
429 int cont_idx = rewrite_top (contents_filename, contents_placebo);
435 contents_update_html (toc_fp);
437 contents_update_info (toc_fp);
439 if (fwrite (toc_buf + cont_idx, 1, input_text_length - cont_idx, toc_fp)
440 != input_text_length - cont_idx
441 || fclose (toc_fp) != 0)
442 fs_error (contents_filename);
446 shortcontents_update ()
448 int cont_idx = rewrite_top (shortcontents_filename, shortcontents_placebo);
454 shortcontents_update_html (toc_fp);
456 shortcontents_update_info (toc_fp);
458 if (fwrite (toc_buf + cont_idx, 1, input_text_length - cont_idx - 1, toc_fp)
459 != input_text_length - cont_idx - 1
460 || fclose (toc_fp) != 0)
461 fs_error (shortcontents_filename);
467 if (!html && !no_headers)
470 if (contents_filename)
472 if (shortcontents_filename)
473 shortcontents_update ();
480 if ((html || no_headers) && arg == START)
482 if (contents_filename)
484 free (contents_filename);
485 contents_filename = NULL;
488 if (contents_filename && STREQ (contents_filename, "-"))
491 contents_update_html (stdout);
493 contents_update_info (stdout);
497 if (!executing_string && html)
499 contents_filename = xstrdup (current_output_filename);
500 insert_string (contents_placebo); /* just mark it, for now */
506 cm_shortcontents (arg)
509 if ((html || no_headers) && arg == START)
511 if (shortcontents_filename)
513 free (shortcontents_filename);
514 shortcontents_filename = NULL;
517 if (shortcontents_filename && STREQ (shortcontents_filename, "-"))
520 shortcontents_update_html (stdout);
522 shortcontents_update_info (stdout);
526 if (!executing_string && html)
528 shortcontents_filename = xstrdup (current_output_filename);
529 insert_string (shortcontents_placebo); /* just mark it, for now */