diff options
Diffstat (limited to 'contrib/texinfo/makeinfo/toc.c')
-rw-r--r-- | contrib/texinfo/makeinfo/toc.c | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/contrib/texinfo/makeinfo/toc.c b/contrib/texinfo/makeinfo/toc.c new file mode 100644 index 000000000000..41c5ffb8edcf --- /dev/null +++ b/contrib/texinfo/makeinfo/toc.c @@ -0,0 +1,476 @@ +/* toc.c -- table of contents handling. + $Id: toc.c,v 1.14 1999/08/09 20:28:18 karl Exp $ + + Copyright (C) 1999 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 2, 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + 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 "lang.h" +#include "makeinfo.h" +#include "sectioning.h" +#include "toc.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; + +/* the file where we found the @contents directive */ +char *contents_filename; + +/* the file where we found the @shortcontents directive */ +char *shortcontents_filename; + +static const char contents_placebo[] = "\n...Table of Contents...\n"; +static const char shortcontents_placebo[] = "\n...Short Contents...\n"; +static const char lots_of_stars[] = +"***************************************************************************"; + + +/* Routine to add an entry to the table of contents */ +int +toc_add_entry (tocname, level, node_name, anchor) + char *tocname; + int level; + char *node_name; + char *anchor; +{ + char *tocname_and_node, *expanded_node, *s, *d; + + 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; + /* Sigh... Need to HTML-escape the expanded node name like + add_anchor_name does, except that we are not writing this to + the output, so can't use add_anchor_name... */ + /* The factor 5 in the next allocation is because the maximum + expansion of HTML-escaping is for the & character, which is + output as "&". 2 is for "> that separates node from tocname. */ + d = tocname_and_node = (char *)xmalloc (2 + 5 * strlen (expanded_node) + + strlen (tocname) + 1); + if (!anchor) + { + for (; *s; s++) + { + if (*s == '&') + { + strcpy (d, "&"); + d += 5; + } + else if (! URL_SAFE_CHAR (*s)) + { + sprintf (d, "%%%x", (unsigned char) *s); + /* do this manually since sprintf returns char * on + SunOS 4 and other old systems. */ + while (*d) + d++; + } + else + *d++ = *s; + } + strcpy (d, "\">"); + } + else + /* Section outside any node, they provided explicit anchor. */ + strcpy (d, anchor); + strcat (d, tocname); + free (tocname); /* it was malloc'ed by substring() */ + free (expanded_node); + toc_entry_alist[toc_counter]->name = tocname_and_node; + } + 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; + + /* 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 (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 () +{ + 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, may be we can produce a standalone + HTML file? */ +static void +contents_update_html (fp) + FILE *fp; +{ + int i; + int k; + int last_level; + + /* does exist any toc? */ + if (!toc_counter) + /* no, so return to sender ;-) */ + return; + + flush_output (); /* in case we are writing stdout */ + + fprintf (fp, "\n<h1>%s</h1>\n<ul>\n", _("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++) + fputs ("<ul>\n", fp); + } + 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++) + fputs ("</ul>\n", fp); + } + + fprintf (fp, "<li><a href=\"#%s</a>\n", 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++) + fputs ("</ul>\n", fp); + + fputs ("</ul>\n\n", fp); +} + +/* print table of contents in ASCII (--no-headers) + May be we should create a new command line switch --ascii ? */ +static void +contents_update_info (fp) + FILE *fp; +{ + int i; + int k; + + if (!toc_counter) + return; + + flush_output (); /* in case we are writing stdout */ + + fprintf (fp, "%s\n%.*s\n\n", _("Table of Contents"), + (int) strlen (_("Table of Contents")), lots_of_stars); + + for (i = 0; i < toc_counter; i++) + { + if (toc_entry_alist[i]->level == 0) + fputs ("\n", fp); + + /* indention with two spaces per level, should this + changed? */ + for (k = 0; k < toc_entry_alist[i]->level; k++) + fputs (" ", fp); + + fprintf (fp, "%s\n", toc_entry_alist[i]->name); + } + fputs ("\n\n", fp); +} + +/* shortcontents in HTML; Should this produce a standalone file? */ +static void +shortcontents_update_html (fp) + FILE *fp; +{ + int i; + + /* does exist any toc? */ + if (!toc_counter) + return; + + flush_output (); /* in case we are writing stdout */ + + fprintf (fp, "\n<h1>%s</h1>\n<ul>\n", _("Short Contents")); + + for (i = 0; i < toc_counter; i++) + { + if ((toc_entry_alist[i])->level == 0) + { + fputs ("<li>", fp); + fprintf (fp, "<a href=\"#%s\n", toc_entry_alist[i]->name); + } + } + + fputs ("</ul>\n\n", fp); +} + +/* short contents in ASCII (--no-headers). + May be we should create a new command line switch --ascii ? */ +static void +shortcontents_update_info (fp) + FILE *fp; +{ + int i; + + if (!toc_counter) + return; + + flush_output (); /* in case we are writing stdout */ + + fprintf (fp, "%s\n%.*s\n\n", _("Short Contents"), + (int) strlen (_("Short Contents")), lots_of_stars); + + for (i = 0; i < toc_counter; i++) + { + if ((toc_entry_alist[i])->level == 0) + fprintf (fp, "%s\n", toc_entry_alist[i]->name); + } + fputs ("\n\n", fp); +} + + +static FILE *toc_fp; +static char *toc_buf; + +static int +rewrite_top (fname, placebo) + const char *fname, *placebo; +{ + int idx; + + toc_buf = find_and_load (fname); + + if (!toc_buf) + { + /* Can't rewrite standard output. No point in complaining. */ + if (!STREQ (fname, "-")) + fs_error (fname); + return -1; + } + + idx = search_forward (placebo, 0); + + if (idx < 0) + { + error (_("%s: TOC should be here, but it was not found"), fname); + return -1; + } + + toc_fp = fopen (fname, "w"); + if (!toc_fp) + { + fs_error (fname); + return -1; + } + + if (fwrite (toc_buf, 1, idx, toc_fp) != idx) + { + fs_error (fname); + return -1; + } + + return idx + strlen (placebo); +} + +static void +contents_update () +{ + int cont_idx = rewrite_top (contents_filename, contents_placebo); + + if (cont_idx < 0) + return; + + if (html) + contents_update_html (toc_fp); + else + contents_update_info (toc_fp); + + if (fwrite (toc_buf + cont_idx, 1, input_text_length - cont_idx, toc_fp) + != input_text_length - cont_idx + || fclose (toc_fp) != 0) + fs_error (contents_filename); +} + +static void +shortcontents_update () +{ + int cont_idx = rewrite_top (shortcontents_filename, shortcontents_placebo); + + if (cont_idx < 0) + return; + + if (html) + shortcontents_update_html (toc_fp); + else + shortcontents_update_info (toc_fp); + + if (fwrite (toc_buf + cont_idx, 1, input_text_length - cont_idx - 1, toc_fp) + != input_text_length - cont_idx - 1 + || fclose (toc_fp) != 0) + fs_error (shortcontents_filename); +} + +void +toc_update () +{ + if (!html && !no_headers) + return; + + if (contents_filename) + contents_update (); + if (shortcontents_filename) + shortcontents_update (); +} + +void +cm_contents (arg) + int arg; +{ + if ((html || no_headers) && arg == START) + { + if (contents_filename) + { + free (contents_filename); + contents_filename = NULL; + } + + if (contents_filename && STREQ (contents_filename, "-")) + { + if (html) + contents_update_html (stdout); + else + contents_update_info (stdout); + } + else + { + contents_filename = xstrdup (current_output_filename); + insert_string (contents_placebo); /* just mark it, for now */ + } + } +} + +void +cm_shortcontents (arg) + int arg; +{ + if ((html || no_headers) && arg == START) + { + if (shortcontents_filename) + { + free (shortcontents_filename); + shortcontents_filename = NULL; + } + + if (shortcontents_filename && STREQ (shortcontents_filename, "-")) + { + if (html) + shortcontents_update_html (stdout); + else + shortcontents_update_info (stdout); + } + else + { + shortcontents_filename = xstrdup (current_output_filename); + insert_string (shortcontents_placebo); /* just mark it, for now */ + } + } +} |