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