1 /* sectioning.c -- all related stuff @chapter, @section... @contents 2 $Id: sectioning.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 "cmds.h" 24 #include "macro.h" 25 #include "makeinfo.h" 26 #include "node.h" 27 #include "toc.h" 28 #include "sectioning.h" 29 30 /* See comment in sectioning.h. */ 31 section_alist_type section_alist[] = { 32 { "unnumberedsubsubsec", 5, ENUM_SECT_NO, TOC_YES }, 33 { "unnumberedsubsec", 4, ENUM_SECT_NO, TOC_YES }, 34 { "unnumberedsec", 3, ENUM_SECT_NO, TOC_YES }, 35 { "unnumbered", 2, ENUM_SECT_NO, TOC_YES }, 36 37 { "appendixsubsubsec", 5, ENUM_SECT_APP, TOC_YES }, /* numbered like A.X.X.X */ 38 { "appendixsubsec", 4, ENUM_SECT_APP, TOC_YES }, 39 { "appendixsec", 3, ENUM_SECT_APP, TOC_YES }, 40 { "appendixsection", 3, ENUM_SECT_APP, TOC_YES }, 41 { "appendix", 2, ENUM_SECT_APP, TOC_YES }, 42 43 { "subsubsec", 5, ENUM_SECT_YES, TOC_YES }, 44 { "subsubsection", 5, ENUM_SECT_YES, TOC_YES }, 45 { "subsection", 4, ENUM_SECT_YES, TOC_YES }, 46 { "section", 3, ENUM_SECT_YES, TOC_YES }, 47 { "chapter", 2, ENUM_SECT_YES, TOC_YES }, 48 49 { "subsubheading", 5, ENUM_SECT_NO, TOC_NO }, 50 { "subheading", 4, ENUM_SECT_NO, TOC_NO }, 51 { "heading", 3, ENUM_SECT_NO, TOC_NO }, 52 { "chapheading", 2, ENUM_SECT_NO, TOC_NO }, 53 { "majorheading", 2, ENUM_SECT_NO, TOC_NO }, 54 55 { "top", 1, ENUM_SECT_NO, TOC_YES }, 56 { NULL, 0, 0, 0 } 57 }; 58 59 /* The argument of @settitle, used for HTML. */ 60 char *title = NULL; 61 62 63 #define APPENDIX_MAGIC 1024 64 #define UNNUMBERED_MAGIC 2048 65 66 /* Number memory for every level @chapter, @section, 67 @subsection, @subsubsection. */ 68 static int numbers [] = { 0, 0, 0, 0 }; 69 70 /* enum_marker == APPENDIX_MAGIC then we are counting appendencies 71 enum_marker == UNNUMBERED_MAGIC then we are within unnumbered area. 72 Handling situations like this: 73 @unnumbered .. 74 @section ... */ 75 static int enum_marker = 0; 76 77 /* Organized by level commands. That is, "*" == chapter, "=" == section. */ 78 static char *scoring_characters = "*=-."; 79 80 /* Amount to offset the name of sectioning commands to levels by. */ 81 static int section_alist_offset = 0; 82 83 84 /* num == ENUM_SECT_NO means unnumbered (should never call this) 85 num == ENUM_SECT_YES means numbered 86 num == ENUM_SECT_APP means numbered like A.1 and so on */ 87 char * 88 get_sectioning_number (level, num) 89 int level; 90 int num; 91 { 92 static char s[100]; /* should ever be enough for 99.99.99.99 93 Appendix A.1 */ 94 95 char *p; 96 int i; 97 98 s[0] = 0; 99 100 /* create enumeration in front of chapter, section, subsection and so on. */ 101 for (i = 0; i < level; i++) 102 { 103 p = s + strlen (s); 104 if ((i == 0) && (enum_marker == APPENDIX_MAGIC)) 105 sprintf (p, "%c.", numbers[i] + 64); /* Should be changed to 106 be more portable */ 107 else 108 sprintf (p, "%d.", numbers[i]); 109 } 110 111 /* the last number is never followed by a dot */ 112 p = s + strlen (s); 113 if ((num == ENUM_SECT_APP) 114 && (i == 0) 115 && (enum_marker == APPENDIX_MAGIC)) 116 sprintf (p, _("Appendix %c "), numbers[i] + 64); 117 else 118 sprintf (p, "%d ", numbers[i]); 119 120 return s; 121 } 122 123 124 /* Set the level of @top to LEVEL. Return the old level of @top. */ 125 int 126 set_top_section_level (level) 127 int level; 128 { 129 int i, result = -1; 130 131 for (i = 0; section_alist[i].name; i++) 132 if (strcmp (section_alist[i].name, "top") == 0) 133 { 134 result = section_alist[i].level; 135 section_alist[i].level = level; 136 break; 137 } 138 return result; 139 } 140 141 142 /* return the index of the given sectioning command in section_alist */ 143 int 144 search_sectioning (text) 145 char *text; 146 { 147 int i; 148 char *t; 149 150 /* ignore the optional command prefix */ 151 if (text[0] == COMMAND_PREFIX) 152 text++; 153 154 for (i = 0; (t = section_alist[i].name); i++) 155 { 156 if (strcmp (t, text) == 0) 157 { 158 return i; 159 } 160 } 161 return -1; 162 } 163 164 /* Return an integer which identifies the type section present in TEXT. */ 165 int 166 what_section (text) 167 char *text; 168 { 169 int index, j; 170 char *temp; 171 int return_val; 172 173 find_section_command: 174 for (j = 0; text[j] && cr_or_whitespace (text[j]); j++); 175 if (text[j] != COMMAND_PREFIX) 176 return -1; 177 178 text = text + j + 1; 179 180 /* We skip @c, @comment, and @?index commands. */ 181 if ((strncmp (text, "comment", strlen ("comment")) == 0) || 182 (text[0] == 'c' && cr_or_whitespace (text[1])) || 183 (strcmp (text + 1, "index") == 0)) 184 { 185 while (*text++ != '\n'); 186 goto find_section_command; 187 } 188 189 /* Handle italicized sectioning commands. */ 190 if (*text == 'i') 191 text++; 192 193 for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++); 194 195 temp = xmalloc (1 + j); 196 strncpy (temp, text, j); 197 temp[j] = 0; 198 199 index = search_sectioning (temp); 200 free (temp); 201 if (index >= 0) 202 { 203 return_val = section_alist[index].level + section_alist_offset; 204 if (return_val < 0) 205 return_val = 0; 206 else if (return_val > 5) 207 return_val = 5; 208 return return_val; 209 } 210 return -1; 211 } 212 213 214 void 215 sectioning_underscore (cmd) 216 char *cmd; 217 { 218 char character; 219 char *temp; 220 int level; 221 222 temp = xmalloc (2 + strlen (cmd)); 223 temp[0] = COMMAND_PREFIX; 224 strcpy (&temp[1], cmd); 225 level = what_section (temp); 226 free (temp); 227 level -= 2; 228 229 if (level < 0) 230 level = 0; 231 232 if (html) 233 sectioning_html (level, cmd); 234 else 235 { 236 character = scoring_characters[level]; 237 insert_and_underscore (level, character, cmd); 238 } 239 } 240 241 /* insert_and_underscore and sectioning_html are the 242 only functions which call this. 243 I have created this, because it was exactly the same 244 code in both functions. */ 245 static char * 246 handle_enum_increment (level, index) 247 int level; 248 int index; 249 { 250 /* special for unnumbered */ 251 if (number_sections && section_alist[index].num == ENUM_SECT_NO) 252 { 253 if (level == 0 254 && enum_marker != UNNUMBERED_MAGIC) 255 enum_marker = UNNUMBERED_MAGIC; 256 } 257 /* enumerate only things which are allowed */ 258 if (number_sections && section_alist[index].num) 259 { 260 /* reset the marker if we get into enumerated areas */ 261 if (section_alist[index].num == ENUM_SECT_YES 262 && level == 0 263 && enum_marker == UNNUMBERED_MAGIC) 264 enum_marker = 0; 265 /* This is special for appendix; if we got the first 266 time an appendix command then we are entering appendix. 267 Thats the point we have to start countint with A, B and so on. */ 268 if (section_alist[index].num == ENUM_SECT_APP 269 && level == 0 270 && enum_marker != APPENDIX_MAGIC) 271 { 272 enum_marker = APPENDIX_MAGIC; 273 numbers [0] = 0; /* this means we start with Appendix A */ 274 } 275 276 /* only increment counters if we are not in unnumbered 277 area. This handles situations like this: 278 @unnumbered .... This sets enum_marker to UNNUMBERED_MAGIC 279 @section .... */ 280 if (enum_marker != UNNUMBERED_MAGIC) 281 { 282 int i; 283 284 /* reset all counters which are one level deeper */ 285 for (i = level; i < 3; i++) 286 numbers [i + 1] = 0; 287 288 numbers[level]++; 289 return xstrdup 290 (get_sectioning_number (level, section_alist[index].num)); 291 } 292 } /* if (number_sections)... */ 293 294 return xstrdup (""); 295 } 296 297 298 /* Insert the text following input_text_offset up to the end of the line 299 in a new, separate paragraph. Directly underneath it, insert a 300 line of WITH_CHAR, the same length of the inserted text. */ 301 void 302 insert_and_underscore (level, with_char, cmd) 303 int level; 304 int with_char; 305 char *cmd; 306 { 307 int i, len; 308 int index; 309 int old_no_indent; 310 unsigned char *starting_pos, *ending_pos; 311 char *temp; 312 313 close_paragraph (); 314 filling_enabled = indented_fill = 0; 315 old_no_indent = no_indent; 316 no_indent = 1; 317 318 if (macro_expansion_output_stream && !executing_string) 319 append_to_expansion_output (input_text_offset + 1); 320 321 get_rest_of_line (0, &temp); 322 starting_pos = output_paragraph + output_paragraph_offset; 323 324 index = search_sectioning (cmd); 325 if (index < 0) 326 { 327 /* should never happen, but a poor guy, named Murphy ... */ 328 warning (_("Internal error (search_sectioning) \"%s\"!"), cmd); 329 return; 330 } 331 332 /* This is a bit tricky: we must produce "X.Y SECTION-NAME" in the 333 Info output and in TOC, but only SECTION-NAME in the macro-expanded 334 output. */ 335 336 /* Step 1: produce "X.Y" and add it to Info output. */ 337 add_word (handle_enum_increment (level, index)); 338 339 /* Step 2: add "SECTION-NAME" to both Info and macro-expanded output. */ 340 if (macro_expansion_output_stream && !executing_string) 341 { 342 char *temp1 = xmalloc (2 + strlen (temp)); 343 sprintf (temp1, "%s\n", temp); 344 remember_itext (input_text, input_text_offset); 345 me_execute_string (temp1); 346 free (temp1); 347 } 348 else 349 execute_string ("%s\n", temp); 350 351 /* Step 3: pluck "X.Y SECTION-NAME" from the output buffer and 352 insert it into the TOC. */ 353 ending_pos = output_paragraph + output_paragraph_offset; 354 if (section_alist[index].toc == TOC_YES) 355 toc_add_entry (substring (starting_pos, ending_pos - 1), 356 level, current_node, NULL); 357 358 free (temp); 359 360 len = (ending_pos - starting_pos) - 1; 361 for (i = 0; i < len; i++) 362 add_char (with_char); 363 insert ('\n'); 364 close_paragraph (); 365 filling_enabled = 1; 366 no_indent = old_no_indent; 367 } 368 369 /* Insert the text following input_text_offset up to the end of the 370 line as an HTML heading element of the appropriate `level' and 371 tagged as an anchor for the current node.. */ 372 void 373 sectioning_html (level, cmd) 374 int level; 375 char *cmd; 376 { 377 static int toc_ref_count = 0; 378 int index; 379 int old_no_indent; 380 unsigned char *starting_pos, *ending_pos; 381 char *temp, *toc_anchor = NULL; 382 383 close_paragraph (); 384 filling_enabled = indented_fill = 0; 385 old_no_indent = no_indent; 386 no_indent = 1; 387 388 add_word_args ("<h%d>", level + 1); /* level 0 is <h1> */ 389 390 /* If we are outside of any node, produce an anchor that 391 the TOC could refer to. */ 392 if (!current_node || !*current_node) 393 { 394 starting_pos = output_paragraph + output_paragraph_offset; 395 add_word_args ("<a name=\"TOC%d\">", toc_ref_count++); 396 toc_anchor = substring (starting_pos + 9, 397 output_paragraph + output_paragraph_offset); 398 } 399 starting_pos = output_paragraph + output_paragraph_offset; 400 401 if (macro_expansion_output_stream && !executing_string) 402 append_to_expansion_output (input_text_offset + 1); 403 404 get_rest_of_line (0, &temp); 405 406 index = search_sectioning (cmd); 407 if (index < 0) 408 { 409 /* should never happen, but a poor guy, named Murphy ... */ 410 warning (_("Internal error (search_sectioning) \"%s\"!"), cmd); 411 return; 412 } 413 414 /* Produce "X.Y" and add it to HTML output. */ 415 add_word (handle_enum_increment (level, index)); 416 417 /* add the section name to both HTML and macro-expanded output. */ 418 if (macro_expansion_output_stream && !executing_string) 419 { 420 remember_itext (input_text, input_text_offset); 421 me_execute_string (temp); 422 write_region_to_macro_output ("\n", 0, 1); 423 } 424 else 425 execute_string ("%s", temp); 426 427 ending_pos = output_paragraph + output_paragraph_offset; 428 429 /* Pluck ``X.Y SECTION-NAME'' from the output buffer and insert it 430 into the TOC. */ 431 if (section_alist[index].toc == TOC_YES) 432 toc_add_entry (substring (starting_pos, ending_pos), 433 level, current_node, toc_anchor); 434 435 free (temp); 436 437 if (outstanding_node) 438 outstanding_node = 0; 439 440 add_word_args ("</h%d>", level+1); 441 close_paragraph(); 442 filling_enabled = 1; 443 no_indent = old_no_indent; 444 } 445 446 447 /* Shift the meaning of @section to @chapter. */ 448 void 449 cm_raisesections () 450 { 451 discard_until ("\n"); 452 section_alist_offset--; 453 } 454 455 /* Shift the meaning of @chapter to @section. */ 456 void 457 cm_lowersections () 458 { 459 discard_until ("\n"); 460 section_alist_offset++; 461 } 462 463 /* The command still works, but prints a warning message in addition. */ 464 void 465 cm_ideprecated (arg, start, end) 466 int arg, start, end; 467 { 468 warning (_("%c%s is obsolete; use %c%s instead"), 469 COMMAND_PREFIX, command, COMMAND_PREFIX, command + 1); 470 sectioning_underscore (command + 1); 471 } 472 473 474 /* Treat this just like @unnumbered. The only difference is 475 in node defaulting. */ 476 void 477 cm_top () 478 { 479 /* It is an error to have more than one @top. */ 480 if (top_node_seen && strcmp (current_node, "Top") != 0) 481 { 482 TAG_ENTRY *tag = tag_table; 483 484 line_error (_("Node with %ctop as a section already exists"), 485 COMMAND_PREFIX); 486 487 while (tag) 488 { 489 if (tag->flags & TAG_FLAG_IS_TOP) 490 { 491 int old_line_number = line_number; 492 char *old_input_filename = input_filename; 493 494 line_number = tag->line_no; 495 input_filename = tag->filename; 496 line_error (_("Here is the %ctop node"), COMMAND_PREFIX); 497 input_filename = old_input_filename; 498 line_number = old_line_number; 499 return; 500 } 501 tag = tag->next_ent; 502 } 503 } 504 else 505 { 506 TAG_ENTRY *top_node = find_node ("Top"); 507 top_node_seen = 1; 508 509 /* It is an error to use @top before you have used @node. */ 510 if (!tag_table) 511 { 512 char *top_name; 513 514 get_rest_of_line (0, &top_name); 515 line_error (_("%ctop used before %cnode, defaulting to %s"), 516 COMMAND_PREFIX, COMMAND_PREFIX, top_name); 517 execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name); 518 free (top_name); 519 return; 520 } 521 else if (html && splitting) 522 { 523 char *next = top_node ? top_node->next : NULL; 524 525 add_word ("<p>"); 526 if (next) 527 { 528 add_word (_("Next:")); 529 add_word ("<a rel=next href=\""); 530 add_anchor_name (next, 1); 531 add_word ("\">"); 532 execute_string (next); 533 add_word ("</a>\n"); 534 } 535 } 536 537 cm_unnumbered (); 538 539 /* The most recently defined node is the top node. */ 540 tag_table->flags |= TAG_FLAG_IS_TOP; 541 542 /* Now set the logical hierarchical level of the Top node. */ 543 { 544 int orig_offset = input_text_offset; 545 546 input_text_offset = search_forward (node_search_string, orig_offset); 547 548 if (input_text_offset > 0) 549 { 550 int this_section; 551 552 /* We have encountered a non-top node, so mark that one exists. */ 553 non_top_node_seen = 1; 554 555 /* Move to the end of this line, and find out what the 556 sectioning command is here. */ 557 while (input_text[input_text_offset] != '\n') 558 input_text_offset++; 559 560 if (input_text_offset < input_text_length) 561 input_text_offset++; 562 563 this_section = what_section (input_text + input_text_offset); 564 565 /* If we found a sectioning command, then give the top section 566 a level of this section - 1. */ 567 if (this_section != -1) 568 set_top_section_level (this_section - 1); 569 } 570 input_text_offset = orig_offset; 571 } 572 } 573 } 574 575 /* The remainder of the text on this line is a chapter heading. */ 576 void 577 cm_chapter () 578 { 579 sectioning_underscore ("chapter"); 580 } 581 582 /* The remainder of the text on this line is a section heading. */ 583 void 584 cm_section () 585 { 586 sectioning_underscore ("section"); 587 } 588 589 /* The remainder of the text on this line is a subsection heading. */ 590 void 591 cm_subsection () 592 { 593 sectioning_underscore ("subsection"); 594 } 595 596 /* The remainder of the text on this line is a subsubsection heading. */ 597 void 598 cm_subsubsection () 599 { 600 sectioning_underscore ("subsubsection"); 601 } 602 603 /* The remainder of the text on this line is an unnumbered heading. */ 604 void 605 cm_unnumbered () 606 { 607 sectioning_underscore ("unnumbered"); 608 } 609 610 /* The remainder of the text on this line is an unnumbered section heading. */ 611 void 612 cm_unnumberedsec () 613 { 614 sectioning_underscore ("unnumberedsec"); 615 } 616 617 /* The remainder of the text on this line is an unnumbered 618 subsection heading. */ 619 void 620 cm_unnumberedsubsec () 621 { 622 sectioning_underscore ("unnumberedsubsec"); 623 } 624 625 /* The remainder of the text on this line is an unnumbered 626 subsubsection heading. */ 627 void 628 cm_unnumberedsubsubsec () 629 { 630 sectioning_underscore ("unnumberedsubsubsec"); 631 } 632 633 /* The remainder of the text on this line is an appendix heading. */ 634 void 635 cm_appendix () 636 { 637 sectioning_underscore ("appendix"); 638 } 639 640 /* The remainder of the text on this line is an appendix section heading. */ 641 void 642 cm_appendixsec () 643 { 644 sectioning_underscore ("appendixsec"); 645 } 646 647 /* The remainder of the text on this line is an appendix subsection heading. */ 648 void 649 cm_appendixsubsec () 650 { 651 sectioning_underscore ("appendixsubsec"); 652 } 653 654 /* The remainder of the text on this line is an appendix 655 subsubsection heading. */ 656 void 657 cm_appendixsubsubsec () 658 { 659 sectioning_underscore ("appendixsubsubsec"); 660 } 661 662 /* Compatibility functions substitute for chapter, section, etc. */ 663 void 664 cm_majorheading () 665 { 666 sectioning_underscore ("majorheading"); 667 } 668 669 void 670 cm_chapheading () 671 { 672 sectioning_underscore ("chapheading"); 673 } 674 675 void 676 cm_heading () 677 { 678 sectioning_underscore ("heading"); 679 } 680 681 void 682 cm_subheading () 683 { 684 sectioning_underscore ("subheading"); 685 } 686 687 void 688 cm_subsubheading () 689 { 690 sectioning_underscore ("subsubheading"); 691 } 692