xref: /openbsd-src/gnu/usr.bin/texinfo/makeinfo/toc.c (revision a1acfa9b69ad64eb720639240c8438f11107dc85)
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 "&amp;".  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