3 /* sectioning.c -- for @chapter, @section, ..., @contents ...
4 Id: sectioning.c,v 1.25 2004/07/05 22:23:23 karl Exp
6 Copyright (C) 1999, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>. */
30 #include "sectioning.h"
33 /* See comment in sectioning.h. */
34 section_alist_type section_alist
[] = {
35 { "unnumberedsubsubsec", 5, ENUM_SECT_NO
, TOC_YES
},
36 { "unnumberedsubsec", 4, ENUM_SECT_NO
, TOC_YES
},
37 { "unnumberedsec", 3, ENUM_SECT_NO
, TOC_YES
},
38 { "unnumbered", 2, ENUM_SECT_NO
, TOC_YES
},
39 { "centerchap", 2, ENUM_SECT_NO
, TOC_YES
},
41 { "appendixsubsubsec", 5, ENUM_SECT_APP
, TOC_YES
}, /* numbered like A.X.X.X */
42 { "appendixsubsec", 4, ENUM_SECT_APP
, TOC_YES
},
43 { "appendixsec", 3, ENUM_SECT_APP
, TOC_YES
},
44 { "appendixsection", 3, ENUM_SECT_APP
, TOC_YES
},
45 { "appendix", 2, ENUM_SECT_APP
, TOC_YES
},
47 { "subsubsec", 5, ENUM_SECT_YES
, TOC_YES
},
48 { "subsubsection", 5, ENUM_SECT_YES
, TOC_YES
},
49 { "subsection", 4, ENUM_SECT_YES
, TOC_YES
},
50 { "section", 3, ENUM_SECT_YES
, TOC_YES
},
51 { "chapter", 2, ENUM_SECT_YES
, TOC_YES
},
53 { "subsubheading", 5, ENUM_SECT_NO
, TOC_NO
},
54 { "subheading", 4, ENUM_SECT_NO
, TOC_NO
},
55 { "heading", 3, ENUM_SECT_NO
, TOC_NO
},
56 { "chapheading", 2, ENUM_SECT_NO
, TOC_NO
},
57 { "majorheading", 2, ENUM_SECT_NO
, TOC_NO
},
59 { "top", 1, ENUM_SECT_NO
, TOC_YES
},
63 /* The argument of @settitle, used for HTML. */
67 #define APPENDIX_MAGIC 1024
68 #define UNNUMBERED_MAGIC 2048
70 /* Number memory for every level @chapter, @section,
71 @subsection, @subsubsection. */
72 static int numbers
[] = { 0, 0, 0, 0 };
74 /* enum_marker == APPENDIX_MAGIC then we are counting appendencies
75 enum_marker == UNNUMBERED_MAGIC then we are within unnumbered area.
76 Handling situations like this:
79 static int enum_marker
= 0;
81 /* Organized by level commands. That is, "*" == chapter, "=" == section. */
82 static char *scoring_characters
= "*=-.";
84 /* Amount to offset the name of sectioning commands to levels by. */
85 static int section_alist_offset
= 0;
87 /* These two variables are for @float, @cindex like commands that need to know
88 in which section they are used. */
89 /* Last value returned by get_sectioning_number. */
90 static char *last_sectioning_number
= "";
91 /* Last title used by sectioning_underscore, etc. */
92 static char *last_sectioning_title
= "";
94 /* num == ENUM_SECT_NO means unnumbered (should never call this)
95 num == ENUM_SECT_YES means numbered
96 num == ENUM_SECT_APP means numbered like A.1 and so on */
98 get_sectioning_number (int level
, int num
)
100 static char s
[100]; /* should ever be enough for 99.99.99.99
108 /* create enumeration in front of chapter, section, subsection and so on. */
109 for (i
= 0; i
< level
; i
++)
112 if ((i
== 0) && (enum_marker
== APPENDIX_MAGIC
))
113 sprintf (p
, "%c.", numbers
[i
] + 64); /* Should be changed to
116 sprintf (p
, "%d.", numbers
[i
]);
119 /* the last number is never followed by a dot */
121 if ((num
== ENUM_SECT_APP
)
123 && (enum_marker
== APPENDIX_MAGIC
))
124 sprintf (p
, _("Appendix %c"), numbers
[i
] + 64);
126 sprintf (p
, "%d", numbers
[i
]);
128 /* Poor man's cache :-) */
129 if (strlen (last_sectioning_number
))
130 free (last_sectioning_number
);
131 last_sectioning_number
= xstrdup (s
);
137 /* Set the level of @top to LEVEL. Return the old level of @top. */
139 set_top_section_level (int level
)
143 for (i
= 0; section_alist
[i
].name
; i
++)
144 if (strcmp (section_alist
[i
].name
, "top") == 0)
146 result
= section_alist
[i
].level
;
147 section_alist
[i
].level
= level
;
154 /* return the index of the given sectioning command in section_alist */
156 search_sectioning (char *text
)
161 /* ignore the optional command prefix */
162 if (text
[0] == COMMAND_PREFIX
)
165 for (i
= 0; (t
= section_alist
[i
].name
); i
++)
167 if (strcmp (t
, text
) == 0)
175 /* Return an integer which identifies the type of section present in
176 TEXT -- 1 for @top, 2 for chapters, ..., 5 for subsubsections (as
177 specified in section_alist). We take into account any @lowersections
178 and @raisesections. If SECNAME is non-NULL, also return the
179 corresponding section name. */
181 what_section (char *text
, char **secname
)
187 find_section_command
:
188 for (j
= 0; text
[j
] && cr_or_whitespace (text
[j
]); j
++);
189 if (text
[j
] != COMMAND_PREFIX
)
194 /* We skip @c, @comment, and @?index commands. */
195 if ((strncmp (text
, "comment", strlen ("comment")) == 0) ||
196 (text
[0] == 'c' && cr_or_whitespace (text
[1])) ||
197 (strcmp (text
+ 1, "index") == 0))
199 while (*text
++ != '\n');
200 goto find_section_command
;
203 /* Handle italicized sectioning commands. */
207 for (j
= 0; text
[j
] && !cr_or_whitespace (text
[j
]); j
++);
209 temp
= xmalloc (1 + j
);
210 strncpy (temp
, text
, j
);
213 index
= search_sectioning (temp
);
217 return_val
= section_alist
[index
].level
+ section_alist_offset
;
220 else if (return_val
> 5)
226 int alist_size
= sizeof (section_alist
) / sizeof(section_alist_type
);
227 /* Find location of offset sectioning entry, but don't go off
228 either end of the array. */
229 int index_offset
= MAX (index
- section_alist_offset
, 0);
230 index_offset
= MIN (index_offset
, alist_size
- 1);
232 /* Also make sure we don't go into the next "group" of
233 sectioning changes, e.g., change from an @appendix to an
234 @heading or some such. */
235 #define SIGN(expr) ((expr) < 0 ? -1 : 1)
236 for (i
= index
; i
!= index_offset
; i
-= SIGN (section_alist_offset
))
238 /* As it happens, each group has unique .num/.toc values. */
239 if (section_alist
[i
].num
!= section_alist
[index_offset
].num
240 || section_alist
[i
].toc
!= section_alist
[index_offset
].toc
)
243 *secname
= section_alist
[i
].name
;
250 /* Returns current top level division (ie. chapter, unnumbered) number.
251 - For chapters, returns the number.
252 - For unnumbered sections, returns empty string.
253 - For appendices, returns A, B, etc. */
255 current_chapter_number (void)
257 if (enum_marker
== UNNUMBERED_MAGIC
)
259 else if (enum_marker
== APPENDIX_MAGIC
)
262 sprintf (s
, "%c", numbers
[0] + 64);
268 sprintf (s
, "%d", numbers
[0]);
273 /* Returns number of the last sectioning command used. */
275 current_sectioning_number (void)
277 if (enum_marker
== UNNUMBERED_MAGIC
|| !number_sections
)
280 return xstrdup (last_sectioning_number
);
283 /* Returns arguments of the last sectioning command used. */
285 current_sectioning_name (void)
287 return xstrdup (last_sectioning_title
);
290 /* insert_and_underscore, sectioning_underscore and sectioning_html call this. */
293 handle_enum_increment (int level
, int index
)
295 /* Here is how TeX handles enumeration:
296 - Anything starting with @unnumbered is not enumerated.
297 - @majorheading and the like are not enumberated. */
300 /* First constraint above. */
301 if (enum_marker
== UNNUMBERED_MAGIC
&& level
== 0)
304 /* Second constraint. */
305 if (section_alist
[index
].num
== ENUM_SECT_NO
)
308 /* reset all counters which are one level deeper */
309 for (i
= level
; i
< 3; i
++)
313 if (section_alist
[index
].num
== ENUM_SECT_NO
|| enum_marker
== UNNUMBERED_MAGIC
317 return xstrdup (get_sectioning_number (level
, section_alist
[index
].num
));
322 sectioning_underscore (char *cmd
)
324 char *temp
, *secname
;
327 /* If we're not indenting the first paragraph, we shall make it behave
328 like @noindent is called directly after the section heading. */
329 if (! do_first_par_indent
)
332 temp
= xmalloc (2 + strlen (cmd
));
333 temp
[0] = COMMAND_PREFIX
;
334 strcpy (&temp
[1], cmd
);
335 level
= what_section (temp
, &secname
);
341 /* If the argument to @top is empty, we try using the one from @settitle.
342 Warn if both are unusable. */
343 if (STREQ (command
, "top"))
345 int save_input_text_offset
= input_text_offset
;
347 get_rest_of_line (0, &temp
);
349 /* Due to get_rest_of_line ... */
352 if (strlen (temp
) == 0 && (!title
|| strlen (title
) == 0))
353 warning ("Must specify a title with least one of @settitle or @top");
355 input_text_offset
= save_input_text_offset
;
360 /* If the section appears in the toc, it means it's a real section
361 unlike majorheading, chapheading etc. */
362 if (section_alist
[search_sectioning (cmd
)].toc
== TOC_YES
)
364 xml_close_sections (level
);
365 /* Mark the beginning of the section
366 If the next command is printindex, we will remove
367 the section and put an Index instead */
369 xml_last_section_output_position
= output_paragraph_offset
;
371 get_rest_of_line (0, &temp
);
373 /* Use @settitle value if @top parameter is empty. */
374 if (STREQ (command
, "top") && strlen(temp
) == 0)
375 temp
= xstrdup (title
? title
: "");
377 /* Docbook does not support @unnumbered at all. So we provide numbers
378 that other formats use. @appendix seems to be fine though, so we let
379 Docbook handle that as usual. */
380 if (docbook
&& enum_marker
!= APPENDIX_MAGIC
)
382 if (section_alist
[search_sectioning (cmd
)].num
== ENUM_SECT_NO
383 && section_alist
[search_sectioning (cmd
)].toc
== TOC_YES
)
384 xml_insert_element_with_attribute (xml_element (secname
),
385 START
, "label=\"%s\" xreflabel=\"%s\"",
386 handle_enum_increment (level
, search_sectioning (cmd
)),
387 text_expansion (temp
));
389 xml_insert_element_with_attribute (xml_element (secname
),
390 START
, "label=\"%s\"",
391 handle_enum_increment (level
, search_sectioning (cmd
)));
394 xml_insert_element (xml_element (secname
), START
);
396 xml_insert_element (TITLE
, START
);
397 xml_open_section (level
, secname
);
398 execute_string ("%s", temp
);
399 xml_insert_element (TITLE
, END
);
408 xml_insert_element_with_attribute (xml_element (secname
), START
,
409 "renderas=\"sect%d\"", level
);
411 xml_insert_element_with_attribute (xml_element (secname
), START
,
412 "renderas=\"other\"");
415 xml_insert_element (xml_element (secname
), START
);
417 get_rest_of_line (0, &temp
);
418 execute_string ("%s", temp
);
421 xml_insert_element (xml_element (secname
), END
);
425 sectioning_html (level
, secname
);
427 insert_and_underscore (level
, secname
);
431 /* Insert the text following input_text_offset up to the end of the line
432 in a new, separate paragraph. Directly underneath it, insert a
433 line of WITH_CHAR, the same length of the inserted text. */
435 insert_and_underscore (int level
, char *cmd
)
440 unsigned char *starting_pos
, *ending_pos
;
442 char with_char
= scoring_characters
[level
];
445 filling_enabled
= indented_fill
= 0;
446 old_no_indent
= no_indent
;
449 if (macro_expansion_output_stream
&& !executing_string
)
450 append_to_expansion_output (input_text_offset
+ 1);
452 get_rest_of_line (0, &temp
);
454 /* Use @settitle value if @top parameter is empty. */
455 if (STREQ (command
, "top") && strlen(temp
) == 0)
456 temp
= xstrdup (title
? title
: "");
458 starting_pos
= output_paragraph
+ output_paragraph_offset
;
460 /* Poor man's cache for section title. */
461 if (strlen (last_sectioning_title
))
462 free (last_sectioning_title
);
463 last_sectioning_title
= xstrdup (temp
);
465 index
= search_sectioning (cmd
);
468 /* should never happen, but a poor guy, named Murphy ... */
469 warning (_("Internal error (search_sectioning) `%s'!"), cmd
);
473 /* This is a bit tricky: we must produce "X.Y SECTION-NAME" in the
474 Info output and in TOC, but only SECTION-NAME in the macro-expanded
477 /* Step 1: produce "X.Y" and add it to Info output. */
478 add_word_args ("%s ", handle_enum_increment (level
, index
));
480 /* Step 2: add "SECTION-NAME" to both Info and macro-expanded output. */
481 if (macro_expansion_output_stream
&& !executing_string
)
483 char *temp1
= xmalloc (2 + strlen (temp
));
484 sprintf (temp1
, "%s\n", temp
);
485 remember_itext (input_text
, input_text_offset
);
486 me_execute_string (temp1
);
490 execute_string ("%s\n", temp
);
492 /* Step 3: pluck "X.Y SECTION-NAME" from the output buffer and
493 insert it into the TOC. */
494 ending_pos
= output_paragraph
+ output_paragraph_offset
;
495 if (section_alist
[index
].toc
== TOC_YES
)
496 toc_add_entry (substring (starting_pos
, ending_pos
- 1),
497 level
, current_node
, NULL
);
501 len
= (ending_pos
- starting_pos
) - 1;
502 for (i
= 0; i
< len
; i
++)
503 add_char (with_char
);
507 no_indent
= old_no_indent
;
510 /* Insert the text following input_text_offset up to the end of the
511 line as an HTML heading element of the appropriate `level' and
512 tagged as an anchor for the current node.. */
515 sectioning_html (int level
, char *cmd
)
517 static int toc_ref_count
= 0;
520 unsigned char *starting_pos
, *ending_pos
;
521 char *temp
, *toc_anchor
= NULL
;
524 filling_enabled
= indented_fill
= 0;
525 old_no_indent
= no_indent
;
528 /* level 0 (chapter) is <h2>, and we go down from there. */
529 add_html_block_elt_args ("<h%d class=\"%s\">", level
+ 2, cmd
);
531 /* If we are outside of any node, produce an anchor that
532 the TOC could refer to. */
533 if (!current_node
|| !*current_node
)
535 static const char a_name
[] = "<a name=\"";
537 starting_pos
= output_paragraph
+ output_paragraph_offset
;
538 add_word_args ("%sTOC%d\">", a_name
, toc_ref_count
++);
539 toc_anchor
= substring (starting_pos
+ sizeof (a_name
) - 1,
540 output_paragraph
+ output_paragraph_offset
);
541 /* This must be added after toc_anchor is extracted, since
542 toc_anchor cannot include the closing </a>. For details,
543 see toc.c:toc_add_entry and toc.c:contents_update_html.
545 Also, the anchor close must be output before the section name
546 in case the name itself contains an anchor. */
549 starting_pos
= output_paragraph
+ output_paragraph_offset
;
551 if (macro_expansion_output_stream
&& !executing_string
)
552 append_to_expansion_output (input_text_offset
+ 1);
554 get_rest_of_line (0, &temp
);
556 /* Use @settitle value if @top parameter is empty. */
557 if (STREQ (command
, "top") && strlen(temp
) == 0)
558 temp
= xstrdup (title
? title
: "");
560 index
= search_sectioning (cmd
);
563 /* should never happen, but a poor guy, named Murphy ... */
564 warning (_("Internal error (search_sectioning) \"%s\"!"), cmd
);
568 /* Produce "X.Y" and add it to HTML output. */
570 char *title_number
= handle_enum_increment (level
, index
);
571 if (strlen (title_number
) > 0)
572 add_word_args ("%s ", title_number
);
575 /* add the section name to both HTML and macro-expanded output. */
576 if (macro_expansion_output_stream
&& !executing_string
)
578 remember_itext (input_text
, input_text_offset
);
579 me_execute_string (temp
);
580 write_region_to_macro_output ("\n", 0, 1);
583 execute_string ("%s", temp
);
585 ending_pos
= output_paragraph
+ output_paragraph_offset
;
587 /* Pluck ``X.Y SECTION-NAME'' from the output buffer and insert it
589 if (section_alist
[index
].toc
== TOC_YES
)
590 toc_add_entry (substring (starting_pos
, ending_pos
),
591 level
, current_node
, toc_anchor
);
595 if (outstanding_node
)
596 outstanding_node
= 0;
598 add_word_args ("</h%d>", level
+ 2);
601 no_indent
= old_no_indent
;
605 /* Shift the meaning of @section to @chapter. */
607 cm_raisesections (void)
609 discard_until ("\n");
610 section_alist_offset
--;
613 /* Shift the meaning of @chapter to @section. */
615 cm_lowersections (void)
617 discard_until ("\n");
618 section_alist_offset
++;
621 /* The command still works, but prints a warning message in addition. */
623 cm_ideprecated (int arg
, int start
, int end
)
625 warning (_("%c%s is obsolete; use %c%s instead"),
626 COMMAND_PREFIX
, command
, COMMAND_PREFIX
, command
+ 1);
627 sectioning_underscore (command
+ 1);
631 /* Treat this just like @unnumbered. The only difference is
632 in node defaulting. */
636 /* It is an error to have more than one @top. */
637 if (top_node_seen
&& strcmp (current_node
, "Top") != 0)
639 TAG_ENTRY
*tag
= tag_table
;
641 line_error (_("Node with %ctop as a section already exists"),
646 if (tag
->flags
& TAG_FLAG_IS_TOP
)
648 file_line_error (tag
->filename
, tag
->line_no
,
649 _("Here is the %ctop node"), COMMAND_PREFIX
);
659 /* It is an error to use @top before using @node. */
664 get_rest_of_line (0, &top_name
);
665 line_error (_("%ctop used before %cnode, defaulting to %s"),
666 COMMAND_PREFIX
, COMMAND_PREFIX
, top_name
);
667 execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name
);
674 /* The most recently defined node is the top node. */
675 tag_table
->flags
|= TAG_FLAG_IS_TOP
;
677 /* Now set the logical hierarchical level of the Top node. */
679 int orig_offset
= input_text_offset
;
681 input_text_offset
= search_forward (node_search_string
, orig_offset
);
683 if (input_text_offset
> 0)
687 /* We have encountered a non-top node, so mark that one exists. */
688 non_top_node_seen
= 1;
690 /* Move to the end of this line, and find out what the
691 sectioning command is here. */
692 while (input_text
[input_text_offset
] != '\n')
695 if (input_text_offset
< input_text_length
)
698 this_section
= what_section (input_text
+ input_text_offset
,
701 /* If we found a sectioning command, then give the top section
702 a level of this section - 1. */
703 if (this_section
!= -1)
704 set_top_section_level (this_section
- 1);
706 input_text_offset
= orig_offset
;
711 /* The remainder of the text on this line is a chapter heading. */
716 sectioning_underscore ("chapter");
719 /* The remainder of the text on this line is a section heading. */
723 sectioning_underscore ("section");
726 /* The remainder of the text on this line is a subsection heading. */
730 sectioning_underscore ("subsection");
733 /* The remainder of the text on this line is a subsubsection heading. */
735 cm_subsubsection (void)
737 sectioning_underscore ("subsubsection");
740 /* The remainder of the text on this line is an unnumbered heading. */
744 enum_marker
= UNNUMBERED_MAGIC
;
745 sectioning_underscore ("unnumbered");
748 /* The remainder of the text on this line is an unnumbered section heading. */
750 cm_unnumberedsec (void)
752 sectioning_underscore ("unnumberedsec");
755 /* The remainder of the text on this line is an unnumbered
756 subsection heading. */
758 cm_unnumberedsubsec (void)
760 sectioning_underscore ("unnumberedsubsec");
763 /* The remainder of the text on this line is an unnumbered
764 subsubsection heading. */
766 cm_unnumberedsubsubsec (void)
768 sectioning_underscore ("unnumberedsubsubsec");
771 /* The remainder of the text on this line is an appendix heading. */
775 /* Reset top level number so we start from Appendix A */
776 if (enum_marker
!= APPENDIX_MAGIC
)
778 enum_marker
= APPENDIX_MAGIC
;
779 sectioning_underscore ("appendix");
782 /* The remainder of the text on this line is an appendix section heading. */
784 cm_appendixsec (void)
786 sectioning_underscore ("appendixsec");
789 /* The remainder of the text on this line is an appendix subsection heading. */
791 cm_appendixsubsec (void)
793 sectioning_underscore ("appendixsubsec");
796 /* The remainder of the text on this line is an appendix
797 subsubsection heading. */
799 cm_appendixsubsubsec (void)
801 sectioning_underscore ("appendixsubsubsec");
804 /* Compatibility functions substitute for chapter, section, etc. */
806 cm_majorheading (void)
808 sectioning_underscore ("majorheading");
812 cm_chapheading (void)
814 sectioning_underscore ("chapheading");
820 sectioning_underscore ("heading");
826 sectioning_underscore ("subheading");
830 cm_subsubheading (void)
832 sectioning_underscore ("subsubheading");