text: /* 
   * Copyright © 2008 Ryan Lortie
   * 
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   * 
   * See the included COPYING file for more information.
   */
  
  #include <string.h>
  #include <stdio.h>
  #include <glib.h>
  
  /* keep track of GString instances to make sure nothing leaks */
  static int strings_allocated;
  
  /* === the GMarkupParser functions === */
  static void
  subparser_start_element (GMarkupParseContext  *context,
                           const gchar          *element_name,
                           const gchar         **attribute_names,
                           const gchar         **attribute_values,
                           gpointer              user_data,
                           GError              **error)
  {
    g_string_append_printf (user_data, "{%s}", element_name);
  
    /* we don't like trouble... */
    if (strcmp (element_name, "trouble") == 0)
      g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
                   "we don't like trouble");
  }
  
  static void
  subparser_end_element (GMarkupParseContext  *context,
                         const gchar          *element_name,
                         gpointer              user_data,
                         GError              **error)
  {
    g_string_append_printf (user_data, "{/%s}", element_name);
  }
  
  static void
  subparser_error (GMarkupParseContext *context,
                   GError              *error,
                   gpointer             user_data)
  {
    g_string_free (user_data, TRUE);
    strings_allocated--;
  }
  
  static GMarkupParser subparser_parser =
  {
    subparser_start_element,
    subparser_end_element,
    NULL,
    NULL,
    subparser_error
  };
  
  /* convenience functions for a parser that does not
   * replay the starting tag into the subparser...
   */
  static void
  subparser_start (GMarkupParseContext *ctx)
  {
    gpointer user_data;
  
    user_data = g_string_new (NULL);
    strings_allocated++;
    g_markup_parse_context_push (ctx, &subparser_parser, user_data);
  }
  
  static char *
  subparser_end (GMarkupParseContext  *ctx,
                 GError              **error)
  {
    GString *string;
    char *result;
  
    string = g_markup_parse_context_pop (ctx);
    result = string->str;
  
    g_string_free (string, FALSE);
    strings_allocated--;
  
    if (result == NULL || result[0] == '\0')
      {
        g_free (result);
        g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
                     "got no data");
  
        return NULL;
      }
  
    return result;
  }
  
  /* convenience functions for a parser that -does-
   * replay the starting tag into the subparser...
   */
  static gboolean
  replay_parser_start (GMarkupParseContext  *ctx,
                       const char           *element_name,
                       const char          **attribute_names,
                       const char          **attribute_values,
                       GError              **error)
  {
    GError *tmp_error = NULL;
    gpointer user_data;
  
    user_data = g_string_new (NULL);
    strings_allocated++;
  
    subparser_parser.start_element (ctx, element_name,
                                    attribute_names, attribute_values,
                                    user_data, &tmp_error);
  
    if (tmp_error)
      {
        g_propagate_error (error, tmp_error);
        g_string_free (user_data, TRUE);
        strings_allocated--;
  
        return FALSE;
      }
  
    g_markup_parse_context_push (ctx, &subparser_parser, user_data);
  
    return TRUE;
  }
  
  static char *
  replay_parser_end (GMarkupParseContext  *ctx,
                     GError              **error)
  {
    GError *tmp_error = NULL;
    GString *string;
    char *result;
  
    string = g_markup_parse_context_pop (ctx);
  
    subparser_parser.end_element (ctx, g_markup_parse_context_get_element (ctx),
                                  string, &tmp_error);
  
    if (tmp_error)
      {
        g_propagate_error (error, tmp_error);
        g_string_free (string, TRUE);
        strings_allocated--;
  
        return NULL;
      }
  
    result = string->str;
  
    g_string_free (string, FALSE);
    strings_allocated--;
  
    if (result == NULL || result[0] == '\0')
      {
        g_free (result);
        g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
                     "got no data");
  
        return NULL;
      }
  
    return result;
  }
  
  
  /* === start interface between subparser and calling parser === */
  static void      subparser_start      (GMarkupParseContext  *ctx);
  static char     *subparser_end        (GMarkupParseContext  *ctx,
                                         GError              **error);
  /* === end interface between subparser and calling parser === */
  
  /* === start interface between replay parser and calling parser === */
  static gboolean  replay_parser_start  (GMarkupParseContext  *ctx,
                                         const char           *element_name,
                                         const char          **attribute_names,
                                         const char          **attribute_values,
                                         GError              **error);
  static char     *replay_parser_end    (GMarkupParseContext  *ctx,
                                         GError              **error);
  /* === end interface between replay parser and calling parser === */
  
  
  
  /* now comes our parser for the test.
   *
   * we recognise the tags <test> and <sub>.
   * <test> is ignored.
   * <sub> invokes the subparser (no replay).
   *
   * "unknown tags" are passed to the reply subparser
   * (so the unknown tag is fed to the subparser...)
   */
  static void
  start_element (GMarkupParseContext  *context,
                 const gchar          *element_name,
                 const gchar         **attribute_names,
                 const gchar         **attribute_values,
                 gpointer              user_data,
                 GError              **error)
  {
    g_string_append_printf (user_data, "<%s>", element_name);
  
    if (strcmp (element_name, "test") == 0)
      {
        /* do nothing */
      }
    else if (strcmp (element_name, "sub") == 0)
      {
        /* invoke subparser */
        subparser_start (context);
      }
    else
      {
        /* unknown tag.  invoke replay subparser */
        if (!replay_parser_start (context, element_name,
                                  attribute_names, attribute_values,
                                  error))
          return;
      }
  }
  
  static void
  end_element (GMarkupParseContext  *context,
               const gchar          *element_name,
               gpointer              user_data,
               GError              **error)
  {
    if (strcmp (element_name, "test") == 0)
      {
        /* do nothing */
      }
    else if (strcmp (element_name, "sub") == 0)
      {
        char *result;
  
        if ((result = subparser_end (context, error)) == NULL)
          return;
  
        g_string_append_printf (user_data, "<<%s>>", result);
        g_free (result);
      }
    else
      {
        char *result;
  
        if ((result = replay_parser_end (context, error)) == NULL)
          return;
  
        g_string_append_printf (user_data, "[[%s]]", result);
        g_free (result);
      }
  
    g_string_append_printf (user_data, "</%s>", element_name);
  }
  
  static GMarkupParser parser =
  {
    start_element,
    end_element
  };
  
  typedef struct
  {
    const char *markup;
    const char *result;
    const char *error_message;
  } TestCase;
  
  static void
  test (gconstpointer user_data)
  {
    const TestCase *tc = user_data;
    GMarkupParseContext *ctx;
    GString *string;
    gboolean result;
    GError *error;
  
    error = NULL;
    string = g_string_new (NULL);
    ctx = g_markup_parse_context_new (&parser, 0, string, NULL);
    result = g_markup_parse_context_parse (ctx, tc->markup,
                                           strlen (tc->markup), &error);
    if (result)
      result = g_markup_parse_context_end_parse (ctx, &error);
    g_markup_parse_context_free (ctx);
    g_assert (strings_allocated == 0);
  
    if (result)
      {
        if (tc->error_message)
          g_error ("expected failure (about '%s') passed!\n"
                   "  in: %s\n  out: %s",
                   tc->error_message, tc->markup, string->str);
      }
    else
      {
        if (!tc->error_message)
          g_error ("unexpected failure: '%s'\n"
                   "  in: %s\n  out: %s",
                   error->message, tc->markup, string->str);
  
        if (!strstr (error->message, tc->error_message))
          g_error ("failed for the wrong reason.\n"
                   "  expecting message about '%s'\n"
                   "  got message '%s'\n"
                   "  in: %s\n  out: %s",
                   tc->error_message, error->message, tc->markup, string->str);
      }
  
    if (strcmp (string->str, tc->result) != 0)
      g_error ("got the wrong result.\n"
               "  expected: '%s'\n"
               "  got: '%s'\n"
               "  input: %s",
               tc->result, string->str, tc->markup);
  
    if (error)
      g_error_free (error);
  
    g_string_free (string, TRUE);
  }
  
  TestCase test_cases[] = /* successful runs */
  {
      /* in */                    /* out */
    { "<test/>",                  "<test></test>" },
    { "<sub><foo/></sub>",        "<sub><<{foo}{/foo}>></sub>" },
    { "<sub><foo/><bar/></sub>",  "<sub><<{foo}{/foo}{bar}{/bar}>></sub>" },
    { "<foo><bar/></foo>",        "<foo>[[{foo}{bar}{/bar}{/foo}]]</foo>" },
    { "<foo><x/><y/></foo>",      "<foo>[[{foo}{x}{/x}{y}{/y}{/foo}]]</foo>" },
    { "<foo/>",                   "<foo>[[{foo}{/foo}]]</foo>" },
    { "<sub><foo/></sub><bar/>",  "<sub><<{foo}{/foo}>></sub>"
                                  "<bar>[[{bar}{/bar}]]</bar>" }
  };
  
  TestCase error_cases[] = /* error cases */
  {
      /* in */                    /* out */                       /* error */
    { "<foo><>",                  "<foo>",                        ">"},
    { "",                         "",                             "empty" },
    { "<trouble/>",               "<trouble>",                    "trouble" },
    { "<sub><trouble>",           "<sub>",                        "trouble" },
    { "<foo><trouble>",           "<foo>",                        "trouble" },
    { "<sub></sub>",              "<sub>",                        "no data" },
    { "<sub/>",                   "<sub>",                        "no data" }
  };
  
  #define add_tests(func, basename, array) \
    G_STMT_START { \
      int __add_tests_i;                                                  \
                                                                          \
      for (__add_tests_i  = 0;                                            \
           __add_tests_i < G_N_ELEMENTS (array);                          \
           __add_tests_i++)                                               \
        {                                                                 \
          char *testname;                                                 \
                                                                          \
          testname = g_strdup_printf ("%s/%d", basename, __add_tests_i);  \
          g_test_add_data_func (testname, &array[__add_tests_i], func);   \
          g_free (testname);                                              \
        }                                                                 \
    } G_STMT_END
  
  int
  main (int argc, char **argv)
  {
    g_setenv ("LC_ALL", "C", TRUE);
    g_test_init (&argc, &argv, NULL);
    add_tests (test, "/glib/markup/subparser/success", test_cases);
    add_tests (test, "/glib/markup/subparser/failure", error_cases);
    return g_test_run ();
  }
  