Back to index

tetex-bin  3.0
toc.c
Go to the documentation of this file.
00001 /* toc.c -- table of contents handling.
00002    $Id: toc.c,v 1.6 2004/04/11 17:56:47 karl Exp $
00003 
00004    Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2, or (at your option)
00009    any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software
00018    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019 
00020    Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>.  */
00021 
00022 #include "system.h"
00023 #include "makeinfo.h"
00024 #include "cmds.h"
00025 #include "files.h"
00026 #include "macro.h"
00027 #include "node.h"
00028 #include "html.h"
00029 #include "lang.h"
00030 #include "makeinfo.h"
00031 #include "sectioning.h"
00032 #include "toc.h"
00033 #include "xml.h"
00034 
00035 /* array of toc entries */
00036 static TOC_ENTRY_ELT **toc_entry_alist = NULL;
00037 
00038 /* toc_counter start from 0 ... n for every @chapter, @section ... */
00039 static int toc_counter = 0;
00040 
00041 /* Routine to add an entry to the table of contents */
00042 int
00043 toc_add_entry (char *tocname, int level, char *node_name, char *anchor)
00044 {
00045   char *tocname_and_node, *expanded_node, *d;
00046   char *s = NULL;
00047   char *filename = NULL;
00048 
00049   if (!node_name)
00050     node_name = "";
00051 
00052   /* I assume that xrealloc behaves like xmalloc if toc_entry_alist is
00053      NULL */
00054   toc_entry_alist = xrealloc (toc_entry_alist,
00055                               (toc_counter + 1) * sizeof (TOC_ENTRY_ELT *));
00056 
00057   toc_entry_alist[toc_counter] = xmalloc (sizeof (TOC_ENTRY_ELT));
00058 
00059   if (html)
00060     {
00061       /* We need to insert the expanded node name into the toc, so
00062          that when we eventually output the toc, its <a ref= link will
00063          point to the <a name= tag created by cm_node in the navigation
00064          bar.  We cannot expand the containing_node member, for the
00065          reasons explained in the WARNING below.  We also cannot wait
00066          with the node name expansion until the toc is actually output,
00067          since by that time the macro definitions may have been changed.
00068          So instead we store in the tocname member the expanded node
00069          name and the toc name concatenated together (with the necessary
00070          html markup), since that's how they are output.  */
00071       if (!anchor)
00072         s = expanded_node = expand_node_name (node_name);
00073       else
00074         expanded_node = anchor;
00075       if (splitting)
00076        {
00077          if (!anchor)
00078            filename = nodename_to_filename (expanded_node);
00079          else
00080            filename = filename_part (current_output_filename);
00081        }
00082       /* Sigh...  Need to HTML-escape the expanded node name like
00083          add_anchor_name does, except that we are not writing this to
00084          the output, so can't use add_anchor_name...  */
00085       /* The factor 5 in the next allocation is because the maximum
00086          expansion of HTML-escaping is for the & character, which is
00087          output as "&amp;".  2 is for "> that separates node from tocname.  */
00088       d = tocname_and_node = (char *)xmalloc (2 + 5 * strlen (expanded_node)
00089                                               + strlen (tocname) + 1);
00090       if (!anchor)
00091         {
00092           for (; *s; s++)
00093             {
00094               if (cr_or_whitespace (*s))
00095                 *d++ = '-';
00096               else if (! URL_SAFE_CHAR (*s))
00097                 {
00098                   sprintf (d, "_00%x", (unsigned char) *s);
00099                   /* do this manually since sprintf returns char * on
00100                      SunOS 4 and other old systems.  */
00101                   while (*d)
00102                     d++;
00103                 }
00104               else
00105                 *d++ = *s;
00106             }
00107           strcpy (d, "\">");
00108         }
00109       else
00110         /* Section outside any node, they provided explicit anchor.  */
00111         strcpy (d, anchor);
00112       strcat (d, tocname);
00113       free (tocname);       /* it was malloc'ed by substring() */
00114       free (expanded_node);
00115       toc_entry_alist[toc_counter]->name = tocname_and_node;
00116     }
00117   else
00118     toc_entry_alist[toc_counter]->name = tocname;
00119   /* WARNING!  The node name saved in containing_node member must
00120      be the node name with _only_ macros expanded (the macros in
00121      the node name are expanded by cm_node when it grabs the name
00122      from the @node directive).  Non-macros, like @value, @@ and
00123      other @-commands must NOT be expanded in containing_node,
00124      because toc_find_section_of_node looks up the node name where
00125      they are also unexpanded.  You *have* been warned!  */
00126   toc_entry_alist[toc_counter]->containing_node = xstrdup (node_name);
00127   toc_entry_alist[toc_counter]->level = level;
00128   toc_entry_alist[toc_counter]->number = toc_counter;
00129   toc_entry_alist[toc_counter]->html_file = filename;
00130 
00131   /* have to be done at least */
00132   return toc_counter++;
00133 }
00134 
00135 /* Return the name of a chapter/section/subsection etc. that
00136    corresponds to the node NODE.  If the node isn't found,
00137    return NULL.
00138 
00139    WARNING!  This function relies on NODE being unexpanded
00140    except for macros (i.e., @value, @@, and other non-macros
00141    should NOT be expanded), because the containing_node member
00142    stores unexpanded node names.
00143 
00144    Note that this function returns the first section whose
00145    containing node is NODE.  Thus, they will lose if they use
00146    more than a single chapter structioning command in a node,
00147    or if they have a node without any structuring commands.  */
00148 char *
00149 toc_find_section_of_node (char *node)
00150 {
00151   int i;
00152 
00153   if (!node)
00154     node = "";
00155   for (i = 0; i < toc_counter; i++)
00156     if (STREQ (node, toc_entry_alist[i]->containing_node))
00157       return toc_entry_alist[i]->name;
00158 
00159   return NULL;
00160 }
00161 
00162 /* free up memory used by toc entries */
00163 void
00164 toc_free (void)
00165 {
00166   int i;
00167 
00168   if (toc_counter)
00169     {
00170       for (i = 0; i < toc_counter; i++)
00171         {
00172           free (toc_entry_alist[i]->name);
00173           free (toc_entry_alist[i]->containing_node);
00174           free (toc_entry_alist[i]);
00175         }
00176 
00177       free (toc_entry_alist);
00178       toc_entry_alist = NULL; /* to be sure ;-) */
00179       toc_counter = 0; /* to be absolutley sure ;-) */
00180     }
00181 }
00182 
00183 /* Print table of contents in HTML.  */
00184 
00185 static void
00186 contents_update_html (void)
00187 {
00188   int i;
00189   int k;
00190   int last_level;
00191 
00192   /* does exist any toc? */
00193   if (!toc_counter)
00194       /* no, so return to sender ;-) */
00195       return;
00196 
00197   add_html_block_elt_args ("\n<div class=\"contents\">\n<h2>%s</h2>\n<ul>\n", _("Table of Contents"));
00198 
00199   last_level = toc_entry_alist[0]->level;
00200 
00201   for (i = 0; i < toc_counter; i++)
00202     {
00203       if (toc_entry_alist[i]->level > last_level)
00204         {
00205           /* unusual, but it is possible
00206              @chapter ...
00207              @subsubsection ...      ? */
00208           for (k = 0; k < (toc_entry_alist[i]->level-last_level); k++)
00209             add_html_block_elt ("<ul>\n");
00210         }
00211       else if (toc_entry_alist[i]->level < last_level)
00212         {
00213           /* @subsubsection ...
00214              @chapter ... this IS usual.*/
00215           for (k = 0; k < (last_level-toc_entry_alist[i]->level); k++)
00216             add_word ("</li></ul>\n");
00217         }
00218 
00219       /* No double entries in TOC.  */
00220       if (!(i && strcmp (toc_entry_alist[i]->name,
00221                       toc_entry_alist[i-1]->name) == 0))
00222         {
00223           /* each toc entry is a list item.  */
00224           add_word ("<li>");
00225 
00226           /* Insert link -- to an external file if splitting, or
00227              within the current document if not splitting.  */
00228          add_word ("<a ");
00229           /* For chapters (only), insert an anchor that the short contents
00230              will link to.  */
00231           if (toc_entry_alist[i]->level == 0)
00232            {
00233              char *p = toc_entry_alist[i]->name;
00234 
00235              /* toc_entry_alist[i]->name has the form `foo">bar',
00236                that is, it includes both the node name and anchor
00237                text.  We need to find where `foo', the node name,
00238                ends, and use that in toc_FOO.  */
00239              while (*p && *p != '"')
00240               p++;
00241              add_word_args ("name=\"toc_%.*s\" ",
00242                      p - toc_entry_alist[i]->name, toc_entry_alist[i]->name);
00243            }
00244          add_word_args ("href=\"%s#%s</a>\n",
00245                  splitting ? toc_entry_alist[i]->html_file : "",
00246                  toc_entry_alist[i]->name);
00247         }
00248 
00249       last_level = toc_entry_alist[i]->level;
00250     }
00251 
00252   /* Go back to start level. */
00253   if (toc_entry_alist[0]->level < last_level)
00254     for (k = 0; k < (last_level-toc_entry_alist[0]->level); k++)
00255       add_word ("</li></ul>\n");
00256 
00257   add_word ("</li></ul>\n</div>\n\n");
00258 }
00259 
00260 /* print table of contents in ASCII (--no-headers)
00261    May be we should create a new command line switch --ascii ? */
00262 static void
00263 contents_update_info (void)
00264 {
00265   int i;
00266   int k;
00267 
00268   if (!toc_counter)
00269       return;
00270 
00271   insert_string ((char *) _("Table of Contents"));
00272   insert ('\n');
00273   for (i = 0; i < strlen (_("Table of Contents")); i++)
00274     insert ('*');
00275   insert_string ("\n\n");
00276 
00277   for (i = 0; i < toc_counter; i++)
00278     {
00279       if (toc_entry_alist[i]->level == 0)
00280         add_char ('\n');
00281 
00282       /* indention with two spaces per level, should this
00283          changed? */
00284       for (k = 0; k < toc_entry_alist[i]->level; k++)
00285         insert_string ("  ");
00286 
00287       insert_string (toc_entry_alist[i]->name);
00288       insert ('\n');
00289     }
00290   insert_string ("\n\n");
00291 }
00292 
00293 /* shortcontents in HTML; Should this produce a standalone file? */
00294 static void
00295 shortcontents_update_html (char *contents_filename)
00296 {
00297   int i;
00298   char *toc_file = NULL;
00299 
00300   /* does exist any toc? */
00301   if (!toc_counter)
00302     return;
00303 
00304   add_html_block_elt_args ("\n<div class=\"shortcontents\">\n<h2>%s</h2>\n<ul>\n", _("Short Contents"));
00305 
00306   if (contents_filename)
00307     toc_file = filename_part (contents_filename);
00308 
00309   for (i = 0; i < toc_counter; i++)
00310     {
00311       char *name = toc_entry_alist[i]->name;
00312 
00313       if (toc_entry_alist[i]->level == 0)
00314        {
00315          if (contents_filename)
00316            add_word_args ("<li><a href=\"%s#toc_%s</a></li>\n",
00317                    splitting ? toc_file : "", name);
00318          else
00319            add_word_args ("<a href=\"%s#%s</a>\n",
00320                    splitting ? toc_entry_alist[i]->html_file : "", name);
00321        }
00322     }
00323   add_word ("</ul>\n</div>\n\n");
00324   if (contents_filename)
00325     free (toc_file);
00326 }
00327 
00328 /* short contents in ASCII (--no-headers).  */
00329 static void
00330 shortcontents_update_info (void)
00331 {
00332   int i;
00333 
00334   if (!toc_counter)
00335       return;
00336 
00337   insert_string ((char *) _("Short Contents"));
00338   insert ('\n');
00339   for (i = 0; i < strlen (_("Short Contents")); i++)
00340     insert ('*');
00341   insert_string ("\n\n");
00342 
00343   for (i = 0; i < toc_counter; i++)
00344     {
00345       if (toc_entry_alist[i]->level == 0)
00346         {
00347           insert_string (toc_entry_alist[i]->name);
00348           insert ('\n');
00349         }
00350     }
00351   insert_string ("\n\n");
00352 }
00353 
00354 void
00355 cm_contents (int arg)
00356 {
00357   /* the file where we found the @contents directive */
00358   static char *contents_filename;
00359 
00360   /* No need to mess with delayed stuff for XML and Docbook.  */
00361   if (xml)
00362     {
00363       if (arg == START)
00364         {
00365           int elt = STREQ (command, "contents") ? CONTENTS : SHORTCONTENTS;
00366           xml_insert_element (elt, START);
00367           xml_insert_element (elt, END);
00368         }
00369     }
00370   else if (!handling_delayed_writes)
00371     {
00372       register_delayed_write (STREQ (command, "contents")
00373           ? "@contents" : "@shortcontents");
00374 
00375       if (html && STREQ (command, "contents"))
00376         {
00377           if (contents_filename)
00378             free (contents_filename);
00379           contents_filename = xstrdup (current_output_filename);
00380         }
00381     }
00382   else if (html)
00383     STREQ (command, "contents")
00384       ? contents_update_html () : shortcontents_update_html (contents_filename);
00385   else if (no_headers)
00386     STREQ (command, "contents")
00387       ? contents_update_info () : shortcontents_update_info ();
00388 }