Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / contrib / texinfo / makeinfo / sectioning.c
1 /* sectioning.c -- all related stuff @chapter, @section... @contents
2    $Id: sectioning.c,v 1.17 2002/02/09 00:54:51 karl Exp $
3
4    Copyright (C) 1999, 2001, 02 Free Software Foundation, Inc.
5
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)
9    any later version.
10
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.
15
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.
19
20    Written by Karl Heinz Marbaise <kama@hippo.fido.de>.  */
21
22 #include "system.h"
23 #include "cmds.h"
24 #include "macro.h"
25 #include "makeinfo.h"
26 #include "node.h"
27 #include "toc.h"
28 #include "sectioning.h"
29 #include "xml.h"
30
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
38   { "appendixsubsubsec",   5, ENUM_SECT_APP, TOC_YES },  /* numbered like A.X.X.X */
39   { "appendixsubsec",      4, ENUM_SECT_APP, TOC_YES },
40   { "appendixsec",         3, ENUM_SECT_APP, TOC_YES },
41   { "appendixsection",     3, ENUM_SECT_APP, TOC_YES },
42   { "appendix",            2, ENUM_SECT_APP, TOC_YES },
43
44   { "subsubsec",           5, ENUM_SECT_YES, TOC_YES },
45   { "subsubsection",       5, ENUM_SECT_YES, TOC_YES },
46   { "subsection",          4, ENUM_SECT_YES, TOC_YES },
47   { "section",             3, ENUM_SECT_YES, TOC_YES },
48   { "chapter",             2, ENUM_SECT_YES, TOC_YES },
49
50   { "subsubheading",       5, ENUM_SECT_NO,  TOC_NO },
51   { "subheading",          4, ENUM_SECT_NO,  TOC_NO },
52   { "heading",             3, ENUM_SECT_NO,  TOC_NO },
53   { "chapheading",         2, ENUM_SECT_NO,  TOC_NO },
54   { "majorheading",        2, ENUM_SECT_NO,  TOC_NO },
55   
56   { "top",                 1, ENUM_SECT_NO,  TOC_YES },
57   { NULL,                  0, 0, 0 }
58 };
59 \f
60 /* The argument of @settitle, used for HTML. */
61 char *title = NULL;
62
63
64 #define APPENDIX_MAGIC   1024
65 #define UNNUMBERED_MAGIC 2048
66
67 /* Number memory for every level @chapter, @section,
68    @subsection, @subsubsection. */
69 static int numbers [] = { 0, 0, 0, 0 };
70
71 /* enum_marker == APPENDIX_MAGIC then we are counting appendencies
72    enum_marker == UNNUMBERED_MAGIC then we are within unnumbered area.
73    Handling situations like this:
74    @unnumbered ..
75    @section ...   */
76 static int enum_marker = 0;
77
78 /* Organized by level commands.  That is, "*" == chapter, "=" == section. */
79 static char *scoring_characters = "*=-.";
80
81 /* Amount to offset the name of sectioning commands to levels by. */
82 static int section_alist_offset = 0;
83
84 \f
85 /* num == ENUM_SECT_NO  means unnumbered (should never call this)
86    num == ENUM_SECT_YES means numbered
87    num == ENUM_SECT_APP means numbered like A.1 and so on */
88 char *
89 get_sectioning_number (level, num)
90       int level;
91       int num;
92 {
93   static char s[100]; /* should ever be enough for 99.99.99.99
94                          Appendix A.1 */
95
96   char *p;
97   int i;
98
99   s[0] = 0;
100
101   /* create enumeration in front of chapter, section, subsection and so on. */
102   for (i = 0; i < level; i++)
103     {
104       p = s + strlen (s);
105       if ((i == 0) && (enum_marker == APPENDIX_MAGIC))
106         sprintf (p, "%c.", numbers[i] + 64); /* Should be changed to
107                                                 be more portable */
108       else
109         sprintf (p, "%d.", numbers[i]);
110     }
111
112   /* the last number is never followed by a dot */
113   p = s + strlen (s);
114   if ((num == ENUM_SECT_APP)
115       && (i == 0)
116       && (enum_marker == APPENDIX_MAGIC))
117     sprintf (p, _("Appendix %c "), numbers[i] + 64);
118   else
119     sprintf (p, "%d ", numbers[i]);
120
121   return s;
122 }
123
124
125 /* Set the level of @top to LEVEL.  Return the old level of @top. */
126 int
127 set_top_section_level (level)
128      int level;
129 {
130   int i, result = -1;
131
132   for (i = 0; section_alist[i].name; i++)
133     if (strcmp (section_alist[i].name, "top") == 0)
134       {
135         result = section_alist[i].level;
136         section_alist[i].level = level;
137         break;
138       }
139   return result;
140 }
141
142
143 /* return the index of the given sectioning command in section_alist */
144 int
145 search_sectioning (text)
146      char *text;
147 {
148   int i;
149   char *t;
150
151   /* ignore the optional command prefix */
152   if (text[0] == COMMAND_PREFIX)
153     text++;
154   
155   for (i = 0; (t = section_alist[i].name); i++)
156     {
157       if (strcmp (t, text) == 0)
158         {
159           return i;
160         }
161     }
162   return -1;
163 }
164     
165 /* Return an integer which identifies the type section present in TEXT. */
166 int
167 what_section (text)
168      char *text;
169 {
170   int index, j;
171   char *temp;
172   int return_val;
173
174  find_section_command:
175   for (j = 0; text[j] && cr_or_whitespace (text[j]); j++);
176   if (text[j] != COMMAND_PREFIX)
177     return -1;
178
179   text = text + j + 1;
180
181   /* We skip @c, @comment, and @?index commands. */
182   if ((strncmp (text, "comment", strlen ("comment")) == 0) ||
183       (text[0] == 'c' && cr_or_whitespace (text[1])) ||
184       (strcmp (text + 1, "index") == 0))
185     {
186       while (*text++ != '\n');
187       goto find_section_command;
188     }
189
190   /* Handle italicized sectioning commands. */
191   if (*text == 'i')
192     text++;
193
194   for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++);
195
196   temp = xmalloc (1 + j);
197   strncpy (temp, text, j);
198   temp[j] = 0;
199
200   index = search_sectioning (temp);
201   free (temp);
202   if (index >= 0)
203     {
204       return_val = section_alist[index].level + section_alist_offset;
205       if (return_val < 0)
206         return_val = 0;
207       else if (return_val > 5)
208           return_val = 5;
209       return return_val;
210     }
211   return -1;
212 }
213
214 void
215 sectioning_underscore (cmd)
216      char *cmd;
217 {
218   if (xml)
219     {
220       char *temp;
221       int level;
222       temp = xmalloc (2 + strlen (cmd));
223       temp[0] = COMMAND_PREFIX;
224       strcpy (&temp[1], cmd);
225       level = what_section (temp);
226       level -= 2;
227       free (temp);
228       xml_close_sections (level);
229       /* Mark the beginning of the section
230          If the next command is printindex, we will remove
231          the section and put an Index instead */
232       flush_output ();
233       xml_last_section_output_position = output_paragraph_offset;
234       
235       xml_insert_element (xml_element (cmd), START);
236       xml_insert_element (TITLE, START);
237       xml_open_section (level, cmd);
238       get_rest_of_line (0, &temp);
239       execute_string ("%s\n", temp);
240       free (temp);
241       xml_insert_element (TITLE, END);
242     } 
243   else 
244     {
245   char character;
246   char *temp;
247   int level;
248
249   temp = xmalloc (2 + strlen (cmd));
250   temp[0] = COMMAND_PREFIX;
251   strcpy (&temp[1], cmd);
252   level = what_section (temp);
253   free (temp);
254   level -= 2;
255
256   if (level < 0)
257     level = 0;
258
259   if (html)
260     sectioning_html (level, cmd);
261   else
262     {
263       character = scoring_characters[level];
264       insert_and_underscore (level, character, cmd);
265         }
266     }
267 }
268
269 /* insert_and_underscore and sectioning_html are the
270    only functions which call this.
271    I have created this, because it was exactly the same
272    code in both functions. */
273 static char *
274 handle_enum_increment (level, index)
275      int level;
276      int index;
277 {
278   /* special for unnumbered */
279   if (number_sections && section_alist[index].num == ENUM_SECT_NO)
280     {
281       if (level == 0
282           && enum_marker != UNNUMBERED_MAGIC)
283         enum_marker = UNNUMBERED_MAGIC;
284     }
285   /* enumerate only things which are allowed */
286   if (number_sections && section_alist[index].num)
287     {
288       /* reset the marker if we get into enumerated areas */
289       if (section_alist[index].num == ENUM_SECT_YES
290           && level == 0
291           && enum_marker == UNNUMBERED_MAGIC)
292         enum_marker = 0;
293       /* This is special for appendix; if we got the first
294          time an appendix command then we are entering appendix.
295          Thats the point we have to start countint with A, B and so on. */
296       if (section_alist[index].num == ENUM_SECT_APP
297           && level == 0
298           && enum_marker != APPENDIX_MAGIC)
299         {
300           enum_marker = APPENDIX_MAGIC;
301           numbers [0] = 0; /* this means we start with Appendix A */
302         }
303   
304       /* only increment counters if we are not in unnumbered
305          area. This handles situations like this:
306          @unnumbered ....   This sets enum_marker to UNNUMBERED_MAGIC
307          @section ....   */
308       if (enum_marker != UNNUMBERED_MAGIC)
309         {
310           int i;
311
312           /* reset all counters which are one level deeper */
313           for (i = level; i < 3; i++)
314             numbers [i + 1] = 0;
315   
316           numbers[level]++;
317           return xstrdup
318             (get_sectioning_number (level, section_alist[index].num));
319         }
320     } /* if (number_sections)... */
321
322   return xstrdup ("");
323 }
324
325
326 /* Insert the text following input_text_offset up to the end of the line
327    in a new, separate paragraph.  Directly underneath it, insert a
328    line of WITH_CHAR, the same length of the inserted text. */
329 void
330 insert_and_underscore (level, with_char, cmd)
331      int level;
332      int with_char;
333      char *cmd;
334 {
335   int i, len;
336   int index;
337   int old_no_indent;
338   unsigned char *starting_pos, *ending_pos;
339   char *temp;
340
341   close_paragraph ();
342   filling_enabled =  indented_fill = 0;
343   old_no_indent = no_indent;
344   no_indent = 1;
345
346   if (macro_expansion_output_stream && !executing_string)
347     append_to_expansion_output (input_text_offset + 1);
348
349   get_rest_of_line (0, &temp);
350   starting_pos = output_paragraph + output_paragraph_offset;
351
352   index = search_sectioning (cmd);
353   if (index < 0)
354     {
355       /* should never happen, but a poor guy, named Murphy ... */
356       warning (_("Internal error (search_sectioning) \"%s\"!"), cmd);
357       return;
358     }
359
360   /* This is a bit tricky: we must produce "X.Y SECTION-NAME" in the
361      Info output and in TOC, but only SECTION-NAME in the macro-expanded
362      output.  */
363
364   /* Step 1: produce "X.Y" and add it to Info output.  */
365   add_word (handle_enum_increment (level, index));
366
367   /* Step 2: add "SECTION-NAME" to both Info and macro-expanded output.  */
368   if (macro_expansion_output_stream && !executing_string)
369     {
370       char *temp1 = xmalloc (2 + strlen (temp));
371       sprintf (temp1, "%s\n", temp);
372       remember_itext (input_text, input_text_offset);
373       me_execute_string (temp1);
374       free (temp1);
375     }
376   else
377     execute_string ("%s\n", temp);
378
379   /* Step 3: pluck "X.Y SECTION-NAME" from the output buffer and
380      insert it into the TOC.  */
381   ending_pos = output_paragraph + output_paragraph_offset;
382   if (section_alist[index].toc == TOC_YES)
383     toc_add_entry (substring (starting_pos, ending_pos - 1),
384                    level, current_node, NULL);
385
386   free (temp);
387
388   len = (ending_pos - starting_pos) - 1;
389   for (i = 0; i < len; i++)
390     add_char (with_char);
391   insert ('\n');
392   close_paragraph ();
393   filling_enabled = 1;
394   no_indent = old_no_indent;
395 }
396
397 /* Insert the text following input_text_offset up to the end of the
398    line as an HTML heading element of the appropriate `level' and
399    tagged as an anchor for the current node.. */
400 void
401 sectioning_html (level, cmd)
402      int level;
403      char *cmd;
404 {
405   static int toc_ref_count = 0;
406   int index;
407   int old_no_indent;
408   unsigned char *starting_pos, *ending_pos;
409   char *temp, *toc_anchor = NULL;
410
411   close_paragraph ();
412   filling_enabled =  indented_fill = 0;
413   old_no_indent = no_indent;
414   no_indent = 1;
415
416   add_word_args ("<h%d>", level + 2); /* level 0 (chapter) is <h2> */
417
418   /* If we are outside of any node, produce an anchor that
419      the TOC could refer to.  */
420   if (!current_node || !*current_node)
421     {
422       static const char a_name[] = "<a name=\"";
423
424       starting_pos = output_paragraph + output_paragraph_offset;
425       add_word_args ("%sTOC%d\">", a_name, toc_ref_count++);
426       toc_anchor = substring (starting_pos + sizeof (a_name) - 1,
427                               output_paragraph + output_paragraph_offset);
428       /* This must be added after toc_anchor is extracted, since
429          toc_anchor cannot include the closing </a>.  For details,
430          see toc.c:toc_add_entry and toc.c:contents_update_html.  */
431       add_word ("</a>");
432     }
433   starting_pos = output_paragraph + output_paragraph_offset;
434
435   if (macro_expansion_output_stream && !executing_string)
436     append_to_expansion_output (input_text_offset + 1);
437
438   get_rest_of_line (0, &temp);
439
440   index = search_sectioning (cmd);
441   if (index < 0)
442     {
443       /* should never happen, but a poor guy, named Murphy ... */
444       warning (_("Internal error (search_sectioning) \"%s\"!"), cmd);
445       return;
446     }
447
448   /* Produce "X.Y" and add it to HTML output.  */
449   add_word (handle_enum_increment (level, index));
450
451   /* add the section name to both HTML and macro-expanded output.  */
452   if (macro_expansion_output_stream && !executing_string)
453     {
454       remember_itext (input_text, input_text_offset);
455       me_execute_string (temp);
456       write_region_to_macro_output ("\n", 0, 1);
457     }
458   else
459     execute_string ("%s", temp);
460
461   ending_pos = output_paragraph + output_paragraph_offset;
462
463   /* Pluck ``X.Y SECTION-NAME'' from the output buffer and insert it
464      into the TOC.  */
465   if (section_alist[index].toc == TOC_YES)
466     toc_add_entry (substring (starting_pos, ending_pos),
467                    level, current_node, toc_anchor);
468
469   free (temp);
470
471   if (outstanding_node)
472     outstanding_node = 0;
473
474   add_word_args ("</h%d>", level + 2);
475   close_paragraph();
476   filling_enabled = 1;
477   no_indent = old_no_indent;
478 }
479
480 \f
481 /* Shift the meaning of @section to @chapter. */
482 void
483 cm_raisesections ()
484 {
485   discard_until ("\n");
486   section_alist_offset--;
487 }
488
489 /* Shift the meaning of @chapter to @section. */
490 void
491 cm_lowersections ()
492 {
493   discard_until ("\n");
494   section_alist_offset++;
495 }
496
497 /* The command still works, but prints a warning message in addition. */
498 void
499 cm_ideprecated (arg, start, end)
500      int arg, start, end;
501 {
502   warning (_("%c%s is obsolete; use %c%s instead"),
503            COMMAND_PREFIX, command, COMMAND_PREFIX, command + 1);
504   sectioning_underscore (command + 1);
505 }
506
507 \f
508 /* Treat this just like @unnumbered.  The only difference is
509    in node defaulting. */
510 void
511 cm_top ()
512 {
513   /* It is an error to have more than one @top. */
514   if (top_node_seen && strcmp (current_node, "Top") != 0)
515     {
516       TAG_ENTRY *tag = tag_table;
517
518       line_error (_("Node with %ctop as a section already exists"),
519                   COMMAND_PREFIX);
520
521       while (tag)
522         {
523           if (tag->flags & TAG_FLAG_IS_TOP)
524             {
525               file_line_error (tag->filename, tag->line_no,
526                                _("Here is the %ctop node"), COMMAND_PREFIX);
527               return;
528             }
529           tag = tag->next_ent;
530         }
531     }
532   else
533     {
534       TAG_ENTRY *top_node = find_node ("Top");
535       top_node_seen = 1;
536
537       /* It is an error to use @top before using @node. */
538       if (!tag_table)
539         {
540           char *top_name;
541
542           get_rest_of_line (0, &top_name);
543           line_error (_("%ctop used before %cnode, defaulting to %s"),
544                       COMMAND_PREFIX, COMMAND_PREFIX, top_name);
545           execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name);
546           free (top_name);
547           return;
548         }
549
550       cm_unnumbered ();
551
552       /* The most recently defined node is the top node. */
553       tag_table->flags |= TAG_FLAG_IS_TOP;
554
555       /* Now set the logical hierarchical level of the Top node. */
556       {
557         int orig_offset = input_text_offset;
558
559         input_text_offset = search_forward (node_search_string, orig_offset);
560
561         if (input_text_offset > 0)
562           {
563             int this_section;
564
565             /* We have encountered a non-top node, so mark that one exists. */
566             non_top_node_seen = 1;
567
568             /* Move to the end of this line, and find out what the
569                sectioning command is here. */
570             while (input_text[input_text_offset] != '\n')
571               input_text_offset++;
572
573             if (input_text_offset < input_text_length)
574               input_text_offset++;
575
576             this_section = what_section (input_text + input_text_offset);
577
578             /* If we found a sectioning command, then give the top section
579                a level of this section - 1. */
580             if (this_section != -1)
581               set_top_section_level (this_section - 1);
582           }
583         input_text_offset = orig_offset;
584       }
585     }
586 }
587
588 /* The remainder of the text on this line is a chapter heading. */
589 void
590 cm_chapter ()
591 {
592   sectioning_underscore ("chapter");
593 }
594
595 /* The remainder of the text on this line is a section heading. */
596 void
597 cm_section ()
598 {
599   sectioning_underscore ("section");
600 }
601
602 /* The remainder of the text on this line is a subsection heading. */
603 void
604 cm_subsection ()
605 {
606   sectioning_underscore ("subsection");
607 }
608
609 /* The remainder of the text on this line is a subsubsection heading. */
610 void
611 cm_subsubsection ()
612 {
613   sectioning_underscore ("subsubsection");
614 }
615
616 /* The remainder of the text on this line is an unnumbered heading. */
617 void
618 cm_unnumbered ()
619 {
620   sectioning_underscore ("unnumbered");
621 }
622
623 /* The remainder of the text on this line is an unnumbered section heading. */
624 void
625 cm_unnumberedsec ()
626 {
627   sectioning_underscore ("unnumberedsec");
628 }
629
630 /* The remainder of the text on this line is an unnumbered
631    subsection heading. */
632 void
633 cm_unnumberedsubsec ()
634 {
635   sectioning_underscore ("unnumberedsubsec");
636 }
637
638 /* The remainder of the text on this line is an unnumbered
639    subsubsection heading. */
640 void
641 cm_unnumberedsubsubsec ()
642 {
643   sectioning_underscore ("unnumberedsubsubsec");
644 }
645
646 /* The remainder of the text on this line is an appendix heading. */
647 void
648 cm_appendix ()
649 {
650   sectioning_underscore ("appendix");
651 }
652
653 /* The remainder of the text on this line is an appendix section heading. */
654 void
655 cm_appendixsec ()
656 {
657   sectioning_underscore ("appendixsec");
658 }
659
660 /* The remainder of the text on this line is an appendix subsection heading. */
661 void
662 cm_appendixsubsec ()
663 {
664   sectioning_underscore ("appendixsubsec");
665 }
666
667 /* The remainder of the text on this line is an appendix
668    subsubsection heading. */
669 void
670 cm_appendixsubsubsec ()
671 {
672   sectioning_underscore ("appendixsubsubsec");
673 }
674
675 /* Compatibility functions substitute for chapter, section, etc. */
676 void
677 cm_majorheading ()
678 {
679   sectioning_underscore ("majorheading");
680 }
681
682 void
683 cm_chapheading ()
684 {
685   sectioning_underscore ("chapheading");
686 }
687
688 void
689 cm_heading ()
690 {
691   sectioning_underscore ("heading");
692 }
693
694 void
695 cm_subheading ()
696 {
697   sectioning_underscore ("subheading");
698 }
699
700 void
701 cm_subsubheading ()
702 {
703   sectioning_underscore ("subsubheading");
704 }