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