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@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