sectioning.c revision 114472
1/* sectioning.c -- for @chapter, @section, ..., @contents ...
2   $Id: sectioning.c,v 1.6 2002/11/08 02:21:07 karl Exp $
3
4   Copyright (C) 1999, 2001, 2002 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   Originally 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.  */
32section_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
60/* The argument of @settitle, used for HTML. */
61char *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. */
69static 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 ...   */
76static int enum_marker = 0;
77
78/* Organized by level commands.  That is, "*" == chapter, "=" == section. */
79static char *scoring_characters = "*=-.";
80
81/* Amount to offset the name of sectioning commands to levels by. */
82static int section_alist_offset = 0;
83
84
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 */
88char *
89get_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. */
126int
127set_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 */
144int
145search_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. */
166int
167what_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
214void
215sectioning_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. */
273static char *
274handle_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. */
329void
330insert_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.. */
400void
401sectioning_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  /* level 0 (chapter) is <h2> */
417  add_word_args ("<h%d class=\"%s\">", level + 2, cmd);
418
419  /* If we are outside of any node, produce an anchor that
420     the TOC could refer to.  */
421  if (!current_node || !*current_node)
422    {
423      static const char a_name[] = "<a name=\"";
424
425      starting_pos = output_paragraph + output_paragraph_offset;
426      add_word_args ("%sTOC%d\">", a_name, toc_ref_count++);
427      toc_anchor = substring (starting_pos + sizeof (a_name) - 1,
428                              output_paragraph + output_paragraph_offset);
429      /* This must be added after toc_anchor is extracted, since
430         toc_anchor cannot include the closing </a>.  For details,
431         see toc.c:toc_add_entry and toc.c:contents_update_html.
432
433         Also, the anchor close must be output before the section name
434         in case the name itself contains an anchor. */
435      add_word ("</a>");
436    }
437  starting_pos = output_paragraph + output_paragraph_offset;
438
439  if (macro_expansion_output_stream && !executing_string)
440    append_to_expansion_output (input_text_offset + 1);
441
442  get_rest_of_line (0, &temp);
443
444  index = search_sectioning (cmd);
445  if (index < 0)
446    {
447      /* should never happen, but a poor guy, named Murphy ... */
448      warning (_("Internal error (search_sectioning) \"%s\"!"), cmd);
449      return;
450    }
451
452  /* Produce "X.Y" and add it to HTML output.  */
453  add_word (handle_enum_increment (level, index));
454
455  /* add the section name to both HTML and macro-expanded output.  */
456  if (macro_expansion_output_stream && !executing_string)
457    {
458      remember_itext (input_text, input_text_offset);
459      me_execute_string (temp);
460      write_region_to_macro_output ("\n", 0, 1);
461    }
462  else
463    execute_string ("%s", temp);
464
465  ending_pos = output_paragraph + output_paragraph_offset;
466
467  /* Pluck ``X.Y SECTION-NAME'' from the output buffer and insert it
468     into the TOC.  */
469  if (section_alist[index].toc == TOC_YES)
470    toc_add_entry (substring (starting_pos, ending_pos),
471                   level, current_node, toc_anchor);
472
473  free (temp);
474
475  if (outstanding_node)
476    outstanding_node = 0;
477
478  add_word_args ("</h%d>", level + 2);
479  close_paragraph();
480  filling_enabled = 1;
481  no_indent = old_no_indent;
482}
483
484
485/* Shift the meaning of @section to @chapter. */
486void
487cm_raisesections ()
488{
489  discard_until ("\n");
490  section_alist_offset--;
491}
492
493/* Shift the meaning of @chapter to @section. */
494void
495cm_lowersections ()
496{
497  discard_until ("\n");
498  section_alist_offset++;
499}
500
501/* The command still works, but prints a warning message in addition. */
502void
503cm_ideprecated (arg, start, end)
504     int arg, start, end;
505{
506  warning (_("%c%s is obsolete; use %c%s instead"),
507           COMMAND_PREFIX, command, COMMAND_PREFIX, command + 1);
508  sectioning_underscore (command + 1);
509}
510
511
512/* Treat this just like @unnumbered.  The only difference is
513   in node defaulting. */
514void
515cm_top ()
516{
517  /* It is an error to have more than one @top. */
518  if (top_node_seen && strcmp (current_node, "Top") != 0)
519    {
520      TAG_ENTRY *tag = tag_table;
521
522      line_error (_("Node with %ctop as a section already exists"),
523                  COMMAND_PREFIX);
524
525      while (tag)
526        {
527          if (tag->flags & TAG_FLAG_IS_TOP)
528            {
529              file_line_error (tag->filename, tag->line_no,
530                               _("Here is the %ctop node"), COMMAND_PREFIX);
531              return;
532            }
533          tag = tag->next_ent;
534        }
535    }
536  else
537    {
538      TAG_ENTRY *top_node = find_node ("Top");
539      top_node_seen = 1;
540
541      /* It is an error to use @top before using @node. */
542      if (!tag_table)
543        {
544          char *top_name;
545
546          get_rest_of_line (0, &top_name);
547          line_error (_("%ctop used before %cnode, defaulting to %s"),
548                      COMMAND_PREFIX, COMMAND_PREFIX, top_name);
549          execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name);
550          free (top_name);
551          return;
552        }
553
554      cm_unnumbered ();
555
556      /* The most recently defined node is the top node. */
557      tag_table->flags |= TAG_FLAG_IS_TOP;
558
559      /* Now set the logical hierarchical level of the Top node. */
560      {
561        int orig_offset = input_text_offset;
562
563        input_text_offset = search_forward (node_search_string, orig_offset);
564
565        if (input_text_offset > 0)
566          {
567            int this_section;
568
569            /* We have encountered a non-top node, so mark that one exists. */
570            non_top_node_seen = 1;
571
572            /* Move to the end of this line, and find out what the
573               sectioning command is here. */
574            while (input_text[input_text_offset] != '\n')
575              input_text_offset++;
576
577            if (input_text_offset < input_text_length)
578              input_text_offset++;
579
580            this_section = what_section (input_text + input_text_offset);
581
582            /* If we found a sectioning command, then give the top section
583               a level of this section - 1. */
584            if (this_section != -1)
585              set_top_section_level (this_section - 1);
586          }
587        input_text_offset = orig_offset;
588      }
589    }
590}
591
592/* The remainder of the text on this line is a chapter heading. */
593void
594cm_chapter ()
595{
596  sectioning_underscore ("chapter");
597}
598
599/* The remainder of the text on this line is a section heading. */
600void
601cm_section ()
602{
603  sectioning_underscore ("section");
604}
605
606/* The remainder of the text on this line is a subsection heading. */
607void
608cm_subsection ()
609{
610  sectioning_underscore ("subsection");
611}
612
613/* The remainder of the text on this line is a subsubsection heading. */
614void
615cm_subsubsection ()
616{
617  sectioning_underscore ("subsubsection");
618}
619
620/* The remainder of the text on this line is an unnumbered heading. */
621void
622cm_unnumbered ()
623{
624  sectioning_underscore ("unnumbered");
625}
626
627/* The remainder of the text on this line is an unnumbered section heading. */
628void
629cm_unnumberedsec ()
630{
631  sectioning_underscore ("unnumberedsec");
632}
633
634/* The remainder of the text on this line is an unnumbered
635   subsection heading. */
636void
637cm_unnumberedsubsec ()
638{
639  sectioning_underscore ("unnumberedsubsec");
640}
641
642/* The remainder of the text on this line is an unnumbered
643   subsubsection heading. */
644void
645cm_unnumberedsubsubsec ()
646{
647  sectioning_underscore ("unnumberedsubsubsec");
648}
649
650/* The remainder of the text on this line is an appendix heading. */
651void
652cm_appendix ()
653{
654  sectioning_underscore ("appendix");
655}
656
657/* The remainder of the text on this line is an appendix section heading. */
658void
659cm_appendixsec ()
660{
661  sectioning_underscore ("appendixsec");
662}
663
664/* The remainder of the text on this line is an appendix subsection heading. */
665void
666cm_appendixsubsec ()
667{
668  sectioning_underscore ("appendixsubsec");
669}
670
671/* The remainder of the text on this line is an appendix
672   subsubsection heading. */
673void
674cm_appendixsubsubsec ()
675{
676  sectioning_underscore ("appendixsubsubsec");
677}
678
679/* Compatibility functions substitute for chapter, section, etc. */
680void
681cm_majorheading ()
682{
683  sectioning_underscore ("majorheading");
684}
685
686void
687cm_chapheading ()
688{
689  sectioning_underscore ("chapheading");
690}
691
692void
693cm_heading ()
694{
695  sectioning_underscore ("heading");
696}
697
698void
699cm_subheading ()
700{
701  sectioning_underscore ("subheading");
702}
703
704void
705cm_subsubheading ()
706{
707  sectioning_underscore ("subsubheading");
708}
709