sectioning.c revision 221386
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.  */
32section_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
61/* The argument of @settitle, used for HTML. */
62char *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. */
70static 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 ...   */
77static int enum_marker = 0;
78
79/* Organized by level commands.  That is, "*" == chapter, "=" == section. */
80static char *scoring_characters = "*=-.";
81
82/* Amount to offset the name of sectioning commands to levels by. */
83static 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.  */
88static char *last_sectioning_number = "";
89/* Last title used by sectioning_underscore, etc.  */
90static char *last_sectioning_title = "";
91
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 */
95static char *
96get_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. */
136int
137set_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 */
153static int
154search_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.  */
178int
179what_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. */
252char *
253current_chapter_number (void)
254{
255  if (enum_marker == UNNUMBERED_MAGIC)
256    return xstrdup ("");
257  else if (enum_marker == APPENDIX_MAGIC)
258    {
259      char s[2];
260      sprintf (s, "%c", numbers[0] + 64);
261      return xstrdup (s);
262    }
263  else
264    {
265      char s[11];
266      sprintf (s, "%d", numbers[0]);
267      return xstrdup (s);
268    }
269}
270
271/* Returns number of the last sectioning command used.  */
272char *
273current_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.  */
282char *
283current_sectioning_name (void)
284{
285  return xstrdup (last_sectioning_title);
286}
287
288/* insert_and_underscore, sectioning_underscore and sectioning_html call this.  */
289
290static char *
291handle_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
319void
320sectioning_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. */
432void
433insert_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
512void
513sectioning_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
603/* Shift the meaning of @section to @chapter. */
604void
605cm_raisesections (void)
606{
607  discard_until ("\n");
608  section_alist_offset--;
609}
610
611/* Shift the meaning of @chapter to @section. */
612void
613cm_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. */
620void
621cm_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
629/* Treat this just like @unnumbered.  The only difference is
630   in node defaulting. */
631void
632cm_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. */
710void
711cm_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. */
718void
719cm_section (void)
720{
721  sectioning_underscore ("section");
722}
723
724/* The remainder of the text on this line is a subsection heading. */
725void
726cm_subsection (void)
727{
728  sectioning_underscore ("subsection");
729}
730
731/* The remainder of the text on this line is a subsubsection heading. */
732void
733cm_subsubsection (void)
734{
735  sectioning_underscore ("subsubsection");
736}
737
738/* The remainder of the text on this line is an unnumbered heading. */
739void
740cm_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. */
747void
748cm_unnumberedsec (void)
749{
750  sectioning_underscore ("unnumberedsec");
751}
752
753/* The remainder of the text on this line is an unnumbered
754   subsection heading. */
755void
756cm_unnumberedsubsec (void)
757{
758  sectioning_underscore ("unnumberedsubsec");
759}
760
761/* The remainder of the text on this line is an unnumbered
762   subsubsection heading. */
763void
764cm_unnumberedsubsubsec (void)
765{
766  sectioning_underscore ("unnumberedsubsubsec");
767}
768
769/* The remainder of the text on this line is an appendix heading. */
770void
771cm_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. */
781void
782cm_appendixsec (void)
783{
784  sectioning_underscore ("appendixsec");
785}
786
787/* The remainder of the text on this line is an appendix subsection heading. */
788void
789cm_appendixsubsec (void)
790{
791  sectioning_underscore ("appendixsubsec");
792}
793
794/* The remainder of the text on this line is an appendix
795   subsubsection heading. */
796void
797cm_appendixsubsubsec (void)
798{
799  sectioning_underscore ("appendixsubsubsec");
800}
801
802/* Compatibility functions substitute for chapter, section, etc. */
803void
804cm_majorheading (void)
805{
806  sectioning_underscore ("majorheading");
807}
808
809void
810cm_chapheading (void)
811{
812  sectioning_underscore ("chapheading");
813}
814
815void
816cm_heading (void)
817{
818  sectioning_underscore ("heading");
819}
820
821void
822cm_subheading (void)
823{
824  sectioning_underscore ("subheading");
825}
826
827void
828cm_subsubheading (void)
829{
830  sectioning_underscore ("subsubheading");
831}
832