Home | History | Annotate | Line # | Download | only in makeinfo
sectioning.c revision 1.1
      1 /*	$NetBSD: sectioning.c,v 1.1 2016/01/14 00:11:29 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[1];
    264       sprintf (s, "%c", numbers[0] + 64);
    265       return xstrdup (s);
    266     }
    267   else
    268     {
    269       char s[5];
    270       sprintf (s, "%d", numbers[0]);
    271       return xstrdup (s);
    272     }
    273 }
    274 
    275 /* Returns number of the last sectioning command used.  */
    276 char *
    277 current_sectioning_number (void)
    278 {
    279   if (enum_marker == UNNUMBERED_MAGIC || !number_sections)
    280     return xstrdup ("");
    281   else
    282     return xstrdup (last_sectioning_number);
    283 }
    284 
    285 /* Returns arguments of the last sectioning command used.  */
    286 char *
    287 current_sectioning_name (void)
    288 {
    289   return xstrdup (last_sectioning_title);
    290 }
    291 
    292 /* insert_and_underscore, sectioning_underscore and sectioning_html call this.  */
    293 
    294 static char *
    295 handle_enum_increment (int level, int index)
    296 {
    297   /* Here is how TeX handles enumeration:
    298      - Anything starting with @unnumbered is not enumerated.
    299      - @majorheading and the like are not enumberated.  */
    300   int i;
    301 
    302   /* First constraint above.  */
    303   if (enum_marker == UNNUMBERED_MAGIC && level == 0)
    304     return xstrdup ("");
    305 
    306   /* Second constraint.  */
    307   if (section_alist[index].num == ENUM_SECT_NO)
    308     return xstrdup ("");
    309 
    310   /* reset all counters which are one level deeper */
    311   for (i = level; i < 3; i++)
    312     numbers [i + 1] = 0;
    313 
    314   numbers[level]++;
    315   if (section_alist[index].num == ENUM_SECT_NO || enum_marker == UNNUMBERED_MAGIC
    316       || !number_sections)
    317     return xstrdup ("");
    318   else
    319     return xstrdup (get_sectioning_number (level, section_alist[index].num));
    320 }
    321 
    322 
    323 void
    324 sectioning_underscore (char *cmd)
    325 {
    326   char *temp, *secname;
    327   int level;
    328 
    329   /* If we're not indenting the first paragraph, we shall make it behave
    330      like @noindent is called directly after the section heading. */
    331   if (! do_first_par_indent)
    332     cm_noindent ();
    333 
    334   temp = xmalloc (2 + strlen (cmd));
    335   temp[0] = COMMAND_PREFIX;
    336   strcpy (&temp[1], cmd);
    337   level = what_section (temp, &secname);
    338   level -= 2;
    339   if (level < 0)
    340     level = 0;
    341   free (temp);
    342 
    343   /* If the argument to @top is empty, we try using the one from @settitle.
    344      Warn if both are unusable.  */
    345   if (STREQ (command, "top"))
    346     {
    347       int save_input_text_offset = input_text_offset;
    348 
    349       get_rest_of_line (0, &temp);
    350 
    351       /* Due to get_rest_of_line ... */
    352       line_number--;
    353 
    354       if (strlen (temp) == 0 && (!title || strlen (title) == 0))
    355         warning ("Must specify a title with least one of @settitle or @top");
    356 
    357       input_text_offset = save_input_text_offset;
    358     }
    359 
    360   if (xml)
    361     {
    362       /* If the section appears in the toc, it means it's a real section
    363 	 unlike majorheading, chapheading etc. */
    364       if (section_alist[search_sectioning (cmd)].toc == TOC_YES)
    365 	{
    366 	  xml_close_sections (level);
    367 	  /* Mark the beginning of the section
    368 	     If the next command is printindex, we will remove
    369 	     the section and put an Index instead */
    370 	  flush_output ();
    371 	  xml_last_section_output_position = output_paragraph_offset;
    372 
    373 	  get_rest_of_line (0, &temp);
    374 
    375           /* Use @settitle value if @top parameter is empty.  */
    376           if (STREQ (command, "top") && strlen(temp) == 0)
    377             temp = xstrdup (title ? title : "");
    378 
    379           /* Docbook does not support @unnumbered at all.  So we provide numbers
    380              that other formats use.  @appendix seems to be fine though, so we let
    381              Docbook handle that as usual.  */
    382           if (docbook && enum_marker != APPENDIX_MAGIC)
    383             {
    384               if (section_alist[search_sectioning (cmd)].num == ENUM_SECT_NO
    385                   && section_alist[search_sectioning (cmd)].toc == TOC_YES)
    386                 xml_insert_element_with_attribute (xml_element (secname),
    387                     START, "label=\"%s\" xreflabel=\"%s\"",
    388                     handle_enum_increment (level, search_sectioning (cmd)),
    389                     text_expansion (temp));
    390               else
    391                 xml_insert_element_with_attribute (xml_element (secname),
    392                     START, "label=\"%s\"",
    393                     handle_enum_increment (level, search_sectioning (cmd)));
    394             }
    395           else
    396             xml_insert_element (xml_element (secname), START);
    397 
    398 	  xml_insert_element (TITLE, START);
    399 	  xml_open_section (level, secname);
    400 	  execute_string ("%s", temp);
    401 	  xml_insert_element (TITLE, END);
    402 
    403 	  free (temp);
    404 	}
    405       else
    406         {
    407           if (docbook)
    408             {
    409               if (level > 0)
    410                 xml_insert_element_with_attribute (xml_element (secname), START,
    411                     "renderas=\"sect%d\"", level);
    412               else
    413                 xml_insert_element_with_attribute (xml_element (secname), START,
    414                     "renderas=\"other\"");
    415             }
    416           else
    417             xml_insert_element (xml_element (secname), START);
    418 
    419           get_rest_of_line (0, &temp);
    420           execute_string ("%s", temp);
    421           free (temp);
    422 
    423           xml_insert_element (xml_element (secname), END);
    424         }
    425     }
    426   else if (html)
    427     sectioning_html (level, secname);
    428   else
    429     insert_and_underscore (level, secname);
    430 }
    431 
    432 
    433 /* Insert the text following input_text_offset up to the end of the line
    434    in a new, separate paragraph.  Directly underneath it, insert a
    435    line of WITH_CHAR, the same length of the inserted text. */
    436 void
    437 insert_and_underscore (int level, char *cmd)
    438 {
    439   int i, len;
    440   int index;
    441   int old_no_indent;
    442   unsigned char *starting_pos, *ending_pos;
    443   char *temp;
    444   char with_char = scoring_characters[level];
    445 
    446   close_paragraph ();
    447   filling_enabled =  indented_fill = 0;
    448   old_no_indent = no_indent;
    449   no_indent = 1;
    450 
    451   if (macro_expansion_output_stream && !executing_string)
    452     append_to_expansion_output (input_text_offset + 1);
    453 
    454   get_rest_of_line (0, &temp);
    455 
    456   /* Use @settitle value if @top parameter is empty.  */
    457   if (STREQ (command, "top") && strlen(temp) == 0)
    458     temp = xstrdup (title ? title : "");
    459 
    460   starting_pos = output_paragraph + output_paragraph_offset;
    461 
    462   /* Poor man's cache for section title.  */
    463   if (strlen (last_sectioning_title))
    464     free (last_sectioning_title);
    465   last_sectioning_title = xstrdup (temp);
    466 
    467   index = search_sectioning (cmd);
    468   if (index < 0)
    469     {
    470       /* should never happen, but a poor guy, named Murphy ... */
    471       warning (_("Internal error (search_sectioning) `%s'!"), cmd);
    472       return;
    473     }
    474 
    475   /* This is a bit tricky: we must produce "X.Y SECTION-NAME" in the
    476      Info output and in TOC, but only SECTION-NAME in the macro-expanded
    477      output.  */
    478 
    479   /* Step 1: produce "X.Y" and add it to Info output.  */
    480   add_word_args ("%s ", handle_enum_increment (level, index));
    481 
    482   /* Step 2: add "SECTION-NAME" to both Info and macro-expanded output.  */
    483   if (macro_expansion_output_stream && !executing_string)
    484     {
    485       char *temp1 = xmalloc (2 + strlen (temp));
    486       sprintf (temp1, "%s\n", temp);
    487       remember_itext (input_text, input_text_offset);
    488       me_execute_string (temp1);
    489       free (temp1);
    490     }
    491   else
    492     execute_string ("%s\n", temp);
    493 
    494   /* Step 3: pluck "X.Y SECTION-NAME" from the output buffer and
    495      insert it into the TOC.  */
    496   ending_pos = output_paragraph + output_paragraph_offset;
    497   if (section_alist[index].toc == TOC_YES)
    498     toc_add_entry (substring (starting_pos, ending_pos - 1),
    499                    level, current_node, NULL);
    500 
    501   free (temp);
    502 
    503   len = (ending_pos - starting_pos) - 1;
    504   for (i = 0; i < len; i++)
    505     add_char (with_char);
    506   insert ('\n');
    507   close_paragraph ();
    508   filling_enabled = 1;
    509   no_indent = old_no_indent;
    510 }
    511 
    512 /* Insert the text following input_text_offset up to the end of the
    513    line as an HTML heading element of the appropriate `level' and
    514    tagged as an anchor for the current node.. */
    515 
    516 void
    517 sectioning_html (int level, char *cmd)
    518 {
    519   static int toc_ref_count = 0;
    520   int index;
    521   int old_no_indent;
    522   unsigned char *starting_pos, *ending_pos;
    523   char *temp, *toc_anchor = NULL;
    524 
    525   close_paragraph ();
    526   filling_enabled =  indented_fill = 0;
    527   old_no_indent = no_indent;
    528   no_indent = 1;
    529 
    530   /* level 0 (chapter) is <h2>, and we go down from there.  */
    531   add_html_block_elt_args ("<h%d class=\"%s\">", level + 2, cmd);
    532 
    533   /* If we are outside of any node, produce an anchor that
    534      the TOC could refer to.  */
    535   if (!current_node || !*current_node)
    536     {
    537       static const char a_name[] = "<a name=\"";
    538 
    539       starting_pos = output_paragraph + output_paragraph_offset;
    540       add_word_args ("%sTOC%d\">", a_name, toc_ref_count++);
    541       toc_anchor = substring (starting_pos + sizeof (a_name) - 1,
    542                               output_paragraph + output_paragraph_offset);
    543       /* This must be added after toc_anchor is extracted, since
    544          toc_anchor cannot include the closing </a>.  For details,
    545          see toc.c:toc_add_entry and toc.c:contents_update_html.
    546 
    547          Also, the anchor close must be output before the section name
    548          in case the name itself contains an anchor. */
    549       add_word ("</a>");
    550     }
    551   starting_pos = output_paragraph + output_paragraph_offset;
    552 
    553   if (macro_expansion_output_stream && !executing_string)
    554     append_to_expansion_output (input_text_offset + 1);
    555 
    556   get_rest_of_line (0, &temp);
    557 
    558   /* Use @settitle value if @top parameter is empty.  */
    559   if (STREQ (command, "top") && strlen(temp) == 0)
    560     temp = xstrdup (title ? title : "");
    561 
    562   index = search_sectioning (cmd);
    563   if (index < 0)
    564     {
    565       /* should never happen, but a poor guy, named Murphy ... */
    566       warning (_("Internal error (search_sectioning) \"%s\"!"), cmd);
    567       return;
    568     }
    569 
    570   /* Produce "X.Y" and add it to HTML output.  */
    571   {
    572     char *title_number = handle_enum_increment (level, index);
    573     if (strlen (title_number) > 0)
    574       add_word_args ("%s ", title_number);
    575   }
    576 
    577   /* add the section name to both HTML and macro-expanded output.  */
    578   if (macro_expansion_output_stream && !executing_string)
    579     {
    580       remember_itext (input_text, input_text_offset);
    581       me_execute_string (temp);
    582       write_region_to_macro_output ("\n", 0, 1);
    583     }
    584   else
    585     execute_string ("%s", temp);
    586 
    587   ending_pos = output_paragraph + output_paragraph_offset;
    588 
    589   /* Pluck ``X.Y SECTION-NAME'' from the output buffer and insert it
    590      into the TOC.  */
    591   if (section_alist[index].toc == TOC_YES)
    592     toc_add_entry (substring (starting_pos, ending_pos),
    593                    level, current_node, toc_anchor);
    594 
    595   free (temp);
    596 
    597   if (outstanding_node)
    598     outstanding_node = 0;
    599 
    600   add_word_args ("</h%d>", level + 2);
    601   close_paragraph();
    602   filling_enabled = 1;
    603   no_indent = old_no_indent;
    604 }
    605 
    606 
    607 /* Shift the meaning of @section to @chapter. */
    609 void
    610 cm_raisesections (void)
    611 {
    612   discard_until ("\n");
    613   section_alist_offset--;
    614 }
    615 
    616 /* Shift the meaning of @chapter to @section. */
    617 void
    618 cm_lowersections (void)
    619 {
    620   discard_until ("\n");
    621   section_alist_offset++;
    622 }
    623 
    624 /* The command still works, but prints a warning message in addition. */
    625 void
    626 cm_ideprecated (int arg, int start, int end)
    627 {
    628   warning (_("%c%s is obsolete; use %c%s instead"),
    629            COMMAND_PREFIX, command, COMMAND_PREFIX, command + 1);
    630   sectioning_underscore (command + 1);
    631 }
    632 
    633 
    634 /* Treat this just like @unnumbered.  The only difference is
    636    in node defaulting. */
    637 void
    638 cm_top (void)
    639 {
    640   /* It is an error to have more than one @top. */
    641   if (top_node_seen && strcmp (current_node, "Top") != 0)
    642     {
    643       TAG_ENTRY *tag = tag_table;
    644 
    645       line_error (_("Node with %ctop as a section already exists"),
    646                   COMMAND_PREFIX);
    647 
    648       while (tag)
    649         {
    650           if (tag->flags & TAG_FLAG_IS_TOP)
    651             {
    652               file_line_error (tag->filename, tag->line_no,
    653                                _("Here is the %ctop node"), COMMAND_PREFIX);
    654               return;
    655             }
    656           tag = tag->next_ent;
    657         }
    658     }
    659   else
    660     {
    661       top_node_seen = 1;
    662 
    663       /* It is an error to use @top before using @node. */
    664       if (!tag_table)
    665         {
    666           char *top_name;
    667 
    668           get_rest_of_line (0, &top_name);
    669           line_error (_("%ctop used before %cnode, defaulting to %s"),
    670                       COMMAND_PREFIX, COMMAND_PREFIX, top_name);
    671           execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name);
    672           free (top_name);
    673           return;
    674         }
    675 
    676       cm_unnumbered ();
    677 
    678       /* The most recently defined node is the top node. */
    679       tag_table->flags |= TAG_FLAG_IS_TOP;
    680 
    681       /* Now set the logical hierarchical level of the Top node. */
    682       {
    683         int orig_offset = input_text_offset;
    684 
    685         input_text_offset = search_forward (node_search_string, orig_offset);
    686 
    687         if (input_text_offset > 0)
    688           {
    689             int this_section;
    690 
    691             /* We have encountered a non-top node, so mark that one exists. */
    692             non_top_node_seen = 1;
    693 
    694             /* Move to the end of this line, and find out what the
    695                sectioning command is here. */
    696             while (input_text[input_text_offset] != '\n')
    697               input_text_offset++;
    698 
    699             if (input_text_offset < input_text_length)
    700               input_text_offset++;
    701 
    702             this_section = what_section (input_text + input_text_offset,
    703                                          NULL);
    704 
    705             /* If we found a sectioning command, then give the top section
    706                a level of this section - 1. */
    707             if (this_section != -1)
    708               set_top_section_level (this_section - 1);
    709           }
    710         input_text_offset = orig_offset;
    711       }
    712     }
    713 }
    714 
    715 /* The remainder of the text on this line is a chapter heading. */
    716 void
    717 cm_chapter (void)
    718 {
    719   enum_marker = 0;
    720   sectioning_underscore ("chapter");
    721 }
    722 
    723 /* The remainder of the text on this line is a section heading. */
    724 void
    725 cm_section (void)
    726 {
    727   sectioning_underscore ("section");
    728 }
    729 
    730 /* The remainder of the text on this line is a subsection heading. */
    731 void
    732 cm_subsection (void)
    733 {
    734   sectioning_underscore ("subsection");
    735 }
    736 
    737 /* The remainder of the text on this line is a subsubsection heading. */
    738 void
    739 cm_subsubsection (void)
    740 {
    741   sectioning_underscore ("subsubsection");
    742 }
    743 
    744 /* The remainder of the text on this line is an unnumbered heading. */
    745 void
    746 cm_unnumbered (void)
    747 {
    748   enum_marker = UNNUMBERED_MAGIC;
    749   sectioning_underscore ("unnumbered");
    750 }
    751 
    752 /* The remainder of the text on this line is an unnumbered section heading. */
    753 void
    754 cm_unnumberedsec (void)
    755 {
    756   sectioning_underscore ("unnumberedsec");
    757 }
    758 
    759 /* The remainder of the text on this line is an unnumbered
    760    subsection heading. */
    761 void
    762 cm_unnumberedsubsec (void)
    763 {
    764   sectioning_underscore ("unnumberedsubsec");
    765 }
    766 
    767 /* The remainder of the text on this line is an unnumbered
    768    subsubsection heading. */
    769 void
    770 cm_unnumberedsubsubsec (void)
    771 {
    772   sectioning_underscore ("unnumberedsubsubsec");
    773 }
    774 
    775 /* The remainder of the text on this line is an appendix heading. */
    776 void
    777 cm_appendix (void)
    778 {
    779   /* Reset top level number so we start from Appendix A */
    780   if (enum_marker != APPENDIX_MAGIC)
    781     numbers [0] = 0;
    782   enum_marker = APPENDIX_MAGIC;
    783   sectioning_underscore ("appendix");
    784 }
    785 
    786 /* The remainder of the text on this line is an appendix section heading. */
    787 void
    788 cm_appendixsec (void)
    789 {
    790   sectioning_underscore ("appendixsec");
    791 }
    792 
    793 /* The remainder of the text on this line is an appendix subsection heading. */
    794 void
    795 cm_appendixsubsec (void)
    796 {
    797   sectioning_underscore ("appendixsubsec");
    798 }
    799 
    800 /* The remainder of the text on this line is an appendix
    801    subsubsection heading. */
    802 void
    803 cm_appendixsubsubsec (void)
    804 {
    805   sectioning_underscore ("appendixsubsubsec");
    806 }
    807 
    808 /* Compatibility functions substitute for chapter, section, etc. */
    809 void
    810 cm_majorheading (void)
    811 {
    812   sectioning_underscore ("majorheading");
    813 }
    814 
    815 void
    816 cm_chapheading (void)
    817 {
    818   sectioning_underscore ("chapheading");
    819 }
    820 
    821 void
    822 cm_heading (void)
    823 {
    824   sectioning_underscore ("heading");
    825 }
    826 
    827 void
    828 cm_subheading (void)
    829 {
    830   sectioning_underscore ("subheading");
    831 }
    832 
    833 void
    834 cm_subsubheading (void)
    835 {
    836   sectioning_underscore ("subsubheading");
    837 }
    838