source: trunk/third/texinfo/makeinfo/sectioning.c @ 18945

Revision 18945, 18.6 KB checked in by amb, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18944, which included commits to RCS files with non-trunk default branches.
Line 
1/* sectioning.c -- for @chapter, @section, ..., @contents ...
2   $Id: sectioning.c,v 1.1.1.2 2003-02-28 17:44:38 amb 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}
Note: See TracBrowser for help on using the repository browser.