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