1/* $NetBSD: sectioning.c,v 1.1.1.5 2008/09/02 07:50:20 christos Exp $ */ 2 3/* sectioning.c -- for @chapter, @section, ..., @contents ... 4 Id: sectioning.c,v 1.25 2004/07/05 22:23:23 karl Exp 5 6 Copyright (C) 1999, 2001, 2002, 2003, 2004 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 "cmds.h" 26#include "macro.h" 27#include "makeinfo.h" 28#include "node.h" 29#include "toc.h" 30#include "sectioning.h" 31#include "xml.h" 32 33/* See comment in sectioning.h. */ 34section_alist_type section_alist[] = { 35 { "unnumberedsubsubsec", 5, ENUM_SECT_NO, TOC_YES }, 36 { "unnumberedsubsec", 4, ENUM_SECT_NO, TOC_YES }, 37 { "unnumberedsec", 3, ENUM_SECT_NO, TOC_YES }, 38 { "unnumbered", 2, ENUM_SECT_NO, TOC_YES }, 39 { "centerchap", 2, ENUM_SECT_NO, TOC_YES }, 40 41 { "appendixsubsubsec", 5, ENUM_SECT_APP, TOC_YES }, /* numbered like A.X.X.X */ 42 { "appendixsubsec", 4, ENUM_SECT_APP, TOC_YES }, 43 { "appendixsec", 3, ENUM_SECT_APP, TOC_YES }, 44 { "appendixsection", 3, ENUM_SECT_APP, TOC_YES }, 45 { "appendix", 2, ENUM_SECT_APP, TOC_YES }, 46 47 { "subsubsec", 5, ENUM_SECT_YES, TOC_YES }, 48 { "subsubsection", 5, ENUM_SECT_YES, TOC_YES }, 49 { "subsection", 4, ENUM_SECT_YES, TOC_YES }, 50 { "section", 3, ENUM_SECT_YES, TOC_YES }, 51 { "chapter", 2, ENUM_SECT_YES, TOC_YES }, 52 53 { "subsubheading", 5, ENUM_SECT_NO, TOC_NO }, 54 { "subheading", 4, ENUM_SECT_NO, TOC_NO }, 55 { "heading", 3, ENUM_SECT_NO, TOC_NO }, 56 { "chapheading", 2, ENUM_SECT_NO, TOC_NO }, 57 { "majorheading", 2, ENUM_SECT_NO, TOC_NO }, 58 59 { "top", 1, ENUM_SECT_NO, TOC_YES }, 60 { NULL, 0, 0, 0 } 61}; 62 63/* The argument of @settitle, used for HTML. */ 64char *title = NULL; 65 66 67#define APPENDIX_MAGIC 1024 68#define UNNUMBERED_MAGIC 2048 69 70/* Number memory for every level @chapter, @section, 71 @subsection, @subsubsection. */ 72static int numbers [] = { 0, 0, 0, 0 }; 73 74/* enum_marker == APPENDIX_MAGIC then we are counting appendencies 75 enum_marker == UNNUMBERED_MAGIC then we are within unnumbered area. 76 Handling situations like this: 77 @unnumbered .. 78 @section ... */ 79static int enum_marker = 0; 80 81/* Organized by level commands. That is, "*" == chapter, "=" == section. */ 82static char *scoring_characters = "*=-."; 83 84/* Amount to offset the name of sectioning commands to levels by. */ 85static int section_alist_offset = 0; 86 87/* These two variables are for @float, @cindex like commands that need to know 88 in which section they are used. */ 89/* Last value returned by get_sectioning_number. */ 90static char *last_sectioning_number = ""; 91/* Last title used by sectioning_underscore, etc. */ 92static char *last_sectioning_title = ""; 93 94/* num == ENUM_SECT_NO means unnumbered (should never call this) 95 num == ENUM_SECT_YES means numbered 96 num == ENUM_SECT_APP means numbered like A.1 and so on */ 97static char * 98get_sectioning_number (int level, int num) 99{ 100 static char s[100]; /* should ever be enough for 99.99.99.99 101 Appendix A.1 */ 102 103 char *p; 104 int i; 105 106 s[0] = 0; 107 108 /* create enumeration in front of chapter, section, subsection and so on. */ 109 for (i = 0; i < level; i++) 110 { 111 p = s + strlen (s); 112 if ((i == 0) && (enum_marker == APPENDIX_MAGIC)) 113 sprintf (p, "%c.", numbers[i] + 64); /* Should be changed to 114 be more portable */ 115 else 116 sprintf (p, "%d.", numbers[i]); 117 } 118 119 /* the last number is never followed by a dot */ 120 p = s + strlen (s); 121 if ((num == ENUM_SECT_APP) 122 && (i == 0) 123 && (enum_marker == APPENDIX_MAGIC)) 124 sprintf (p, _("Appendix %c"), numbers[i] + 64); 125 else 126 sprintf (p, "%d", numbers[i]); 127 128 /* Poor man's cache :-) */ 129 if (strlen (last_sectioning_number)) 130 free (last_sectioning_number); 131 last_sectioning_number = xstrdup (s); 132 133 return s; 134} 135 136 137/* Set the level of @top to LEVEL. Return the old level of @top. */ 138int 139set_top_section_level (int level) 140{ 141 int i, result = -1; 142 143 for (i = 0; section_alist[i].name; i++) 144 if (strcmp (section_alist[i].name, "top") == 0) 145 { 146 result = section_alist[i].level; 147 section_alist[i].level = level; 148 break; 149 } 150 return result; 151} 152 153 154/* return the index of the given sectioning command in section_alist */ 155static int 156search_sectioning (char *text) 157{ 158 int i; 159 char *t; 160 161 /* ignore the optional command prefix */ 162 if (text[0] == COMMAND_PREFIX) 163 text++; 164 165 for (i = 0; (t = section_alist[i].name); i++) 166 { 167 if (strcmp (t, text) == 0) 168 { 169 return i; 170 } 171 } 172 return -1; 173} 174 175/* Return an integer which identifies the type of section present in 176 TEXT -- 1 for @top, 2 for chapters, ..., 5 for subsubsections (as 177 specified in section_alist). We take into account any @lowersections 178 and @raisesections. If SECNAME is non-NULL, also return the 179 corresponding section name. */ 180int 181what_section (char *text, char **secname) 182{ 183 int index, j; 184 char *temp; 185 int return_val; 186 187 find_section_command: 188 for (j = 0; text[j] && cr_or_whitespace (text[j]); j++); 189 if (text[j] != COMMAND_PREFIX) 190 return -1; 191 192 text = text + j + 1; 193 194 /* We skip @c, @comment, and @?index commands. */ 195 if ((strncmp (text, "comment", strlen ("comment")) == 0) || 196 (text[0] == 'c' && cr_or_whitespace (text[1])) || 197 (strcmp (text + 1, "index") == 0)) 198 { 199 while (*text++ != '\n'); 200 goto find_section_command; 201 } 202 203 /* Handle italicized sectioning commands. */ 204 if (*text == 'i') 205 text++; 206 207 for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++); 208 209 temp = xmalloc (1 + j); 210 strncpy (temp, text, j); 211 temp[j] = 0; 212 213 index = search_sectioning (temp); 214 free (temp); 215 if (index >= 0) 216 { 217 return_val = section_alist[index].level + section_alist_offset; 218 if (return_val < 0) 219 return_val = 0; 220 else if (return_val > 5) 221 return_val = 5; 222 223 if (secname) 224 { 225 int i; 226 int alist_size = sizeof (section_alist) / sizeof(section_alist_type); 227 /* Find location of offset sectioning entry, but don't go off 228 either end of the array. */ 229 int index_offset = MAX (index - section_alist_offset, 0); 230 index_offset = MIN (index_offset, alist_size - 1); 231 232 /* Also make sure we don't go into the next "group" of 233 sectioning changes, e.g., change from an @appendix to an 234 @heading or some such. */ 235#define SIGN(expr) ((expr) < 0 ? -1 : 1) 236 for (i = index; i != index_offset; i -= SIGN (section_alist_offset)) 237 { 238 /* As it happens, each group has unique .num/.toc values. */ 239 if (section_alist[i].num != section_alist[index_offset].num 240 || section_alist[i].toc != section_alist[index_offset].toc) 241 break; 242 } 243 *secname = section_alist[i].name; 244 } 245 return return_val; 246 } 247 return -1; 248} 249 250/* Returns current top level division (ie. chapter, unnumbered) number. 251 - For chapters, returns the number. 252 - For unnumbered sections, returns empty string. 253 - For appendices, returns A, B, etc. */ 254char * 255current_chapter_number (void) 256{ 257 if (enum_marker == UNNUMBERED_MAGIC) 258 return xstrdup (""); 259 else if (enum_marker == APPENDIX_MAGIC) 260 { 261 char s[2] = { numbers[0] + 64, '\0' }; 262 return xstrdup (s); 263 } 264 else 265 { 266 char s[11]; 267 snprintf (s, sizeof(s), "%d", numbers[0]); 268 return xstrdup (s); 269 } 270} 271 272/* Returns number of the last sectioning command used. */ 273char * 274current_sectioning_number (void) 275{ 276 if (enum_marker == UNNUMBERED_MAGIC || !number_sections) 277 return xstrdup (""); 278 else 279 return xstrdup (last_sectioning_number); 280} 281 282/* Returns arguments of the last sectioning command used. */ 283char * 284current_sectioning_name (void) 285{ 286 return xstrdup (last_sectioning_title); 287} 288 289/* insert_and_underscore, sectioning_underscore and sectioning_html call this. */ 290 291static char * 292handle_enum_increment (int level, int index) 293{ 294 /* Here is how TeX handles enumeration: 295 - Anything starting with @unnumbered is not enumerated. 296 - @majorheading and the like are not enumberated. */ 297 int i; 298 299 /* First constraint above. */ 300 if (enum_marker == UNNUMBERED_MAGIC && level == 0) 301 return xstrdup (""); 302 303 /* Second constraint. */ 304 if (section_alist[index].num == ENUM_SECT_NO) 305 return xstrdup (""); 306 307 /* reset all counters which are one level deeper */ 308 for (i = level; i < 3; i++) 309 numbers [i + 1] = 0; 310 311 numbers[level]++; 312 if (section_alist[index].num == ENUM_SECT_NO || enum_marker == UNNUMBERED_MAGIC 313 || !number_sections) 314 return xstrdup (""); 315 else 316 return xstrdup (get_sectioning_number (level, section_alist[index].num)); 317} 318 319 320void 321sectioning_underscore (char *cmd) 322{ 323 char *temp, *secname; 324 int level; 325 326 /* If we're not indenting the first paragraph, we shall make it behave 327 like @noindent is called directly after the section heading. */ 328 if (! do_first_par_indent) 329 cm_noindent (); 330 331 temp = xmalloc (2 + strlen (cmd)); 332 temp[0] = COMMAND_PREFIX; 333 strcpy (&temp[1], cmd); 334 level = what_section (temp, &secname); 335 level -= 2; 336 if (level < 0) 337 level = 0; 338 free (temp); 339 340 /* If the argument to @top is empty, we try using the one from @settitle. 341 Warn if both are unusable. */ 342 if (STREQ (command, "top")) 343 { 344 int save_input_text_offset = input_text_offset; 345 346 get_rest_of_line (0, &temp); 347 348 /* Due to get_rest_of_line ... */ 349 line_number--; 350 351 if (strlen (temp) == 0 && (!title || strlen (title) == 0)) 352 warning ("Must specify a title with least one of @settitle or @top"); 353 354 input_text_offset = save_input_text_offset; 355 } 356 357 if (xml) 358 { 359 /* If the section appears in the toc, it means it's a real section 360 unlike majorheading, chapheading etc. */ 361 if (section_alist[search_sectioning (cmd)].toc == TOC_YES) 362 { 363 xml_close_sections (level); 364 /* Mark the beginning of the section 365 If the next command is printindex, we will remove 366 the section and put an Index instead */ 367 flush_output (); 368 xml_last_section_output_position = output_paragraph_offset; 369 370 get_rest_of_line (0, &temp); 371 372 /* Use @settitle value if @top parameter is empty. */ 373 if (STREQ (command, "top") && strlen(temp) == 0) 374 temp = xstrdup (title ? title : ""); 375 376 /* Docbook does not support @unnumbered at all. So we provide numbers 377 that other formats use. @appendix seems to be fine though, so we let 378 Docbook handle that as usual. */ 379 if (docbook && enum_marker != APPENDIX_MAGIC) 380 { 381 if (section_alist[search_sectioning (cmd)].num == ENUM_SECT_NO 382 && section_alist[search_sectioning (cmd)].toc == TOC_YES) 383 xml_insert_element_with_attribute (xml_element (secname), 384 START, "label=\"%s\" xreflabel=\"%s\"", 385 handle_enum_increment (level, search_sectioning (cmd)), 386 text_expansion (temp)); 387 else 388 xml_insert_element_with_attribute (xml_element (secname), 389 START, "label=\"%s\"", 390 handle_enum_increment (level, search_sectioning (cmd))); 391 } 392 else 393 xml_insert_element (xml_element (secname), START); 394 395 xml_insert_element (TITLE, START); 396 xml_open_section (level, secname); 397 execute_string ("%s", temp); 398 xml_insert_element (TITLE, END); 399 400 free (temp); 401 } 402 else 403 { 404 if (docbook) 405 { 406 if (level > 0) 407 xml_insert_element_with_attribute (xml_element (secname), START, 408 "renderas=\"sect%d\"", level); 409 else 410 xml_insert_element_with_attribute (xml_element (secname), START, 411 "renderas=\"other\""); 412 } 413 else 414 xml_insert_element (xml_element (secname), START); 415 416 get_rest_of_line (0, &temp); 417 execute_string ("%s", temp); 418 free (temp); 419 420 xml_insert_element (xml_element (secname), END); 421 } 422 } 423 else if (html) 424 sectioning_html (level, secname); 425 else 426 insert_and_underscore (level, secname); 427} 428 429 430/* Insert the text following input_text_offset up to the end of the line 431 in a new, separate paragraph. Directly underneath it, insert a 432 line of WITH_CHAR, the same length of the inserted text. */ 433void 434insert_and_underscore (int level, char *cmd) 435{ 436 int i, len; 437 int index; 438 int old_no_indent; 439 unsigned char *starting_pos, *ending_pos; 440 char *temp; 441 char with_char = scoring_characters[level]; 442 443 close_paragraph (); 444 filling_enabled = indented_fill = 0; 445 old_no_indent = no_indent; 446 no_indent = 1; 447 448 if (macro_expansion_output_stream && !executing_string) 449 append_to_expansion_output (input_text_offset + 1); 450 451 get_rest_of_line (0, &temp); 452 453 /* Use @settitle value if @top parameter is empty. */ 454 if (STREQ (command, "top") && strlen(temp) == 0) 455 temp = xstrdup (title ? title : ""); 456 457 starting_pos = output_paragraph + output_paragraph_offset; 458 459 /* Poor man's cache for section title. */ 460 if (strlen (last_sectioning_title)) 461 free (last_sectioning_title); 462 last_sectioning_title = xstrdup (temp); 463 464 index = search_sectioning (cmd); 465 if (index < 0) 466 { 467 /* should never happen, but a poor guy, named Murphy ... */ 468 warning (_("Internal error (search_sectioning) `%s'!"), cmd); 469 return; 470 } 471 472 /* This is a bit tricky: we must produce "X.Y SECTION-NAME" in the 473 Info output and in TOC, but only SECTION-NAME in the macro-expanded 474 output. */ 475 476 /* Step 1: produce "X.Y" and add it to Info output. */ 477 add_word_args ("%s ", handle_enum_increment (level, index)); 478 479 /* Step 2: add "SECTION-NAME" to both Info and macro-expanded output. */ 480 if (macro_expansion_output_stream && !executing_string) 481 { 482 char *temp1 = xmalloc (2 + strlen (temp)); 483 sprintf (temp1, "%s\n", temp); 484 remember_itext (input_text, input_text_offset); 485 me_execute_string (temp1); 486 free (temp1); 487 } 488 else 489 execute_string ("%s\n", temp); 490 491 /* Step 3: pluck "X.Y SECTION-NAME" from the output buffer and 492 insert it into the TOC. */ 493 ending_pos = output_paragraph + output_paragraph_offset; 494 if (section_alist[index].toc == TOC_YES) 495 toc_add_entry (substring (starting_pos, ending_pos - 1), 496 level, current_node, NULL); 497 498 free (temp); 499 500 len = (ending_pos - starting_pos) - 1; 501 for (i = 0; i < len; i++) 502 add_char (with_char); 503 insert ('\n'); 504 close_paragraph (); 505 filling_enabled = 1; 506 no_indent = old_no_indent; 507} 508 509/* Insert the text following input_text_offset up to the end of the 510 line as an HTML heading element of the appropriate `level' and 511 tagged as an anchor for the current node.. */ 512 513void 514sectioning_html (int level, char *cmd) 515{ 516 static int toc_ref_count = 0; 517 int index; 518 int old_no_indent; 519 unsigned char *starting_pos, *ending_pos; 520 char *temp, *toc_anchor = NULL; 521 522 close_paragraph (); 523 filling_enabled = indented_fill = 0; 524 old_no_indent = no_indent; 525 no_indent = 1; 526 527 /* level 0 (chapter) is <h2>, and we go down from there. */ 528 add_html_block_elt_args ("<h%d class=\"%s\">", level + 2, cmd); 529 530 /* If we are outside of any node, produce an anchor that 531 the TOC could refer to. */ 532 if (!current_node || !*current_node) 533 { 534 static const char a_name[] = "<a name=\""; 535 536 starting_pos = output_paragraph + output_paragraph_offset; 537 add_word_args ("%sTOC%d\">", a_name, toc_ref_count++); 538 toc_anchor = substring (starting_pos + sizeof (a_name) - 1, 539 output_paragraph + output_paragraph_offset); 540 /* This must be added after toc_anchor is extracted, since 541 toc_anchor cannot include the closing </a>. For details, 542 see toc.c:toc_add_entry and toc.c:contents_update_html. 543 544 Also, the anchor close must be output before the section name 545 in case the name itself contains an anchor. */ 546 add_word ("</a>"); 547 } 548 starting_pos = output_paragraph + output_paragraph_offset; 549 550 if (macro_expansion_output_stream && !executing_string) 551 append_to_expansion_output (input_text_offset + 1); 552 553 get_rest_of_line (0, &temp); 554 555 /* Use @settitle value if @top parameter is empty. */ 556 if (STREQ (command, "top") && strlen(temp) == 0) 557 temp = xstrdup (title ? title : ""); 558 559 index = search_sectioning (cmd); 560 if (index < 0) 561 { 562 /* should never happen, but a poor guy, named Murphy ... */ 563 warning (_("Internal error (search_sectioning) \"%s\"!"), cmd); 564 return; 565 } 566 567 /* Produce "X.Y" and add it to HTML output. */ 568 { 569 char *title_number = handle_enum_increment (level, index); 570 if (strlen (title_number) > 0) 571 add_word_args ("%s ", title_number); 572 } 573 574 /* add the section name to both HTML and macro-expanded output. */ 575 if (macro_expansion_output_stream && !executing_string) 576 { 577 remember_itext (input_text, input_text_offset); 578 me_execute_string (temp); 579 write_region_to_macro_output ("\n", 0, 1); 580 } 581 else 582 execute_string ("%s", temp); 583 584 ending_pos = output_paragraph + output_paragraph_offset; 585 586 /* Pluck ``X.Y SECTION-NAME'' from the output buffer and insert it 587 into the TOC. */ 588 if (section_alist[index].toc == TOC_YES) 589 toc_add_entry (substring (starting_pos, ending_pos), 590 level, current_node, toc_anchor); 591 592 free (temp); 593 594 if (outstanding_node) 595 outstanding_node = 0; 596 597 add_word_args ("</h%d>", level + 2); 598 close_paragraph(); 599 filling_enabled = 1; 600 no_indent = old_no_indent; 601} 602 603 604/* Shift the meaning of @section to @chapter. */ 605void 606cm_raisesections (void) 607{ 608 discard_until ("\n"); 609 section_alist_offset--; 610} 611 612/* Shift the meaning of @chapter to @section. */ 613void 614cm_lowersections (void) 615{ 616 discard_until ("\n"); 617 section_alist_offset++; 618} 619 620/* The command still works, but prints a warning message in addition. */ 621void 622cm_ideprecated (int arg, int start, int end) 623{ 624 warning (_("%c%s is obsolete; use %c%s instead"), 625 COMMAND_PREFIX, command, COMMAND_PREFIX, command + 1); 626 sectioning_underscore (command + 1); 627} 628 629 630/* Treat this just like @unnumbered. The only difference is 631 in node defaulting. */ 632void 633cm_top (void) 634{ 635 /* It is an error to have more than one @top. */ 636 if (top_node_seen && strcmp (current_node, "Top") != 0) 637 { 638 TAG_ENTRY *tag = tag_table; 639 640 line_error (_("Node with %ctop as a section already exists"), 641 COMMAND_PREFIX); 642 643 while (tag) 644 { 645 if (tag->flags & TAG_FLAG_IS_TOP) 646 { 647 file_line_error (tag->filename, tag->line_no, 648 _("Here is the %ctop node"), COMMAND_PREFIX); 649 return; 650 } 651 tag = tag->next_ent; 652 } 653 } 654 else 655 { 656 top_node_seen = 1; 657 658 /* It is an error to use @top before using @node. */ 659 if (!tag_table) 660 { 661 char *top_name; 662 663 get_rest_of_line (0, &top_name); 664 line_error (_("%ctop used before %cnode, defaulting to %s"), 665 COMMAND_PREFIX, COMMAND_PREFIX, top_name); 666 execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name); 667 free (top_name); 668 return; 669 } 670 671 cm_unnumbered (); 672 673 /* The most recently defined node is the top node. */ 674 tag_table->flags |= TAG_FLAG_IS_TOP; 675 676 /* Now set the logical hierarchical level of the Top node. */ 677 { 678 int orig_offset = input_text_offset; 679 680 input_text_offset = search_forward (node_search_string, orig_offset); 681 682 if (input_text_offset > 0) 683 { 684 int this_section; 685 686 /* We have encountered a non-top node, so mark that one exists. */ 687 non_top_node_seen = 1; 688 689 /* Move to the end of this line, and find out what the 690 sectioning command is here. */ 691 while (input_text[input_text_offset] != '\n') 692 input_text_offset++; 693 694 if (input_text_offset < input_text_length) 695 input_text_offset++; 696 697 this_section = what_section (input_text + input_text_offset, 698 NULL); 699 700 /* If we found a sectioning command, then give the top section 701 a level of this section - 1. */ 702 if (this_section != -1) 703 set_top_section_level (this_section - 1); 704 } 705 input_text_offset = orig_offset; 706 } 707 } 708} 709 710/* The remainder of the text on this line is a chapter heading. */ 711void 712cm_chapter (void) 713{ 714 enum_marker = 0; 715 sectioning_underscore ("chapter"); 716} 717 718/* The remainder of the text on this line is a section heading. */ 719void 720cm_section (void) 721{ 722 sectioning_underscore ("section"); 723} 724 725/* The remainder of the text on this line is a subsection heading. */ 726void 727cm_subsection (void) 728{ 729 sectioning_underscore ("subsection"); 730} 731 732/* The remainder of the text on this line is a subsubsection heading. */ 733void 734cm_subsubsection (void) 735{ 736 sectioning_underscore ("subsubsection"); 737} 738 739/* The remainder of the text on this line is an unnumbered heading. */ 740void 741cm_unnumbered (void) 742{ 743 enum_marker = UNNUMBERED_MAGIC; 744 sectioning_underscore ("unnumbered"); 745} 746 747/* The remainder of the text on this line is an unnumbered section heading. */ 748void 749cm_unnumberedsec (void) 750{ 751 sectioning_underscore ("unnumberedsec"); 752} 753 754/* The remainder of the text on this line is an unnumbered 755 subsection heading. */ 756void 757cm_unnumberedsubsec (void) 758{ 759 sectioning_underscore ("unnumberedsubsec"); 760} 761 762/* The remainder of the text on this line is an unnumbered 763 subsubsection heading. */ 764void 765cm_unnumberedsubsubsec (void) 766{ 767 sectioning_underscore ("unnumberedsubsubsec"); 768} 769 770/* The remainder of the text on this line is an appendix heading. */ 771void 772cm_appendix (void) 773{ 774 /* Reset top level number so we start from Appendix A */ 775 if (enum_marker != APPENDIX_MAGIC) 776 numbers [0] = 0; 777 enum_marker = APPENDIX_MAGIC; 778 sectioning_underscore ("appendix"); 779} 780 781/* The remainder of the text on this line is an appendix section heading. */ 782void 783cm_appendixsec (void) 784{ 785 sectioning_underscore ("appendixsec"); 786} 787 788/* The remainder of the text on this line is an appendix subsection heading. */ 789void 790cm_appendixsubsec (void) 791{ 792 sectioning_underscore ("appendixsubsec"); 793} 794 795/* The remainder of the text on this line is an appendix 796 subsubsection heading. */ 797void 798cm_appendixsubsubsec (void) 799{ 800 sectioning_underscore ("appendixsubsubsec"); 801} 802 803/* Compatibility functions substitute for chapter, section, etc. */ 804void 805cm_majorheading (void) 806{ 807 sectioning_underscore ("majorheading"); 808} 809 810void 811cm_chapheading (void) 812{ 813 sectioning_underscore ("chapheading"); 814} 815 816void 817cm_heading (void) 818{ 819 sectioning_underscore ("heading"); 820} 821 822void 823cm_subheading (void) 824{ 825 sectioning_underscore ("subheading"); 826} 827 828void 829cm_subsubheading (void) 830{ 831 sectioning_underscore ("subsubheading"); 832} 833