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