xref: /openbsd/gnu/usr.bin/texinfo/makeinfo/toc.c (revision 78b63d65)
1 /* toc.c -- table of contents handling.
2    $Id: toc.c,v 1.1.1.1 2000/02/09 01:25:31 espie Exp $
3 
4    Copyright (C) 1999 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    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 "lang.h"
29 #include "makeinfo.h"
30 #include "sectioning.h"
31 #include "toc.h"
32 
33 
34 
35 
36 /* array of toc entries */
37 static TOC_ENTRY_ELT **toc_entry_alist = NULL;
38 
39 /* toc_counter start from 0 ... n for every @chapter, @section ... */
40 static int toc_counter = 0;
41 
42 /* the file where we found the @contents directive */
43 char *contents_filename;
44 
45 /* the file where we found the @shortcontents directive */
46 char *shortcontents_filename;
47 
48 static const char contents_placebo[] = "\n...Table of Contents...\n";
49 static const char shortcontents_placebo[] = "\n...Short Contents...\n";
50 static const char lots_of_stars[] =
51 "***************************************************************************";
52 
53 
54 /* Routine to add an entry to the table of contents */
55 int
56 toc_add_entry (tocname, level, node_name, anchor)
57      char *tocname;
58      int level;
59      char *node_name;
60      char *anchor;
61 {
62   char *tocname_and_node, *expanded_node, *s, *d;
63 
64   if (!node_name)
65     node_name = "";
66 
67   /* I assume that xrealloc behaves like xmalloc if toc_entry_alist is
68      NULL */
69   toc_entry_alist = xrealloc (toc_entry_alist,
70                               (toc_counter + 1) * sizeof (TOC_ENTRY_ELT *));
71 
72   toc_entry_alist[toc_counter] = xmalloc (sizeof (TOC_ENTRY_ELT));
73 
74   if (html)
75     {
76       /* We need to insert the expanded node name into the TOC, so
77 	 that when we eventually output the TOC, its <A REF= link will
78 	 point to the <A NAME= tag created by cm_node in the navigation
79 	 bar.  We cannot expand the containing_node member, for the
80 	 reasons explained in the WARNING below.  We also cannot wait
81 	 with the node name expansion until the TOC is actually output,
82 	 since by that time the macro definitions may have been changed.
83 	 So instead we store in the tocname member the expanded node
84 	 name and the TOC name concatenated together (with the necessary
85 	 HTML markup), since that's how they are output.  */
86       if (!anchor)
87 	s = expanded_node = expand_node_name (node_name);
88       else
89 	expanded_node = anchor;
90       /* Sigh...  Need to HTML-escape the expanded node name like
91 	 add_anchor_name does, except that we are not writing this to
92 	 the output, so can't use add_anchor_name...  */
93       /* The factor 5 in the next allocation is because the maximum
94 	 expansion of HTML-escaping is for the & character, which is
95 	 output as "&amp;".  2 is for "> that separates node from tocname.  */
96       d = tocname_and_node = (char *)xmalloc (2 + 5 * strlen (expanded_node)
97 					      + strlen (tocname) + 1);
98       if (!anchor)
99 	{
100 	  for (; *s; s++)
101 	    {
102 	      if (*s == '&')
103 		{
104 		  strcpy (d, "&amp;");
105 		  d += 5;
106 		}
107 	      else if (! URL_SAFE_CHAR (*s))
108 	        {
109 		  sprintf (d, "%%%x", (unsigned char) *s);
110 	          /* do this manually since sprintf returns char * on
111 	             SunOS 4 and other old systems.  */
112 		  while (*d)
113 		    d++;
114 	        }
115 	      else
116 		*d++ = *s;
117 	    }
118 	  strcpy (d, "\">");
119 	}
120       else
121 	/* Section outside any node, they provided explicit anchor.  */
122 	strcpy (d, anchor);
123       strcat (d, tocname);
124       free (tocname);       /* it was malloc'ed by substring() */
125       free (expanded_node);
126       toc_entry_alist[toc_counter]->name = tocname_and_node;
127     }
128   else
129     toc_entry_alist[toc_counter]->name = tocname;
130   /* WARNING!  The node name saved in containing_node member must
131      be the node name with _only_ macros expanded (the macros in
132      the node name are expanded by cm_node when it grabs the name
133      from the @node directive).  Non-macros, like @value, @@ and
134      other @-commands must NOT be expanded in containing_node,
135      because toc_find_section_of_node looks up the node name where
136      they are also unexpanded.  You *have* been warned!  */
137   toc_entry_alist[toc_counter]->containing_node = xstrdup (node_name);
138   toc_entry_alist[toc_counter]->level = level;
139   toc_entry_alist[toc_counter]->number = toc_counter;
140 
141   /* have to be done at least */
142   return toc_counter++;
143 }
144 
145 /* Return the name of a chapter/section/subsection etc. that
146    corresponds to the node NODE.  If the node isn't found,
147    return NULL.
148 
149    WARNING!  This function relies on NODE being unexpanded
150    except for macros (i.e., @value, @@, and other non-macros
151    should NOT be expanded), because the containing_node member
152    stores unexpanded node names.
153 
154    Note that this function returns the first section whose
155    containing node is NODE.  Thus, they will lose if they use
156    more than a single chapter structioning command in a node,
157    or if they have a node without any structuring commands.  */
158 char *
159 toc_find_section_of_node (node)
160      char *node;
161 {
162   int i;
163 
164   if (!node)
165     node = "";
166   for (i = 0; i < toc_counter; i++)
167     if (STREQ (node, toc_entry_alist[i]->containing_node))
168       return toc_entry_alist[i]->name;
169 
170   return NULL;
171 }
172 
173 /* free up memory used by toc entries */
174 void
175 toc_free ()
176 {
177   int i;
178 
179   if (toc_counter)
180     {
181       for (i = 0; i < toc_counter; i++)
182         {
183           free (toc_entry_alist[i]->name);
184           free (toc_entry_alist[i]->containing_node);
185           free (toc_entry_alist[i]);
186         }
187 
188       free (toc_entry_alist);
189       toc_entry_alist = NULL; /* to be sure ;-) */
190       toc_counter = 0; /* to be absolutley sure ;-) */
191     }
192 }
193 
194 
195 /* print table of contents in HTML, may be we can produce a standalone
196    HTML file? */
197 static void
198 contents_update_html (fp)
199      FILE *fp;
200 {
201   int i;
202   int k;
203   int last_level;
204 
205   /* does exist any toc? */
206   if (!toc_counter)
207       /* no, so return to sender ;-) */
208       return;
209 
210   flush_output ();      /* in case we are writing stdout */
211 
212   fprintf (fp, "\n<h1>%s</h1>\n<ul>\n", _("Table of Contents"));
213 
214   last_level = toc_entry_alist[0]->level;
215 
216   for (i = 0; i < toc_counter; i++)
217     {
218       if (toc_entry_alist[i]->level > last_level)
219         {
220           /* unusual, but it is possible
221              @chapter ...
222              @subsubsection ...      ? */
223           for (k = 0; k < (toc_entry_alist[i]->level-last_level); k++)
224             fputs ("<ul>\n", fp);
225         }
226       else if (toc_entry_alist[i]->level < last_level)
227         {
228           /* @subsubsection ...
229              @chapter ... this IS usual.*/
230           for (k = 0; k < (last_level-toc_entry_alist[i]->level); k++)
231             fputs ("</ul>\n", fp);
232         }
233 
234       fprintf (fp, "<li><a href=\"#%s</a>\n", toc_entry_alist[i]->name);
235 
236       last_level = toc_entry_alist[i]->level;
237     }
238 
239   /* Go back to start level. */
240   if (toc_entry_alist[0]->level < last_level)
241     for (k = 0; k < (last_level-toc_entry_alist[0]->level); k++)
242       fputs ("</ul>\n", fp);
243 
244   fputs ("</ul>\n\n", fp);
245 }
246 
247 /* print table of contents in ASCII (--no-headers)
248    May be we should create a new command line switch --ascii ? */
249 static void
250 contents_update_info (fp)
251      FILE *fp;
252 {
253   int i;
254   int k;
255 
256   if (!toc_counter)
257       return;
258 
259   flush_output ();      /* in case we are writing stdout */
260 
261   fprintf (fp, "%s\n%.*s\n\n", _("Table of Contents"),
262            (int) strlen (_("Table of Contents")), lots_of_stars);
263 
264   for (i = 0; i < toc_counter; i++)
265     {
266       if (toc_entry_alist[i]->level == 0)
267         fputs ("\n", fp);
268 
269       /* indention with two spaces per level, should this
270          changed? */
271       for (k = 0; k < toc_entry_alist[i]->level; k++)
272         fputs ("  ", fp);
273 
274       fprintf (fp, "%s\n", toc_entry_alist[i]->name);
275     }
276   fputs ("\n\n", fp);
277 }
278 
279 /* shortcontents in HTML; Should this produce a standalone file? */
280 static void
281 shortcontents_update_html (fp)
282      FILE *fp;
283 {
284   int i;
285 
286   /* does exist any toc? */
287   if (!toc_counter)
288     return;
289 
290   flush_output ();      /* in case we are writing stdout */
291 
292   fprintf (fp, "\n<h1>%s</h1>\n<ul>\n", _("Short Contents"));
293 
294   for (i = 0; i < toc_counter; i++)
295     {
296       if ((toc_entry_alist[i])->level == 0)
297         {
298           fputs ("<li>", fp);
299           fprintf (fp, "<a href=\"#%s\n", toc_entry_alist[i]->name);
300         }
301     }
302 
303   fputs ("</ul>\n\n", fp);
304 }
305 
306 /* short contents in ASCII (--no-headers).
307    May be we should create a new command line switch --ascii ?  */
308 static void
309 shortcontents_update_info (fp)
310      FILE *fp;
311 {
312   int i;
313 
314   if (!toc_counter)
315       return;
316 
317   flush_output ();      /* in case we are writing stdout */
318 
319   fprintf (fp, "%s\n%.*s\n\n", _("Short Contents"),
320            (int) strlen (_("Short Contents")), lots_of_stars);
321 
322   for (i = 0; i < toc_counter; i++)
323     {
324       if ((toc_entry_alist[i])->level == 0)
325         fprintf (fp, "%s\n", toc_entry_alist[i]->name);
326     }
327   fputs ("\n\n", fp);
328 }
329 
330 
331 static FILE *toc_fp;
332 static char *toc_buf;
333 
334 static int
335 rewrite_top (fname, placebo)
336      const char *fname, *placebo;
337 {
338   int idx;
339 
340   toc_buf = find_and_load (fname);
341 
342   if (!toc_buf)
343     {
344       /* Can't rewrite standard output.  No point in complaining.  */
345       if (!STREQ (fname, "-"))
346         fs_error (fname);
347       return -1;
348     }
349 
350   idx = search_forward (placebo, 0);
351 
352   if (idx < 0)
353     {
354       error (_("%s: TOC should be here, but it was not found"), fname);
355       return -1;
356     }
357 
358   toc_fp = fopen (fname, "w");
359   if (!toc_fp)
360     {
361       fs_error (fname);
362       return -1;
363     }
364 
365   if (fwrite (toc_buf, 1, idx, toc_fp) != idx)
366     {
367       fs_error (fname);
368       return -1;
369     }
370 
371   return idx + strlen (placebo);
372 }
373 
374 static void
375 contents_update ()
376 {
377   int cont_idx = rewrite_top (contents_filename, contents_placebo);
378 
379   if (cont_idx < 0)
380     return;
381 
382   if (html)
383     contents_update_html (toc_fp);
384   else
385     contents_update_info (toc_fp);
386 
387   if (fwrite (toc_buf + cont_idx, 1, input_text_length - cont_idx, toc_fp)
388       != input_text_length - cont_idx
389       || fclose (toc_fp) != 0)
390     fs_error (contents_filename);
391 }
392 
393 static void
394 shortcontents_update ()
395 {
396   int cont_idx = rewrite_top (shortcontents_filename, shortcontents_placebo);
397 
398   if (cont_idx < 0)
399     return;
400 
401   if (html)
402     shortcontents_update_html (toc_fp);
403   else
404     shortcontents_update_info (toc_fp);
405 
406   if (fwrite (toc_buf + cont_idx, 1, input_text_length - cont_idx - 1, toc_fp)
407       != input_text_length - cont_idx - 1
408       || fclose (toc_fp) != 0)
409     fs_error (shortcontents_filename);
410 }
411 
412 void
413 toc_update ()
414 {
415   if (!html && !no_headers)
416     return;
417 
418   if (contents_filename)
419     contents_update ();
420   if (shortcontents_filename)
421     shortcontents_update ();
422 }
423 
424 void
425 cm_contents (arg)
426      int arg;
427 {
428   if ((html || no_headers) && arg == START)
429     {
430       if (contents_filename)
431         {
432           free (contents_filename);
433           contents_filename = NULL;
434         }
435 
436       if (contents_filename && STREQ (contents_filename, "-"))
437         {
438           if (html)
439             contents_update_html (stdout);
440           else
441             contents_update_info (stdout);
442         }
443       else
444         {
445           contents_filename = xstrdup (current_output_filename);
446           insert_string (contents_placebo); /* just mark it, for now */
447         }
448     }
449 }
450 
451 void
452 cm_shortcontents (arg)
453      int arg;
454 {
455   if ((html || no_headers) && arg == START)
456     {
457       if (shortcontents_filename)
458         {
459           free (shortcontents_filename);
460           shortcontents_filename = NULL;
461         }
462 
463       if (shortcontents_filename && STREQ (shortcontents_filename, "-"))
464         {
465           if (html)
466             shortcontents_update_html (stdout);
467           else
468             shortcontents_update_info (stdout);
469         }
470       else
471         {
472           shortcontents_filename = xstrdup (current_output_filename);
473           insert_string (shortcontents_placebo); /* just mark it, for now */
474         }
475     }
476 }
477