code:
/* Copyright 2010, 2011, 2012, 2013, 2014, 2015
   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 <stdlib.h>
#include <string.h>

#include "parser.h"
#include "tree.h"
#include "text.h"
#include "errors.h"
#include "convert.h"
#include "input.h"
#include "labels.h"

// 3600
/* Add the contents of CURRENT as an element to the extra value with
   key KEY, except that some "empty space" elements are removed.  Used for
   'brace_command_contents' for the arguments to a brace command, and
   'block_command_line_contents' for the arguments to a block line command.

   For a brace command $element, $element->{'args'} has pretty much the same
   information as $element->{'extra'}->{'brace_command_contents'}. */
void
register_command_arg (ELEMENT *current, char *key)
{
  ELEMENT *value;
  ELEMENT *new;
  KEY_PAIR *k;

  new = trim_spaces_comment_from_content (current);
  if (new->contents.number == 0)
    {
      free (new);
      new = 0;
    }

  /* FIXME: Could we add all the command args together, instead of one-by-one,
     to avoid having to look for the extra value every time? */
  k = lookup_extra_key (current->parent, key);
  if (k)
    value = k->value;
  else
    {
      value = new_element (ET_NONE);
      value->parent_type = route_not_in_tree;
      add_extra_contents_array (current->parent, key, value);
    }

  add_to_contents_as_array (value, new);
}

/* 4888 */
ELEMENT *
handle_open_brace (ELEMENT *current, char **line_inout)
{
  char *line = *line_inout;

  abort_empty_line (&current, NULL);
  /* 4890 */
  if (command_flags(current) & CF_brace)
    {
      enum command_id command;
      ELEMENT *arg;

      command = current->cmd;
      /* 4896 */
      counter_push (&count_remaining_args, current,
                    command_data(current->cmd).data);
      counter_dec (&count_remaining_args);

      arg = new_element (ET_NONE);
      add_to_element_args (current, arg);
      current = arg;

      if (command == CM_verb)
        {
          current->type = ET_brace_command_arg;
          /* Save the deliminating character in 'type'.  This is a reuse of
             'type' for a different purpose. */
          if (!*line || *line == '\n')
            {
              line_error ("@verb without associated character");
              current->parent->type = 0;
            }
          else
            current->parent->type = (enum element_type) *line++;
        }
        /* 4903 */
      else if (command_data(command).data == BRACE_context)
        {
          if (command == CM_caption || command == CM_shortcaption)
            {
#define float floatxx
              ELEMENT *float;
              if (!current->parent->parent
                  || current->parent->parent->cmd != CM_float)
                {
                  float = current->parent;
                  while (float->parent && float->cmd != CM_float)
                    float = float->parent;
                  if (float->cmd != CM_float)
                    {
                      line_error ("@%s is not meaningful outside "
                                  "`@float' environment",
                                  command_name(command));
                      float = 0;
                    }
                  else
                    line_warn ("@%s should be right below `@float'",
                               command_name(command));
                }
              else
                float = current->parent->parent;
              if (float)
                {
                  if (lookup_extra_key (float, command_name(command)))
                    line_warn ("ignoring multiple @%s",
                               command_name(command));
                  else
                    {
                      add_extra_element (current->parent, "float", float);
                      add_extra_element (float, command_name(command),
                                             current->parent);
                    }
                }
#undef float
        }

          /* Add to context stack. */
          switch (command)
            {
            case CM_footnote:
              push_context (ct_footnote);
              break;
            case CM_caption:
              push_context (ct_caption);
              break;
            case CM_shortcaption:
              push_context (ct_shortcaption);
              break;
            case CM_math:
              push_context (ct_math);
              break;
            default:
              abort ();
            }

          {
            ELEMENT *e;
            int n;
            n = strspn (line, whitespace_chars_except_newline);
            e = new_element (ET_empty_spaces_before_argument);
            text_append_n (&e->text, line, n);
            add_to_element_contents (current, e);
            add_extra_element (current->parent,
                                   "spaces_before_argument", e);
            line += n;
          }
          current->type = ET_brace_command_context;
        }

      /* 4945 */
      else /* not context brace */
        {
          current->type = ET_brace_command_arg;

          /* Commands which are said to take a positive number of arguments
             disregard leading and trailing whitespace.  In
             'handle_close_brace', the 'brace_command_contents' array
             is set.  */
          if (command_data(command).data > 0)
            {
              ELEMENT *e;
              e = new_element (ET_empty_spaces_before_argument);
              /* See comment in parser.c:merge_text */
              text_append (&e->text, "");
              add_to_element_contents (current, e);
              add_extra_element (current->parent,
                                     "spaces_before_argument", e);

              if (command == CM_inlineraw)
                push_context (ct_inlineraw);
            }
        }
      debug ("OPENED");
    }

  /* 4967 */
  else if (current->parent && (current->parent->cmd == CM_multitable
           || current->parent->type == ET_def_line))
    {
      ELEMENT *b, *e;
      b = new_element (ET_bracketed);
      add_to_element_contents (current, b);
      current = b;

      /* We need the line number here in case @ protects the
         end of the line.  */
      if (current->parent->parent->type == ET_def_line)
        current->line_nr = line_nr;

      e = new_element (ET_empty_spaces_before_argument);
      text_append (&e->text, ""); /* See comment in parser.c:merge_text */
      add_to_element_contents (current, e);
      debug ("BRACKETED in def/multitable");
      add_extra_element (current, "spaces_before_argument", e);
    }
  else if (current->type == ET_rawpreformatted)
    {
      ELEMENT *e = new_element (ET_NONE);
      text_append (&e->text, "{");
      add_to_element_contents (current, e);
    }

  // 4993
  else if (current_context() == ct_math
           || current_context() == ct_rawpreformatted
           || current_context() == ct_inlineraw)
    {
      ELEMENT *b = new_element (ET_bracketed);
      b->line_nr = line_nr;
      add_to_element_contents (current, b);
      current = b;
      debug ("BRACKETED in math");
    }
  else
    line_error ("misplaced {");

  *line_inout = line;
  return current;
}

/* 5007 */
ELEMENT *
handle_close_brace (ELEMENT *current, char **line_inout)
{
  char *line = *line_inout;

  abort_empty_line (&current, NULL);

  if (current->type == ET_bracketed)
    {
      /* Used in @math */
      current = current->parent;
      goto funexit;
    }
  else if (command_flags(current->parent) & CF_brace)
    {
      enum command_id closed_command;
      // 5019
      if (command_data(current->parent->cmd).data == BRACE_context)
        {
          enum context c;
          c = pop_context ();
          /* The Perl code here checks that the popped context and the
             parent command match as strings. */
        }
      else if (command_data(current->parent->cmd).data > 0) /* sic */
        {
          // 5033
          /* @inline* always have end spaces considered as normal text */
          if (!(command_flags(current->parent) & CF_inline))
            isolate_last_space (current, 0);
          register_command_arg (current, "brace_command_contents");
          remove_empty_content_arguments (current);
        }

      closed_command = current->parent->cmd;
      debug ("CLOSING(brace) %s", command_data(closed_command).cmdname);
      counter_pop (&count_remaining_args);

      // 5044
      if (current->contents.number > 0
          && command_data(closed_command).data == 0)
        line_warn ("command @%s does not accept arguments",
                   command_name(closed_command));

      if (closed_command == CM_anchor) // 5051
        {
          NODE_SPEC_EXTRA *parsed_anchor;
          current->parent->line_nr = line_nr;
          parsed_anchor = parse_node_manual (current);
          if (check_node_label (parsed_anchor, CM_anchor))
            {
              register_label (current->parent, parsed_anchor);
              if (current_region ())
                add_extra_element (current, "region", current_region ());
            }
        }
      else if (command_data(closed_command).flags & CF_ref) // 5062
        {
          ELEMENT *ref = current->parent, *args;
          KEY_PAIR *k;
          if (ref->args.number > 0)
            {
              k = lookup_extra_key (ref, "brace_command_contents");
              args = k->value;
              if ((closed_command == CM_inforef
                   && (args->contents.number <= 0
                       || !args->contents.list[0])
                   && (args->contents.number <= 2
                       || !args->contents.list[2]))
                  || (closed_command != CM_inforef
                       && (args->contents.number <= 0
                           || !args->contents.list[0])
                       && (args->contents.number <= 3
                           || !args->contents.list[3])
                       && (args->contents.number <= 4
                           || !args->contents.list[4])))
                {
                  line_warn ("command @%s missing a node or external manual "
                             "argument", command_name(closed_command));
                }
              else
                {
                  NODE_SPEC_EXTRA *nse;
                  nse = parse_node_manual (args_child_by_index (ref, 0));
                  if (nse && (nse->manual_content || nse->node_content))
                    add_extra_node_spec (ref, "node_argument", nse);
                  else
                    free (nse);
                  if (closed_command != CM_inforef
                      && (args->contents.number <= 3
                          || args->contents.number <= 4
                             && !contents_child_by_index(args, 3)
                          || (!contents_child_by_index(args, 3)
                               && !contents_child_by_index(args, 4)))
                      && !nse->manual_content)
                    {
                      remember_internal_xref (ref);
                    }
                }
              // TODO 5085 check node name not empty after normalization
            }
        }
      else if (closed_command == CM_image) // 5109
        {
          ELEMENT *image = current->parent;
          KEY_PAIR *k;
          if (image->args.number == 0)
            goto image_no_args;
          k = lookup_extra_key (image, "brace_command_contents");
          if (!k)
            goto image_no_args;
          if (!contents_child_by_index (k->value, 0))
            goto image_no_args;
          if (0)
            {
          image_no_args:
              line_error ("@image missing filename argument");
            }
        }
      else if (closed_command == CM_dotless)
        {
          if (current->contents.number > 0)
            {
              char *text = current->contents.list[0]->text.text;
              if (!text || (strcmp (text, "i") && strcmp (text, "j")))
                {
                  line_error ("@dotless expects `i' or `j' as argument, "
                              "not `%s'", text);
                }
            }
        }
      else if ((command_data(closed_command).flags & CF_inline)
               || closed_command == CM_abbr
               || closed_command == CM_acronym)
        { // 5129
          KEY_PAIR *k;
          if (current->parent->cmd == CM_inlineraw)
            {
              if (ct_inlineraw != pop_context ())
                abort ();
            }
          if (current->parent->args.number == 0
              || !(k = lookup_extra_key (current->parent,
                                         "brace_command_contents"))
              || !k->value || k->value->contents.number == 0
              || !k->value->contents.list[0])
            {
              line_warn ("@%s missing first argument",
                         command_name(current->parent->cmd));
            }
        }
      else if (closed_command == CM_errormsg) // 5173
        {
          int i;
          /* Find arg */
          /* Should we use trim_spaces_comment_from_content instead? */
          for (i = 0; i < current->contents.number; i++)
            {
              enum element_type t = current->contents.list[i]->type;
              if (current->contents.list[i]->text.end > 0
                  && t != ET_empty_line_after_command
                  && t != ET_empty_spaces_after_command
                  && t != ET_empty_spaces_before_argument
                  && t != ET_empty_space_at_end_def_bracketed
                  && t != ET_empty_spaces_after_close_brace)
                break;
            }
          if (i == current->contents.number)
            ;
          else
            {
              char *arg = current->contents.list[i]->text.text;
              line_error (arg);
            }
        }
      else if (closed_command == CM_U)
        {
          int i;
          /* Find arg */
          /* Should we use trim_spaces_comment_from_content instead? */
          for (i = 0; i < current->contents.number; i++)
            {
              enum element_type t = current->contents.list[i]->type;
              if (current->contents.list[i]->text.end > 0
                  && t != ET_empty_line_after_command
                  && t != ET_empty_spaces_after_command
                  && t != ET_empty_spaces_before_argument
                  && t != ET_empty_space_at_end_def_bracketed
                  && t != ET_empty_spaces_after_close_brace)
                break;
            }
          if (i == current->contents.number)
            {
              line_warn ("no argument specified for @U");
            }
          else
            {
              char *arg = current->contents.list[i]->text.text;
              int n = strspn (arg, "0123456789ABCDEFabcdef");
              if (arg[n])
                {
                  line_error ("non-hex digits in argument for @U: %s", arg);
                }
              else if (n < 4)
                {
                  line_warn
                    ("fewer than four hex digits in argument for @U: %s", arg);
                }
              else
                {
                  int val;
                  int ret = sscanf (arg, "%d", &val);
                  if (ret != 1)
                    {
                      debug ("hex sscanf failed %s", arg);
                      /* unknown error.  possibly argument is too large
                         for an int. */
                    }
                  if (ret != 1 || val > 0x10FFF)
                    {
                      line_error
                       ("argument for @U exceeds Unicode maximum 0x10FFFF: %s",
                        arg);
                    }
                }

            }
        }
      else if (command_with_command_as_argument (current->parent->parent)
               && current->contents.number == 0)
        {
          debug ("FOR PARENT ... command_as_argument_braces ...");
          if (!current->parent->type)
            current->parent->type = ET_command_as_argument;
          add_extra_element (current->parent->parent->parent,
                             "command_as_argument", current->parent);
        }
      register_global_command (current->parent->cmd, current->parent);

      // 5190
      if (current->parent->cmd == CM_anchor
          || current->parent->cmd == CM_hyphenation
          || current->parent->cmd == CM_caption
          || current->parent->cmd == CM_shortcaption)
        {
          ELEMENT *e;
          e = new_element (ET_empty_spaces_after_close_brace);
          text_append (&e->text, "");
          add_to_element_contents (current->parent->parent, e);
        }

      current = current->parent->parent;
      if (close_preformatted_command(closed_command))
        current = begin_preformatted (current);
    } /* CF_brace */
  else if (current->type == ET_rawpreformatted) // 5199
    {
      /* lone right braces are accepted in a rawpreformatted */
      ELEMENT *e = new_element (ET_NONE);
      text_append_n (&e->text, "}", 1);
      add_to_element_contents (current, e);
      goto funexit;
    }
  // 5203 -- context brace command (e.g. @footnote)
  else if (current_context() == ct_footnote
           || current_context() == ct_caption
           || current_context() == ct_shortcaption
           || current_context() == ct_math)
    {
      enum context c;

      current = end_paragraph (current, 0, 0);
      if (current->parent
          && (command_flags(current->parent) & CF_brace)
          && (command_data(current->parent->cmd).data == BRACE_context))
        {
          enum command_id closed_command;
          c = pop_context ();
          debug ("CLOSING(context command)");
          closed_command = current->parent->cmd;

          register_global_command (current->parent->cmd, current->parent);
          // 5220
          current = current->parent->parent;
          if (close_preformatted_command(closed_command))
            current = begin_preformatted (current);
        }
    }
  else // 5224
    {
      line_error ("misplaced }");
      goto funexit;
    }

funexit:
  *line_inout = line;
  return current;
}

// 2577
/* Remove 'brace_command_contents' or 'block_command_line_contents'
   extra value if empty.
   TODO: If not empty, remove empty elements thereof. */
void
remove_empty_content_arguments (ELEMENT *current)
{
  KEY_PAIR *k;

  k = lookup_extra_key (current, "block_command_line_contents");
  if (!k)
    k = lookup_extra_key (current, "brace_command_contents");
  if (!k)
    return;

  while (k->value->contents.number > 0
         && !last_contents_child(k->value)) // ->contents.number == 0)
    pop_element_from_contents (k->value);

  if (k->value->contents.number == 0)
    {
      k->key = "";
      k->type = extra_deleted;
    }
}

/* Handle a comma separating arguments to a Texinfo command. */
/* 5228 */
ELEMENT *
handle_comma (ELEMENT *current, char **line_inout)
{
  char *line = *line_inout;
  enum element_type type;
  ELEMENT *new_arg, *e;

  abort_empty_line (&current, NULL);

  /* Register brace_command_contents or block_command_line_contents in extra
     key. */
  if (command_flags(current->parent) & CF_brace
      && command_data(current->parent->cmd).data > 0)
    {
      // 5033
      isolate_last_space (current, 0);
      register_command_arg (current, "brace_command_contents");
      remove_empty_content_arguments (current);
    }
  else
    {
      isolate_last_space (current, 0);
      if (command_flags(current->parent) & CF_block)
        {
          register_command_arg (current, "block_command_line_contents");
        }
    }

  type = current->type;
  current = current->parent;

  // 5244
  if (command_flags(current) & CF_inline)
    {
      KEY_PAIR *k;
      int expandp = 0;
      debug ("THE INLINE PART");
      k = lookup_extra_key (current, "format");
      if (!k)
        {
          KEY_PAIR *k;
          char *inline_type = 0;
          k = lookup_extra_key (current, "brace_command_contents");
          if (k)
            {
              ELEMENT *args = 0, *arg = 0;
              int i;
              args = (ELEMENT *) k->value;
              if (!args)
                goto inline_no_arg;
              if (args->contents.number == 0)
                goto inline_no_arg;
              arg = args->contents.list[0];
              if (!arg)
                goto inline_no_arg; /* Possible if registered as 'undef'. */
              /* Find text argument.
                 TODO: Should we use trim_spaces_comment_from_content instead?
                 Or use a function for this? */
              for (i = 0; i < arg->contents.number; i++)
                {
                  enum element_type t = arg->contents.list[i]->type;
                  if (arg->contents.list[i]->text.end > 0
                      && t != ET_empty_line_after_command
                      && t != ET_empty_spaces_after_command
                      && t != ET_empty_spaces_before_argument
                      && t != ET_empty_space_at_end_def_bracketed
                      && t != ET_empty_spaces_after_close_brace)
                    break;
                }
              if (i != arg->contents.number)
                {
                  inline_type = arg->contents.list[i]->text.text;
                }
            }

          debug ("INLINE <%s>", inline_type);
          if (!inline_type)
            {
inline_no_arg:
              /* Condition is missing */
              debug ("INLINE COND MISSING");
            }
          else if (current->cmd == CM_inlineraw
              || current->cmd == CM_inlinefmt
              || current->cmd == CM_inlinefmtifelse)
            {
              if (format_expanded_p (inline_type))
                {
                  expandp = 1;
                  add_extra_string (current, "expand_index", "1");
                }
              else
                expandp = 0;
            }
          else if (current->cmd == CM_inlineifset
                   || current->cmd == CM_inlineifclear)
            {
              expandp = 0;
              if (fetch_value (inline_type, strlen (inline_type)))
                expandp = 1;
              if (current->cmd == CM_inlineifclear)
                expandp = !expandp;
              if (expandp)
                add_extra_string (current, "expand_index", "1");
            }
          else
            expandp = 0;

          add_extra_string (current, "format", inline_type);

          /* Skip first argument for a false @inlinefmtifelse */
          if (!expandp && current->cmd == CM_inlinefmtifelse)
            {
              ELEMENT *e;
              int brace_count = 1;

              add_extra_string (current, "expand_index", "2");

              /* Add a dummy argument for the first argument. */
              e = new_element (ET_elided);
              add_to_element_args (current, e);
              register_command_arg (e, "brace_command_contents");

              /* Scan forward to get the next argument. */
              while (brace_count > 0)
                {
                  line += strcspn (line, "{},");
                  switch (*line)
                    {
                    case ',':
                      if (brace_count == 1)
                        {
                          line++;
                          goto inlinefmtifelse_done;
                        }
                      break;
                    case '{':
                      brace_count++;
                      break;
                    case '}':
                      brace_count--;
                      break;
                    default:
                      line = next_text ();
                      if (!line)
                        {
                          /* ERROR - unbalanced brace */
                        }
                      continue;
                    }
                  line++;
                }
inlinefmtifelse_done:
              /* Check if the second argument is missing. */
              if (brace_count == 0)
                {
                  line--; /* on '}' */
                }

              counter_dec (&count_remaining_args);
              expandp = 1;
            }
        }
      else if (current->cmd == CM_inlinefmtifelse)
        {
          /* Second art of @inlinefmtifelse when condition is true.  Discard
             second argument. */
          expandp = 0;
        }

      /* If this command is not being expanded, add a dummy argument, and
         scan forward to the closing brace. */
      if (!expandp)
        {
          ELEMENT *e;
          int brace_count = 1;
          e = new_element (ET_elided);
          add_to_element_args (current, e);
          while (brace_count > 0)
            {
              line += strcspn (line, "{}");
              switch (*line)
                {
                case '{':
                  brace_count++;
                  break;
                case '}':
                  brace_count--;
                  break;
                default:
                  line = next_text ();
                  if (!line)
                    {
                      /* ERROR - unbalanced brace */
                    }
                  continue;
                }
              line++;
            }
          current = last_args_child (current);
          line--;  /* on '}' */
          goto funexit;
        }
    }

  counter_dec (&count_remaining_args);
  new_arg = new_element (type);
  add_to_element_args (current, new_arg);
  current = new_arg;
  e = new_element (ET_empty_spaces_before_argument);
  text_append (&e->text, ""); /* See comment in parser.c:merge_text */
  add_to_element_contents (current, e);

funexit:
  *line_inout = line;
  return current;
}

/* Actions to be taken when a special character appears in the input. */
ELEMENT *
handle_separator (ELEMENT *current, char separator, char **line_inout)
{
  char *line = *line_inout;

  if (separator == '{') // 4888
    {
      current = handle_open_brace (current, &line);
    }
  else if (separator == '}') // 5007
    {
      current = handle_close_brace (current, &line);
    }
  /* If a comma is seen after all the arguments for the command have been
     read, it is included in the last argument. */
  else if (separator == ',' // 5228
           && counter_value (&count_remaining_args, current->parent) > 0)
    {
      current = handle_comma (current, &line);
    }
  else if (separator == ',' && current->type == ET_misc_line_arg
           && current->parent->cmd == CM_node) // 5297
    {
      line_warn ("superfluous arguments for node");
    }
  /* 5303 After a separator in a menu. */
  else if ((separator == ','
            || separator == '\t'
            || separator == '.')
           && current->type == ET_menu_entry_node
           || separator == ':' && current->type == ET_menu_entry_name)
    {
      ELEMENT *e;

      current = current->parent;
      e = new_element (ET_menu_entry_separator);
      text_append_n (&e->text, &separator, 1);
      add_to_element_args (current, e);

      /* Note in 'handle_menu' in menus.c, if a '.' is not followed by
         whitespace, we revert was was done here. */
    }
  else if (separator == '\f' && current->type == ET_paragraph)
    {
      ELEMENT *e;

      /* A form feed stops and restarts a paragraph. */
      current = end_paragraph (current, 0, 0);
      e = new_element (ET_empty_line);
      text_append_n (&e->text, "\f", 1);
      add_to_element_contents (current, e);
      e = new_element (ET_empty_line);
      add_to_element_contents (current, e);
    }
  else // 5322
    {
      /* Default - merge the character as usual. */
      char t[2];
      t[0] = separator;
      t[1] = '\0';
      current = merge_text (current, t);
    }

  *line_inout = line;
  return current;
}

repo_name: mthl/texinfo

license: mit


code:
/* Copyright 2014, 2015 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/>. */

#define _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

#include "tree_types.h"
#include "text.h"

/* Make sure there are LEN free bytes. */
void
text_alloc (TEXT *t, size_t len)
{
  if (t->end + len > t->space)
    {
      t->space = t->end + len;
      if (t->space < 10)
        t->space = 10;
      /* This makes a huge difference under Valgrind, is not noticable
         otherwise. */
      t->space *= 2;
      t->text = realloc (t->text, t->space);
      if (!t->text)
        abort ();
    }
}

void
text_printf (TEXT *t, char *format, ...)
{
  va_list v;
  char *s;

  va_start (v, format);
  vasprintf (&s, format, v);
  text_append (t, s);
  free (s);
  va_end (v);
}

void
text_append_n (TEXT *t, char *s, size_t len)
{
  text_alloc (t, len + 1);
  memcpy (t->text + t->end, s, len);
  t->end += len;
  t->text[t->end] = '\0';
}

void
text_append (TEXT *t, char *s)
{
  size_t len = strlen (s);
  text_append_n (t, s, len);
}

void
text_init (TEXT *t)
{
  t->end = t->space = 0;
  t->text = 0;
}


code:
/* Copyright 2014, 2015 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/>. */

void text_init (TEXT *t);
void text_append (TEXT *t, char *s);
void text_append_n (TEXT *t, char *s, size_t len);
void text_printf (TEXT *t, char *format, ...);
void text_alloc (TEXT *t, size_t len);

#define text_base(t) ((t)->space ? (t)->text : (char *) 0)


code:
/* Copyright 2010, 2011, 2012, 2013, 2014, 2015
   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 <stdlib.h>
#include <string.h>

#include "tree_types.h"
#include "tree.h"

//int element_counter;

ELEMENT *
new_element (enum element_type type)
{
  ELEMENT *e = malloc (sizeof (ELEMENT));

  //element_counter++;

  /* Zero all elements */
  memset (e, 0, sizeof (*e));

  e->type = type;
  e->cmd = CM_NONE;
  e->args.list = 0;
  e->args.space = 0;
  e->args.number = 0;
  e->contents.list = 0;
  e->contents.space = 0;
  e->contents.number = 0;
  e->parent = 0;
  e->extra = 0;

  return e;
}

void
destroy_element (ELEMENT *e)
{
  free (e->text.text);

  /* Note the pointers in these lists are not themselves freed. */
  free (e->contents.list);
  free (e->args.list);

  /* FIXME destroy extra values as well? */

  free (e);
}

/* Recursively destroy this element and all data in its descendants.
   TODO: Also extra information. */
void
destroy_element_and_children (ELEMENT *e)
{
  int i;

  for (i = 0; i < e->contents.number; i++)
    destroy_element_and_children (e->contents.list[i]);
  for (i = 0; i < e->args.number; i++)
    destroy_element_and_children (e->args.list[i]);

  destroy_element (e);
}

/* Make sure there is space for at least one more element. */
static void
reallocate_list (ELEMENT_LIST *list)
{
  if (list->number + 1 >= list->space)
    {
      list->space += 10;
      list->list = realloc (list->list, list->space * sizeof (ELEMENT *));
      if (!list->list)
        abort (); /* Out of memory. */
    }
}

void
add_to_element_contents (ELEMENT *parent, ELEMENT *e)
{
  ELEMENT_LIST *list = &parent->contents;
  reallocate_list (list);

  list->list[list->number++] = e;
  e->parent = parent;
}

/* Special purpose function for when we are only using PARENT as an
   array, and we don't want to overwrite E->parent. */
void
add_to_contents_as_array (ELEMENT *parent, ELEMENT *e)
{
  ELEMENT_LIST *list = &parent->contents;
  reallocate_list (list);

  list->list[list->number++] = e;
}

void
add_to_element_args (ELEMENT *parent, ELEMENT *e)
{
  ELEMENT_LIST *list = &parent->args;
  reallocate_list (list);

  list->list[list->number++] = e;
  e->parent = parent;
}

/* Add the element E into the contents of PARENT at index WHERE. */
void
insert_into_contents (ELEMENT *parent, ELEMENT *e, int where)
{
  ELEMENT_LIST *list = &parent->contents;
  reallocate_list (list);

  if (where < 0)
    where = list->number + where;

  if (where < 0 || where > list->number)
    abort ();

  memmove (&list->list[where + 1], &list->list[where],
           (list->number - where) * sizeof (ELEMENT *));
  list->list[where] = e;
  e->parent = parent;
  list->number++;
}

ELEMENT *
remove_from_contents (ELEMENT *parent, int where)
{
  ELEMENT_LIST *list = &parent->contents;
  ELEMENT *removed;

  if (where < 0)
    where = list->number + where;

  if (where < 0 || where > list->number)
    abort ();

  removed = list->list[where];
  memmove (&list->list[where], &list->list[where + 1],
           (list->number - (where+1)) * sizeof (ELEMENT *));
  list->number--;
  return removed;
}

ELEMENT *
pop_element_from_args (ELEMENT *parent)
{
  ELEMENT_LIST *list = &parent->args;

  return list->list[--list->number];
}

ELEMENT *
pop_element_from_contents (ELEMENT *parent)
{
  ELEMENT_LIST *list = &parent->contents;

  return list->list[--list->number];
}

ELEMENT *
last_args_child (ELEMENT *current)
{
  if (current->args.number == 0)
    return 0;

  return current->args.list[current->args.number - 1];
}

ELEMENT *
last_contents_child (ELEMENT *current)
{
  if (current->contents.number == 0)
    return 0;

  return current->contents.list[current->contents.number - 1];
}

ELEMENT *
contents_child_by_index (ELEMENT *e, int index)
{
  if (index < 0)
    index = e->contents.number + index;

  if (index < 0 || index >= e->contents.number)
    return 0;

  return e->contents.list[index];
}

ELEMENT *
args_child_by_index (ELEMENT *e, int index)
{
  if (index < 0)
    index = e->args.number + index;

  if (index < 0 || index >= e->args.number)
    return 0;

  return e->args.list[index];
}


code:
/* Copyright 2010, 2011, 2012, 2013, 2014, 2015
   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/>. */

ELEMENT *new_element (enum element_type type);
void add_to_element_contents (ELEMENT *parent, ELEMENT *e);
void add_to_contents_as_array (ELEMENT *parent, ELEMENT *e);
void add_to_element_args (ELEMENT *parent, ELEMENT *e);
void insert_into_contents (ELEMENT *parent, ELEMENT *e, int where);
ELEMENT *remove_from_contents (ELEMENT *parent, int where);
ELEMENT *last_args_child (ELEMENT *current);
ELEMENT *last_contents_child (ELEMENT *current);
ELEMENT *pop_element_from_args (ELEMENT *parent);
ELEMENT *pop_element_from_contents (ELEMENT *parent);
ELEMENT *contents_child_by_index (ELEMENT *e, int index);
ELEMENT *args_child_by_index (ELEMENT *e, int index);
void destroy_element (ELEMENT *e);
void destroy_element_and_children (ELEMENT *e);

#define element_contents_number(e) ((e)->contents.number)
#define element_args_number(e) ((e)->args.number)
#define element_text(e) (text_base (&(e)->text))


code:
/* Copyright 2010, 2011, 2012, 2013, 2014, 2015, 2016
   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 <stdlib.h>

#include "command_ids.h"
#include "element_types.h"

typedef struct TEXT {
    char *text;
    size_t space; /* Allocated bytes in 'text', including terminating null. */
    size_t end;
} TEXT;

enum extra_type {
    extra_element,
    extra_element_contents,
    extra_element_contents_array,
    extra_element_text,
    extra_index_entry,
    extra_misc_args,
    extra_node_spec,
    extra_node_spec_array,
    extra_string,
    extra_def_args,
    extra_float_type,
    extra_deleted
};

typedef struct KEY_PAIR {
    char *key;
    enum extra_type type;
    struct ELEMENT *value;
} KEY_PAIR;

typedef struct {
    enum command_id cmd;
    char *begin;
    char *end;
} INFO_ENCLOSE;

typedef struct ELEMENT_LIST {
    struct ELEMENT **list;
    size_t number;
    size_t space;
} ELEMENT_LIST;

typedef struct LINE_NR {
    int line_nr;
    char *file_name;
    char *macro;
} LINE_NR;

/* Type of a link in the route from the root of the tree to an element. */
enum route_element_type { route_uninitialized, route_contents, route_args,
    route_not_in_tree };

typedef struct {
    /* Element that contains a reference to this one. */
    struct ELEMENT *element;

    /* Index into the referring element's extra keys that is the reference. */
    int extra_index;
} PENDING_REFERENCE;

typedef struct ELEMENT {
    enum command_id cmd;
    TEXT text;
    enum element_type type;
    ELEMENT_LIST args;
    ELEMENT_LIST contents;
    struct ELEMENT *parent;
    LINE_NR line_nr;

    KEY_PAIR *extra;
    size_t extra_number;
    size_t extra_space;

    /* Set to route_not_in_tree if element not in main tree.  Also
       used for routing information along with 'index_in_parent' when
       dumping to a text stream. */
    enum route_element_type parent_type;

    /********* Used when dumping to a text stream only. ************/

    int index_in_parent;

    PENDING_REFERENCE *pending_references;
    size_t pending_number;
    size_t pending_space;

    /********* Used when building Perl tree only ********************/
    /* should be HV *hv; */
    void *hv;
} ELEMENT;

typedef struct GLOBAL_INFO {
    char *input_file_name;
    char *input_encoding_name;

    /* Elements that should be unique. */
    // 288 and Common.pm:164
    ELEMENT *settitle; /* Title of document. */
    ELEMENT *copying;
    ELEMENT *title;
    ELEMENT *titlepage;
    ELEMENT *top;
    ELEMENT *setfilename;
    ELEMENT *documentdescription;
    ELEMENT *setcontentsaftertitlepage;
    ELEMENT *setshortcontentsaftertitlepage;
    ELEMENT *novalidate;
    ELEMENT *validatemenus;
    ELEMENT *pagesizes;
    ELEMENT *fonttextsize;
    ELEMENT *footnotestyle;
    ELEMENT *setchapternewpage;
    ELEMENT *everyheading;
    ELEMENT *everyfooting;
    ELEMENT *evenheading;
    ELEMENT *evenfooting;
    ELEMENT *oddheading;
    ELEMENT *oddfooting;
    ELEMENT *everyheadingmarks;
    ELEMENT *everyfootingmarks;
    ELEMENT *evenheadingmarks;
    ELEMENT *oddheadingmarks;
    ELEMENT *evenfootingmarks;
    ELEMENT *oddfootingmarks;
    ELEMENT *shorttitlepage;

    /* Arrays of elements */
    ELEMENT footnotes;
    ELEMENT hyphenation;
    ELEMENT insertcopying;
    ELEMENT printindex;
    ELEMENT subtitle;
    ELEMENT titlefont;
    ELEMENT listoffloats;
    ELEMENT detailmenu;
    ELEMENT part;

    ELEMENT allowcodebreaks;
    ELEMENT clickstyle;
    ELEMENT codequotebacktick;
    ELEMENT codequoteundirected;
    ELEMENT contents;
    ELEMENT deftypefnnewline;
    ELEMENT documentencoding;
    ELEMENT documentlanguage;
    ELEMENT exampleindent;
    ELEMENT firstparagraphindent;
    ELEMENT frenchspacing;
    ELEMENT headings;
    ELEMENT kbdinputstyle;
    ELEMENT paragraphindent;
    ELEMENT shortcontents;
    ELEMENT urefbreakstyle;
    ELEMENT xrefautomaticsectiontitle;
} GLOBAL_INFO;

typedef struct CONF {
    int show_menu;
} CONF;

typedef struct {
    char *index_name;
    char *index_prefix;
    enum command_id index_at_command;
    enum command_id index_type_command;

    /* content->contents is the index entry text */
    ELEMENT *content;
    /* content_normalized */
    ELEMENT *command;
    ELEMENT *node;
    int number; /* Index of entry in containing index, 1-based. */
    ELEMENT *region;
} INDEX_ENTRY;

typedef struct INDEX {
    char *name;
    char *prefix;
    int in_code;

    struct INDEX *merged_in; /* Index this index is merged into, if any. */

    INDEX_ENTRY *index_entries;
    size_t index_number;
    size_t index_space;

    /********* Used when building Perl hash value ********************/
    void *hv;
    void *contained_hv;
} INDEX;

/* Used when dumping to a text stream only.  A reference to an
   index entry, in the "index_entry" extra key of an element.
   index->index_entries[entry] is the referred-to index entry. */
typedef struct {
    INDEX *index;
    int entry;
} INDEX_ENTRY_REF;

/* See parse_node_manual function. */
typedef struct {
    ELEMENT *manual_content;
    ELEMENT *node_content;
} NODE_SPEC_EXTRA;

typedef struct {
    char **labels;
    ELEMENT **elements;
    int nelements;
    int space;
} DEF_ARGS_EXTRA;

typedef struct {
    ELEMENT *content;
    char *normalized;
} EXTRA_FLOAT_TYPE;

enum error_type { error, warning };

typedef struct {
    char *macro_name;
    ELEMENT *element;
    enum command_id cmd;
    char *macrobody;
} MACRO;


code:
#! /bin/sh
# Copyright 2012 Free Software Foundation.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved.

if [ z"$srcdir" = 'z' ]; then
  srcdir='.'
fi

prove -I "$srcdir" -I "$srcdir"/maintain/lib/Unicode-EastAsianWidth/lib/ -I "$srcdir"/maintain/lib/libintl-perl/lib -I "$srcdir"/maintain/lib/Text-Unidecode/lib/ "$srcdir"/t/*.t


code:
# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl 01use.t'

#########################

use strict;

use Test::More;
use File::Spec;
BEGIN { plan tests => 2;
        if (defined($ENV{'top_srcdir'})) {unshift @INC, File::Spec->catdir($ENV{'top_srcdir'}, 'tp');} }
use Texinfo::Convert::Texinfo;
ok(1, "modules loading"); # If we made it this far, we're ok.

#########################

# Insert your test code below, the Test::More module is use()ed here so read
# its man page ( perldoc Test::More ) for help writing this test script.

use vars qw($manual_tree $manual_tree_result);

require 't/manual_tree.pl';

is (Texinfo::Convert::Texinfo::convert($manual_tree),
     $manual_tree_result, "tree_to_texi on a manually written tree");


code:
# $Id$
use strict;

use File::Spec;
BEGIN { if (defined($ENV{'top_srcdir'})) {unshift @INC, File::Spec->catdir($ENV{'top_srcdir'}, 'tp');} }

require 't/test_utils.pl';

my @test_cases = (
['commands','@@ @TeX{} @
@"e @^{@dotless{i}} @~{a} @ringaccent a
@clicksequence{File @click{} Open}@.
@email{a, b} @code{code --- -- \'\' ``}
--- -- \'\' ``. @sc{small caps}.

@cindex index entry ``in text\'\' @LaTeX{}

@majorheading majorheading @b{in b}
'],
['lettered_accent_and_spaces',
'@ringaccent    a
'],
['accent_and_dash',
'@^{a--}'],
['math',
'Simple math
@math{--{x^i}\over{\tan y}}

Math with @@-command
@math{@code{math code} a < b \sum@sub{i} q@sup{2}}

Complex
@math{ \underline{@code{math \hbox{ code }}} @\i \sum_{i}{\underline{f}}}

@@\ outside of math
@\

The 3@sup{rd}is the I@sub{r}.

'],
['empty_w',
'@w{} @w{a b}.'
],
['hyphenation',
'
@hyphenation{some-where }

'],
['block_commands','
@group
in group
@end group

@quotation warning
in quotation
@end quotation

@float a float, b float
In float

@caption{in caption

in caption}
@end float
'],
['raggedright','
@raggedright
in raggedright
@end raggedright
'],
['item_container','
@itemize +
@item i--tem +
@end itemize

@itemize @bullet
@item
 b--ullet
@end itemize

@itemize
@item no itemize argument
@end itemize
'],
['table','
@table @asis
@item table item
@itemx table itemx

In table
@end table

@vtable @samp@c samp
@c after vtable
@cindex index entry

@item vtable item
@c after item
@kindex key
@itemx itemx vtable @comment comment in itemx line

@end vtable
'],
['table_command_comment',
'@table @code@c in comment
@item item
@end table'
],
['multitable','
@multitable @columnfractions 0.6 0.4
@headitem mu--ltitable headitem @tab another tab
@item mu--ltitable item @tab multitable tab
@c comment in multitable
@item mu--ltitable item 2 @tab multitable tab 2
@cindex index entry within multitable
@item lone mu--ltitable item
@end multitable

@multitable {truc} {bidule}
@item truc @tab bidule
@end multitable
'],
['preformatted','
@example
e--xample  some

   text
@end example

@format
@vtable @code
@item item in table in format

in table in format
@end vtable
@end format
'],
['def','
@deffn {c--ategory} d--effn_name a--rguments...
d--effn
@end deffn

@deffn cate--gory de--ffn_name ar--guments @
   more args @* even more so
def--fn
@end deffn

@deffn fset @var{i} a g
@deffnx {cmde} truc {}
@deffnx Command {log trap} {}
@deffnx Command { log trap1} {}
@deffnx Command {log trap2 } {}
@deffnx cmde @b{id ule} truc
@deffnx cmde2 @b{id @samp{i} ule} truc
@deffnx{} machin
@deffnx{} {bidule machin}
@deffnx{truc} machin
@deffnx {truc} followed by a comment
@c comment
@deffnx {truc} after a deff item
@c comment
@deffnx {truc} deffnx before end deffn
Various deff lines
@end deffn
'],
['symbol_after_command',
'@table @asis
@item. dot
@item, comma
@item@@ @@
@item@{ @{
@item! exclam
@item\'\' quotes
@item: colon
@end table

@@. @{, @@@@ @?, @!: @@@{ @@\'\' @@:
'],
['nested_block_commands',
'@group
In group
@quotation
in quotation
@end quotation
@end group

@group
@quotation
in quotation
@end quotation
@end group

@group
In group
@quotation
in quotation
@end quotation
after quotation
@end group

@group

In group sp b
@quotation
in quotation
@end quotation
@end group

@group

In group sp b a

@quotation
in quotation
@end quotation
@end group

@group
In group sp a

@quotation
in quotation
@end quotation
@end group

@group
@quotation

in quotation sp b
@end quotation
@end group

@group
@quotation

in quotation sp b a

@end quotation
@end group

@group
@quotation
in quotation sp a

@end quotation
@end group

@group
@quotation
in quotation
@end quotation

After quotation sp b
@end group

@group
@quotation
in quotation
@end quotation
After quotation sp a

@end group

@group
@quotation
in quotation
@end quotation

After quotation sp b a

@end group
'],
['cartouche',
'@cartouche
in cartouche.
@end cartouche'],
['indentedblock',
'@indentedblock
  indented block

Text
@end indentedblock
'],
['comment_on_cartouche_line',
'@cartouche @c comment
in cartouche.
@end cartouche
'],
['comment_on_group_line',
'@group @c comment
in group.
@end group
'],
['insertcopying',
'@copying
License.

@quotation
You are not allowed.
@end quotation
@end copying

License:

@insertcopying
'],
['flushleft_flushright',
'
@flushleft
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ccccccccccccccc
ldskf dsflj
     lklsdlv l    lll. Bbb.

lhds
@end flushleft

@flushright
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ccccccccccccccc
ldskf dsflj
     lklsdlv l    lll. Bbb.

lhds
@end flushright
'],
['quotation_author',
'@quotation

@author First Author
quotation
@author S@~econd author
quotation continues.

@end quotation
'],
['flushleft_flushright_in_quotation',
'
@quotation
@flushleft
   f l
@end flushleft

@flushright
  f r
@end flushright
@end quotation
'],
['cartouche_in_quotation',
'@quotation
In quotation
@cartouche
In cartouche in quotation
@end cartouche
end quotation
@end quotation
'],
['cartouche_in_example',
'@example
In example
@cartouche
In cartouche in example
@end cartouche
end example
@end example
'],
['flushright_example_nested',
'@example
@flushright
in   --- flushright

@end flushright
@end example

@flushright
@example
in   --- example

flush then example
@end example
@end flushright
'],
['center',
'
@center  in center @code{in code}

'],
['titlefont_in_center',
'
@center @titlefont{A manual}
'],
['exdent_and_formats',
'@example
 Example   Hoho. @TeX{} . @* @@.

@exdent Other line
not exdented
@end example

@example
@exdent exdented  and dash --- in example
Not exdented
@exdent exdented
Not exdented
@end example

Para1.

@quotation
qsddsqkdsqkkmljsqjsqodmmdsqdsmqj dqs sdq sqd sdq dsq sdq sqd sqd sdq sdq
qsd dsq sdq dsq dssdq sdq sdq sdq dsq sdq dsq dsq sdq dsq sdqsd q
@end quotation

Para2.

Para3.

Last para.

@quotation
quotation1
quotation2
@end quotation

@quotation
quotation para 1

quotation para 2
@end quotation

@example
Example   Hoho.
@example
Nested Other line
@exdent exdented nested other line
@end example
@end example

@quotation
@exdent exdented quotation line   and dash --- in quotation
@end quotation

@quotation
Not exdented followed by exdented
@exdent  exdented quotation line
@end quotation

@quotation
@exdent  exdented quotation line
Followed by not exdented
@end quotation

@quotation
@exdent exdented quotation line oidfjslsd aez a aze azaz aeae za eaz az
@exdent azeaz az az az az
@end quotation

@quotation
@exdent exdented quotation line oidfjslsd aez a aze azaz aeae za eaz az azeaz end line
@exdent az az az az
@end quotation

@quotation
quotation line oidfjslsd aez a aze azaz aeae za eaz az zelef  aaa
@exdent ezkfjklezf
@end quotation

@quotation
@exdent very loooooooooooooooooooooooooooooooooong exdented quotation line oidfjslsd aez a aze azaz aeae za eaz az azeaz end line
@exdent az az az az
@end quotation
'],
['exdent_and_commands',
'@quotation
quotation1
@exdent in exdented protected eol @
following
@exdent in exdented a @@* @* and following
after exdented
@end quotation

@example
example
@exdent in exdented protected eol @
following
@exdent in exdented a @@* @* and following
after exdented
@end example
'],
['verbatiminclude',
'
@verbatiminclude incl-incl.txi
'],
['punctuation_in_line_commands',
'
@node node! After punc
@top top! After punc

@table @asis
@item item! After punc
@end table

@quotation quotation arg! After punc
In quotation
@exdent exdent quotation! After punc
@end quotation

@float float type! After punc, float label! After punc
In float
@shortcaption{shortcaption! After punc}
@end float

@float float type! After punc, label with caption
In float with caption.
@caption{caption! After punc}
@end float

@listoffloats float type! After punc

@cindex index! After punc

@heading heading! After punc

@titlefont{titlefont! After punc}

@center center! After punc

@printindex cp

@contents

'],
['test_tie',
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@tie{}BBBBBBBBBBB
'],
['test_today',
'@today{}.', {}, {'TEST' => 1}],
['at_after_accent_command',
'@ringaccent @@. @^@@.
'],
['setfilename_in_paragraph',
'Some text
@setfilename filename.info
'],
['invalid_U',
'@U @U{} @U{z} @U{abc} @U{9999999999999} @U{110000} @U{10FFFF}
'],
['test_errormsg',
'Some text
@errormsg{Text @~e @code{code}}
'],
['unknown_accents',
'@"X @"x @"Q @"Y

@\'z @\'q @\'r
@\'Z @\'Q @\'R

@,{H} @,{h}

@^z @^r @^t @^k
@^Z @^R @^T @^K

@`z @`m @`p

@~Z @~s
'],
);

my @test_tree = (
['arg_in_brace_no_arg_command',
'@TeX{in tex}
'],
['accents_errors',
'accent at end of line @ringaccent
accent at end of line and spaces @ringaccent
accent followed by @@ @ringaccent@.

accent character with spaces @~ following.
accent character at end of line @~
accent character followed by @@ @~@.
'],
['accent_no_closed',
'@~{e'],
['accent_no_closed_newline',
'@~{e

'],
['accent_no_closed_paragraph',
'@~{e

other para.
'],
['accent_no_closed_comment',
'@~{e @c comment
'],
['not_i_j_in_dotless',
'@dotless a bc, @^{@dotless{q}}.'],
['indicateurl_end_lines',
'@indicateurl{http://begin
continue on other line}

@indicateurl{http://begin2

cut by blank line}
'],
['spaces_after_braced_command',
'@code {b}

@samp
{v}
'],
['spaces_no_brace_after_braced_command',
'@code b
'],
['flushright_not_closed',
'@flushright

text flushed right
'],
['group_not_closed',
'@group

text in group
'],
['unknown_commands',
'@unknwon
@#
'],
['symbol_after_block',
'@html
In html
@end html. On the line.

@html
In html
@end html@@ On the line.

Verbatim:

@verbatim
In verbatim
@end verbatim;

@verbatim
In verbatim
@end verbatim@@

Table:

@table @emph
@item a
l--ine
@end table+

@table @emph
@item a
l--ine
@end table@@

Itemize:

@itemize @bullet
@item e--mph item
@end itemize\'\'

@itemize @bullet
@item e--mph item
@end itemize@@

Multitable:

@multitable @columnfractions 6 7
@headitem mu--ltitable headitem @tab another tab
@item mu--ltitable item @tab multitable tab
@end multitable^

@multitable @columnfractions 6 7
@headitem mu--ltitable headitem @tab another tab
@item mu--ltitable item @tab multitable tab
@end multitable@{

Flushleft:

@flushleft
flushleft
@end flushleft!

@flushleft
flushleft
@end flushleft@@

Copying:

@copying
Copying
@end copying*

@copying
Copying
@end copying@@
'],
['formats_titles',
'@itemize
before first itemize
@item item
@end itemize

@enumerate
before first enumerate
@item enum
@end enumerate

@table @emph
before first table
@item tab
@end table

@vtable @asis
before first vtable
@item vtab
@end vtable

@multitable @columnfractions 0.5 0.5
before first multitable
@item item multit @tab tab multit
@end multitable
'],
['formats_only_titles',
'@itemize
before first itemize
@end itemize

@enumerate
before first enumerate
@end enumerate

@table @emph
before first table
@end table

@vtable @asis
before first vtable
@end vtable

@multitable @columnfractions 0.5 0.5
before first multitable
@end multitable
'],
['formats_only_title_comments',
'@itemize
@c comment itemize
before first itemize
@end itemize

@enumerate @c comment on enumerate line
@c comment enumerate
before first enumerate
@end enumerate

@table @emph
@c comment table
before first table
@end table

@vtable @asis @c comment on vtable line
before first vtable
@end vtable

@multitable @columnfractions 0.5 0.5
@c comment multitable
before first multitable
@end multitable
'],
['delcomment', undef, {'test_file' => 'delcomment.texi'}
],
['group_beginning_and_end_on_line',
'@group within @end group
'],
);

foreach my $test (@test_cases) {
  push @{$test->[2]->{'test_formats'}}, 'plaintext';
  push @{$test->[2]->{'test_formats'}}, 'html_text';
  push @{$test->[2]->{'test_formats'}}, 'xml';
  push @{$test->[2]->{'test_formats'}}, 'docbook';
}

our ($arg_test_case, $arg_generate, $arg_debug);

run_all ('coverage', [@test_cases, @test_tree], $arg_test_case,
   $arg_generate, $arg_debug);


code:
use strict;

use File::Spec;
BEGIN { if (defined($ENV{'top_srcdir'})) {unshift @INC, File::Spec->catdir($ENV{'top_srcdir'}, 'tp');} }

require 't/test_utils.pl';

my $two_footnotes_in_nodes_text =
'@node Top
@top 2 footnotes in 2 nodes

A@footnote{Footnote Top/1

para2}

B@footnote{Footnote Top/2

para2}

@menu
* chapter::
@end menu

@node chapter
@chapter chapter

C@footnote{Footnote chapter/1

para2}

D@footnote{Footnote chapter/2

para2}

';

my @test_cases = (
['simple', '@b{in  b}.'],
['empty_second_email_argument', '@email{ a@@b.c, }'],
['email_possibilities',
'@email{--a,--b}
@email{,--b}
@email{--a}
'],
['abbr_acronym',
'@acronym{--a,an accronym}
@acronym{--a}
@acronym{--a,an accronym @comma{} @enddots{}}
@abbr{@\'E--a. @comma{}A., @\'Etude--@comma{} @b{Autonome} }
@abbr{@\'E--a. @comma{}A.}
'],
['recursive_acronym',
'@acronym{GNU, @acronym{GNU}\'s Not Unix}
'],
['uref_url',
'@uref{--a,--b}
@uref{--c}
@uref{,--d}
@uref{--e,--f,--g}
@uref{,--h,--i}
@uref{--j,,--k}
@uref{,,--l}
@url{--m,--n}
@url{--o,--p,--q}
'],
['empty_commands',
'@titlefont{}

@uref{} @abbr{} @email{} @code{}@footnote{} @~{} @dotless{} @verb{;;}
'],
['nested', 'type the characters @kbd{l o g o u t @key{RET}}.'],
['nested_args', '@xref{@@ @samp{in samp}, descr @b{in b}, S@~{e}ction,
@cite{manual}}.'],
['verb_in_xref',
'@node Top

@xref{Top, @verb{*with
verb

ggg *}}.
'],
['nested_in_sc',
'@sc{@sc{aaa @~n @aa{} @TeX{} @~{@aa{}} @footnote{In footnote}, @abbr{ABr, expl},
@verb{:in verb:}}}
', {'test_formats' => ['docbook']}],
['ref_in_style_command', '@samp{@ref{(manula)other node}}.'],
['uref_in_ref',
'@ref{(file)node, cross ref with uref @uref{href://http/myhost.com/index.html,uref1}, title with uref2 @uref{href://http/myhost.com/index2.html,uref2}, info file with uref3 @uref{href://http/myhost.com/index3.html,uref3}, printed manual with uref4 @uref{href://http/myhost.com/index4.html,uref4}}
'],
['too_much_args', '@abbr{AZE, A truc Z b, E eep}'],
['inforef_too_much_args',
'@node chapter

@inforef{chapter, cross ref name, file name, spurious arg}
'],
['footnote', 'text@footnote{in footnote.

@r{in footnote r}. } after footnote.'],
['space_in_footnote','text@footnote{ in footnote.}'],
['footnote_ending_on_empty_line','text@footnote{ in footnote.

}'],
['heading_in_footnote',
'T@footnote{
AAA
@heading H1
@cindex ind e
BBB
}
'],
['footnote_no_node',
'@top top

F@footnote{In footnote}.
'],
['space_in_image','@image{ a ,bb, cc,dd ,e }. @image{ f }.'],
['space_command_in_image', '@image{f--ile,,,@ } @image{f--ile,,,@:}'],
['end_line_in_anchor',
'@anchor{an
anchor}
'],
['space_in_anchor',
'@anchor{   anchor  name   }.
'],
['ctrl',
'
@ctrl{A}

With @ctrl{B}.
'],
['test_w',
'@w{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbb bbbbb bbb b b b b b b b b b bb .
ccc dddd}.

a a a a a a a a a a a a a a a a a a a a a a a a a a a a  @w{b a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a}

@w{}

@w{ a rr
  ggg.}

AA@w{}bbb.

FF@w{d}nnn.

aa@w{  f  f}ggg.

aa2@w{  f  f }ggg2.

aa3 @w{  f  f }ggg3.

aa4 @w{  f  f } ggg4.

aa5 @w{} ggg5.

aa6 @w{}ggg6.

aa6@w{} ggg6.

aaf @w{ } bbb.

begin and end quotations: @w{@t{`@w{}`@dots{}\'@w{}\'}}.  This
convention should be followed in Texinfo files.  TeX converts
two single quotes to left- and right-hand doubled.
'],
['test_image',
'@macro files-char
@image{f@@i--le@/.@.  a@@<"%@@@verb{:  < & @ % " :} ,,,,ext@@i--le@/.@.  a@@<"%@@@verb{:  < & @ % " :}}

In text @image{f@@i--le@/.@.  a@@<"%@@@verb{:  < & @ % " :} ,1in, 1cm, alt @b{@^{@dotless{i}}--le@/.@.}  a@*<"%@@@verb{:  < & @ % " :}  ,ext@@i--le@/.@.  a@@<"%@@@verb{:  < & @ % " :}}.
@end macro

@copying
Text in copying.
@files-char{}
@end copying

@node Top

@files-char{}

Insertcopying
@insertcopying

@example
@files-char{}
@end example

@float Image, Image with commands
@files-char{}

@caption{@files-char{}}
@end float

@listoffloats Image

', {'test_formats' => ['html']} ],
['format_on_first_footnote_line',
'Texte@footnote{@quotation
in quotation on the first footnote line
@end quotation
}.

Last text @footnote{@example
in example
@end example

}
'],
['kbd',
'kbd: @code{in kbd}.

kbd in code: @code{in first @kbd{in nested kbd}}.

kbd in kbd: @kbd{in top kbd @kbd{in nested kbd}}.

kbd in @@example:

@example
in example
@code{in example in kbd}.
@end example

kbd in @@format:

@format
in format
@code{in format in kbd}.
@end format
'],
['two_footnotes_in_nodes',
$two_footnotes_in_nodes_text,
, {'test_formats' => ['html', 'info']} ],
# FIXME no footnotes text in HTML...
['two_footnotes_in_nodes_separate',
'@footnotestyle separate
'.$two_footnotes_in_nodes_text,
, {'test_formats' => ['html', 'info']} ],
['form_feed_in_brace_commands',
'@option{ aa} @anchor{aa}something @email{aaa,  fff}@footnote{
 f1 } @footnote{  ggjj}.
', {'test_formats' => ['xml']}]
);

my @test_invalid = (
['no_brace', '@TeX and @code code and @footnote footnote ',
{'test_formats' => ['html_text']}
],
['no_brace_space_end_line',
'@code {c}.

@TeX
text
@code
Arg.'
],
['empty_line_in_anchor',
'@anchor{an

anchor}
'],
['unmatched_brace','@samp{Closing} @samp{ @} without opening macro }.}'],
['brace_opened_no_command','anchor{truc@} @anchor{truc}.
@bye'],
['caption_not_closed',
'@float Text

@caption{Not closed caption

The caption is closed as soon as @@end float is encountered, since
as much as possible is closed in order to find the @@float beginning.

@end float

@bye
'],
['code_not_closed','@code{in code'],
['anchor_not_closed',
'@anchor{my anchor

'],
['footnote_not_closed',
'AAA@footnote{ in footnote

Second paragraph.
'],
['math_not_closed','@math{\delta + 2'],
['math_bracketed_not_closed','@math{{x^i}\over{\tan '],
['math_bracketed_inside_not_closed','@math{{x^i}\over{\tan}'],
['math_bracketed_not_closed_next_line',
'@math{aa {

'],
['unknown_command_with_braces',
'Unknown thing @thing{}

Unknown macro @unknown{ first paragraph

second paragraph}

@unknown2{ first paragraph

second paragraph

third}
'],
['footnote_in_command_not_closed',
'aaa@code{in code@footnote{in footnote'],
['empty_images',
'@image{}

@image{ ,aa,bb,cc ,dd}

@image{,,,@ }
@image{,,,@:}
'],
['empty_ref',
'
@ref{} @xref{,,something}. @inforef{ }
 @inforef{ , arg}.
'],
['ref_empty_node',
'
@ref{,,,manual} @ref{,,, , Manual} @inforef{,,imanual}
@xref{ , Bidule, Truc, file, Printed}.
']
);

our ($arg_test_case, $arg_generate, $arg_debug);

foreach my $test (@test_cases) {
  push @{$test->[2]->{'test_formats'}}, 'plaintext';
  push @{$test->[2]->{'test_formats'}}, 'html_text';
}

run_all ('coverage_braces', [@test_cases, @test_invalid], $arg_test_case,
   $arg_generate, $arg_debug);

1;


code:
use strict;

use File::Spec;
BEGIN { if (defined($ENV{'top_srcdir'})) {unshift @INC, File::Spec->catdir($ENV{'top_srcdir'}, 'tp');} }

require 't/test_utils.pl';

my @test_cases = (
['simple', '@verb{.  }   .  .}'],
['multiline', '@verb{@
qdsqsddqsdsq

@}after verb.'],
['nested', '@b{in b @verb{/ in verb { } /} in b end}'],
['comments', '@verb{a comment @c comment
in a verb a}@c comment

something @verb{, in verb,} @c other comment'],
['end_of_line', 'verb at end of line @verb{

@{ in verb at end of line @c comment
and now end}after end.'],
['verb_not_closed', '@verb{.  }   . '],
['verb_not_closed_with_char',
'@verb{*

In verb

*'],
['verb_and_char_end_of_line_not_closed',
'verb, not  closed @verb{*

@bye
'],
['verb_at_end_of_line_not_closed',
 'verb at end of line @verb{

'],
);

our ($arg_test_case, $arg_generate, $arg_debug);

run_all ('verb', \@test_cases, $arg_test_case,
   $arg_generate, $arg_debug);


code:
# $Id$
use strict;

use File::Spec;
BEGIN { if (defined($ENV{'top_srcdir'})) {unshift @INC, File::Spec->catdir($ENV{'top_srcdir'}, 'tp');} }

require 't/test_utils.pl';

my @test_cases = (
[ 'text', 'text' ],
[ 'text_comment', 'text@c comment' ],
[ 'text_space_comment', ' text  @c space comment' ],
[ 'text_line', '
text

'],
[ 'comment_between_text', 'text
@c comment
end para' ],
[ 'two_lines','text l 1
text l 2
'],
[ 'no_paragraph_commands', '@*
@titlefont{title font}
@image{aa,bb,cc,dd,ee}
'],
[ 'paragraph_command','@@

@b{aaa}.

@dotless{i} also dotless i.

@U{0075} also U+0075.

@email{m1} email.

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

@footnote{lone footnote}.

'],
['image_in_paragraph',
'Para. @image{aa,bb,cc,dd,ee}. After image.
'],
['close_paragraph_command',
'para @titlefont{in titlefont} after titlefont.

p before sp
@sp 4
inew p after sp'
],
['paragraph_indent_asis',
'@paragraphindent asis

  para
  fff

@quotation
  in quotation
@end quotation

'],
['paragraph_count_and_example',
'@example
in example
@end example

After.
'],
['comment_in_quotation',
'
@quotation
Quotation @c
@end quotation

@quotation
Quotation no space@c
@end quotation
'],
['commands_in_flushright',
'@flushright

@group
in group
@end group

@quotation type
in quotation
@end quotation

@enumerate
@item item

@item other item
@end enumerate

@table @emph
@item table item
@itemx table itemx
Table text

Text.
@end table

@multitable @columnfractions 0.5 0.5
@item col1 @tab col2
@item text

in multitable
@tab

text col2
@end multitable

@float label, type

in float

@caption{in caption}
@end float

Sp:
@sp 2

@smallexample
in example
@end smallexample

@format
in format
@end format

@flushleft
in flushleft
@end flushleft

@center in center

@end flushright
'],
);

my @test_invalid = (
['paragraph_in_style_command',
'a 2 paragraphs sample @samp{in first paragraph

in second}.

a 3 paragraphs sample @samp{in first paragraph

in second

in third}.
'],
['double_style_paragraph',
'@emph{@strong{
First para.

Second para.
}
}
'],
);

foreach my $test (@test_cases) {
  push @{$test->[2]->{'test_formats'}}, 'plaintext';
  push @{$test->[2]->{'test_formats'}}, 'html_text';
}

our ($arg_test_case, $arg_generate, $arg_debug);

run_all ('paragraph', [@test_cases, @test_invalid], $arg_test_case,
   $arg_generate, $arg_debug);


code:
use Test::More;
use strict;

use File::Spec;
BEGIN { if (defined($ENV{'top_srcdir'})) {unshift @INC, File::Spec->catdir($ENV{'top_srcdir'}, 'tp');} }

require 't/test_utils.pl';

my @test_cases = (
[ 'good', '@multitable @columnfractions 0.4 .6 5.
@end multitable' ],
[ 'good_comment', '@multitable @columnfractions 0.4 .6 5.@c comment
@end multitable' ],
[ 'good_space_comment', '@multitable @columnfractions 0 1  @c space comment
@end multitable' ],
[ 'not_fraction','@multitable @columnfractions aaa
@end multitable' ],
[ 'empty', '@multitable @columnfractions
@end multitable' ],
[ 'empty_comment', '@multitable @columnfractions @c
@end multitable' ],
[ 'wrong_command', '@multitable @columnfractions @b{3.4}
@end multitable' ],
[ 'out_of_multitable', '@columnfractions 0.6 0.4 aaa' ],
[ 'something_before_columnfractions', '@multitable aaa @columnfractions 0.3 0.7
@end multitable' ]
);

our $arg_generate;
our $arg_complete;
our $arg_test_case;
our $arg_debug;
our $nr_comparisons;

ok(1);

my $test = new_test('columnfractions', $arg_generate, $arg_debug);

my @done_tests;

foreach my $test_case (@test_cases) {
  next if ($arg_test_case and $arg_test_case ne $test_case->[0]);
  if ($arg_complete) {
    $test->output_texi_file($test_case);
  } else {
    $test->test($test_case);
    push @done_tests, $test_case;
  }
}

if ($arg_generate or $arg_complete) {
  plan tests => 2;
}
else {
  plan tests => (2 + scalar(@done_tests) * $nr_comparisons);
}


code:
use strict;

use File::Spec;
BEGIN { if (defined($ENV{'top_srcdir'})) {unshift @INC, File::Spec->catdir($ENV{'top_srcdir'}, 'tp');} }

require 't/test_utils.pl';

my @test_cases = (
['simple', '@defindex idx'],
['many_lines', 'pagesizes @pagesizes 200mm,150mm following @@pagesizes arg
afourpaper @afourpaper on line following afourpaper
smallbook @smallbook on line following smallbook
headings @headings on line following headings
oddfooting @oddfooting on line following oddfooting
everyheading @everyheading on line following everyheading
everyfooting @everyfooting on line following everyfooting
evenheading @evenheading on line following evenheading
evenfooting @evenfooting on line following evenfooting
oddheading @oddheading on line following
need @need 0.1 on line following
need @need 0.1
setchapternewpage @setchapternewpage on line following setchapternewpage
raisesections @raisesections on line following raisesections
lowersections @lowersections on line following lowersections
kbdinputstyle code @kbdinputstyle code something
afourpaper @afourpaper on line following afourpaper
afourlatex @afourlatex on line following afourlatex
afourwide @afourwide on line following afourwide
paragraphindent asis @paragraphindent asis
paragraphindent 0 @paragraphindent 0
paragraphindent none @paragraphindent none
paragraphindent 4 @paragraphindent 4
firstparagraphindent none @firstparagraphindent none
exampleindent 6 @exampleindent 6 on line following exampleindent
exampleindent 6 @exampleindent 6
footnotestyle @footnotestyle end
footnotestyle @footnotestyle separate
documentencoding @documentencoding US-ascii encoding name
@documentencoding ISO-8859-1
frenchspacing @frenchspacing on
frenchspacing @frenchspacing off
fonttextsize @fonttextsize 10
everyheadingmarks @everyheadingmarks bottom on line following everyheadingmarks
@allowcodebreaks false
@allowcodebreaks true

Text line followed by finalout on the same line and another below @finalout
@finalout
Text line after the finalout followed by a fianlout @finalout
Text line after the text line followed by the finalout.

Test text after finalout
@finalout a word after finalout
Line after finalout
'],
['also_not_line',
'

page @page
noindent @noindent refill @refill something

@noindent     noindent at beginning of line
@noindent
noindent on the preceding line
  @noindent    noindent after space at beginning of line
@page         page at beginning of line
@page
After page on its own line.

page @page
noindent @noindent    refill @refill
novalidate @novalidate something

@subheading special

Only valid between iftex:
vskip @vskip
cropmarks @cropmarks

exdent @exdent line after exdent
'],
['sp',
'
Text line followed on the same line and another below @sp 1
@sp 2
Text line after the sp followed by a sp @sp 1
Text line after the text line followed by the sp.
@ifnottex
@sp 2 a number after sp
@end ifnottex
'],
['clickstyle',
'@clickstyle @result

A @click{} (result).
'],
['bye',
'@bye
'],
['text_before_and_after_bye',
'Before bye @bye on bye line

after bye
'],
['invalid_clickstyle',
'
@clickstyle @result on the same line

A @click{} (result on the same line).

@clickstyle @nocmd

A @click{} (nocmd).

@clickstyle something

A @click{} (something).
'],
['contents','
first @@contents @contents line following first content
second @@contents @contents line following second content
Third content on the following line on his own
@contents
Line following contents
shortcontents @shortcontents eol
shortcontents @shortcontents eol
summarycontents @summarycontents line following summarycontents
'],
['invalid_kbdinputstyle',
'kbdinputstyle @kbdinputstyle wrong arg on line following kbdinputstyle
'],
['kbdinputstyle',
'@macro codekbdmacro
@code{in code out of example @code{in nested code}}.
@kbd{kbd out of example}.
@code{kbd @kbd{in code}}.

in example
@example
@code{in code in example @code{in nested code}}.
@kbd{kbd in example}.
@code{kbd @kbd{in code} in example}.
@end example
@end macro

@@kbdinputstyle code

@kbdinputstyle code

@codekbdmacro{}

@@kbdinputstyle example

@kbdinputstyle example

@codekbdmacro{}

@@kbdinputstyle distinct

@kbdinputstyle distinct

@codekbdmacro{}
'],
['index_entries',
'@cindex cindex entry

@defindex truc

@trucindex index truc
'],
['command_line_in_paragraph',
'documentlanguage on its line
@documentlanguage  en
line following documentlanguage
'],
['bad_documentlanguage',
'
documentlanguage @documentlanguage  en

@documentlanguage  en @documentlanguage  en

@documentlanguage  en  after documentlanguage
'],
['empty_documentencoding',
'@documentencoding
'],
['invalid_documentencoding',
'@c this one is valid
@documentencoding us-ascii@asis{}

@documentencoding latin1@asis{a}
@documentencoding YS-ASCII
@@documentencoding @documentencoding bad encoding name
@documentencoding 1
@documentencoding %
@documentencoding @@
@documentencoding AAA@TeX{}
@documentencoding BBB@\'e
'],
['defindex_too_much_args',
'@defindex chose    machin'
],
['synindex_too_much_args',
'@syncodeindex cp fn    truc
'],
['command_not_closed_in_documentencoding',
'@documentencoding @strong{'],
['text_before_line_command',
'before title @title the title

i@node LD Version Scripts
--- @chapter LD Version Scripts

ddd @contents

@quotation
aaa @author quotation author
quotation
@end quotation
'],
['author_outside_titlepage_quotation',
'@author Some One
'],
['double_exdent',
'@example
Example   Hoho.
@example
Nested Other line
@exdent @exdent double  exdented nested other line
@end example
@end example
'],
);

my @converted_test_cases = (
['setfilename',
'@setfilename file_comment@c comment
@setfilename file_and_spaces
@setfilename file_space_comment @c comment
@setfilename @ @verb{: name :}@

'],
['definfoenclose',
'
definfoenclose phoo,//,\\  @definfoenclose phoo,//,\\

@phoo{bar}

definfoenclose phi,:,:  @definfoenclose phi,:,:

@phi{bar}

@strong{very strong}

@definfoenclose strong,(strong:,:)

@strong{ is it really strong? }
'],
['definfoenclose_with_empty_arg',
'
@definfoenclose headword, , :

My @headword{something}.

'],
['no_empty_line_between_headings',
'@top Top
@subheading Subheading
@chapter Chapter
@section Section
'],
['comments_in_text',
'
@c lone comment

Text line followed by a comment on the same line and another below @c comment
@c comment
Text line after the comment followed by a comment @comment c
Text line after the text line followed by the comment.

Comment at the end of the line @c comment
'],
['noindent_indent',
'
@indent
First para

@noindent
qsddsqkdsqkkmljsqjsqodmmdsqdsmqj dqs sdq sqd sdq dsq sdq sqd sqd sdq sdq
qsd dsq sdq dsq dssdq sdq sdq sdq dsq sdq dsq dsq sdq dsq sdqsd q

@noindent noindent in para.

@indent unneeded indent
'],
['indent_in_quotation',
'@quotation
@indent
indent in quotation
@end quotation
'],
['noindent_after_smallexample',
'To obtain.
@smallexample
$ wget \'http://savannah.gnu.org/cgi-bin/viewcvs/config/config/config.guess?rev=HEAD&content-type=text/plain\'
$ wget \'http://savannah.gnu.org/cgi-bin/viewcvs/config/config/config.sub?rev=HEAD&content-type=text/plain\'
@end smallexample
@noindent
Less recent versions are also present.
'],
['noindent_in_paragraph',
'Begin para
@noindent
after noindent.

Begin para2
@noindent
after noindent2.
'],
['empty_center',
'@center
'],
['empty_center_with_arg',
'@center @w{}
'],
['ref_in_center',
'@node Top

@center @ref{Top}

@center @ref{Top, ,title
very long}
'],
['footnote_in_center',
'@center Centered text with a footnote@footnote{This footnote
shows an important feature of the centered text.

@example
@table @asis
@item item
line
@end table
@end example
} and text after the footnote.
'],
['test_allowcodebreaks',
'@node Top

@macro testallowcodebreakspara {nr}
Out of code --- out-of-code.
@code{1aaa} @code{2aaa-} @code{-3bbb} @code{4aaa-bbb}
@code{ 5aaa-bb} @code{6aaa-bb } @code{ccc 7aaa-bbb} @code{ccc 8aaa-bbb ddd}
@code{9aaa-bbb rrr_vv}
@samp{fff-- --- minus@minus{}b aa-tt@\'eff_gg aa@r{r-oman}
anc-hor@anchor{A node\nr\}}
@end macro

@macro testallowcodebreaksexample{}

@example
@code{in-example}
@end example
@end macro

Before first allowcodebreaks
@testallowcodebreakspara{0}
@testallowcodebreaksexample{}

@allowcodebreaks false
After false
@testallowcodebreakspara{1}
@testallowcodebreaksexample{}

In w:
@w{@testallowcodebreakspara{w}
}

@example
@w{@code{in-example}}
@end example

@allowcodebreaks true
After true
@testallowcodebreakspara{2}
@testallowcodebreaksexample{}
'],
['incorrect_allowcodebreaks_argument',
'@allowcodebreaks _arg
@code{b a}
'],
['codequoteundirected_codequotebacktick',
'@macro quotes
``simple-double--three---four----\'\' `simple\' quotedblleft: @quotedblleft{} @*
code: @code{``simple-double--three---four----\'\' `simple\' quotedblleft: @quotedblleft{}} @*
asis: @asis{``simple-double--three---four----\'\' `simple\' quotedblleft: @quotedblleft{}} @*
strong: @strong{``simple-double--three---four----\'\' `simple\' quotedblleft: @quotedblleft{}} @*
kbd: @kbd{``simple-double--three---four----\'\' `simple\' quotedblleft: @quotedblleft{}} @*
samp: @samp{``simple-double--three---four----\'\' `simple\' quotedblleft: @quotedblleft{}} @*
@end macro

@macro allquotes
@quotes{}

@example
@@codequoteundirected on
@@codequotebacktick on
@end example
@codequoteundirected
@codequotebacktick
@quotes{}

@example
@@codequoteundirected off
@@codequotebacktick off
@end example
@codequoteundirected off
@codequotebacktick off
@quotes{}
@end macro

@copying
@allquotes{}
@end copying
@titlepage
Titlepage
@insertcopying
@end titlepage

@node Top
@top test quotes

@allquotes{}

@example
@allquotes{}
@end example

'],
['comment_space_command_on_line',
'@settitle Settitle @ @c settittle

@node Top  @comment @node Top
@top top element@  @comment @top

@frenchspacing on @c comment frenchspacing

@cindex index entry @  @c index entry

@heading Heading @     @c heading

@printindex cp @c printindex

@float Text, label
float
@end float @c end

@listoffloats Text @ @c listoffloats

@bye @c bye
'],
['command_in_heading_footing',
'@everyheading something @thispage @thischapternum

In text @thispage @thischapternum text.
'],
['vskip',
'@vskip 0pt plus 1filll
']
);

my %info_tests = (
  'comment_space_command_on_line' => 1,
  'setfilename' => 1,
);

my %xml_tests = (
  'definfoenclose_with_empty_arg' => 1,
  'vskip' => 1,
);

my %docbook_tests = (
  'setfilename' => 1,
  'definfoenclose' => 1,
  'no_empty_line_between_headings' => 1,
  'comments_in_text' => 1,
  'noindent_indent' => 1,
  'empty_center' => 1,
  'ref_in_center' => 1,
  'footnote_in_center' => 1,
  'codequoteundirected_codequotebacktick' => 1,
  'comment_space_command_on_line' => 1,
  'command_in_heading_footing' => 1,
);

foreach my $test (@converted_test_cases) {
  push @{$test->[2]->{'test_formats'}}, 'plaintext';
  push @{$test->[2]->{'test_formats'}}, 'html_text';
  if ($docbook_tests{$test->[0]}) {
    push @{$test->[2]->{'test_formats'}}, 'docbook';
  }
  if ($info_tests{$test->[0]}) {
    push @{$test->[2]->{'test_formats'}}, 'info';
    push @{$test->[2]->{'test_formats'}}, 'xml';
  }
  if ($xml_tests{$test->[0]}) {
    push @{$test->[2]->{'test_formats'}}, 'xml';
  }
}

our ($arg_test_case, $arg_generate, $arg_debug);

run_all ('misc_commands', [@test_cases, @converted_test_cases], $arg_test_case,
   $arg_generate, $arg_debug);


code:
use strict;

use File::Spec;
BEGIN { if (defined($ENV{'top_srcdir'})) {unshift @INC, File::Spec->catdir($ENV{'top_srcdir'}, 'tp');} }

require 't/test_utils.pl';

my @test_cases = (
['wrong_synindex',
'@defindex aaa
@synindex aaa aaa'],
['recursive_synindex',
'@defindex aaa
@defindex bbb
@synindex aaa bbb
@synindex bbb aaa'],
['complex_recursive_synindex',
'@node Top

@defindex aaa
@defindex bbb
@defindex ccc
@defindex ddd
@defindex eee

@synindex aaa bbb
@synindex bbb aaa

@synindex ccc aaa
@synindex ddd bbb
@eeeindex e before synindex

@synindex eee ddd
@synindex bbb eee

@cccindex c index
@bbbindex b entry
@aaaindex a entry
@dddindex d index
@eeeindex e index

bbb
@printindex bbb

aaa
@printindex aaa

ccc
@printindex ccc

ddd
@printindex ddd

eee
@printindex eee
'],
['unknown_index_entry',
'@someindex someindex entry.
'],
['unknown_then_known_index_entry',
'@someindex someindex entry.
@defindex some
@someindex someindex entry.
'],
['unknown_printindex',
'@printindex bidule'],
['unknown_synindex',
'@synindex c truc'],
['print_merged_index',
'@syncodeindex fn cp
@node Top
@printindex fn'],
['printindex_before_document',
'
@printindex cp
'],
);

my $index_in_footnote_text = '@node Top

@menu
* First::
@end menu

Top node@footnote{in footnote
@cindex index entry in footnote

Blah

Blih

@cindex counting entry
}

@node First

@printindex cp
';

my @test_formatted = (
['double_index_entry',
'@node Top

@cindex aaa

Text

@cindex aaa

@menu
* other node::
@end menu

@node other node,,,Top

@cindex aaa

@printindex cp
'],
['ftable_vtable',
'
@node Top

@ftable @emph
@item function1
@itemx function2x
Text
@item fun3
@end ftable

@vtable @emph
@item var1 @error{}
@itemx varx2
Text
@item var3
@end vtable

@findex function1

@printindex fn

@printindex vr

'],
['index_entries_locations',
'
@node Top

Initial paragraph.

Paragraph
@cindex entry in paragraph
end paragraph.
@cindex entry at end of paragraph.

New paragraph.

@cindex Entry before paragraph
Another paragraph.

@cindex lone entry

Last paragraph.

@printindex cp
'],
['syncode_index_print_both',
'@syncodeindex fn cp

@node Top

@cindex c---oncept
@findex f---un

Print fn

@printindex fn

Print vr

@printindex cp

'],
['index_entry_before_node',
'@node Top

@menu
* first::
* second::
@end menu

@node first,second,,Top

Para.

@cindex lone entry

@node second,,first,Top

@printindex cp
'],
['printindex_with_space_before',
'@node Top

@cindex index

Text.
@printindex cp
'],
['image_lines_count',
'@node Top

@image{count_image}

@cindex index entry

@printindex cp
'],
['image_text_lines_count',
'@node Top

@image{count_image_text}

@cindex index entry

@printindex cp
'],
['empty_index_entry',
'@node Top

@deffn {} { }
@end deffn

@printindex fn
'],
['empty_cindex_entry',
'@node Top
@cindex
'],
['empty_string_index_entry',
'@node Top

@findex @w{}

@ftable @code
@item @w{}
@end ftable

@defun {@w{}} {} {}
@defunx {@w{}} {} {}
@end defun

@printindex fn
'],
['printindex_index_entry_in_copying',
'@copying

@findex Copying this document

@defun fun arg
@defunx xf arg
Defun
@end defun

@ftable @asis
@item ftable item
@itemx xftable xitem
@end ftable

@printindex fn

@end copying

@node Top
@top top

Top.
@insertcopying

@menu
* chapter::
@end menu

@node chapter
@chapter Chapter

@insertcopying

@printindex fn

'],
['printindex_index_entry_in_copying_no_insertcopying',
'@copying

@findex Copying this document

@defun fun arg
@defunx xf arg
Defun
@end defun

@ftable @asis
@item ftable item
@itemx xftable xitem
@end ftable

@printindex fn

@end copying

@node Top
@top top

Top.

@menu
* chapter::
@end menu

@node chapter
@chapter Chapter

@printindex fn

'],
['index_entry_before_first_node',
'
@cindex before nodes

@node Top

@printindex cp
'],
['index_entry_in_footnote',
'@node Top

Top node@footnote{in footnote
@cindex index entry in footnote
}

@printindex cp
'],
['index_entry_in_footnote_separate',
'@footnotestyle separate
'.$index_in_footnote_text
],
['index_entry_in_footnote_different_node_end',
$index_in_footnote_text
],
['printindex_index_entry_in_copying_in_footnote',
'@copying

Copying@footnote{
In footnote
@cindex Copying this document

}.

@printindex cp

@end copying

@node Top
@top top

Top.
@insertcopying

@menu
* chapter::
@end menu

@node chapter
@chapter Chapter

@insertcopying

'],
['double_syncodeindex',
'@syncodeindex cp fn
@syncodeindex cp fn

@node Top

@cindex cindex entry
@findex findex entry

@printindex fn
@printindex cp
'],
['syncodeindex_to_plain',
'@syncodeindex ky cp

@node Top

@kindex --k1
@vindex --v
@cindex --c

cp index.
@printindex cp

vr index.
@printindex vr
'],
['def_syn_indices',
'
@syncodeindex cp fn

@node Top

definedx truc
@defindex truc
after

@trucindex index truc

@defcodeindex codeidx

@codeidxindex a @var{index entry} t@~e @^{@dotless{i}}

@cindex cindex entry

@syncodeindex ky pg

@kindex truc kindex
@pindex pindex codeidx

@synindex truc cp

@defindex abc
@defindex defg

@synindex abc defg
@synindex defg ky

@defgindex defg index entry

@abcindex abc index entry

pg
@printindex pg

ky
@printindex ky

truc
@printindex truc

value truc
@printindex truc

cp
@printindex cp

value cp
@printindex cp

defg
@printindex defg

abc
@printindex abc

fn
@printindex fn
'],
['default_cp_index_and_one_letter_syncodeindex',
'@node Top

@syncodeindex cp fn

@cindex --cindex
@cpindex --cpindex

@printindex fn
'],
['same_index_entry_merged_indices',
'@syncodeindex vr fn

@node Top
@top

Text.

@deffn Command mmm-mode @var{arg}
Toggle the state of MMM Mode.
@end deffn

@defvar mmm-mode
This variable represents MMM Mode.
@end defvar

@printindex fn
'],
['explicit_sort_key',
'@node Top
@top

@defindex SK

@SKindex @sortas{A} bbbbbbb (first)
@SKindex @sortas{B} aaaaaaa (second)
@SKindex @sortas{0} zero sort key
@SKindex @b{aaa @sortas{invalid nesting}}

in a reuglar para @sortas{foo}. @code{inside another @sortas{command}}.

@sortas{and cmd @b{inside}}.
@url{and cmd @sortas{inside}}.

@printindex SK
']
);

sub encoding_index_text($)
{
  my $eacutes = shift;
  return
'
@node Top
@top top

@cindex '."$eacutes
".'@cindex @"{i} @"{i}@"{i}@"{i}
@cindex @^i @^i@^i@^i@^i
@cindex a
@cindex b
@cindex c
@cindex d
@cindex e
@cindex f
@cindex g
@cindex h
@cindex i
@cindex j
@cindex k
@cindex l
@cindex m
@cindex n
@cindex o
@cindex p
@cindex q
@cindex r
@cindex s
@cindex t
@cindex u
@cindex v
@cindex w
@cindex x
@cindex y
@cindex z
@cindex A
@cindex B
@cindex @l{}
@cindex @L{}
@cindex @dotless{i} @dotless{i} (dotless)
@cindex @^{@dotless{i}} @^{@dotless{i}} (dotless)
@cindex @ss{}
@cindex @euro{}
@cindex @AE{}
@cindex @exclamdown{}
@cindex @TH{}
@cindex @DH{}
@cindex @textdegree{}
@cindex 0
@cindex 9
@cindex @quotedblleft{}
@cindex @geq{}
@cindex @comma{}
@cindex ``
@cindex `
@cindex \'
@cindex \'\'
@cindex "
@cindex @@
@cindex --
@cindex ---

@printindex cp
';
}

my @file_tests = (
['encoding_index_ascii',
'
@setfilename encoding_index_ascii.info
@documentencoding us-ascii
'.encoding_index_text('@\'e @\'e'), {}, {'ENABLE_ENCODING' => 0}
],
# \x{e9} is e with a acute
['encoding_index_latin1',
'
@setfilename encoding_index_latin1.info
@documentencoding iso-8859-1
'.encoding_index_text("\x{e9} \x{e9}"), {}, {'ENABLE_ENCODING' => 0}
],
['encoding_index_utf8',
'
@setfilename encoding_index_utf8.info
@documentencoding utf-8
'.encoding_index_text("\x{e9} \x{e9}"), {}, {'ENABLE_ENCODING' => 0}
],
['encoding_index_ascii_enable_encoding',
'
@setfilename encoding_index_ascii_enable_encoding.info
@documentencoding us-ascii
'.encoding_index_text('@\'e @\'e'), {}, {'ENABLE_ENCODING' => 1}
],
# \x{e9} is e with a acute
['encoding_index_latin1_enable_encoding',
'
@setfilename encoding_index_latin1_enable_encoding.info
@documentencoding iso-8859-1
'.encoding_index_text("\x{e9} \x{e9}"), {}, {'ENABLE_ENCODING' => 1}
],
['encoding_index_utf8_enable_encoding',
'
@setfilename encoding_index_utf8_enable_encoding.info
@documentencoding utf-8
'.encoding_index_text("\x{e9} \x{e9}"), {}, {'ENABLE_ENCODING' => 1}
],
);

foreach my $test (@test_formatted) {
  push @{$test->[2]->{'test_formats'}}, 'info';
  push @{$test->[2]->{'test_formats'}}, 'plaintext';
  push @{$test->[2]->{'test_formats'}}, 'html_text';
}

foreach my $test (@file_tests) {
  push @{$test->[2]->{'test_formats'}}, 'file_html';
  push @{$test->[2]->{'test_formats'}}, 'file_plaintext';
  push @{$test->[2]->{'test_formats'}}, 'file_info';
}

our ($arg_test_case, $arg_generate, $arg_debug);

run_all ('indices', [@test_cases, @test_formatted, @file_tests],
   $arg_test_case, $arg_generate, $arg_debug);


code:
use strict;

use File::Spec;
BEGIN { if (defined($ENV{'top_srcdir'})) {unshift @INC, File::Spec->catdir($ENV{'top_srcdir'}, 'tp');} }

require 't/test_utils.pl';

my $simple_menu_text = '
Before menu.

@menu
comment
* menu entry name : node.    description
* node name::                description new
* other entry: node,
* last::
                 description next line
                 still description

new comment
* last node entry ::
@end menu

After menu.
';

my @test_cases = (
['menu_title_before_entries',
'@node Top

@menu
Horizontal space
* subnode::
@end menu

@node subnode
'],
['simple', $simple_menu_text],
['simple_no_menu', $simple_menu_text, {'SHOW_MENU' => 0}, {'SHOW_MENU' => 0}],
['menu_entry_node',
'@menu
* (f)a1::
* (f)a2::
* (f)a3::         d
* (f)@@a@:::
@end menu
'],
['menu_entry_node_comments',
'@menu
* (f)a1c::@c c
* (f)a2c::  @c c
* (f)a3c::         d@c c
* (f) a4 ::   @c c
@end menu
'],
['menu_entry_name',
'@menu
* a: (f)b
* a: (f)b1.c,
* a: (f)b2.c,
* a: (f)b3.c,    d
* a: (f)b1..
* a : (f) b2 ..
* a.n: (f)b3..         d
* a: (f)b1.c.
* a: (f)b2.c.
* a: (f)b3.c. d
@end menu
'],
['menu_entry_name_comment',
'@menu
* a: (f)b@c c
* a: (f)b @c c
* a: (f)b1c.c,@c c
* a: (f)b2c.c,     @c c
* a: (f)b3c.c,    d@c c
@end menu
'],
['comment_on_menu_line',
'@node Top

@menu  @c
* first::
@end menu

@chapter first
@node first
'],
['invalid_info_menu_entry',
'@node Top

@menu
* (f)@asis{a:}::
* @asis{name:}: (f)node.
* name: (f)@asis{node. }. d
* name: (f)@asis{node,n}. d2
* name: (f)@asis{node	n}. d3
@end menu
'],
['no_colon_in_menu',
'@node Top

@menu
* a
@end menu
'],
['detailmenu',
'
@menu
* Copying Conditions::          Your rights.

@detailmenu
* detailmenu::
@end detailmenu
@end menu
'],
['detailmenu_in_description',
'
@menu
* Copying Conditions::          Your rights.
@detailmenu
* detailmenu::
@end detailmenu
@end menu
'],
['entry_after_detailmenu',
'@node Top

@menu
@detailmenu
* second::
@end detailmenu

Menu comment

* first::

@detailmenu
* second::
@end detailmenu
* second::
@end menu

@node first
@node second
'],
['detailmenu_on_subnodes',
'@node Top
@top top

@menu
* chapter::
* chapter 2::

@detailmenu
* section::
@end detailmenu

@detailmenu
Chap 2

* section chap 2::
* unnumberedsec::
@end detailmenu
@end menu

@node chapter
@chapter chapter

@menu
* section::
@end menu

@node section
@section section

@node chapter 2
@chapter chapter 2

@menu
* section chap 2::
* unnumberedsec::
@end menu

@node section chap 2
@section section chap 2

@node unnumberedsec
@unnumberedsec unnumberedsec
'],
['detailmenu_unknown_node',
'@node Top

@menu
@detailmenu
* unknown::
@end detailmenu
@end menu
'],
['reference_to_external_manual',
'
@node Top

@menu
* (info)::
@end menu
'],
['menu_entry_no_entry',
'@menu
* a1:
* a2:
* a1c:@c c
* a2c:  @c c
* a4@b{a}:
* a5@b{b}: @c c
@end menu
'],
['menu_entry_no_entry_location',
'@menu
* (manual):: begin description
* not an entry ?
* (manual):: an entry
  in description
* and here ?
with text.
* (last manual)::
@end menu
'],
['example_in_menu_comment',
'@menu
* entry::

@example
in

example
@end example

menu comment.

@end menu
'],
['dash_in_menu',
'
@menu
* entry---in: (manual)n---ode.         description --- in.

comment --- in.
@end menu
'],
['example_in_menu_description',
'@node Top
@top top

@menu
* (manual)::
@example
in

example
@end example

@end menu
'],
['block_commands_in_menu_description',
'@node Top
@top top

@menu
* (manual)::
@itemize

@item in item

@end itemize
@table @asis

@item table item

Text.

@end table
@html

<b> in html </b>

@end html
@verbatim

@end verbatim
@end menu
'],
['verb_in_menu_description',
'@node Top
@top top

@menu
* (manual)::
@verb{: in verb

:}

@end menu
'],
['inlineraw_in_menu_description',
'@node Top
@top top

@menu
* (manual)::
@inlineraw{html, in inline raw

end inlineraw}

@end menu
'],
['sc_in_menu',
'
@node Top

@menu
* @sc{value}:@sc{node}.   @sc{descrip tion}

@sc{menu comment}

@example
* @sc{menu-example value}:(dir)@sc{menu-example node}.   @sc{menu-example descrip tion}

@sc{menu-example comment}
@end example
@end menu

@node @sc{node}

@example
@menu
* @sc{example value}:(dir)@sc{example node}.   @sc{example descrip tion}

@sc{example comment}
@end menu
@end example

'],
['formats_in_menu',
'@node Top

@menu
* (gcc):: text
@table @asis
@item item
* table line::
@end table

* (info)::
@verbatim
some @ verb{x atim
* texinfo:: in @ verbatim
@end verbatim
* (manual2):: %
@html

<i></i>
@end html
Log$
* (truc):: @acronym{description
* acronym::}

@example
* subsubsection::
Menu comment
@end example

@end menu
'],
['menu_in_example',
'@node Top

@example
@menu
* (entry)::
@cartouche
in cartouche in description in menu in example
@end cartouche

@cartouche
in cartouche in menu comment in menu in example
@end cartouche

* (node) menu::      a node in menu
* a menu name:(other) node.
@end menu
@end example
'],
['menu_pointing_to_anchor',
'@node Top
@top top

Text

@anchor{An anchor}

@menu
* An anchor::                menu entry pointing to the anchor.
@end menu
']
);

my @test_invalid = (
['bad_beginning',
'@menu
*
*
*something::
*@code{in code}::
@end menu'
],
['menu_no_closed_after_empty_line',
'@menu
* (manual_in_menu):: desc2

'],
['menu_no_closed_in_description',
'@menu
* (manual_in_menu):: desc'
],
['menu_no_closed_in_entry',
'@menu
* a1:'
],
['menu_no_closed_entry_beginning',
'@menu
* node'
],
['menu_no_closed_star',
'@menu
* '
],
['direntry_dircategory_after_first_node',
'@node Top
@top direntry direcategory after first node

@dircategory Cat
@direntry
* in text: (in_text). in text
@end direntry
'],
['menu_in_deffn',
'@node Top

@deffn a b c
@menu
* (mynode)::
@end menu
@end deffn
'],
['multiple_menus',
'@node Top

@menu
* (a)a::
@end menu

@menu
* (b)b::
@end menu
'],
['empty_menu_entry_name',
'@node Top

@menu
* : (vvv). fff
* :aaa,
@end menu

@node aaa
']
);

foreach my $test (@test_cases) {
  $test->[3]->{'SHOW_MENU'} = 1 if (!defined($test->[3]->{'SHOW_MENU'}));
  push @{$test->[2]->{'test_formats'}}, 'plaintext';
  push @{$test->[2]->{'test_formats'}}, 'html';
  push @{$test->[2]->{'test_formats'}}, 'xml';
}

our ($arg_test_case, $arg_generate, $arg_debug);

run_all ('menu', [@test_cases, @test_invalid], $arg_test_case,
   $arg_generate, $arg_debug);