11cc83814Sespie /* toc.c -- table of contents handling.
2*a1acfa9bSespie $Id: toc.c,v 1.1.1.3 2006/07/17 16:03:48 espie Exp $
31cc83814Sespie
4*a1acfa9bSespie Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
51cc83814Sespie
61cc83814Sespie This program is free software; you can redistribute it and/or modify
71cc83814Sespie it under the terms of the GNU General Public License as published by
81cc83814Sespie the Free Software Foundation; either version 2, or (at your option)
91cc83814Sespie any later version.
101cc83814Sespie
111cc83814Sespie This program is distributed in the hope that it will be useful,
121cc83814Sespie but WITHOUT ANY WARRANTY; without even the implied warranty of
131cc83814Sespie MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
141cc83814Sespie GNU General Public License for more details.
151cc83814Sespie
161cc83814Sespie You should have received a copy of the GNU General Public License
171cc83814Sespie along with this program; if not, write to the Free Software
181cc83814Sespie Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
191cc83814Sespie
20*a1acfa9bSespie Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
211cc83814Sespie
221cc83814Sespie #include "system.h"
231cc83814Sespie #include "makeinfo.h"
241cc83814Sespie #include "cmds.h"
251cc83814Sespie #include "files.h"
261cc83814Sespie #include "macro.h"
271cc83814Sespie #include "node.h"
283fb98d4aSespie #include "html.h"
291cc83814Sespie #include "lang.h"
301cc83814Sespie #include "makeinfo.h"
311cc83814Sespie #include "sectioning.h"
321cc83814Sespie #include "toc.h"
33*a1acfa9bSespie #include "xml.h"
341cc83814Sespie
351cc83814Sespie /* array of toc entries */
361cc83814Sespie static TOC_ENTRY_ELT **toc_entry_alist = NULL;
371cc83814Sespie
381cc83814Sespie /* toc_counter start from 0 ... n for every @chapter, @section ... */
391cc83814Sespie static int toc_counter = 0;
401cc83814Sespie
411cc83814Sespie /* Routine to add an entry to the table of contents */
421cc83814Sespie int
toc_add_entry(char * tocname,int level,char * node_name,char * anchor)43*a1acfa9bSespie toc_add_entry (char *tocname, int level, char *node_name, char *anchor)
441cc83814Sespie {
45*a1acfa9bSespie char *tocname_and_node, *expanded_node, *d;
46*a1acfa9bSespie char *s = NULL;
473fb98d4aSespie char *filename = NULL;
481cc83814Sespie
491cc83814Sespie if (!node_name)
501cc83814Sespie node_name = "";
511cc83814Sespie
521cc83814Sespie /* I assume that xrealloc behaves like xmalloc if toc_entry_alist is
531cc83814Sespie NULL */
541cc83814Sespie toc_entry_alist = xrealloc (toc_entry_alist,
551cc83814Sespie (toc_counter + 1) * sizeof (TOC_ENTRY_ELT *));
561cc83814Sespie
571cc83814Sespie toc_entry_alist[toc_counter] = xmalloc (sizeof (TOC_ENTRY_ELT));
581cc83814Sespie
591cc83814Sespie if (html)
601cc83814Sespie {
613fb98d4aSespie /* We need to insert the expanded node name into the toc, so
623fb98d4aSespie that when we eventually output the toc, its <a ref= link will
633fb98d4aSespie point to the <a name= tag created by cm_node in the navigation
641cc83814Sespie bar. We cannot expand the containing_node member, for the
651cc83814Sespie reasons explained in the WARNING below. We also cannot wait
663fb98d4aSespie with the node name expansion until the toc is actually output,
671cc83814Sespie since by that time the macro definitions may have been changed.
681cc83814Sespie So instead we store in the tocname member the expanded node
693fb98d4aSespie name and the toc name concatenated together (with the necessary
703fb98d4aSespie html markup), since that's how they are output. */
711cc83814Sespie if (!anchor)
721cc83814Sespie s = expanded_node = expand_node_name (node_name);
731cc83814Sespie else
741cc83814Sespie expanded_node = anchor;
753fb98d4aSespie if (splitting)
763fb98d4aSespie {
773fb98d4aSespie if (!anchor)
783fb98d4aSespie filename = nodename_to_filename (expanded_node);
793fb98d4aSespie else
803fb98d4aSespie filename = filename_part (current_output_filename);
813fb98d4aSespie }
821cc83814Sespie /* Sigh... Need to HTML-escape the expanded node name like
831cc83814Sespie add_anchor_name does, except that we are not writing this to
841cc83814Sespie the output, so can't use add_anchor_name... */
851cc83814Sespie /* The factor 5 in the next allocation is because the maximum
861cc83814Sespie expansion of HTML-escaping is for the & character, which is
871cc83814Sespie output as "&". 2 is for "> that separates node from tocname. */
881cc83814Sespie d = tocname_and_node = (char *)xmalloc (2 + 5 * strlen (expanded_node)
891cc83814Sespie + strlen (tocname) + 1);
901cc83814Sespie if (!anchor)
911cc83814Sespie {
921cc83814Sespie for (; *s; s++)
931cc83814Sespie {
94*a1acfa9bSespie if (cr_or_whitespace (*s))
95*a1acfa9bSespie *d++ = '-';
961cc83814Sespie else if (! URL_SAFE_CHAR (*s))
971cc83814Sespie {
98*a1acfa9bSespie sprintf (d, "_00%x", (unsigned char) *s);
991cc83814Sespie /* do this manually since sprintf returns char * on
1001cc83814Sespie SunOS 4 and other old systems. */
1011cc83814Sespie while (*d)
1021cc83814Sespie d++;
1031cc83814Sespie }
1041cc83814Sespie else
1051cc83814Sespie *d++ = *s;
1061cc83814Sespie }
1071cc83814Sespie strcpy (d, "\">");
1081cc83814Sespie }
1091cc83814Sespie else
1101cc83814Sespie /* Section outside any node, they provided explicit anchor. */
1111cc83814Sespie strcpy (d, anchor);
1121cc83814Sespie strcat (d, tocname);
1131cc83814Sespie free (tocname); /* it was malloc'ed by substring() */
1141cc83814Sespie free (expanded_node);
1151cc83814Sespie toc_entry_alist[toc_counter]->name = tocname_and_node;
1161cc83814Sespie }
1171cc83814Sespie else
1181cc83814Sespie toc_entry_alist[toc_counter]->name = tocname;
1191cc83814Sespie /* WARNING! The node name saved in containing_node member must
1201cc83814Sespie be the node name with _only_ macros expanded (the macros in
1211cc83814Sespie the node name are expanded by cm_node when it grabs the name
1221cc83814Sespie from the @node directive). Non-macros, like @value, @@ and
1231cc83814Sespie other @-commands must NOT be expanded in containing_node,
1241cc83814Sespie because toc_find_section_of_node looks up the node name where
1251cc83814Sespie they are also unexpanded. You *have* been warned! */
1261cc83814Sespie toc_entry_alist[toc_counter]->containing_node = xstrdup (node_name);
1271cc83814Sespie toc_entry_alist[toc_counter]->level = level;
1281cc83814Sespie toc_entry_alist[toc_counter]->number = toc_counter;
1293fb98d4aSespie toc_entry_alist[toc_counter]->html_file = filename;
1301cc83814Sespie
1311cc83814Sespie /* have to be done at least */
1321cc83814Sespie return toc_counter++;
1331cc83814Sespie }
1341cc83814Sespie
1351cc83814Sespie /* Return the name of a chapter/section/subsection etc. that
1361cc83814Sespie corresponds to the node NODE. If the node isn't found,
1371cc83814Sespie return NULL.
1381cc83814Sespie
1391cc83814Sespie WARNING! This function relies on NODE being unexpanded
1401cc83814Sespie except for macros (i.e., @value, @@, and other non-macros
1411cc83814Sespie should NOT be expanded), because the containing_node member
1421cc83814Sespie stores unexpanded node names.
1431cc83814Sespie
1441cc83814Sespie Note that this function returns the first section whose
1451cc83814Sespie containing node is NODE. Thus, they will lose if they use
1461cc83814Sespie more than a single chapter structioning command in a node,
1471cc83814Sespie or if they have a node without any structuring commands. */
1481cc83814Sespie char *
toc_find_section_of_node(char * node)149*a1acfa9bSespie toc_find_section_of_node (char *node)
1501cc83814Sespie {
1511cc83814Sespie int i;
1521cc83814Sespie
1531cc83814Sespie if (!node)
1541cc83814Sespie node = "";
1551cc83814Sespie for (i = 0; i < toc_counter; i++)
1561cc83814Sespie if (STREQ (node, toc_entry_alist[i]->containing_node))
1571cc83814Sespie return toc_entry_alist[i]->name;
1581cc83814Sespie
1591cc83814Sespie return NULL;
1601cc83814Sespie }
1611cc83814Sespie
1621cc83814Sespie /* free up memory used by toc entries */
1631cc83814Sespie void
toc_free(void)164*a1acfa9bSespie toc_free (void)
1651cc83814Sespie {
1661cc83814Sespie int i;
1671cc83814Sespie
1681cc83814Sespie if (toc_counter)
1691cc83814Sespie {
1701cc83814Sespie for (i = 0; i < toc_counter; i++)
1711cc83814Sespie {
1721cc83814Sespie free (toc_entry_alist[i]->name);
1731cc83814Sespie free (toc_entry_alist[i]->containing_node);
1741cc83814Sespie free (toc_entry_alist[i]);
1751cc83814Sespie }
1761cc83814Sespie
1771cc83814Sespie free (toc_entry_alist);
1781cc83814Sespie toc_entry_alist = NULL; /* to be sure ;-) */
1791cc83814Sespie toc_counter = 0; /* to be absolutley sure ;-) */
1801cc83814Sespie }
1811cc83814Sespie }
1821cc83814Sespie
1833fb98d4aSespie /* Print table of contents in HTML. */
1843fb98d4aSespie
1851cc83814Sespie static void
contents_update_html(void)186*a1acfa9bSespie contents_update_html (void)
1871cc83814Sespie {
1881cc83814Sespie int i;
1891cc83814Sespie int k;
1901cc83814Sespie int last_level;
1911cc83814Sespie
1921cc83814Sespie /* does exist any toc? */
1931cc83814Sespie if (!toc_counter)
1941cc83814Sespie /* no, so return to sender ;-) */
1951cc83814Sespie return;
1961cc83814Sespie
197*a1acfa9bSespie add_html_block_elt_args ("\n<div class=\"contents\">\n<h2>%s</h2>\n<ul>\n", _("Table of Contents"));
1981cc83814Sespie
1991cc83814Sespie last_level = toc_entry_alist[0]->level;
2001cc83814Sespie
2011cc83814Sespie for (i = 0; i < toc_counter; i++)
2021cc83814Sespie {
2031cc83814Sespie if (toc_entry_alist[i]->level > last_level)
2041cc83814Sespie {
2051cc83814Sespie /* unusual, but it is possible
2061cc83814Sespie @chapter ...
2071cc83814Sespie @subsubsection ... ? */
2081cc83814Sespie for (k = 0; k < (toc_entry_alist[i]->level-last_level); k++)
209*a1acfa9bSespie add_html_block_elt ("<ul>\n");
2101cc83814Sespie }
2111cc83814Sespie else if (toc_entry_alist[i]->level < last_level)
2121cc83814Sespie {
2131cc83814Sespie /* @subsubsection ...
2141cc83814Sespie @chapter ... this IS usual.*/
2151cc83814Sespie for (k = 0; k < (last_level-toc_entry_alist[i]->level); k++)
216*a1acfa9bSespie add_word ("</li></ul>\n");
2171cc83814Sespie }
2181cc83814Sespie
2193fb98d4aSespie /* No double entries in TOC. */
2203fb98d4aSespie if (!(i && strcmp (toc_entry_alist[i]->name,
2213fb98d4aSespie toc_entry_alist[i-1]->name) == 0))
2223fb98d4aSespie {
2233fb98d4aSespie /* each toc entry is a list item. */
224*a1acfa9bSespie add_word ("<li>");
2253fb98d4aSespie
226*a1acfa9bSespie /* Insert link -- to an external file if splitting, or
227*a1acfa9bSespie within the current document if not splitting. */
228*a1acfa9bSespie add_word ("<a ");
2293fb98d4aSespie /* For chapters (only), insert an anchor that the short contents
2303fb98d4aSespie will link to. */
2313fb98d4aSespie if (toc_entry_alist[i]->level == 0)
2323fb98d4aSespie {
2333fb98d4aSespie char *p = toc_entry_alist[i]->name;
2343fb98d4aSespie
2353fb98d4aSespie /* toc_entry_alist[i]->name has the form `foo">bar',
2363fb98d4aSespie that is, it includes both the node name and anchor
2373fb98d4aSespie text. We need to find where `foo', the node name,
2383fb98d4aSespie ends, and use that in toc_FOO. */
2393fb98d4aSespie while (*p && *p != '"')
2403fb98d4aSespie p++;
241*a1acfa9bSespie add_word_args ("name=\"toc_%.*s\" ",
2423fb98d4aSespie p - toc_entry_alist[i]->name, toc_entry_alist[i]->name);
2433fb98d4aSespie }
244*a1acfa9bSespie add_word_args ("href=\"%s#%s</a>\n",
2453fb98d4aSespie splitting ? toc_entry_alist[i]->html_file : "",
2463fb98d4aSespie toc_entry_alist[i]->name);
2473fb98d4aSespie }
2481cc83814Sespie
2491cc83814Sespie last_level = toc_entry_alist[i]->level;
2501cc83814Sespie }
2511cc83814Sespie
2521cc83814Sespie /* Go back to start level. */
2531cc83814Sespie if (toc_entry_alist[0]->level < last_level)
2541cc83814Sespie for (k = 0; k < (last_level-toc_entry_alist[0]->level); k++)
255*a1acfa9bSespie add_word ("</li></ul>\n");
2561cc83814Sespie
257*a1acfa9bSespie add_word ("</li></ul>\n</div>\n\n");
2581cc83814Sespie }
2591cc83814Sespie
2601cc83814Sespie /* print table of contents in ASCII (--no-headers)
2611cc83814Sespie May be we should create a new command line switch --ascii ? */
2621cc83814Sespie static void
contents_update_info(void)263*a1acfa9bSespie contents_update_info (void)
2641cc83814Sespie {
2651cc83814Sespie int i;
2661cc83814Sespie int k;
2671cc83814Sespie
2681cc83814Sespie if (!toc_counter)
2691cc83814Sespie return;
2701cc83814Sespie
271*a1acfa9bSespie insert_string ((char *) _("Table of Contents"));
272*a1acfa9bSespie insert ('\n');
273*a1acfa9bSespie for (i = 0; i < strlen (_("Table of Contents")); i++)
274*a1acfa9bSespie insert ('*');
275*a1acfa9bSespie insert_string ("\n\n");
2761cc83814Sespie
2771cc83814Sespie for (i = 0; i < toc_counter; i++)
2781cc83814Sespie {
2791cc83814Sespie if (toc_entry_alist[i]->level == 0)
280*a1acfa9bSespie add_char ('\n');
2811cc83814Sespie
2821cc83814Sespie /* indention with two spaces per level, should this
2831cc83814Sespie changed? */
2841cc83814Sespie for (k = 0; k < toc_entry_alist[i]->level; k++)
285*a1acfa9bSespie insert_string (" ");
2861cc83814Sespie
287*a1acfa9bSespie insert_string (toc_entry_alist[i]->name);
288*a1acfa9bSespie insert ('\n');
2891cc83814Sespie }
290*a1acfa9bSespie insert_string ("\n\n");
2911cc83814Sespie }
2921cc83814Sespie
2931cc83814Sespie /* shortcontents in HTML; Should this produce a standalone file? */
2941cc83814Sespie static void
shortcontents_update_html(char * contents_filename)295*a1acfa9bSespie shortcontents_update_html (char *contents_filename)
2961cc83814Sespie {
2971cc83814Sespie int i;
298*a1acfa9bSespie char *toc_file = NULL;
2991cc83814Sespie
3001cc83814Sespie /* does exist any toc? */
3011cc83814Sespie if (!toc_counter)
3021cc83814Sespie return;
3031cc83814Sespie
304*a1acfa9bSespie add_html_block_elt_args ("\n<div class=\"shortcontents\">\n<h2>%s</h2>\n<ul>\n", _("Short Contents"));
3053fb98d4aSespie
3063fb98d4aSespie if (contents_filename)
3073fb98d4aSespie toc_file = filename_part (contents_filename);
3081cc83814Sespie
3091cc83814Sespie for (i = 0; i < toc_counter; i++)
3101cc83814Sespie {
3113fb98d4aSespie char *name = toc_entry_alist[i]->name;
3123fb98d4aSespie
3133fb98d4aSespie if (toc_entry_alist[i]->level == 0)
3141cc83814Sespie {
3153fb98d4aSespie if (contents_filename)
316*a1acfa9bSespie add_word_args ("<li><a href=\"%s#toc_%s</a></li>\n",
3173fb98d4aSespie splitting ? toc_file : "", name);
3183fb98d4aSespie else
319*a1acfa9bSespie add_word_args ("<a href=\"%s#%s</a>\n",
3203fb98d4aSespie splitting ? toc_entry_alist[i]->html_file : "", name);
3211cc83814Sespie }
3221cc83814Sespie }
323*a1acfa9bSespie add_word ("</ul>\n</div>\n\n");
3243fb98d4aSespie if (contents_filename)
3253fb98d4aSespie free (toc_file);
3261cc83814Sespie }
3271cc83814Sespie
3283fb98d4aSespie /* short contents in ASCII (--no-headers). */
3291cc83814Sespie static void
shortcontents_update_info(void)330*a1acfa9bSespie shortcontents_update_info (void)
3311cc83814Sespie {
3321cc83814Sespie int i;
3331cc83814Sespie
3341cc83814Sespie if (!toc_counter)
3351cc83814Sespie return;
3361cc83814Sespie
337*a1acfa9bSespie insert_string ((char *) _("Short Contents"));
338*a1acfa9bSespie insert ('\n');
339*a1acfa9bSespie for (i = 0; i < strlen (_("Short Contents")); i++)
340*a1acfa9bSespie insert ('*');
341*a1acfa9bSespie insert_string ("\n\n");
3421cc83814Sespie
3431cc83814Sespie for (i = 0; i < toc_counter; i++)
3441cc83814Sespie {
3453fb98d4aSespie if (toc_entry_alist[i]->level == 0)
3461cc83814Sespie {
347*a1acfa9bSespie insert_string (toc_entry_alist[i]->name);
348*a1acfa9bSespie insert ('\n');
3491cc83814Sespie }
3501cc83814Sespie }
351*a1acfa9bSespie insert_string ("\n\n");
3521cc83814Sespie }
3531cc83814Sespie
3541cc83814Sespie void
cm_contents(int arg)355*a1acfa9bSespie cm_contents (int arg)
3561cc83814Sespie {
357*a1acfa9bSespie /* the file where we found the @contents directive */
358*a1acfa9bSespie static char *contents_filename;
3591cc83814Sespie
360*a1acfa9bSespie /* No need to mess with delayed stuff for XML and Docbook. */
361*a1acfa9bSespie if (xml)
362*a1acfa9bSespie {
363*a1acfa9bSespie if (arg == START)
364*a1acfa9bSespie {
365*a1acfa9bSespie int elt = STREQ (command, "contents") ? CONTENTS : SHORTCONTENTS;
366*a1acfa9bSespie xml_insert_element (elt, START);
367*a1acfa9bSespie xml_insert_element (elt, END);
3681cc83814Sespie }
369*a1acfa9bSespie }
370*a1acfa9bSespie else if (!handling_delayed_writes)
3711cc83814Sespie {
372*a1acfa9bSespie register_delayed_write (STREQ (command, "contents")
373*a1acfa9bSespie ? "@contents" : "@shortcontents");
374*a1acfa9bSespie
375*a1acfa9bSespie if (html && STREQ (command, "contents"))
3761cc83814Sespie {
3771cc83814Sespie if (contents_filename)
3781cc83814Sespie free (contents_filename);
3791cc83814Sespie contents_filename = xstrdup (current_output_filename);
3801cc83814Sespie }
3811cc83814Sespie }
382*a1acfa9bSespie else if (html)
383*a1acfa9bSespie STREQ (command, "contents")
384*a1acfa9bSespie ? contents_update_html () : shortcontents_update_html (contents_filename);
385*a1acfa9bSespie else if (no_headers)
386*a1acfa9bSespie STREQ (command, "contents")
387*a1acfa9bSespie ? contents_update_info () : shortcontents_update_info ();
3881cc83814Sespie }
389