xref: /netbsd-src/external/gpl2/texinfo/dist/makeinfo/toc.c (revision 29619d2afe564e54d657b83e5a3ae89584f83720)
1 /*	$NetBSD: toc.c,v 1.1.1.1 2016/01/14 00:11:29 christos Exp $	*/
2 
3 /* toc.c -- table of contents handling.
4    Id: toc.c,v 1.6 2004/04/11 17:56:47 karl Exp
5 
6    Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
7 
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2, or (at your option)
11    any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 
22    Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>.  */
23 
24 #include "system.h"
25 #include "makeinfo.h"
26 #include "cmds.h"
27 #include "files.h"
28 #include "macro.h"
29 #include "node.h"
30 #include "html.h"
31 #include "lang.h"
32 #include "makeinfo.h"
33 #include "sectioning.h"
34 #include "toc.h"
35 #include "xml.h"
36 
37 /* array of toc entries */
38 static TOC_ENTRY_ELT **toc_entry_alist = NULL;
39 
40 /* toc_counter start from 0 ... n for every @chapter, @section ... */
41 static int toc_counter = 0;
42 
43 /* Routine to add an entry to the table of contents */
44 int
toc_add_entry(char * tocname,int level,char * node_name,char * anchor)45 toc_add_entry (char *tocname, int level, char *node_name, char *anchor)
46 {
47   char *tocname_and_node, *expanded_node, *d;
48   char *s = NULL;
49   char *filename = NULL;
50 
51   if (!node_name)
52     node_name = "";
53 
54   /* I assume that xrealloc behaves like xmalloc if toc_entry_alist is
55      NULL */
56   toc_entry_alist = xrealloc (toc_entry_alist,
57                               (toc_counter + 1) * sizeof (TOC_ENTRY_ELT *));
58 
59   toc_entry_alist[toc_counter] = xmalloc (sizeof (TOC_ENTRY_ELT));
60 
61   if (html)
62     {
63       /* We need to insert the expanded node name into the toc, so
64          that when we eventually output the toc, its <a ref= link will
65          point to the <a name= tag created by cm_node in the navigation
66          bar.  We cannot expand the containing_node member, for the
67          reasons explained in the WARNING below.  We also cannot wait
68          with the node name expansion until the toc is actually output,
69          since by that time the macro definitions may have been changed.
70          So instead we store in the tocname member the expanded node
71          name and the toc name concatenated together (with the necessary
72          html markup), since that's how they are output.  */
73       if (!anchor)
74         s = expanded_node = expand_node_name (node_name);
75       else
76         expanded_node = anchor;
77       if (splitting)
78 	{
79 	  if (!anchor)
80 	    filename = nodename_to_filename (expanded_node);
81 	  else
82 	    filename = filename_part (current_output_filename);
83 	}
84       /* Sigh...  Need to HTML-escape the expanded node name like
85          add_anchor_name does, except that we are not writing this to
86          the output, so can't use add_anchor_name...  */
87       /* The factor 5 in the next allocation is because the maximum
88          expansion of HTML-escaping is for the & character, which is
89          output as "&amp;".  2 is for "> that separates node from tocname.  */
90       d = tocname_and_node = (char *)xmalloc (2 + 5 * strlen (expanded_node)
91                                               + strlen (tocname) + 1);
92       if (!anchor)
93         {
94           for (; *s; s++)
95             {
96               if (cr_or_whitespace (*s))
97                 *d++ = '-';
98               else if (! URL_SAFE_CHAR (*s))
99                 {
100                   sprintf (d, "_00%x", (unsigned char) *s);
101                   /* do this manually since sprintf returns char * on
102                      SunOS 4 and other old systems.  */
103                   while (*d)
104                     d++;
105                 }
106               else
107                 *d++ = *s;
108             }
109           strcpy (d, "\">");
110         }
111       else
112         /* Section outside any node, they provided explicit anchor.  */
113         strcpy (d, anchor);
114       strcat (d, tocname);
115       free (tocname);       /* it was malloc'ed by substring() */
116       free (expanded_node);
117       toc_entry_alist[toc_counter]->name = tocname_and_node;
118     }
119   else
120     toc_entry_alist[toc_counter]->name = tocname;
121   /* WARNING!  The node name saved in containing_node member must
122      be the node name with _only_ macros expanded (the macros in
123      the node name are expanded by cm_node when it grabs the name
124      from the @node directive).  Non-macros, like @value, @@ and
125      other @-commands must NOT be expanded in containing_node,
126      because toc_find_section_of_node looks up the node name where
127      they are also unexpanded.  You *have* been warned!  */
128   toc_entry_alist[toc_counter]->containing_node = xstrdup (node_name);
129   toc_entry_alist[toc_counter]->level = level;
130   toc_entry_alist[toc_counter]->number = toc_counter;
131   toc_entry_alist[toc_counter]->html_file = filename;
132 
133   /* have to be done at least */
134   return toc_counter++;
135 }
136 
137 /* Return the name of a chapter/section/subsection etc. that
138    corresponds to the node NODE.  If the node isn't found,
139    return NULL.
140 
141    WARNING!  This function relies on NODE being unexpanded
142    except for macros (i.e., @value, @@, and other non-macros
143    should NOT be expanded), because the containing_node member
144    stores unexpanded node names.
145 
146    Note that this function returns the first section whose
147    containing node is NODE.  Thus, they will lose if they use
148    more than a single chapter structioning command in a node,
149    or if they have a node without any structuring commands.  */
150 char *
toc_find_section_of_node(char * node)151 toc_find_section_of_node (char *node)
152 {
153   int i;
154 
155   if (!node)
156     node = "";
157   for (i = 0; i < toc_counter; i++)
158     if (STREQ (node, toc_entry_alist[i]->containing_node))
159       return toc_entry_alist[i]->name;
160 
161   return NULL;
162 }
163 
164 /* free up memory used by toc entries */
165 void
toc_free(void)166 toc_free (void)
167 {
168   int i;
169 
170   if (toc_counter)
171     {
172       for (i = 0; i < toc_counter; i++)
173         {
174           free (toc_entry_alist[i]->name);
175           free (toc_entry_alist[i]->containing_node);
176           free (toc_entry_alist[i]);
177         }
178 
179       free (toc_entry_alist);
180       toc_entry_alist = NULL; /* to be sure ;-) */
181       toc_counter = 0; /* to be absolutley sure ;-) */
182     }
183 }
184 
185 /* Print table of contents in HTML.  */
186 
187 static void
contents_update_html(void)188 contents_update_html (void)
189 {
190   int i;
191   int k;
192   int last_level;
193 
194   /* does exist any toc? */
195   if (!toc_counter)
196       /* no, so return to sender ;-) */
197       return;
198 
199   add_html_block_elt_args ("\n<div class=\"contents\">\n<h2>%s</h2>\n<ul>\n", _("Table of Contents"));
200 
201   last_level = toc_entry_alist[0]->level;
202 
203   for (i = 0; i < toc_counter; i++)
204     {
205       if (toc_entry_alist[i]->level > last_level)
206         {
207           /* unusual, but it is possible
208              @chapter ...
209              @subsubsection ...      ? */
210           for (k = 0; k < (toc_entry_alist[i]->level-last_level); k++)
211             add_html_block_elt ("<ul>\n");
212         }
213       else if (toc_entry_alist[i]->level < last_level)
214         {
215           /* @subsubsection ...
216              @chapter ... this IS usual.*/
217           for (k = 0; k < (last_level-toc_entry_alist[i]->level); k++)
218             add_word ("</li></ul>\n");
219         }
220 
221       /* No double entries in TOC.  */
222       if (!(i && strcmp (toc_entry_alist[i]->name,
223 			 toc_entry_alist[i-1]->name) == 0))
224         {
225           /* each toc entry is a list item.  */
226           add_word ("<li>");
227 
228           /* Insert link -- to an external file if splitting, or
229              within the current document if not splitting.  */
230 	  add_word ("<a ");
231           /* For chapters (only), insert an anchor that the short contents
232              will link to.  */
233           if (toc_entry_alist[i]->level == 0)
234 	    {
235 	      char *p = toc_entry_alist[i]->name;
236 
237 	      /* toc_entry_alist[i]->name has the form `foo">bar',
238 		 that is, it includes both the node name and anchor
239 		 text.  We need to find where `foo', the node name,
240 		 ends, and use that in toc_FOO.  */
241 	      while (*p && *p != '"')
242 		p++;
243 	      add_word_args ("name=\"toc_%.*s\" ",
244 		       p - toc_entry_alist[i]->name, toc_entry_alist[i]->name);
245 	    }
246 	  add_word_args ("href=\"%s#%s</a>\n",
247 		   splitting ? toc_entry_alist[i]->html_file : "",
248 		   toc_entry_alist[i]->name);
249         }
250 
251       last_level = toc_entry_alist[i]->level;
252     }
253 
254   /* Go back to start level. */
255   if (toc_entry_alist[0]->level < last_level)
256     for (k = 0; k < (last_level-toc_entry_alist[0]->level); k++)
257       add_word ("</li></ul>\n");
258 
259   add_word ("</li></ul>\n</div>\n\n");
260 }
261 
262 /* print table of contents in ASCII (--no-headers)
263    May be we should create a new command line switch --ascii ? */
264 static void
contents_update_info(void)265 contents_update_info (void)
266 {
267   int i;
268   int k;
269 
270   if (!toc_counter)
271       return;
272 
273   insert_string ((char *) _("Table of Contents"));
274   insert ('\n');
275   for (i = 0; i < strlen (_("Table of Contents")); i++)
276     insert ('*');
277   insert_string ("\n\n");
278 
279   for (i = 0; i < toc_counter; i++)
280     {
281       if (toc_entry_alist[i]->level == 0)
282         add_char ('\n');
283 
284       /* indention with two spaces per level, should this
285          changed? */
286       for (k = 0; k < toc_entry_alist[i]->level; k++)
287         insert_string ("  ");
288 
289       insert_string (toc_entry_alist[i]->name);
290       insert ('\n');
291     }
292   insert_string ("\n\n");
293 }
294 
295 /* shortcontents in HTML; Should this produce a standalone file? */
296 static void
shortcontents_update_html(char * contents_filename)297 shortcontents_update_html (char *contents_filename)
298 {
299   int i;
300   char *toc_file = NULL;
301 
302   /* does exist any toc? */
303   if (!toc_counter)
304     return;
305 
306   add_html_block_elt_args ("\n<div class=\"shortcontents\">\n<h2>%s</h2>\n<ul>\n", _("Short Contents"));
307 
308   if (contents_filename)
309     toc_file = filename_part (contents_filename);
310 
311   for (i = 0; i < toc_counter; i++)
312     {
313       char *name = toc_entry_alist[i]->name;
314 
315       if (toc_entry_alist[i]->level == 0)
316 	{
317 	  if (contents_filename)
318 	    add_word_args ("<li><a href=\"%s#toc_%s</a></li>\n",
319 		     splitting ? toc_file : "", name);
320 	  else
321 	    add_word_args ("<a href=\"%s#%s</a>\n",
322 		     splitting ? toc_entry_alist[i]->html_file : "", name);
323 	}
324     }
325   add_word ("</ul>\n</div>\n\n");
326   if (contents_filename)
327     free (toc_file);
328 }
329 
330 /* short contents in ASCII (--no-headers).  */
331 static void
shortcontents_update_info(void)332 shortcontents_update_info (void)
333 {
334   int i;
335 
336   if (!toc_counter)
337       return;
338 
339   insert_string ((char *) _("Short Contents"));
340   insert ('\n');
341   for (i = 0; i < strlen (_("Short Contents")); i++)
342     insert ('*');
343   insert_string ("\n\n");
344 
345   for (i = 0; i < toc_counter; i++)
346     {
347       if (toc_entry_alist[i]->level == 0)
348         {
349           insert_string (toc_entry_alist[i]->name);
350           insert ('\n');
351         }
352     }
353   insert_string ("\n\n");
354 }
355 
356 void
cm_contents(int arg)357 cm_contents (int arg)
358 {
359   /* the file where we found the @contents directive */
360   static char *contents_filename;
361 
362   /* No need to mess with delayed stuff for XML and Docbook.  */
363   if (xml)
364     {
365       if (arg == START)
366         {
367           int elt = STREQ (command, "contents") ? CONTENTS : SHORTCONTENTS;
368           xml_insert_element (elt, START);
369           xml_insert_element (elt, END);
370         }
371     }
372   else if (!handling_delayed_writes)
373     {
374       register_delayed_write (STREQ (command, "contents")
375           ? "@contents" : "@shortcontents");
376 
377       if (html && STREQ (command, "contents"))
378         {
379           if (contents_filename)
380             free (contents_filename);
381           contents_filename = xstrdup (current_output_filename);
382         }
383     }
384   else if (html)
385     STREQ (command, "contents")
386       ? contents_update_html () : shortcontents_update_html (contents_filename);
387   else if (no_headers)
388     STREQ (command, "contents")
389       ? contents_update_info () : shortcontents_update_info ();
390 }
391