summaryrefslogtreecommitdiff
path: root/makeinfo/toc.c
diff options
context:
space:
mode:
Diffstat (limited to 'makeinfo/toc.c')
-rw-r--r--makeinfo/toc.c378
1 files changed, 378 insertions, 0 deletions
diff --git a/makeinfo/toc.c b/makeinfo/toc.c
new file mode 100644
index 0000000..3f0fd87
--- /dev/null
+++ b/makeinfo/toc.c
@@ -0,0 +1,378 @@
+/* toc.c -- table of contents handling.
+ $Id: toc.c,v 1.12 2008/05/19 18:26:48 karl Exp $
+
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2007, 2008
+ Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
+
+#include "system.h"
+#include "makeinfo.h"
+#include "cmds.h"
+#include "files.h"
+#include "macro.h"
+#include "node.h"
+#include "html.h"
+#include "lang.h"
+#include "makeinfo.h"
+#include "sectioning.h"
+#include "toc.h"
+#include "xml.h"
+
+/* array of toc entries */
+static TOC_ENTRY_ELT **toc_entry_alist = NULL;
+
+/* toc_counter start from 0 ... n for every @chapter, @section ... */
+static int toc_counter = 0;
+
+/* Routine to add an entry to the table of contents */
+int
+toc_add_entry (char *tocname, int level, char *node_name, char *anchor)
+{
+ char *expanded_node, *d;
+ char *s = NULL;
+ char *filename = NULL;
+
+ if (!node_name)
+ node_name = "";
+
+ /* I assume that xrealloc behaves like xmalloc if toc_entry_alist is
+ NULL */
+ toc_entry_alist = xrealloc (toc_entry_alist,
+ (toc_counter + 1) * sizeof (TOC_ENTRY_ELT *));
+
+ toc_entry_alist[toc_counter] = xmalloc (sizeof (TOC_ENTRY_ELT));
+
+ if (html)
+ {
+ /* We need to insert the expanded node name into the toc, so
+ that when we eventually output the toc, its <a ref= link will
+ point to the <a name= tag created by cm_node in the navigation
+ bar. We cannot expand the containing_node member, for the
+ reasons explained in the WARNING below. We also cannot wait
+ with the node name expansion until the toc is actually output,
+ since by that time the macro definitions may have been changed.
+ So instead we store in the tocname member the expanded node
+ name and the toc name concatenated together (with the necessary
+ html markup), since that's how they are output. */
+ if (!anchor)
+ s = expanded_node = expand_node_name (node_name);
+ else
+ expanded_node = anchor;
+ if (splitting)
+ {
+ if (!anchor)
+ filename = nodename_to_filename (expanded_node);
+ else
+ filename = filename_part (current_output_filename);
+ }
+ if (!anchor)
+ /* Need to HTML-escape the expanded node name like
+ add_anchor_name does... */
+ d = escaped_anchor_name (expanded_node);
+ else
+ /* Section outside any node, they provided explicit anchor. */
+ d = xstrdup(anchor);
+
+ /* Add space for the "> which may be needed, and the tocname */
+ d = xrealloc (d, strlen (d) + strlen (tocname) + 3);
+ if (!anchor)
+ strcat (d, "\">");
+ strcat (d, tocname);
+ free (tocname); /* it was malloc'ed by substring() */
+ free (expanded_node);
+ toc_entry_alist[toc_counter]->name = d;
+ }
+ else
+ toc_entry_alist[toc_counter]->name = tocname;
+ /* WARNING! The node name saved in containing_node member must
+ be the node name with _only_ macros expanded (the macros in
+ the node name are expanded by cm_node when it grabs the name
+ from the @node directive). Non-macros, like @value, @@ and
+ other @-commands must NOT be expanded in containing_node,
+ because toc_find_section_of_node looks up the node name where
+ they are also unexpanded. You *have* been warned! */
+ toc_entry_alist[toc_counter]->containing_node = xstrdup (node_name);
+ toc_entry_alist[toc_counter]->level = level;
+ toc_entry_alist[toc_counter]->number = toc_counter;
+ toc_entry_alist[toc_counter]->html_file = filename;
+
+ /* have to be done at least */
+ return toc_counter++;
+}
+
+/* Return the name of a chapter/section/subsection etc. that
+ corresponds to the node NODE. If the node isn't found,
+ return NULL.
+
+ WARNING! This function relies on NODE being unexpanded
+ except for macros (i.e., @value, @@, and other non-macros
+ should NOT be expanded), because the containing_node member
+ stores unexpanded node names.
+
+ Note that this function returns the first section whose
+ containing node is NODE. Thus, they will lose if they use
+ more than a single chapter structioning command in a node,
+ or if they have a node without any structuring commands. */
+char *
+toc_find_section_of_node (char *node)
+{
+ int i;
+
+ if (!node)
+ node = "";
+ for (i = 0; i < toc_counter; i++)
+ if (STREQ (node, toc_entry_alist[i]->containing_node))
+ return toc_entry_alist[i]->name;
+
+ return NULL;
+}
+
+/* free up memory used by toc entries */
+void
+toc_free (void)
+{
+ int i;
+
+ if (toc_counter)
+ {
+ for (i = 0; i < toc_counter; i++)
+ {
+ free (toc_entry_alist[i]->name);
+ free (toc_entry_alist[i]->containing_node);
+ free (toc_entry_alist[i]);
+ }
+
+ free (toc_entry_alist);
+ toc_entry_alist = NULL; /* to be sure ;-) */
+ toc_counter = 0; /* to be absolutley sure ;-) */
+ }
+}
+
+/* Print table of contents in HTML. */
+
+static void
+contents_update_html (void)
+{
+ int i;
+ int k;
+ int last_level;
+
+ /* does exist any toc? */
+ if (!toc_counter)
+ /* no, so return to sender ;-) */
+ return;
+
+ add_html_block_elt_args ("\n<div class=\"contents\">\n<h2>%s</h2>\n<ul>\n", gdt("Table of Contents"));
+
+ last_level = toc_entry_alist[0]->level;
+
+ for (i = 0; i < toc_counter; i++)
+ {
+ if (toc_entry_alist[i]->level > last_level)
+ {
+ /* unusual, but it is possible
+ @chapter ...
+ @subsubsection ... ? */
+ for (k = 0; k < (toc_entry_alist[i]->level-last_level); k++)
+ add_html_block_elt ("<ul>\n");
+ }
+ else if (toc_entry_alist[i]->level < last_level)
+ {
+ /* @subsubsection ...
+ @chapter ... this IS usual.*/
+ for (k = 0; k < (last_level-toc_entry_alist[i]->level); k++)
+ add_word ("</li></ul>\n");
+ }
+
+ /* No double entries in TOC. */
+ if (!(i && strcmp (toc_entry_alist[i]->name,
+ toc_entry_alist[i-1]->name) == 0))
+ {
+ /* each toc entry is a list item. */
+ add_word ("<li>");
+
+ /* Insert link -- to an external file if splitting, or
+ within the current document if not splitting. */
+ add_word ("<a ");
+ /* For chapters (only), insert an anchor that the short contents
+ will link to. */
+ if (toc_entry_alist[i]->level == 0)
+ {
+ char *p = toc_entry_alist[i]->name;
+
+ /* toc_entry_alist[i]->name has the form `foo">bar',
+ that is, it includes both the node name and anchor
+ text. We need to find where `foo', the node name,
+ ends, and use that in toc_FOO. */
+ while (*p && *p != '"')
+ p++;
+ add_word_args ("name=\"toc_%.*s\" ",
+ p - toc_entry_alist[i]->name, toc_entry_alist[i]->name);
+ /* save the link if necessary */
+ if (internal_links_stream)
+ {
+ fprintf (internal_links_stream, "%s#toc_%.*s\ttoc\t%s\n",
+ splitting ? toc_entry_alist[i]->html_file : "",
+ p - toc_entry_alist[i]->name, toc_entry_alist[i]->name,
+ p + 2);
+ }
+ }
+ add_word_args ("href=\"%s#%s</a>\n",
+ splitting ? toc_entry_alist[i]->html_file : "",
+ toc_entry_alist[i]->name);
+ }
+
+ last_level = toc_entry_alist[i]->level;
+ }
+
+ /* Go back to start level. */
+ if (toc_entry_alist[0]->level < last_level)
+ for (k = 0; k < (last_level-toc_entry_alist[0]->level); k++)
+ add_word ("</li></ul>\n");
+
+ add_word ("</li></ul>\n</div>\n\n");
+}
+
+/* print table of contents in ASCII (--no-headers)
+ May be we should create a new command line switch --ascii ? */
+static void
+contents_update_info (void)
+{
+ int i;
+ int k;
+
+ if (!toc_counter)
+ return;
+
+ insert_string ((char *) gdt("Table of Contents"));
+ insert ('\n');
+ for (i = 0; i < strlen (gdt("Table of Contents")); i++)
+ insert ('*');
+ insert_string ("\n\n");
+
+ for (i = 0; i < toc_counter; i++)
+ {
+ if (toc_entry_alist[i]->level == 0)
+ add_char ('\n');
+
+ /* indention with two spaces per level, should this
+ changed? */
+ for (k = 0; k < toc_entry_alist[i]->level; k++)
+ insert_string (" ");
+
+ insert_string (toc_entry_alist[i]->name);
+ insert ('\n');
+ }
+ insert_string ("\n\n");
+}
+
+/* shortcontents in HTML; Should this produce a standalone file? */
+static void
+shortcontents_update_html (char *contents_filename)
+{
+ int i;
+ char *toc_file = NULL;
+
+ /* does exist any toc? */
+ if (!toc_counter)
+ return;
+
+ add_html_block_elt_args ("\n<div class=\"shortcontents\">\n<h2>%s</h2>\n<ul>\n", gdt("Short Contents"));
+
+ if (contents_filename)
+ toc_file = filename_part (contents_filename);
+
+ for (i = 0; i < toc_counter; i++)
+ {
+ char *name = toc_entry_alist[i]->name;
+
+ if (toc_entry_alist[i]->level == 0)
+ {
+ if (contents_filename)
+ add_word_args ("<li><a href=\"%s#toc_%s</a></li>\n",
+ splitting ? toc_file : "", name);
+ else
+ add_word_args ("<a href=\"%s#%s</a>\n",
+ splitting ? toc_entry_alist[i]->html_file : "", name);
+ }
+ }
+ add_word ("</ul>\n</div>\n\n");
+ if (contents_filename)
+ free (toc_file);
+}
+
+/* short contents in ASCII (--no-headers). */
+static void
+shortcontents_update_info (void)
+{
+ int i;
+
+ if (!toc_counter)
+ return;
+
+ insert_string ((char *) gdt("Short Contents"));
+ insert ('\n');
+ for (i = 0; i < strlen (gdt("Short Contents")); i++)
+ insert ('*');
+ insert_string ("\n\n");
+
+ for (i = 0; i < toc_counter; i++)
+ {
+ if (toc_entry_alist[i]->level == 0)
+ {
+ insert_string (toc_entry_alist[i]->name);
+ insert ('\n');
+ }
+ }
+ insert_string ("\n\n");
+}
+
+void
+cm_contents (int arg)
+{
+ /* the file where we found the @contents directive */
+ static char *contents_filename;
+
+ /* No need to mess with delayed stuff for XML and Docbook. */
+ if (xml)
+ {
+ if (arg == START)
+ {
+ int elt = STREQ (command, "contents") ? CONTENTS : SHORTCONTENTS;
+ xml_insert_element (elt, START);
+ xml_insert_element (elt, END);
+ }
+ }
+ else if (!handling_delayed_writes)
+ {
+ register_delayed_write (STREQ (command, "contents")
+ ? "@contents" : "@shortcontents");
+
+ if (html && STREQ (command, "contents"))
+ {
+ if (contents_filename)
+ free (contents_filename);
+ contents_filename = xstrdup (current_output_filename);
+ }
+ }
+ else if (html)
+ STREQ (command, "contents")
+ ? contents_update_html () : shortcontents_update_html (contents_filename);
+ else if (no_headers)
+ STREQ (command, "contents")
+ ? contents_update_info () : shortcontents_update_info ();
+}