source: trunk/third/texinfo/makeinfo/toc.c @ 18945

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