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 */ 38static TOC_ENTRY_ELT **toc_entry_alist = NULL; 39 40/* toc_counter start from 0 ... n for every @chapter, @section ... */ 41static int toc_counter = 0; 42 43/* Routine to add an entry to the table of contents */ 44int 45toc_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 "&". 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. */ 150char * 151toc_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 */ 165void 166toc_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 187static void 188contents_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 ? */ 264static void 265contents_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? */ 296static void 297shortcontents_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). */ 331static void 332shortcontents_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 356void 357cm_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