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 "&". 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, "&"); 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