1 /* sectioning.c -- for @chapter, @section, ..., @contents ...
2 $Id: sectioning.c,v 1.25 2004/07/05 22:23:23 karl Exp $
4 Copyright (C) 1999, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
28 #include "sectioning.h"
31 /* See comment in sectioning.h. */
32 section_alist_type section_alist
[] = {
33 { "unnumberedsubsubsec", 5, ENUM_SECT_NO
, TOC_YES
},
34 { "unnumberedsubsec", 4, ENUM_SECT_NO
, TOC_YES
},
35 { "unnumberedsec", 3, ENUM_SECT_NO
, TOC_YES
},
36 { "unnumbered", 2, ENUM_SECT_NO
, TOC_YES
},
37 { "centerchap", 2, ENUM_SECT_NO
, TOC_YES
},
39 { "appendixsubsubsec", 5, ENUM_SECT_APP
, TOC_YES
}, /* numbered like A.X.X.X */
40 { "appendixsubsec", 4, ENUM_SECT_APP
, TOC_YES
},
41 { "appendixsec", 3, ENUM_SECT_APP
, TOC_YES
},
42 { "appendixsection", 3, ENUM_SECT_APP
, TOC_YES
},
43 { "appendix", 2, ENUM_SECT_APP
, TOC_YES
},
45 { "subsubsec", 5, ENUM_SECT_YES
, TOC_YES
},
46 { "subsubsection", 5, ENUM_SECT_YES
, TOC_YES
},
47 { "subsection", 4, ENUM_SECT_YES
, TOC_YES
},
48 { "section", 3, ENUM_SECT_YES
, TOC_YES
},
49 { "chapter", 2, ENUM_SECT_YES
, TOC_YES
},
51 { "subsubheading", 5, ENUM_SECT_NO
, TOC_NO
},
52 { "subheading", 4, ENUM_SECT_NO
, TOC_NO
},
53 { "heading", 3, ENUM_SECT_NO
, TOC_NO
},
54 { "chapheading", 2, ENUM_SECT_NO
, TOC_NO
},
55 { "majorheading", 2, ENUM_SECT_NO
, TOC_NO
},
57 { "top", 1, ENUM_SECT_NO
, TOC_YES
},
61 /* The argument of @settitle, used for HTML. */
65 #define APPENDIX_MAGIC 1024
66 #define UNNUMBERED_MAGIC 2048
68 /* Number memory for every level @chapter, @section,
69 @subsection, @subsubsection. */
70 static int numbers
[] = { 0, 0, 0, 0 };
72 /* enum_marker == APPENDIX_MAGIC then we are counting appendencies
73 enum_marker == UNNUMBERED_MAGIC then we are within unnumbered area.
74 Handling situations like this:
77 static int enum_marker
= 0;
79 /* Organized by level commands. That is, "*" == chapter, "=" == section. */
80 static char *scoring_characters
= "*=-.";
82 /* Amount to offset the name of sectioning commands to levels by. */
83 static int section_alist_offset
= 0;
85 /* These two variables are for @float, @cindex like commands that need to know
86 in which section they are used. */
87 /* Last value returned by get_sectioning_number. */
88 static char *last_sectioning_number
= "";
89 /* Last title used by sectioning_underscore, etc. */
90 static char *last_sectioning_title
= "";
92 /* num == ENUM_SECT_NO means unnumbered (should never call this)
93 num == ENUM_SECT_YES means numbered
94 num == ENUM_SECT_APP means numbered like A.1 and so on */
96 get_sectioning_number (int level
, int num
)
98 static char s
[100]; /* should ever be enough for 99.99.99.99
106 /* create enumeration in front of chapter, section, subsection and so on. */
107 for (i
= 0; i
< level
; i
++)
110 if ((i
== 0) && (enum_marker
== APPENDIX_MAGIC
))
111 sprintf (p
, "%c.", numbers
[i
] + 64); /* Should be changed to
114 sprintf (p
, "%d.", numbers
[i
]);
117 /* the last number is never followed by a dot */
119 if ((num
== ENUM_SECT_APP
)
121 && (enum_marker
== APPENDIX_MAGIC
))
122 sprintf (p
, _("Appendix %c"), numbers
[i
] + 64);
124 sprintf (p
, "%d", numbers
[i
]);
126 /* Poor man's cache :-) */
127 if (strlen (last_sectioning_number
))
128 free (last_sectioning_number
);
129 last_sectioning_number
= xstrdup (s
);
135 /* Set the level of @top to LEVEL. Return the old level of @top. */
137 set_top_section_level (int level
)
141 for (i
= 0; section_alist
[i
].name
; i
++)
142 if (strcmp (section_alist
[i
].name
, "top") == 0)
144 result
= section_alist
[i
].level
;
145 section_alist
[i
].level
= level
;
152 /* return the index of the given sectioning command in section_alist */
154 search_sectioning (char *text
)
159 /* ignore the optional command prefix */
160 if (text
[0] == COMMAND_PREFIX
)
163 for (i
= 0; (t
= section_alist
[i
].name
); i
++)
165 if (strcmp (t
, text
) == 0)
173 /* Return an integer which identifies the type of section present in
174 TEXT -- 1 for @top, 2 for chapters, ..., 5 for subsubsections (as
175 specified in section_alist). We take into account any @lowersections
176 and @raisesections. If SECNAME is non-NULL, also return the
177 corresponding section name. */
179 what_section (char *text
, char **secname
)
185 find_section_command
:
186 for (j
= 0; text
[j
] && cr_or_whitespace (text
[j
]); j
++);
187 if (text
[j
] != COMMAND_PREFIX
)
192 /* We skip @c, @comment, and @?index commands. */
193 if ((strncmp (text
, "comment", strlen ("comment")) == 0) ||
194 (text
[0] == 'c' && cr_or_whitespace (text
[1])) ||
195 (strcmp (text
+ 1, "index") == 0))
197 while (*text
++ != '\n');
198 goto find_section_command
;
201 /* Handle italicized sectioning commands. */
205 for (j
= 0; text
[j
] && !cr_or_whitespace (text
[j
]); j
++);
207 temp
= xmalloc (1 + j
);
208 strncpy (temp
, text
, j
);
211 index
= search_sectioning (temp
);
215 return_val
= section_alist
[index
].level
+ section_alist_offset
;
218 else if (return_val
> 5)
224 int alist_size
= sizeof (section_alist
) / sizeof(section_alist_type
);
225 /* Find location of offset sectioning entry, but don't go off
226 either end of the array. */
227 int index_offset
= MAX (index
- section_alist_offset
, 0);
228 index_offset
= MIN (index_offset
, alist_size
- 1);
230 /* Also make sure we don't go into the next "group" of
231 sectioning changes, e.g., change from an @appendix to an
232 @heading or some such. */
233 #define SIGN(expr) ((expr) < 0 ? -1 : 1)
234 for (i
= index
; i
!= index_offset
; i
-= SIGN (section_alist_offset
))
236 /* As it happens, each group has unique .num/.toc values. */
237 if (section_alist
[i
].num
!= section_alist
[index_offset
].num
238 || section_alist
[i
].toc
!= section_alist
[index_offset
].toc
)
241 *secname
= section_alist
[i
].name
;
248 /* Returns current top level division (ie. chapter, unnumbered) number.
249 - For chapters, returns the number.
250 - For unnumbered sections, returns empty string.
251 - For appendices, returns A, B, etc. */
253 current_chapter_number (void)
255 if (enum_marker
== UNNUMBERED_MAGIC
)
257 else if (enum_marker
== APPENDIX_MAGIC
)
260 sprintf (s
, "%c", numbers
[0] + 64);
266 sprintf (s
, "%d", numbers
[0]);
271 /* Returns number of the last sectioning command used. */
273 current_sectioning_number (void)
275 if (enum_marker
== UNNUMBERED_MAGIC
|| !number_sections
)
278 return xstrdup (last_sectioning_number
);
281 /* Returns arguments of the last sectioning command used. */
283 current_sectioning_name (void)
285 return xstrdup (last_sectioning_title
);
288 /* insert_and_underscore, sectioning_underscore and sectioning_html call this. */
291 handle_enum_increment (int level
, int index
)
293 /* Here is how TeX handles enumeration:
294 - Anything starting with @unnumbered is not enumerated.
295 - @majorheading and the like are not enumberated. */
298 /* First constraint above. */
299 if (enum_marker
== UNNUMBERED_MAGIC
&& level
== 0)
302 /* Second constraint. */
303 if (section_alist
[index
].num
== ENUM_SECT_NO
)
306 /* reset all counters which are one level deeper */
307 for (i
= level
; i
< 3; i
++)
311 if (section_alist
[index
].num
== ENUM_SECT_NO
|| enum_marker
== UNNUMBERED_MAGIC
315 return xstrdup (get_sectioning_number (level
, section_alist
[index
].num
));
320 sectioning_underscore (char *cmd
)
322 char *temp
, *secname
;
325 /* If we're not indenting the first paragraph, we shall make it behave
326 like @noindent is called directly after the section heading. */
327 if (! do_first_par_indent
)
330 temp
= xmalloc (2 + strlen (cmd
));
331 temp
[0] = COMMAND_PREFIX
;
332 strcpy (&temp
[1], cmd
);
333 level
= what_section (temp
, &secname
);
339 /* If the argument to @top is empty, we try using the one from @settitle.
340 Warn if both are unusable. */
341 if (STREQ (command
, "top"))
343 int save_input_text_offset
= input_text_offset
;
345 get_rest_of_line (0, &temp
);
347 /* Due to get_rest_of_line ... */
350 if (strlen (temp
) == 0 && (!title
|| strlen (title
) == 0))
351 warning ("Must specify a title with least one of @settitle or @top");
353 input_text_offset
= save_input_text_offset
;
358 /* If the section appears in the toc, it means it's a real section
359 unlike majorheading, chapheading etc. */
360 if (section_alist
[search_sectioning (cmd
)].toc
== TOC_YES
)
362 xml_close_sections (level
);
363 /* Mark the beginning of the section
364 If the next command is printindex, we will remove
365 the section and put an Index instead */
367 xml_last_section_output_position
= output_paragraph_offset
;
369 get_rest_of_line (0, &temp
);
371 /* Use @settitle value if @top parameter is empty. */
372 if (STREQ (command
, "top") && strlen(temp
) == 0)
373 temp
= xstrdup (title
? title
: "");
375 /* Docbook does not support @unnumbered at all. So we provide numbers
376 that other formats use. @appendix seems to be fine though, so we let
377 Docbook handle that as usual. */
378 if (docbook
&& enum_marker
!= APPENDIX_MAGIC
)
380 if (section_alist
[search_sectioning (cmd
)].num
== ENUM_SECT_NO
381 && section_alist
[search_sectioning (cmd
)].toc
== TOC_YES
)
382 xml_insert_element_with_attribute (xml_element (secname
),
383 START
, "label=\"%s\" xreflabel=\"%s\"",
384 handle_enum_increment (level
, search_sectioning (cmd
)),
385 text_expansion (temp
));
387 xml_insert_element_with_attribute (xml_element (secname
),
388 START
, "label=\"%s\"",
389 handle_enum_increment (level
, search_sectioning (cmd
)));
392 xml_insert_element (xml_element (secname
), START
);
394 xml_insert_element (TITLE
, START
);
395 xml_open_section (level
, secname
);
396 execute_string ("%s", temp
);
397 xml_insert_element (TITLE
, END
);
406 xml_insert_element_with_attribute (xml_element (secname
), START
,
407 "renderas=\"sect%d\"", level
);
409 xml_insert_element_with_attribute (xml_element (secname
), START
,
410 "renderas=\"other\"");
413 xml_insert_element (xml_element (secname
), START
);
415 get_rest_of_line (0, &temp
);
416 execute_string ("%s", temp
);
419 xml_insert_element (xml_element (secname
), END
);
423 sectioning_html (level
, secname
);
425 insert_and_underscore (level
, secname
);
429 /* Insert the text following input_text_offset up to the end of the line
430 in a new, separate paragraph. Directly underneath it, insert a
431 line of WITH_CHAR, the same length of the inserted text. */
433 insert_and_underscore (int level
, char *cmd
)
438 unsigned char *starting_pos
, *ending_pos
;
440 char with_char
= scoring_characters
[level
];
443 filling_enabled
= indented_fill
= 0;
444 old_no_indent
= no_indent
;
447 if (macro_expansion_output_stream
&& !executing_string
)
448 append_to_expansion_output (input_text_offset
+ 1);
450 get_rest_of_line (0, &temp
);
452 /* Use @settitle value if @top parameter is empty. */
453 if (STREQ (command
, "top") && strlen(temp
) == 0)
454 temp
= xstrdup (title
? title
: "");
456 starting_pos
= output_paragraph
+ output_paragraph_offset
;
458 /* Poor man's cache for section title. */
459 if (strlen (last_sectioning_title
))
460 free (last_sectioning_title
);
461 last_sectioning_title
= xstrdup (temp
);
463 index
= search_sectioning (cmd
);
466 /* should never happen, but a poor guy, named Murphy ... */
467 warning (_("Internal error (search_sectioning) `%s'!"), cmd
);
471 /* This is a bit tricky: we must produce "X.Y SECTION-NAME" in the
472 Info output and in TOC, but only SECTION-NAME in the macro-expanded
475 /* Step 1: produce "X.Y" and add it to Info output. */
476 add_word_args ("%s ", handle_enum_increment (level
, index
));
478 /* Step 2: add "SECTION-NAME" to both Info and macro-expanded output. */
479 if (macro_expansion_output_stream
&& !executing_string
)
481 char *temp1
= xmalloc (2 + strlen (temp
));
482 sprintf (temp1
, "%s\n", temp
);
483 remember_itext (input_text
, input_text_offset
);
484 me_execute_string (temp1
);
488 execute_string ("%s\n", temp
);
490 /* Step 3: pluck "X.Y SECTION-NAME" from the output buffer and
491 insert it into the TOC. */
492 ending_pos
= output_paragraph
+ output_paragraph_offset
;
493 if (section_alist
[index
].toc
== TOC_YES
)
494 toc_add_entry (substring (starting_pos
, ending_pos
- 1),
495 level
, current_node
, NULL
);
499 len
= (ending_pos
- starting_pos
) - 1;
500 for (i
= 0; i
< len
; i
++)
501 add_char (with_char
);
505 no_indent
= old_no_indent
;
508 /* Insert the text following input_text_offset up to the end of the
509 line as an HTML heading element of the appropriate `level' and
510 tagged as an anchor for the current node.. */
513 sectioning_html (int level
, char *cmd
)
515 static int toc_ref_count
= 0;
518 unsigned char *starting_pos
, *ending_pos
;
519 char *temp
, *toc_anchor
= NULL
;
522 filling_enabled
= indented_fill
= 0;
523 old_no_indent
= no_indent
;
526 /* level 0 (chapter) is <h2>, and we go down from there. */
527 add_html_block_elt_args ("<h%d class=\"%s\">", level
+ 2, cmd
);
529 /* If we are outside of any node, produce an anchor that
530 the TOC could refer to. */
531 if (!current_node
|| !*current_node
)
533 static const char a_name
[] = "<a name=\"";
535 starting_pos
= output_paragraph
+ output_paragraph_offset
;
536 add_word_args ("%sTOC%d\">", a_name
, toc_ref_count
++);
537 toc_anchor
= substring (starting_pos
+ sizeof (a_name
) - 1,
538 output_paragraph
+ output_paragraph_offset
);
539 /* This must be added after toc_anchor is extracted, since
540 toc_anchor cannot include the closing </a>. For details,
541 see toc.c:toc_add_entry and toc.c:contents_update_html.
543 Also, the anchor close must be output before the section name
544 in case the name itself contains an anchor. */
547 starting_pos
= output_paragraph
+ output_paragraph_offset
;
549 if (macro_expansion_output_stream
&& !executing_string
)
550 append_to_expansion_output (input_text_offset
+ 1);
552 get_rest_of_line (0, &temp
);
554 /* Use @settitle value if @top parameter is empty. */
555 if (STREQ (command
, "top") && strlen(temp
) == 0)
556 temp
= xstrdup (title
? title
: "");
558 index
= search_sectioning (cmd
);
561 /* should never happen, but a poor guy, named Murphy ... */
562 warning (_("Internal error (search_sectioning) \"%s\"!"), cmd
);
566 /* Produce "X.Y" and add it to HTML output. */
568 char *title_number
= handle_enum_increment (level
, index
);
569 if (strlen (title_number
) > 0)
570 add_word_args ("%s ", title_number
);
573 /* add the section name to both HTML and macro-expanded output. */
574 if (macro_expansion_output_stream
&& !executing_string
)
576 remember_itext (input_text
, input_text_offset
);
577 me_execute_string (temp
);
578 write_region_to_macro_output ("\n", 0, 1);
581 execute_string ("%s", temp
);
583 ending_pos
= output_paragraph
+ output_paragraph_offset
;
585 /* Pluck ``X.Y SECTION-NAME'' from the output buffer and insert it
587 if (section_alist
[index
].toc
== TOC_YES
)
588 toc_add_entry (substring (starting_pos
, ending_pos
),
589 level
, current_node
, toc_anchor
);
593 if (outstanding_node
)
594 outstanding_node
= 0;
596 add_word_args ("</h%d>", level
+ 2);
599 no_indent
= old_no_indent
;
603 /* Shift the meaning of @section to @chapter. */
605 cm_raisesections (void)
607 discard_until ("\n");
608 section_alist_offset
--;
611 /* Shift the meaning of @chapter to @section. */
613 cm_lowersections (void)
615 discard_until ("\n");
616 section_alist_offset
++;
619 /* The command still works, but prints a warning message in addition. */
621 cm_ideprecated (int arg
, int start
, int end
)
623 warning (_("%c%s is obsolete; use %c%s instead"),
624 COMMAND_PREFIX
, command
, COMMAND_PREFIX
, command
+ 1);
625 sectioning_underscore (command
+ 1);
629 /* Treat this just like @unnumbered. The only difference is
630 in node defaulting. */
634 /* It is an error to have more than one @top. */
635 if (top_node_seen
&& strcmp (current_node
, "Top") != 0)
637 TAG_ENTRY
*tag
= tag_table
;
639 line_error (_("Node with %ctop as a section already exists"),
644 if (tag
->flags
& TAG_FLAG_IS_TOP
)
646 file_line_error (tag
->filename
, tag
->line_no
,
647 _("Here is the %ctop node"), COMMAND_PREFIX
);
657 /* It is an error to use @top before using @node. */
662 get_rest_of_line (0, &top_name
);
663 line_error (_("%ctop used before %cnode, defaulting to %s"),
664 COMMAND_PREFIX
, COMMAND_PREFIX
, top_name
);
665 execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name
);
672 /* The most recently defined node is the top node. */
673 tag_table
->flags
|= TAG_FLAG_IS_TOP
;
675 /* Now set the logical hierarchical level of the Top node. */
677 int orig_offset
= input_text_offset
;
679 input_text_offset
= search_forward (node_search_string
, orig_offset
);
681 if (input_text_offset
> 0)
685 /* We have encountered a non-top node, so mark that one exists. */
686 non_top_node_seen
= 1;
688 /* Move to the end of this line, and find out what the
689 sectioning command is here. */
690 while (input_text
[input_text_offset
] != '\n')
693 if (input_text_offset
< input_text_length
)
696 this_section
= what_section (input_text
+ input_text_offset
,
699 /* If we found a sectioning command, then give the top section
700 a level of this section - 1. */
701 if (this_section
!= -1)
702 set_top_section_level (this_section
- 1);
704 input_text_offset
= orig_offset
;
709 /* The remainder of the text on this line is a chapter heading. */
714 sectioning_underscore ("chapter");
717 /* The remainder of the text on this line is a section heading. */
721 sectioning_underscore ("section");
724 /* The remainder of the text on this line is a subsection heading. */
728 sectioning_underscore ("subsection");
731 /* The remainder of the text on this line is a subsubsection heading. */
733 cm_subsubsection (void)
735 sectioning_underscore ("subsubsection");
738 /* The remainder of the text on this line is an unnumbered heading. */
742 enum_marker
= UNNUMBERED_MAGIC
;
743 sectioning_underscore ("unnumbered");
746 /* The remainder of the text on this line is an unnumbered section heading. */
748 cm_unnumberedsec (void)
750 sectioning_underscore ("unnumberedsec");
753 /* The remainder of the text on this line is an unnumbered
754 subsection heading. */
756 cm_unnumberedsubsec (void)
758 sectioning_underscore ("unnumberedsubsec");
761 /* The remainder of the text on this line is an unnumbered
762 subsubsection heading. */
764 cm_unnumberedsubsubsec (void)
766 sectioning_underscore ("unnumberedsubsubsec");
769 /* The remainder of the text on this line is an appendix heading. */
773 /* Reset top level number so we start from Appendix A */
774 if (enum_marker
!= APPENDIX_MAGIC
)
776 enum_marker
= APPENDIX_MAGIC
;
777 sectioning_underscore ("appendix");
780 /* The remainder of the text on this line is an appendix section heading. */
782 cm_appendixsec (void)
784 sectioning_underscore ("appendixsec");
787 /* The remainder of the text on this line is an appendix subsection heading. */
789 cm_appendixsubsec (void)
791 sectioning_underscore ("appendixsubsec");
794 /* The remainder of the text on this line is an appendix
795 subsubsection heading. */
797 cm_appendixsubsubsec (void)
799 sectioning_underscore ("appendixsubsubsec");
802 /* Compatibility functions substitute for chapter, section, etc. */
804 cm_majorheading (void)
806 sectioning_underscore ("majorheading");
810 cm_chapheading (void)
812 sectioning_underscore ("chapheading");
818 sectioning_underscore ("heading");
824 sectioning_underscore ("subheading");
828 cm_subsubheading (void)
830 sectioning_underscore ("subsubheading");