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