code:
/* sectioning.c -- for @chapter, @section, ..., @contents ...
   $Id: sectioning.c,v 1.32 2011-01-15 00:23:19 karl Exp $

   Copyright (C) 1999, 2001, 2002, 2003, 2004, 2007, 2008, 2011
   Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

   Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>.  */

#include "system.h"
#include "cmds.h"
#include "macro.h"
#include "makeinfo.h"
#include "node.h"
#include "toc.h"
#include "sectioning.h"
#include "xml.h"

/* See comment in sectioning.h.  */
section_alist_type section_alist[] = {
  { "unnumberedsubsubsec", 5, ENUM_SECT_NO,  TOC_YES },
  { "unnumberedsubsec",    4, ENUM_SECT_NO,  TOC_YES },
  { "unnumberedsec",       3, ENUM_SECT_NO,  TOC_YES },
  { "unnumbered",          2, ENUM_SECT_NO,  TOC_YES },
  { "centerchap",          2, ENUM_SECT_NO,  TOC_YES },

  { "appendixsubsubsec",   5, ENUM_SECT_APP, TOC_YES },  /* numbered like A.X.X.X */
  { "appendixsubsec",      4, ENUM_SECT_APP, TOC_YES },
  { "appendixsec",         3, ENUM_SECT_APP, TOC_YES },
  { "appendixsection",     3, ENUM_SECT_APP, TOC_YES },
  { "appendix",            2, ENUM_SECT_APP, TOC_YES },

  { "subsubsec",           5, ENUM_SECT_YES, TOC_YES },
  { "subsubsection",       5, ENUM_SECT_YES, TOC_YES },
  { "subsection",          4, ENUM_SECT_YES, TOC_YES },
  { "section",             3, ENUM_SECT_YES, TOC_YES },
  { "chapter",             2, ENUM_SECT_YES, TOC_YES },

  { "subsubheading",       5, ENUM_SECT_NO,  TOC_NO },
  { "subheading",          4, ENUM_SECT_NO,  TOC_NO },
  { "heading",             3, ENUM_SECT_NO,  TOC_NO },
  { "chapheading",         2, ENUM_SECT_NO,  TOC_NO },
  { "majorheading",        2, ENUM_SECT_NO,  TOC_NO },

  { "top",                 1, ENUM_SECT_NO,  TOC_YES },
  { NULL,                  0, 0, 0 }
};

/* The argument of @settitle, used for HTML. */
char *title = NULL;

#define APPENDIX_MAGIC   1024
#define UNNUMBERED_MAGIC 2048

/* Number memory for every level @chapter, @section,
   @subsection, @subsubsection. */
static int numbers [] = { 0, 0, 0, 0 };

/* enum_marker == APPENDIX_MAGIC then we are counting appendencies
   enum_marker == UNNUMBERED_MAGIC then we are within unnumbered area.
   Handling situations like this:
   @unnumbered ..
   @section ...   */
static int enum_marker = 0;

/* Organized by level commands.  That is, "*" == chapter, "=" == section. */
static char *scoring_characters = "*=-.";

/* Amount to offset the name of sectioning commands to levels by. */
static int section_alist_offset = 0;

/* These two variables are for @float, @cindex like commands that need to know
   in which section they are used.  */
/* Last value returned by get_sectioning_number.  */
static char *last_sectioning_number = "";
/* Last title used by sectioning_underscore, etc.  */
static char *last_sectioning_title = "";

/* num == ENUM_SECT_NO  means unnumbered (should never call this)
   num == ENUM_SECT_YES means numbered
   num == ENUM_SECT_APP means numbered like A.1 and so on */
static char *
get_sectioning_number (int level, int num)
{
  static char s[100]; /* should ever be enough for 99.99.99.99
                         Appendix A.1 */

  char *p;
  int i;

  s[0] = 0;

  /* create enumeration in front of chapter, section, subsection and so on. */
  for (i = 0; i < level; i++)
    {
      p = s + strlen (s);
      if ((i == 0) && (enum_marker == APPENDIX_MAGIC))
        sprintf (p, "%c.", numbers[i] + 64); /* Should be changed to
                                                be more portable */
      else
        sprintf (p, "%d.", numbers[i]);
    }

  /* the last number is never followed by a dot */
  p = s + strlen (s);
  if ((num == ENUM_SECT_APP)
      && (i == 0)
      && (enum_marker == APPENDIX_MAGIC))
    sprintf (p, getdocumenttext ("Appendix %c"), numbers[i] + 64);
  else
    sprintf (p, "%d", numbers[i]);

  /* Poor man's cache :-)  */
  if (strlen (last_sectioning_number))
    free (last_sectioning_number);
  last_sectioning_number = xstrdup (s);

  return s;
}

/* Set the level of @top to LEVEL.  Return the old level of @top. */
int
set_top_section_level (int level)
{
  int i, result = -1;

  for (i = 0; section_alist[i].name; i++)
    if (strcmp (section_alist[i].name, "top") == 0)
      {
        result = section_alist[i].level;
        section_alist[i].level = level;
        break;
      }
  return result;
}

/* return the index of the given sectioning command in section_alist */
static int
search_sectioning (char *text)
{
  int i;
  char *t;

  /* ignore the optional command prefix */
  if (text[0] == COMMAND_PREFIX)
    text++;

  for (i = 0; (t = section_alist[i].name); i++)
    {
      if (strcmp (t, text) == 0)
        {
          return i;
        }
    }
  return -1;
}

/* Return an integer which identifies the type of section present in
   TEXT -- 1 for @top, 2 for chapters, ..., 5 for subsubsections (as
   specified in section_alist).  We take into account any @lowersections
   and @raisesections.  If SECNAME is non-NULL, also return the
   corresponding section name.  */
int
what_section (char *text, char **secname)
{
  int index, j;
  char *temp;
  int return_val;

 find_section_command:
  for (j = 0; text[j] && cr_or_whitespace (text[j]); j++);
  if (text[j] != COMMAND_PREFIX)
    return -1;

  text = text + j + 1;

  /* We skip @c, @comment, and @?index commands. */
  if ((strncmp (text, "comment", strlen ("comment")) == 0) ||
      (text[0] == 'c' && cr_or_whitespace (text[1])) ||
      (strcmp (text + 1, "index") == 0))
    {
      while (*text++ != '\n');
      goto find_section_command;
    }

  /* Handle italicized sectioning commands. */
  if (*text == 'i')
    text++;

  for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++);

  temp = xmalloc (1 + j);
  strncpy (temp, text, j);
  temp[j] = 0;

  index = search_sectioning (temp);
  free (temp);
  if (index >= 0)
    {
      return_val = section_alist[index].level + section_alist_offset;
      if (return_val < 0)
        return_val = 0;
      else if (return_val > 5)
        return_val = 5;

      if (secname)
        {
          int i;
          int alist_size = sizeof (section_alist) / sizeof(section_alist_type);
          /* Find location of offset sectioning entry, but don't go off
             either end of the array.  */
          int index_offset = MAX (index - section_alist_offset, 0);
          index_offset = MIN (index_offset, alist_size - 1);

          /* Also make sure we don't go into the next "group" of
             sectioning changes, e.g., change from an @appendix to an
             @heading or some such.  */
#define SIGN(expr) ((expr) < 0 ? -1 : 1)
          for (i = index; i != index_offset; i -= SIGN (section_alist_offset))
            {
              /* As it happens, each group has unique .num/.toc values.  */
              if (section_alist[i].num != section_alist[index_offset].num
                  || section_alist[i].toc != section_alist[index_offset].toc)
                break;
            }
          *secname = section_alist[i].name;
        }
      return return_val;
    }
  return -1;
}

/* Returns current top level division (ie. chapter, unnumbered) number.
   - For chapters, returns the number.
   - For unnumbered sections, returns empty string.
   - For appendices, returns A, B, etc. */
char *
current_chapter_number (void)
{
  if (enum_marker == UNNUMBERED_MAGIC)
    return xstrdup ("");
  else if (enum_marker == APPENDIX_MAGIC)
    {
      char s[2];
      sprintf (s, "%c", numbers[0] + 64);
      return xstrdup (s);
    }
  else
    {
      char s[100];
      sprintf (s, "%4d", numbers[0]);
      return xstrdup (s);
    }
}

/* Returns number of the last sectioning command used.  */
char *
current_sectioning_number (void)
{
  if (enum_marker == UNNUMBERED_MAGIC || !number_sections)
    return xstrdup ("");
  else
    return xstrdup (last_sectioning_number);
}

/* Returns arguments of the last sectioning command used.  */
char *
current_sectioning_name (void)
{
  return xstrdup (last_sectioning_title);
}

/* insert_and_underscore, sectioning_underscore and sectioning_html call this.  */

static char *
handle_enum_increment (int level, int index)
{
  /* Here is how TeX handles enumeration:
     - Anything starting with @unnumbered is not enumerated.
     - @majorheading and the like are not enumberated.  */
  int i;

  /* First constraint above.  */
  if (enum_marker == UNNUMBERED_MAGIC && level == 0)
    return xstrdup ("");

  /* Second constraint.  */
  if (section_alist[index].num == ENUM_SECT_NO)
    return xstrdup ("");

  /* reset all counters which are one level deeper */
  for (i = level; i < 3; i++)
    numbers [i + 1] = 0;

  numbers[level]++;
  if (section_alist[index].num == ENUM_SECT_NO || enum_marker == UNNUMBERED_MAGIC
      || !number_sections)
    return xstrdup ("");
  else
    return xstrdup (get_sectioning_number (level, section_alist[index].num));
}

void
sectioning_underscore (char *cmd)
{
  char *temp, *secname;
  int level;

  /* If we're not indenting the first paragraph, we shall make it behave
     like @noindent is called directly after the section heading. */
  if (! do_first_par_indent)
    cm_noindent ();

  temp = xmalloc (2 + strlen (cmd));
  temp[0] = COMMAND_PREFIX;
  strcpy (&temp[1], cmd);
  level = what_section (temp, &secname);
  level -= 2;
  if (level < 0)
    level = 0;
  free (temp);

  /* If the argument to @top is empty, we try using the one from @settitle.
     Warn if both are unusable.  */
  if (STREQ (command, "top"))
    {
      int save_input_text_offset = input_text_offset;

      get_rest_of_line (0, &temp);

      /* Due to get_rest_of_line ... */
      line_number--;

      if (strlen (temp) == 0 && (!title || strlen (title) == 0))
        warning ("Must specify a title with least one of @settitle or @top");

      input_text_offset = save_input_text_offset;
    }

  if (xml)
    {
      /* If the section appears in the toc, it means it's a real section
	 unlike majorheading, chapheading etc. */
      if (section_alist[search_sectioning (cmd)].toc == TOC_YES)
	{
	  xml_close_sections (level);
	  /* Mark the beginning of the section
	     If the next command is printindex, we will remove
	     the section and put an Index instead */
	  flush_output ();
	  xml_last_section_output_position = output_paragraph_offset;

	  get_rest_of_line (0, &temp);

          /* Use @settitle value if @top parameter is empty.  */
          if (STREQ (command, "top") && strlen(temp) == 0)
            temp = xstrdup (title ? title : "");

          /* Docbook does not support @unnumbered at all.  So we provide numbers
             that other formats use.  @appendix seems to be fine though, so we let
             Docbook handle that as usual.  */
          if (docbook && enum_marker != APPENDIX_MAGIC)
            {
              if (section_alist[search_sectioning (cmd)].num == ENUM_SECT_NO
                  && section_alist[search_sectioning (cmd)].toc == TOC_YES)
                xml_insert_element_with_attribute (xml_element (secname),
                    START, "label=\"%s\" xreflabel=\"%s\"",
                    handle_enum_increment (level, search_sectioning (cmd)),
                    text_expansion (temp));
              else
                xml_insert_element_with_attribute (xml_element (secname),
                    START, "label=\"%s\"",
                    handle_enum_increment (level, search_sectioning (cmd)));
            }
          else
            xml_insert_element (xml_element (secname), START);

	  xml_insert_element (TITLE, START);
	  xml_open_section (level, secname);
	  execute_string ("%s", temp);
	  xml_insert_element (TITLE, END);

	  free (temp);
	}
      else
        {
          if (docbook)
            {
              if (level > 0)
                xml_insert_element_with_attribute (xml_element (secname), START,
                    "renderas=\"sect%d\"", level);
              else
                xml_insert_element_with_attribute (xml_element (secname), START,
                    "renderas=\"other\"");
            }
          else
            xml_insert_element (xml_element (secname), START);

          get_rest_of_line (0, &temp);
          execute_string ("%s", temp);
          free (temp);

          xml_insert_element (xml_element (secname), END);
        }
    }
  else if (html)
    sectioning_html (level, secname);
  else
    insert_and_underscore (level, secname);
}

/* Insert the text following input_text_offset up to the end of the line
   in a new, separate paragraph.  Directly underneath it, insert a
   line of WITH_CHAR, the same length of the inserted text. */
void
insert_and_underscore (int level, char *cmd)
{
  int i, len;
  int index;
  int old_no_indent;
  unsigned char *starting_pos, *ending_pos;
  char *temp;
  char with_char = scoring_characters[level];

  close_paragraph ();
  filling_enabled =  indented_fill = 0;
  old_no_indent = no_indent;
  no_indent = 1;

  if (macro_expansion_output_stream && !executing_string)
    append_to_expansion_output (input_text_offset + 1);

  get_rest_of_line (0, &temp);

  /* Use @settitle value if @top parameter is empty.  */
  if (STREQ (command, "top") && strlen(temp) == 0)
    temp = xstrdup (title ? title : "");

  starting_pos = output_paragraph + output_paragraph_offset;

  /* Poor man's cache for section title.  */
  if (strlen (last_sectioning_title))
    free (last_sectioning_title);
  last_sectioning_title = xstrdup (temp);

  index = search_sectioning (cmd);
  if (index < 0)
    {
      /* should never happen, but a poor guy, named Murphy ... */
      warning (_("Internal error (search_sectioning) `%s'!"), cmd);
      return;
    }

  /* This is a bit tricky: we must produce "X.Y SECTION-NAME" in the
     Info output and in TOC, but only SECTION-NAME in the macro-expanded
     output.  */

  /* Step 1: produce "X.Y" and add it to Info output.  */
  add_word_args ("%s ", handle_enum_increment (level, index));

  /* Step 2: add "SECTION-NAME" to both Info and macro-expanded output.  */
  if (macro_expansion_output_stream && !executing_string)
    {
      char *temp1 = xmalloc (2 + strlen (temp));
      sprintf (temp1, "%s\n", temp);
      remember_itext (input_text, input_text_offset);
      me_execute_string (temp1);
      free (temp1);
    }
  else
    execute_string ("%s\n", temp);

  /* Step 3: pluck "X.Y SECTION-NAME" from the output buffer and
     insert it into the TOC.  */
  ending_pos = output_paragraph + output_paragraph_offset;
  if (section_alist[index].toc == TOC_YES)
    toc_add_entry (substring ((char *)starting_pos, (char *)ending_pos - 1),
                   level, current_node, NULL);

  free (temp);

  len = (ending_pos - starting_pos) - 1;
  for (i = 0; i < len; i++)
    add_char (with_char);
  insert ('\n');
  close_paragraph ();
  filling_enabled = 1;
  no_indent = old_no_indent;
}

/* Insert the text following input_text_offset up to the end of the
   line as an HTML heading element of the appropriate `level' and
   tagged as an anchor for the current node.. */

void
sectioning_html (int level, char *cmd)
{
  static int toc_ref_count = 0;
  int index;
  int old_no_indent;
  unsigned char *starting_pos, *ending_pos;
  char *temp, *toc_anchor = NULL;

  close_paragraph ();
  filling_enabled =  indented_fill = 0;
  old_no_indent = no_indent;
  no_indent = 1;

  /* level 0 (chapter) is
, and we go down from there.  */
  add_html_block_elt_args ("<h%d class=\"%s\">", level + 2, cmd);

  /* If we are outside of any node, produce an anchor that
     the TOC could refer to.  */
  if (!current_node || !*current_node)
    {
      static const char a_name[] = "", a_name, toc_ref_count++);
      toc_anchor = substring ((char *)starting_pos + sizeof (a_name) - 1,
                              (char *)output_paragraph
			      + output_paragraph_offset);
      /* This must be added after toc_anchor is extracted, since
         toc_anchor cannot include the closing .  For details,
         see toc.c:toc_add_entry and toc.c:contents_update_html.

         Also, the anchor close must be output before the section name
         in case the name itself contains an anchor. */
      add_word ("");
    }
  starting_pos = output_paragraph + output_paragraph_offset;

  if (macro_expansion_output_stream && !executing_string)
    append_to_expansion_output (input_text_offset + 1);

  get_rest_of_line (0, &temp);

  /* Use @settitle value if @top parameter is empty.  */
  if (STREQ (command, "top") && strlen(temp) == 0)
    temp = xstrdup (title ? title : "");

  index = search_sectioning (cmd);
  if (index < 0)
    {
      /* should never happen, but a poor guy, named Murphy ... */
      warning (_("Internal error (search_sectioning) \"%s\"!"), cmd);
      return;
    }

  /* Produce "X.Y" and add it to HTML output.  */
  {
    char *title_number = handle_enum_increment (level, index);
    if (strlen (title_number) > 0)
      add_word_args ("%s ", title_number);
  }

  /* add the section name to both HTML and macro-expanded output.  */
  if (macro_expansion_output_stream && !executing_string)
    {
      remember_itext (input_text, input_text_offset);
      me_execute_string (temp);
      write_region_to_macro_output ("\n", 0, 1);
    }
  else
    execute_string ("%s", temp);

  ending_pos = output_paragraph + output_paragraph_offset;

  /* Pluck ``X.Y SECTION-NAME'' from the output buffer and insert it
     into the TOC.  */
  if (section_alist[index].toc == TOC_YES)
    toc_add_entry (substring ((char *)starting_pos, (char *)ending_pos),
                   level, current_node, toc_anchor);

  free (temp);

  if (outstanding_node)
    outstanding_node = 0;

  add_word_args ("</h%d>", level + 2);
  close_paragraph();
  filling_enabled = 1;
  no_indent = old_no_indent;
}

/* Shift the meaning of @section to @chapter. */
void
cm_raisesections (void)
{
  discard_until ("\n");
  section_alist_offset--;
}

/* Shift the meaning of @chapter to @section. */
void
cm_lowersections (void)
{
  discard_until ("\n");
  section_alist_offset++;
}

/* The command still works, but prints a warning message in addition. */
void
cm_ideprecated (int arg, int start, int end)
{
  warning (_("%c%s is obsolete; use %c%s instead"),
           COMMAND_PREFIX, command, COMMAND_PREFIX, command + 1);
  sectioning_underscore (command + 1);
}

/* Treat this just like @unnumbered.  The only difference is
   in node defaulting. */
void
cm_top (void)
{
  /* It is an error to have more than one @top. */
  if (top_node_seen && strcmp (current_node, "Top") != 0)
    {
      TAG_ENTRY *tag = tag_table;

      line_error (_("Node with %ctop as a section already exists"),
                  COMMAND_PREFIX);

      while (tag)
        {
          if (tag->flags & TAG_FLAG_IS_TOP)
            {
              file_line_error (tag->filename, tag->line_no,
                               _("Here is the %ctop node"), COMMAND_PREFIX);
              return;
            }
          tag = tag->next_ent;
        }
    }
  else
    {
      top_node_seen = 1;

      /* It is an error to use @top before using @node. */
      if (!tag_table)
        {
          char *top_name;

          get_rest_of_line (0, &top_name);
          line_error (_("%ctop used before %cnode, defaulting to %s"),
                      COMMAND_PREFIX, COMMAND_PREFIX, top_name);
          execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name);
          free (top_name);
          return;
        }

      cm_unnumbered ();

      /* The most recently defined node is the top node. */
      tag_table->flags |= TAG_FLAG_IS_TOP;

      /* Now set the logical hierarchical level of the Top node. */
      {
        int orig_offset = input_text_offset;

        input_text_offset = search_forward (node_search_string, orig_offset);

        if (input_text_offset > 0)
          {
            int this_section;

            /* We have encountered a non-top node, so mark that one exists. */
            non_top_node_seen = 1;

            /* Move to the end of this line, and find out what the
               sectioning command is here. */
            while (input_text[input_text_offset] != '\n')
              input_text_offset++;

            if (input_text_offset < input_text_length)
              input_text_offset++;

            this_section = what_section (input_text + input_text_offset,
                                         NULL);

            /* If we found a sectioning command, then give the top section
               a level of this section - 1. */
            if (this_section != -1)
              set_top_section_level (this_section - 1);
          }
        input_text_offset = orig_offset;
      }
    }
}

/* The remainder of the text on this line is a chapter heading. */
void
cm_chapter (void)
{
  enum_marker = 0;
  sectioning_underscore ("chapter");
}

/* The remainder of the text on this line is a section heading. */
void
cm_section (void)
{
  sectioning_underscore ("section");
}

/* The remainder of the text on this line is a subsection heading. */
void
cm_subsection (void)
{
  sectioning_underscore ("subsection");
}

/* The remainder of the text on this line is a subsubsection heading. */
void
cm_subsubsection (void)
{
  sectioning_underscore ("subsubsection");
}

/* The remainder of the text on this line is an unnumbered heading. */
void
cm_unnumbered (void)
{
  enum_marker = UNNUMBERED_MAGIC;
  sectioning_underscore ("unnumbered");
}

/* The remainder of the text on this line is an unnumbered section heading. */
void
cm_unnumberedsec (void)
{
  sectioning_underscore ("unnumberedsec");
}

/* The remainder of the text on this line is an unnumbered
   subsection heading. */
void
cm_unnumberedsubsec (void)
{
  sectioning_underscore ("unnumberedsubsec");
}

/* The remainder of the text on this line is an unnumbered
   subsubsection heading. */
void
cm_unnumberedsubsubsec (void)
{
  sectioning_underscore ("unnumberedsubsubsec");
}

/* The remainder of the text on this line is an appendix heading. */
void
cm_appendix (void)
{
  /* Reset top level number so we start from Appendix A */
  if (enum_marker != APPENDIX_MAGIC)
    numbers [0] = 0;
  enum_marker = APPENDIX_MAGIC;
  sectioning_underscore ("appendix");
}

/* The remainder of the text on this line is an appendix section heading. */
void
cm_appendixsec (void)
{
  sectioning_underscore ("appendixsec");
}

/* The remainder of the text on this line is an appendix subsection heading. */
void
cm_appendixsubsec (void)
{
  sectioning_underscore ("appendixsubsec");
}

/* The remainder of the text on this line is an appendix
   subsubsection heading. */
void
cm_appendixsubsubsec (void)
{
  sectioning_underscore ("appendixsubsubsec");
}

/* Compatibility functions substitute for chapter, section, etc. */
void
cm_majorheading (void)
{
  sectioning_underscore ("majorheading");
}

void
cm_chapheading (void)
{
  sectioning_underscore ("chapheading");
}

void
cm_heading (void)
{
  sectioning_underscore ("heading");
}

void
cm_subheading (void)
{
  sectioning_underscore ("subheading");
}

void
cm_subsubheading (void)
{
  sectioning_underscore ("subsubheading");
}


code:
/* sectioning.h -- all related stuff @chapter, @section... @contents
   $Id: sectioning.h,v 1.8 2007-07-01 21:20:33 karl Exp $

   Copyright (C) 1999, 2003, 2007 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

   Written by Karl Heinz Marbaise <kama@hippo.fido.de>.  */

#ifndef SECTIONING_H
#define SECTIONING_H

/* Sectioning.  */

/* Level 4.  */
extern void cm_chapter (void),
  cm_unnumbered (void),
  cm_appendix (void),
  cm_top (void);

/* Level 3.  */
extern void cm_section (void),
  cm_unnumberedsec (void),
  cm_appendixsec (void);

/* Level 2.  */
extern void cm_subsection (void),
  cm_unnumberedsubsec (void),
  cm_appendixsubsec (void);

/* Level 1.  */
extern void cm_subsubsection (void),
  cm_unnumberedsubsubsec (void),
  cm_appendixsubsubsec (void);

/* Headings.  */
extern void cm_heading (void),
  cm_chapheading (void),
  cm_subheading (void),
  cm_subsubheading (void),
  cm_majorheading (void);

extern void cm_raisesections (void),
  cm_lowersections (void),
  cm_ideprecated (int arg, int start, int end);

extern void
  sectioning_underscore (char *cmd),
  insert_and_underscore (int level, char *cmd);

/* needed in node.c */
extern int set_top_section_level (int level);

extern void sectioning_html (int level, char *cmd);
extern int what_section (char *text, char **secname);
extern char *current_chapter_number (void),
  *current_sectioning_number (void),
  *current_sectioning_name (void);

/* The argument of @settitle, used for HTML. */
extern char *title;

/* Here is a structure which associates sectioning commands with
   an integer that reflects the depth of the current section. */
typedef struct
{
  char *name;
  int level; /* I can't replace the levels with defines
                because it is changed during run */
  int num; /* ENUM_SECT_NO means no enumeration...
              ENUM_SECT_YES means enumerated version
              ENUM_SECT_APP appendix (Character enumerated
                            at first position */
  int toc; /* TOC_NO means do not enter in toc;
              TOC_YES means enter it in toc */
} section_alist_type;

extern section_alist_type section_alist[];

/* enumerate sections */
#define ENUM_SECT_NO  0
#define ENUM_SECT_YES 1
#define ENUM_SECT_APP 2

/* make entries into toc no/yes */
#define TOC_NO  0
#define TOC_YES 1

#endif /* not SECTIONING_H */

repo_name: mthl/texinfo

license: mit


code:
/* substring.c -- extract substring.
   $Id: substring.c,v 1.5 2007/07/01 21:20:31 karl Exp $

   Copyright (C) 1999, 2004, 2007 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "system.h"

char *
substring (const char *start, const char *end)
{
  char *result = xmalloc (end - start + 1);
  char *scan_result = result;
  const char *scan = start;

  while (scan < end)
    *scan_result++ = *scan++;

  *scan_result = 0;
  return result;
}


code:
# $Id: Makefile.am,v 1.7 2006-07-10 23:07:12 karl Exp $
# Makefile.am for texinfo/tests/makeinfo.
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

TESTS = accent accentenc \
  cond \
  copying \
  defxcond \
  emph-option \
  html-docdesc html-extrali html-min html-manuals html-para html-title \
  html-top \
  include-value \
  macro-at menu-whitespace \
  no-headers \
  node-expand node-value node-whitespace \
  quote-args \
  top \
  twofiles

noinst_SCRIPTS = $(TESTS)

EXTRA_DIST = $(noinst_SCRIPTS) \
  accent.txi accentenc.txi accent-text.txi \
  cond.txi copying.txi \
  defxcond.txi \
  emph-option.txi \
  html-docdesc.txi html-extrali.txi html-min.txi html-para.txi html-title.txi \
  html-top.txi \
  include-value.txi incl-incl.txi \
  macro-at.txi menu-whitespace.txi \
  node-expand.txi node-value.txi node-whitespace.txi \
  quote-args.txi \
  top.txi # top2.txi


code:
#!/bin/sh
# $Id: accent,v 1.6 2010-12-04 00:05:53 karl Exp $
# Public domain.  Test accent output.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}
input=`basename $0`.txi

# html.
houtput=`basename $0`.html
../makeinfo --html --no-split -I$srcdir $srcdir/$input
hexit_status=$?
if test $hexit_status = 0; then
  grep 'ì' $houtput >/dev/null \
  && grep '´j' $houtput >/dev/null \
  && grep 'Ø' $houtput >/dev/null \
  && grep '/L' $houtput >/dev/null
  hexit_status=$?
fi

# info
ioutput=`basename $0`.info
../makeinfo --no-split -I$srcdir $srcdir/$input
iexit_status=$?
if test $iexit_status = 0; then
  grep 'i`' $ioutput >/dev/null \
  && grep 'i"' $ioutput >/dev/null \
  && grep '/L' $ioutput >/dev/null
  iexit_status=$?
fi

rm -f $houtput $ioutput

exit_status=`expr $hexit_status + $iexit_status`
exit $exit_status

path: makeinfo/tests/accent


code:
@c args with braces, without braces/following whitespace

should be e`: @`{e} @`e

should be e': @'{e} @'e

should be e^: @^{e} @^e

should be u": @"{u} @"u

should be i`: @`{i} @`i

should be i': @'{i} @'i

should be i^: @^{i} @^i

should be u": @"{u} @"u

should be c,: @,{c} @,c

should be n~: @~{n} @~n

should be e=: @={e} @=e

should be e@w{'}': @H{e} @H e

should be e.: @dotaccent{e} @dotaccent e

should be e*: @ringaccent{e} @ringaccent e

should be ee[: @tieaccent{ee}

should be e(: @u{e} @u e

should be e_: @ubaraccent{e} @ubaraccent e

should be .e: @udotaccent{e} @udotaccent e

should be e<: @v{e} @v e

upside down: @questiondown{} @exclamdown{}

A-with-circle: @aa{},@AA{}

AE, OE ligatures: @ae{} @AE{} @oe{} @OE{}

dotless i, j: @dotless{i} @dotless{j}

Polish suppressed-L: @l{} @L{}

O-with-slash: @o{} @O{}

es-zet or sharp S: @ss{}

pounds sterling: @pounds{}

@c arg is command -- @dotless{i} is special-cased for HTML
should be dotless i`: @`{@dotless{i}}

should be dotless i': @'{@dotless{i}}

should be dotless i^: @^{@dotless{i}}

should be dotless i": @"{@dotless{i}}

@c arg is command -- @dotless{j}
should be dotless j`: @`{@dotless{j}}

should be dotless j': @'{@dotless{j}}

should be dotless j^: @^{@dotless{j}}

should be dotless j": @"{@dotless{j}}


code:
\input texinfo
@setfilename accent.info
@settitle Accent test

@node Top
@top Accent test top

@include accent-text.txi

@bye


code:
#!/bin/sh
# $Id: accentenc,v 1.4 2010-12-04 00:05:53 karl Exp $
# Public domain.  Test encoded accent info output.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}
input=`basename $0`.txi
output=`basename $0`.info

# we expect two invalid encoded characters:
# accent-text.txi:45: warning: invalid encoded character `#156'.
# accent-text.txi:45: warning: invalid encoded character `#140'.
# These are oe and OE, and they are not supported in Latin 1.
../makeinfo --enable-encoding --no-split -I$srcdir $srcdir/$input 2>/dev/null
exit_status=$?
if test $exit_status = 0; then
  grep '�' $output >/dev/null \
  && grep '�' $output >/dev/null \
  && grep '/L' $output >/dev/null
  exit_status=$?
fi

rm -f $output

exit $exit_status

path: makeinfo/tests/accentenc


code:
\input texinfo
@setfilename accentenc.info
@settitle Accent encoding test
@documentencoding ISO-8859-1

@node Top
@top Accent encoding test top

@include accent-text.txi

@bye


code:
#!/bin/sh
# $Id: cond,v 1.4 2010-12-04 00:05:53 karl Exp $
# Public domain.  Test conditional text.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}

# Default Info output.
../makeinfo -o cond.out $srcdir/cond.txi || exit 1
egrep 'This is (ifnothtml|ifinfo|ifnottex) text' cond.out >/dev/null \
  || exit 2
test `fgrep -c ' text.' cond.out` -eq 3 || exit 3

# Default HTML output.
../makeinfo --no-split --html -o cond.out $srcdir/cond.txi || exit 1
egrep 'This is (html|ifhtml|ifnotinfo|ifnottex) text' cond.out >/dev/null \
 || exit 2
test `fgrep -c ' text.' cond.out` -eq 4 || exit 3

# --ifhtml off, --ifinfo off, --iftex off.
../makeinfo --no-ifhtml --no-ifinfo --no-iftex -o cond.out $srcdir/cond.txi || exit 1
egrep 'This is ifnot(html|info|tex) text' cond.out >/dev/null \
 || exit 2
test `fgrep -c ' text.' cond.out` -eq 3 || exit 3

# Do we really need to test all the other permutations?

# --ifhtml on, --ifinfo on, --iftex on.
../makeinfo --ifhtml --ifinfo --iftex -o cond.out $srcdir/cond.txi || exit 1
egrep 'This is (html|ifhtml|ifinfo|tex|iftex) text' cond.out >/dev/null \
 || exit 2
test `fgrep -c ' text.' cond.out` -eq 5 || exit 3

rm -f cond.out cond.info

path: makeinfo/tests/cond


code:
\input texinfo
@setfilename cond.info

@node Top

@html
This is html text.
@end html

@ifhtml
This is ifhtml text.
@end ifhtml

@ifnothtml
This is ifnothtml text.
@end ifnothtml

@ifinfo
This is ifinfo text.
@end ifinfo

@ifnotinfo
This is ifnotinfo text.
@end ifnotinfo

@tex
This is tex text.
@end tex

@iftex
This is iftex text.
@end iftex

@ifnottex
This is ifnottex text.
@end ifnottex

@bye


code:
\input texinfo   @c -*-texinfo-*-
@copying
Public domain.
@end copying

@insertcopying

@bye


code:
#!/bin/sh
# $Id: defxcond,v 1.3 2010-12-04 00:05:53 karl Exp $
# Public domain.  Test conditionalized @deffnx.
# Report from: Akim Demaille, 14 Aug 2003 12:10:37 +0200.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}

test=defxcond

../makeinfo -Dbar -o $test.out $srcdir/$test.txi || exit 1
grep ' -- bar:' $test.out >/dev/null || exit 2

../makeinfo -Ubar -o $test.out $srcdir/$test.txi || exit 3
grep ' -- bar:' $test.out >/dev/null && exit 4

rm -f $test.out

path: makeinfo/tests/defxcond


code:
\input texinfo
@setfilename defxcond.info

@c set this from the command line.
@c set bar

@c deffnx inside conditional.
@deffn foo

@ifset bar
@deffnx bar
@end ifset

Documentation.

@end deffn

@bye


code:
#!/bin/sh
# Public domain.  Test @option inside @emph.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}

../makeinfo --no-split --html -o emph-option.out $srcdir/emph-option.txi || exit 1
fgrep 'emphasized and referring to <samp>' emph-option.out && exit 3

rm -f emph-option.out

path: makeinfo/tests/emph-option


code:
\input texinfo
@setfilename cond.info

@node Top

Foo @emph{emphasized and referring to @option{--some} option}.

@bye


code:
#!/bin/sh
# Public domain.  Test that a minimal Texinfo file can be converted to HTML.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL

../makeinfo --html --no-split ${srcdir-.}/html-min.txi
exit_status=$?

rm -f html-min.html
exit $exit_status


code:
#!/bin/sh
# Public domain.  Test that @documentdescription works.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL

if ../makeinfo --html --no-split ${srcdir-.}/html-docdesc.txi; then
  grep 'explicit document description' html-docdesc.html >/dev/null
  exit_status=$?
else
  exit_status=1
fi

rm -f html-docdesc.html
exit $exit_status

path: makeinfo/tests/html-docdesc


code:
\input texinfo
@setfilename html-docdesc.info
@settitle HTML docdesc test

@documentdescription
This is the explicit document description.
@end documentdescription

@node Top
@top Top of HTML docdesc test

This is the top.

@bye


code:
#!/bin/sh
# $Id: html-extrali,v 1.3 2010-12-04 00:05:53 karl Exp $
# Public domain.  Test no extra <li> from @menu.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}

li_count=`../makeinfo --no-split --html -o - $srcdir/html-extrali.txi 2>/dev/null \
| grep -c '<li>'`

if test "$li_count" -ne 1; then
  echo "$li_count <li>s instead of one." >&2
  exit 1
else
  exit 0
fi

path: makeinfo/tests/html-extrali


code:
\input texinfo
@setfilename menuli.info

@c extra li generated after <menu>
@c From: Marius Groeger <mag@sysgo.de>, 13nov98.

@menu
* entry1::
@end menu

@bye


code:
#!/bin/sh
# $Id: html-manuals,v 1.4 2010-12-04 00:05:53 karl Exp $
# Public domain.  Test that all the distribution manuals can be
# converted to HTML.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}

for manual in info.texi info-stnd.texi texinfo.txi; do
  base=`echo $manual | sed 's/\.te*xi$//'`
  ../makeinfo --html -I$srcdir/../../doc -I../../doc --no-split \
              $srcdir/../../doc/$manual -o $base.html \
  || exit 1
  rm -f $base.html
done

path: makeinfo/tests/html-manuals


code:
#!/bin/sh
# $Id: html-min,v 1.2 2010-12-04 00:05:54 karl Exp $
# Public domain.  Test that a minimal Texinfo file can be converted to HTML.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL

../makeinfo --html --no-split ${srcdir-.}/html-min.txi
exit_status=$?

rm -f html-min.html
exit $exit_status

path: makeinfo/tests/html-min


code:
\input texinfo
@setfilename html-min.info
@settitle HTML min test

@node Top
@top Top of HTML min test

Top.

Second paragraph.

@bye


code:
#!/bin/sh
# $Id: html-para,v 1.2 2010-12-04 00:05:54 karl Exp $
# Public domain.  Test that paragraph beginnings in HTML work ok.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL

../makeinfo --no-split --html ${srcdir-.}/html-para.txi
exit_status=$?

rm -f html-para.html
exit $exit_status

path: makeinfo/tests/html-para


code:
\input texinfo
@setfilename html-para.info
@settitle HTML paragraph beginning test

@c <p> is emitted at paragraph beginning, which makes the text
@c between START and END not exactly what some cm_xxx functions
@c expect, when they are called by pop_and_call_brace.

@set val @@value@{@}
@definfoenclose foo,\\,//

@node Top
@top Top of HTML paragraph test

@value{val} should work at the beginning of a new paragraph.

@dotless{i} dotless should not trigger error messages at the beginning
of a new paragraph.

@sc{small-caps} should work at the beginning of a new paragraph.

@foo{@@definfoenclose} should work at the beginning of a new paragraph.

@bye


code:
#!/bin/sh
# $Id: html-title,v 1.2 2010-12-04 00:05:54 karl Exp $
# Public domain.  Test that titles with @ commands don't produce markup
# in the <title>.  (And that the @ commands get expanded.)

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL

if ../makeinfo --no-split --html ${srcdir-.}/html-title.txi; then
  grep '^<title>@[^<>]*</title>$' html-title.html >/dev/null
  exit_status=$?
else
  exit_status=1
fi

rm -f html-title.html
exit $exit_status

path: makeinfo/tests/html-title


code:
\input texinfo
@setfilename html-title.info
@settitle @@title @sc{html} @code{test}

@node Top
@top Top of @@title @sc{html} @code{test}

Top.

Second paragraph.

@bye


code:
#!/bin/sh
# $Id: html-top,v 1.2 2010-12-04 00:05:54 karl Exp $
# Public domain.  Test that a bare top node does not crash with --html.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}

# But this input file is erroneous, so throw away errors.
../makeinfo --no-split --force -o html-top.html --html $srcdir/html-top.txi \
2>/dev/null
test -s html-top.html
exit_status=$?

rm -f html-top.html
exit $exit_status

path: makeinfo/tests/html-top


code:
\input texinfo
@setfilename top.info

@node start
@top

@bye


code: This is the @emph{included} file (include-value2.txi).


code:
#!/bin/sh
# $Id: include-value,v 1.5 2010-12-04 00:05:54 karl Exp $
# Public domain.  Test @value expansion in @include and @verbatiminclude names.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}

../makeinfo -I $srcdir $srcdir/include-value.txi
exit_status=$?

if test $exit_status = 0; then
  # should have three instances of _included_,
  # and three of {included}.
  count=`grep -c _included_ include-value.info`
  test $count = 3 || exit_status=`expr $exit_status + 1`

  count=`grep -c '{included}' include-value.info`
  test $count = 3 || exit_status=`expr $exit_status + 1`
fi

rm -f include-value.info
exit $exit_status

path: makeinfo/tests/include-value


code:
\input texinfo
@setfilename include-value.info
@c $Id: include-value.txi,v 1.2 2004-04-11 17:56:47 karl Exp $

@set testvar incl-incl.txi

@c test - in the variable name, and concatenation of text after.
@set test-var incl-incl.tx

@c test - in the variable name, and concatenation of text before and after.
@set test_var ncl-incl.tx

@node Top

testvar include: @include @value{testvar}

testvar verbatiminclude: @verbatiminclude @value{testvar}

test-var include: @include @value{test-var}i

test-var verbatiminclude: @verbatiminclude @value{test-var}i

test_var include: @include i@value{test_var}i

test_var verbatiminclude: @verbatiminclude i@value{test_var}i

@bye


code:
#!/bin/sh
# $Id: macro-at,v 1.3 2010-12-04 00:05:54 karl Exp $
# Public domain.  Test @@ in macro expansions, etc.  --eliz, 14nov99.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL

: ${srcdir=.}
../makeinfo $srcdir/macro-at.txi || exit 1

rm -f macro-at.info

path: makeinfo/tests/macro-at


code:
\input texinfo @c -*- texinfo -*-
@setfilename macro-at.info
@settitle AUTHORS -- who did what on GNU LilyPond

@macro foo
foo-expansion
@end macro

@macro bar
bar-expansion
@end macro

@node Top, , AUTHORS -- who did what on GNU LilyPond, (dir)
@top
@menu
* AUTHORS -- who did what on GNU LilyPond:: AUTHORS -- who did what.
@end menu

@node AUTHORS -- who did what on GNU LilyPond, Top, , Top
@chapter AUTHORS -- who did what on GNU LilyPond?

This file lists authors of GNU LilyPond, and what they wrote.
It also uses foobar@{.

@itemize @bullet
@item @email{pinard@@iro.montreal.ca, Fran@,{c}ois Pinard},
  parts of Documentation.
@item @email{foobar@@baz@@, The Foobar},
  the usual foobarical thing.
@item @email{another@@foobar@{, Buzzer},
  buzzed all the way.
@item @email{@foo{}@@@bar{}},
  also helped.
@item @email{tomcato@@xoommail.com, Tom Cato Amundsen},
  cembalo-partita in mundela.
@end itemize

@bye


code:
#!/bin/sh
# $Id: menu-whitespace,v 1.3 2010-12-04 00:05:54 karl Exp $
# Public domain.  Bug where whitespace after @menu caused confusion.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL

: ${srcdir=.}
input=`basename $0`.txi

../makeinfo -o /dev/null $srcdir/$input
exit $?

Date: 07 Dec 1998 11:23:44 +0100
From: Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
To: bug-texinfo@gnu.org
Subject: Makeinfo mishandles defaulted node links

The following example demonstrates a bug in makeinfo:

$ cat top.texi
@setfilename top.info

@node Top
@top Top

@menu
* first::
@end menu

@node first
@chapter first

@menu   @c
* second::
@end menu

@node second
@section second
$ makeinfo top.texi
Making info file `top.info' from `top.texi'.
./top.texi:3: Next field of node `Top' not pointed to.
./top.texi:17: This node (second) has the bad Prev.
makeinfo: Removing output file `/home/as/test/top.info' due to errors; use --force to preserve.

Makeinfo is being confused by the whitespace after @menu, or rather by its
absence.

1998-12-06  Andreas Schwab  <schwab@issan.cs.uni-dortmund.de>

	* makeinfo/node.c (cm_node): When searching for @menu don't
	require a space after it.

--- texinfo-3.12b/makeinfo/node.c.~1~	Mon Oct 26 23:14:59 1998
+++ texinfo-3.12b/makeinfo/node.c	Sun Dec  6 00:23:59 1998
@@ -523,9 +523,10 @@
                     orig_size = size_of_input_text;

                   input_text_offset =
-                    search_forward ("\n@menu ", orig_offset);
+                    search_forward ("\n@menu", orig_offset);

-                  if (input_text_offset > -1)
+                  if (input_text_offset > -1
+		      && cr_or_whitespace (input_text[input_text_offset + 6]))
                     {
                       char *nodename_from_menu = NULL;

--
Andreas Schwab                                      "And now for something
schwab@issan.cs.uni-dortmund.de                      completely different"
schwab@gnu.org

path: makeinfo/tests/menu-whitespace


code:
@setfilename top.info

@node Top
@top Top

@menu
* first::
@end menu

@node first
@chapter first

@menu   @c
* second::
@end menu

@node second
@section second


code:
#!/bin/sh
# $Id: no-headers,v 1.5 2010-12-04 00:05:54 karl Exp $
# Public domain.  Test that info.texi works with --no-headers (this
# includes node pointer defaulting).

# maybe need "nul" sometimes?  Not clear.
: ${nulldev=/dev/null}

unset TEXINFO_OUTPUT
: ${srcdir=.}
docdir=$srcdir/../../doc

../makeinfo --no-headers -o $nulldev -I$docdir info.texi

path: makeinfo/tests/no-headers


code:
#!/bin/sh
# $Id: node-expand,v 1.3 2010-12-04 00:05:54 karl Exp $
# Public domain.  Test command expansion in node names.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}

../makeinfo --commands-in-node-names $srcdir/node-expand.txi
test -s node-expand.info
exit_status=$?

rm -f node-expand.info
exit $exit_status

path: makeinfo/tests/node-expand


code:
\input texinfo.tex    @c -*-texinfo-*-

@setfilename node-expand.info

@c Makeinfo should expand non-macros such as @@ and @value
@c in node names and node references, including menus.
@c This file deliberately references "Node 1" both via
@c @value and directly; this should not trigger any errors,
@c as long as --commands-in-node-names is used.
@c Index entries should all be expanded as well.

@set node1 Node 1

@ifnottex

@node Top, (dir), (dir), (dir)
@top Expansion in Node Names

@end ifnottex

@menu
* @value{node1}  ::
* @@node `2'::
* ``node'' with---tricks::
@end menu

@xref{@@node `2'}.

@node @value{node1}, @@node `2', Top, Top
@chapter Chapter 1

@cindex entry for chapter 1
This is chapter 1.
@xref{@@node `2'}.

@set sec1 Section 1.1

@menu
* @value{sec1}::
@end menu

@node Section 1.1, , Node 1, Node 1
@comment  node-name,  next,  previous,  up

@cindex entry for section 1.1
This is section 1.1.

@node @@node `2', ``node'' with---tricks, @value{node1}, Top
@comment  node-name,  next,  previous,  up
@chapter Node 2

@cindex entry for chapter 2
This is chapter 2.
@xref{@value{node1}}.  @xref{Node 1}.
@xref{``node'' with---tricks, Node with some tricks}.

@node ``node'' with---tricks, , @@node `2', Top

@cindex tricks
Another node.

@printindex cp

@bye


code:
#!/bin/sh
# $Id: node-value,v 1.3 2010-12-04 00:05:54 karl Exp $
# Public domain.  Test @value expansion in node names.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}

../makeinfo $srcdir/node-value.txi
grep -v "No Value" node-value.info >/dev/null
exit_status=$?

rm -f node-value.info
exit $exit_status

path: makeinfo/tests/node-value


code:
\input texinfo
@setfilename node-value.info
@set a--foo bar

@node Top, @value{a--foo}, (dir), (dir)
@top Var @value{a--foo}
@value{a--foo}

@node @value{a--foo}, BarFoo, Top, (dir)
@chapter BarFoo

@node BarFoo, , @value{a--foo}, (dir)
@chapter bar

@bye


code:
#!/bin/sh
# $Id: node-whitespace,v 1.3 2010-12-04 00:05:54 karl Exp $
# Public domain.  Test whitespace collapse in node names.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}

../makeinfo $srcdir/node-whitespace.txi
test -s node-whitespace.info
exit_status=$?

rm -f node-whitespace.info
exit $exit_status

path: makeinfo/tests/node-whitespace


code:
\input texinfo.tex    @c -*-texinfo-*-

@setfilename node-whitespace.info

@c Makeinfo should collapse whitespace in node names.

@ifnottex

@node Top
@top Whitespace in Node Names

@end ifnottex

@menu
* Chap  1.3  ::
* Chap  1.4  :Chap  1.4.
@end menu

@node Chap  1.3
@chapter Chap 1.3

Can I reach here?

@node Chap      1.4
@chapter Another space test

How about here?
@xref{Chap      1.3}.

@bye


code:
#!/bin/sh
# $Id: quote-args,v 1.3 2010-12-04 00:05:54 karl Exp $
# Public domain.  Test @quote-args facility.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}

TMP=quote-args.out

cat > quote-args.samp <<EOT
*FIXME: Many arguments, separated by commas, are processed here*
natopocotuototam

EOT

../makeinfo --plaintex $srcdir/quote-args.txi | diff - quote-args.samp
exit_code=$?

rm quote-args.samp
exit $exit_code

path: makeinfo/tests/quote-args


code:
\input texinfo
@smallbook
@setfilename quote-args.info
@settitle Quote-args facility in macros

@rmacro cat{a,b}
\a\\b\
@end rmacro

@macro FIXME{a}
@strong{FIXME: \a\}
@end macro

@node Top, , (dir), (dir)

@noindent
@FIXME{Many arguments, separated by commas, are processed here}
@cat{@cat{@cat{@cat{@cat{@cat{na, to}, po}, co}, tu}, oto},tam}
@bye


code:
#!/bin/sh
# $Id: top,v 1.3 2010-12-04 00:05:54 karl Exp $
# Public domain.  Test that a top node can be ignored.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}

../makeinfo $srcdir/top.txi || exit 1

# Expected warnings due to use of @ifinfo instead of @ifnottex.
../makeinfo --no-split --no-warn --html $srcdir/top.txi -o top.html || exit 1

rm -f top*.html top.info

path: makeinfo/tests/top


code:
\input texinfo
@setfilename top.info
@settitle top test

@c This traditional top node uses @ifinfo for testing.
@c Therefore there will be warnings when processing with --html.
@c The solution is to use @ifnottex instead.

@ifinfo
@node Top
@top Top test

Typical top node.
@end ifinfo

@menu
* Subnode::
@end menu

@node Subnode
@chapter Subnode

Subnode.

@bye


code:
#!/bin/sh
# $Id: top2,v 1.2 2010-12-04 00:05:54 karl Exp $
# Public domain.  Test that a bare top node does not crash with --html.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}

# But this input file is erroneous, so throw away errors.
../makeinfo --no-split --force -o top2.html --html $srcdir/top2.txi 2>/dev/null
test -s top2.html
exit_status=$?

rm -f top2.html
exit $exit_status


code:
\input texinfo
@setfilename top.info

@node start
@top

@bye


code:
#!/bin/sh
# $Id: twofiles,v 1.4 2010-12-04 00:05:54 karl Exp $
# Public domain.  Test that an existing and nonexisting file doesn't cause a
# segmentation fault.
# Report from: Arkadiusz Miskiewicz, 15 Feb 2003 13:22:49 +0100.

unset TEXINFO_OUTPUT LANG LANGUAGE
LC_ALL=POSIX; export LC_ALL
: ${srcdir=.}

outfile=outfile
errfile=errfile
trap 'status=$?; rm -f $outfile $errfile && exit $status' 0

../makeinfo -o /dev/null $srcdir/html-min.txi /nonexistent.texinfo \
>$outfile 2>$errfile
exit_status=$?

# we expect one error message about /nonexistent.texinfo and bad exit status.
test $exit_status -ne 0 \
&& grep /nonexistent $errfile >/dev/null \
&& exit_status=0

exit $exit_status

path: makeinfo/tests/twofiles


code:
/* toc.c -- table of contents handling.
   $Id: toc.c,v 1.12 2008-05-19 18:26:48 karl Exp $

   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2007, 2008
   Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

   Originally written by Karl Heinz Marbaise <kama@hippo.fido.de>.  */

#include "system.h"
#include "makeinfo.h"
#include "cmds.h"
#include "files.h"
#include "macro.h"
#include "node.h"
#include "html.h"
#include "lang.h"
#include "makeinfo.h"
#include "sectioning.h"
#include "toc.h"
#include "xml.h"

/* array of toc entries */
static TOC_ENTRY_ELT **toc_entry_alist = NULL;

/* toc_counter start from 0 ... n for every @chapter, @section ... */
static int toc_counter = 0;

/* Routine to add an entry to the table of contents */
int
toc_add_entry (char *tocname, int level, char *node_name, char *anchor)
{
  char *expanded_node, *d;
  char *s = NULL;
  char *filename = NULL;

  if (!node_name)
    node_name = "";

  /* I assume that xrealloc behaves like xmalloc if toc_entry_alist is
     NULL */
  toc_entry_alist = xrealloc (toc_entry_alist,
                              (toc_counter + 1) * sizeof (TOC_ENTRY_ELT *));

  toc_entry_alist[toc_counter] = xmalloc (sizeof (TOC_ENTRY_ELT));

  if (html)
    {
      /* We need to insert the expanded node name into the toc, so
         that when we eventually output the toc, its <a ref= link will
         point to the <a name= tag created by cm_node in the navigation
         bar.  We cannot expand the containing_node member, for the
         reasons explained in the WARNING below.  We also cannot wait
         with the node name expansion until the toc is actually output,
         since by that time the macro definitions may have been changed.
         So instead we store in the tocname member the expanded node
         name and the toc name concatenated together (with the necessary
         html markup), since that's how they are output.  */
      if (!anchor)
        s = expanded_node = expand_node_name (node_name);
      else
        expanded_node = anchor;
      if (splitting)
	{
	  if (!anchor)
	    filename = nodename_to_filename (expanded_node);
	  else
	    filename = filename_part (current_output_filename);
	}
      if (!anchor)
        /* Need to HTML-escape the expanded node name like
            add_anchor_name does...  */
        d = escaped_anchor_name (expanded_node);
      else
        /* Section outside any node, they provided explicit anchor.  */
        d = xstrdup(anchor);

      /* Add space for the "> which may be needed, and the tocname */
      d = xrealloc (d, strlen (d) + strlen (tocname) + 3);
      if (!anchor)
        strcat (d, "\">");
      strcat (d, tocname);
      free (tocname);       /* it was malloc'ed by substring() */
      free (expanded_node);
      toc_entry_alist[toc_counter]->name = d;
    }
  else
    toc_entry_alist[toc_counter]->name = tocname;
  /* WARNING!  The node name saved in containing_node member must
     be the node name with _only_ macros expanded (the macros in
     the node name are expanded by cm_node when it grabs the name
     from the @node directive).  Non-macros, like @value, @@ and
     other @-commands must NOT be expanded in containing_node,
     because toc_find_section_of_node looks up the node name where
     they are also unexpanded.  You *have* been warned!  */
  toc_entry_alist[toc_counter]->containing_node = xstrdup (node_name);
  toc_entry_alist[toc_counter]->level = level;
  toc_entry_alist[toc_counter]->number = toc_counter;
  toc_entry_alist[toc_counter]->html_file = filename;

  /* have to be done at least */
  return toc_counter++;
}

/* Return the name of a chapter/section/subsection etc. that
   corresponds to the node NODE.  If the node isn't found,
   return NULL.

   WARNING!  This function relies on NODE being unexpanded
   except for macros (i.e., @value, @@, and other non-macros
   should NOT be expanded), because the containing_node member
   stores unexpanded node names.

   Note that this function returns the first section whose
   containing node is NODE.  Thus, they will lose if they use
   more than a single chapter structioning command in a node,
   or if they have a node without any structuring commands.  */
char *
toc_find_section_of_node (char *node)
{
  int i;

  if (!node)
    node = "";
  for (i = 0; i < toc_counter; i++)
    if (STREQ (node, toc_entry_alist[i]->containing_node))
      return toc_entry_alist[i]->name;

  return NULL;
}

/* free up memory used by toc entries */
void
toc_free (void)
{
  int i;

  if (toc_counter)
    {
      for (i = 0; i < toc_counter; i++)
        {
          free (toc_entry_alist[i]->name);
          free (toc_entry_alist[i]->containing_node);
          free (toc_entry_alist[i]);
        }

      free (toc_entry_alist);
      toc_entry_alist = NULL; /* to be sure ;-) */
      toc_counter = 0; /* to be absolutley sure ;-) */
    }
}

/* Print table of contents in HTML.  */

static void
contents_update_html (void)
{
  int i;
  int k;
  int last_level;

  /* does exist any toc? */
  if (!toc_counter)
      /* no, so return to sender ;-) */
      return;

  add_html_block_elt_args ("\n
\n
%s
\n
\n", gdt("Table of Contents"));

  last_level = toc_entry_alist[0]->level;

  for (i = 0; i < toc_counter; i++)
    {
      if (toc_entry_alist[i]->level > last_level)
        {
          /* unusual, but it is possible
             @chapter ...
             @subsubsection ...      ? */
          for (k = 0; k < (toc_entry_alist[i]->level-last_level); k++)
            add_html_block_elt ("
\n");
        }
      else if (toc_entry_alist[i]->level < last_level)
        {
          /* @subsubsection ...
             @chapter ... this IS usual.*/
          for (k = 0; k < (last_level-toc_entry_alist[i]->level); k++)
            add_word ("

\n");
        }

      /* No double entries in TOC.  */
      if (!(i && strcmp (toc_entry_alist[i]->name,
			 toc_entry_alist[i-1]->name) == 0))
        {
          /* each toc entry is a list item.  */
          add_word ("
");

          /* Insert link -- to an external file if splitting, or
             within the current document if not splitting.  */
	  add_word ("level == 0)
	    {
	      char *p = toc_entry_alist[i]->name;

	      /* toc_entry_alist[i]->name has the form `foo">bar',
		 that is, it includes both the node name and anchor
		 text.  We need to find where `foo', the node name,
		 ends, and use that in toc_FOO.  */
	      while (*p && *p != '"')
		p++;
	      add_word_args ("name=\"toc_%.*s\" ",
		       p - toc_entry_alist[i]->name, toc_entry_alist[i]->name);
              /* save the link if necessary */
              if (internal_links_stream)
                {
                  fprintf (internal_links_stream, "%s#toc_%.*s\ttoc\t%s\n",
                      splitting ? toc_entry_alist[i]->html_file : "",
                      p - toc_entry_alist[i]->name, toc_entry_alist[i]->name,
                      p + 2);
                }
	    }
	  add_word_args ("href=\"%s#%s\n",
		   splitting ? toc_entry_alist[i]->html_file : "",
		   toc_entry_alist[i]->name);
        }

      last_level = toc_entry_alist[i]->level;
    }

  /* Go back to start level. */
  if (toc_entry_alist[0]->level < last_level)
    for (k = 0; k < (last_level-toc_entry_alist[0]->level); k++)
      add_word ("

\n");

  add_word ("

\n
\n\n");
}

/* print table of contents in ASCII (--no-headers)
   May be we should create a new command line switch --ascii ? */
static void
contents_update_info (void)
{
  int i;
  int k;

  if (!toc_counter)
      return;

  insert_string ((char *) gdt("Table of Contents"));
  insert ('\n');
  for (i = 0; i < strlen (gdt("Table of Contents")); i++)
    insert ('*');
  insert_string ("\n\n");

  for (i = 0; i < toc_counter; i++)
    {
      if (toc_entry_alist[i]->level == 0)
        add_char ('\n');

      /* indention with two spaces per level, should this
         changed? */
      for (k = 0; k < toc_entry_alist[i]->level; k++)
        insert_string ("  ");

      insert_string (toc_entry_alist[i]->name);
      insert ('\n');
    }
  insert_string ("\n\n");
}

/* shortcontents in HTML; Should this produce a standalone file? */
static void
shortcontents_update_html (char *contents_filename)
{
  int i;
  char *toc_file = NULL;

  /* does exist any toc? */
  if (!toc_counter)
    return;

  add_html_block_elt_args ("\n
\n
%s
\n
\n", gdt("Short Contents"));

  if (contents_filename)
    toc_file = filename_part (contents_filename);

  for (i = 0; i < toc_counter; i++)
    {
      char *name = toc_entry_alist[i]->name;

      if (toc_entry_alist[i]->level == 0)
	{
	  if (contents_filename)
	    add_word_args ("
<a href=\"%s#toc_%s
\n",
		     splitting ? toc_file : "", name);
	  else
	    add_word_args ("<a href=\"%s#%s\n",
		     splitting ? toc_entry_alist[i]->html_file : "", name);
	}
    }
  add_word ("
\n
\n\n");
  if (contents_filename)
    free (toc_file);
}

/* short contents in ASCII (--no-headers).  */
static void
shortcontents_update_info (void)
{
  int i;

  if (!toc_counter)
      return;

  insert_string ((char *) gdt("Short Contents"));
  insert ('\n');
  for (i = 0; i < strlen (gdt("Short Contents")); i++)
    insert ('*');
  insert_string ("\n\n");

  for (i = 0; i < toc_counter; i++)
    {
      if (toc_entry_alist[i]->level == 0)
        {
          insert_string (toc_entry_alist[i]->name);
          insert ('\n');
        }
    }
  insert_string ("\n\n");
}

void
cm_contents (int arg)
{
  /* the file where we found the @contents directive */
  static char *contents_filename;

  /* No need to mess with delayed stuff for XML and Docbook.  */
  if (xml)
    {
      if (arg == START)
        {
          int elt = STREQ (command, "contents") ? CONTENTS : SHORTCONTENTS;
          xml_insert_element (elt, START);
          xml_insert_element (elt, END);
        }
    }
  else if (!handling_delayed_writes)
    {
      register_delayed_write (STREQ (command, "contents")
          ? "@contents" : "@shortcontents");

      if (html && STREQ (command, "contents"))
        {
          if (contents_filename)
            free (contents_filename);
          contents_filename = xstrdup (current_output_filename);
        }
    }
  else if (html)
    STREQ (command, "contents")
      ? contents_update_html () : shortcontents_update_html (contents_filename);
  else if (no_headers)
    STREQ (command, "contents")
      ? contents_update_info () : shortcontents_update_info ();
}


code:
/* toc.h -- table of contents handling.
   $Id: toc.h,v 1.5 2007-07-01 21:20:33 karl Exp $

   Copyright (C) 1999, 2007 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

   Written by Karl Heinz Marbaise <kama@hippo.fido.de>.  */

#ifndef TOC_H
#define TOC_H

/* Structure to hold one entry for the toc. */
typedef struct toc_entry_elt {
  char *name;
  char *containing_node; /* Name of node containing this section.  */
  char *html_file;       /* Name of HTML node-file in split-HTML mode */
  int number;            /* counting number from 0...n independent from
                            chapter/section can be used for anchors or
                            references to it.  */
  int level;             /* level: chapter, section, subsection... */
} TOC_ENTRY_ELT;

/* all routines which have relationship with TOC should start with
   toc_ (this is a kind of name-space) */
extern int toc_add_entry (char *tocname, int level,
    char *node_name, char *anchor); /* return the number for the toc-entry */
extern void toc_free (void);
extern char *toc_find_section_of_node (char *node);

extern void cm_contents (int arg);

#endif /* not TOC_H */


code:
/* xexit.c -- exit with attention to return values and closing stdout.
   $Id: xexit.c,v 1.8 2007/07/01 21:20:31 karl Exp $

   Copyright (C) 1999, 2003, 2004, 2007 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "system.h"

/* SunOS 4.1.1 gets STDC_HEADERS defined, but it doesn't provide
   EXIT_FAILURE.  So far no system has defined one of EXIT_FAILURE and
   EXIT_SUCCESS without the other.  */
#ifdef EXIT_SUCCESS
 /* The following test is to work around the gross typo in
    systems like Sony NEWS-OS Release 4.0C, whereby EXIT_FAILURE
    is defined to 0, not 1.  */
# if !EXIT_FAILURE
#  undef EXIT_FAILURE
#  define EXIT_FAILURE 1
# endif
#else /* not EXIT_SUCCESS */
# ifdef VMS /* these values suppress some messages; from gnuplot */
#   define EXIT_SUCCESS 1
#   define EXIT_FAILURE 0x10000002
# else /* not VMS */
#  define EXIT_SUCCESS 0
#  define EXIT_FAILURE 1
# endif /* not VMS */
#endif /* not EXIT_SUCCESS */

/* Flush stdout first, exit if failure (therefore, xexit should be
   called to exit every program, not just `return' from main).
   Otherwise, if EXIT_STATUS is zero, exit successfully, else
   unsuccessfully.  */

void
xexit (int exit_status)
{
  if (ferror (stdout))
    {
      fputs (_("ferror on stdout\n"), stderr);
      exit_status = 1;
    }
  else if (fflush (stdout) != 0)
    {
      fputs (_("fflush error on stdout\n"), stderr);
      exit_status = 1;
    }

  exit_status = exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE;

  exit (exit_status);
}

/* Why do we care about stdout you may ask?  Here's why, from Jim
   Meyering in the lib/closeout.c file.  */

/* If a program writes *anything* to stdout, that program should close
   stdout and make sure that the close succeeds.  Otherwise, suppose that
   you go to the extreme of checking the return status of every function
   that does an explicit write to stdout.  The last printf can succeed in
   writing to the internal stream buffer, and yet the fclose(stdout) could
   still fail (due e.g., to a disk full error) when it tries to write
   out that buffered data.  Thus, you would be left with an incomplete
   output file and the offending program would exit successfully.

   Besides, it's wasteful to check the return value from every call
   that writes to stdout -- just let the internal stream state record
   the failure.  That's what the ferror test is checking below.

   It's important to detect such failures and exit nonzero because many
   tools (most notably `make' and other build-management systems) depend
   on being able to detect failure in other tools via their exit status.  */