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