/* 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 . Originally written by Karl Heinz Marbaise . */ #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 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
\n

%s

\n
\n"); add_word ("\n
\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
\n

%s

\n\n
\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 (); }