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