static gboolean
define_root_context (GtkSourceContextData *ctx_data,
		     GtkSourceLanguage    *language)
{
	gboolean result;
	gchar *id;
	GError *error = NULL;

	g_return_val_if_fail (language->priv->id != NULL, FALSE);

	id = g_strdup_printf ("%s:%s", language->priv->id, language->priv->id);
	result = _gtk_source_context_data_define_context (ctx_data, id,
							  NULL, NULL, NULL, NULL,
							  NULL, NULL,
							  GTK_SOURCE_CONTEXT_EXTEND_PARENT,
							  &error);

	if (error != NULL)
	{
		g_warning ("%s", error->message);
		g_error_free (error);
	}

	g_free (id);
	return result;
}
static gboolean
ctx_data_add_syntax_pattern (GtkSourceContextData *ctx_data,
			     GtkSourceLanguage    *language,
			     const gchar          *id,
			     const gchar          *style,
			     const gchar          *pattern_start,
			     const gchar          *pattern_end,
			     gboolean              end_at_line_end)
{
	gboolean result;
	gchar *real_id, *root_id;
	gchar *fixed_start, *fixed_end;
	GError *error = NULL;
	GtkSourceContextFlags flags = GTK_SOURCE_CONTEXT_EXTEND_PARENT;

	g_return_val_if_fail (id != NULL, FALSE);

	root_id = g_strdup_printf ("%s:%s", language->priv->id, language->priv->id);
	real_id = g_strdup_printf ("%s:%s", language->priv->id, id);

	fixed_start = fix_pattern (pattern_start, &end_at_line_end);
	fixed_end = fix_pattern (pattern_end, &end_at_line_end);

	if (end_at_line_end)
		flags |= GTK_SOURCE_CONTEXT_END_AT_LINE_END;

	result = _gtk_source_context_data_define_context (ctx_data, real_id, root_id,
							  NULL,
							  pattern_start,
							  pattern_end,
							  style,
							  NULL,
							  flags,
							  &error);

	if (error != NULL)
	{
		g_warning ("%s", error->message);
		g_error_free (error);
	}

	g_free (real_id);
	g_free (root_id);
	g_free (fixed_start);
	g_free (fixed_end);

	return result;
}
static gboolean
ctx_data_add_simple_pattern (GtkSourceContextData *ctx_data,
			     GtkSourceLanguage    *language,
			     const gchar          *id,
			     const gchar          *style,
			     const gchar          *pattern)
{
	gboolean result;
	gchar *real_id, *root_id, *fixed;
	GError *error = NULL;

	g_return_val_if_fail (id != NULL, FALSE);

	root_id = g_strdup_printf ("%s:%s", language->priv->id, language->priv->id);
	real_id = g_strdup_printf ("%s:%s", language->priv->id, id);

	fixed = fix_pattern (pattern, NULL);

	result = _gtk_source_context_data_define_context (ctx_data, real_id,
							  root_id,
							  fixed, NULL, NULL,
							  style, NULL,
							  GTK_SOURCE_CONTEXT_EXTEND_PARENT |
								GTK_SOURCE_CONTEXT_END_AT_LINE_END,
							  &error);

	if (error != NULL)
	{
		g_warning ("%s", error->message);
		g_error_free (error);
	}

	g_free (fixed);
	g_free (real_id);
	g_free (root_id);
	return result;
}
static void
handle_context_element (ParserState *parser_state)
{
	gchar *id, *parent_id, *style_ref;
	xmlChar *ref, *sub_pattern, *tmp;
	int is_empty;
	gboolean success;
	gboolean ignore_style = FALSE;
	GtkSourceContextRefOptions options = 0;
	GSList *context_classes;

	GError *tmp_error = NULL;

	g_return_if_fail (parser_state->error == NULL);

	ref = xmlTextReaderGetAttribute (parser_state->reader, BAD_CAST "ref");
	sub_pattern = xmlTextReaderGetAttribute (parser_state->reader,
						 BAD_CAST "sub-pattern");

	tmp = xmlTextReaderGetAttribute (parser_state->reader, BAD_CAST "ignore-style");
	if (tmp != NULL && str_to_bool (tmp))
		ignore_style = TRUE;
	xmlFree (tmp);

	tmp = xmlTextReaderGetAttribute (parser_state->reader, BAD_CAST "style-ref");
	if (tmp == NULL || id_is_decorated ((gchar*) tmp, NULL))
		style_ref = g_strdup ((gchar*) tmp);
	else
		style_ref = decorate_id (parser_state, (gchar*) tmp);
	xmlFree (tmp);

	if (ignore_style && ref == NULL)
	{
		g_set_error (&parser_state->error,
			     PARSER_ERROR,
			     PARSER_ERROR_WRONG_STYLE,
			     "ignore-style used not in a reference to context");

		xmlFree (ref);
		g_free (style_ref);

		return;
	}

	if (ignore_style)
	{
		options |= GTK_SOURCE_CONTEXT_IGNORE_STYLE;

		if (style_ref != NULL)
			g_warning ("in file %s: style-ref and ignore-style used simultaneously",
				   parser_state->filename);
	}

	/* XXX */
	if (!ignore_style && style_ref != NULL &&
	    g_hash_table_lookup (parser_state->styles_mapping, style_ref) == NULL)
	{
		g_warning ("in file %s: style '%s' not defined", parser_state->filename, style_ref);
	}

	context_classes = parse_classes (parser_state);

	if (ref != NULL)
	{
		tmp = xmlTextReaderGetAttribute (parser_state->reader, BAD_CAST "original");
		if (tmp != NULL && str_to_bool (tmp))
			options |= GTK_SOURCE_CONTEXT_REF_ORIGINAL;
		xmlFree (tmp);

		if (style_ref != NULL)
			options |= GTK_SOURCE_CONTEXT_OVERRIDE_STYLE;

		add_ref (parser_state,
		         (gchar*) ref,
		         options,
		         style_ref,
		         &tmp_error);
	}
	else
	{
		char *freeme = NULL;

		tmp = xmlTextReaderGetAttribute (parser_state->reader, BAD_CAST "id");
		if (tmp == NULL)
		{
			freeme = generate_new_id (parser_state);
			tmp = xmlStrdup (BAD_CAST freeme);
		}

		if (id_is_decorated ((gchar*) tmp, NULL))
			id = g_strdup ((gchar*) tmp);
		else
			id = decorate_id (parser_state, (gchar*) tmp);

		g_free (freeme);
		xmlFree (tmp);

		if (parser_state->ctx_data != NULL)
		{
			if (sub_pattern != NULL)
			{
				create_sub_pattern (parser_state,
				                    id,
				                    (gchar *)sub_pattern,
				                    style_ref,
				                    context_classes,
				                    &tmp_error);
			}
			else
			{
				parent_id = g_queue_peek_head (
						parser_state->curr_parents);

				is_empty = xmlTextReaderIsEmptyElement (
						parser_state->reader);

				if (is_empty)
					success = _gtk_source_context_data_define_context (parser_state->ctx_data,
											   id,
											   parent_id,
											   "$^",
											   NULL,
											   NULL,
											   NULL,
											   NULL,
											   0,
											   &tmp_error);
				else
					success = create_definition (parser_state,
					                             id,
					                             parent_id,
					                             style_ref,
					                             context_classes,
					                             &tmp_error);

				if (success && !is_empty)
				{
					/* Push the new context in the curr_parents
					 * stack only if other contexts can be
					 * defined inside it */
					g_queue_push_head (parser_state->curr_parents,
							   g_strdup (id));
				}
			}
		}

		g_free (id);
	}

	g_slist_free_full (context_classes, (GDestroyNotify)gtk_source_context_class_free);

	g_free (style_ref);
	xmlFree (sub_pattern);
	xmlFree (ref);

	if (tmp_error != NULL)
		g_propagate_error (&parser_state->error, tmp_error);
}
static gboolean
create_definition (ParserState *parser_state,
		   gchar       *id,
		   gchar       *parent_id,
		   gchar       *style,
		   GSList      *context_classes,
		   GError     **error)
{
	gchar *match = NULL, *start = NULL, *end = NULL;
	gchar *prefix = NULL, *suffix = NULL;
	GtkSourceContextFlags flags;

	xmlNode *context_node, *child;

	GString *all_items = NULL;

	GRegexCompileFlags match_flags = 0, start_flags = 0, end_flags = 0;

	GError *tmp_error = NULL;

	g_assert (parser_state->ctx_data != NULL);

	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	flags = get_context_flags (parser_state);

	DEBUG (g_message ("creating context %s, child of %s", id, parent_id ? parent_id : "(null)"));

	/* Fetch the content of the sublements using the tree API on
	 * the current node */
	context_node = xmlTextReaderExpand (parser_state->reader);

	/* The file should be validated so this should not happen */
	g_assert (context_node != NULL);

	for (child = context_node->children; child != NULL; child = child->next)
	{
		if (child->type != XML_ELEMENT_NODE)
			continue;

		/* FIXME: add PCRE_EXTRA support in EggRegex
		 * Huh? */

		g_assert (child->name);
		if (xmlStrcmp (BAD_CAST "match", child->name) == 0
				&& child->children != NULL)
		{
			/* <match> */
			match = g_strdup ((gchar *)child->children->content);
			match_flags = get_regex_flags (child, parser_state->regex_compile_flags);
		}
		else if (xmlStrcmp (BAD_CAST "start", child->name) == 0)
		{
			/* <start> */
			if (child->children != NULL)
			{
				start = g_strdup ((gchar *)child->children->content);
			}
			else
			{
				/* If the <start> element is present but
				 * has no content use an empty string */
				start = g_strdup ("");
			}
			start_flags = get_regex_flags (child, parser_state->regex_compile_flags);
		}
		else if (xmlStrcmp (BAD_CAST "end", child->name) == 0)
		{
			/* <end> */
			if (child->children != NULL)
				end = g_strdup ((gchar *)child->children->content);
			else
			{
				/* If the <end> element is present but
				 * has no content use an empty string */
				end = g_strdup ("");
			}
			end_flags = get_regex_flags (child, parser_state->regex_compile_flags);
		}
		else if (xmlStrcmp (BAD_CAST "prefix", child->name) == 0)
		{
			/* <prefix> */
			if (child->children != NULL)
				prefix = g_strdup ((gchar*) child->children->content);
			else
				prefix = g_strdup ("");
		}
		else if (xmlStrcmp (BAD_CAST "suffix", child->name) == 0)
		{
			/* <suffix> */
			if (child->children != NULL)
				suffix = g_strdup ((gchar*) child->children->content);
			else
				suffix = g_strdup ("");
		}
		else if (xmlStrcmp (BAD_CAST "keyword", child->name) == 0 &&
			 child->children != NULL)
		{
			/* FIXME: how to specify regex options for keywords?
			 * They can be specified in prefix, so it's not really
			 * important, but would be nice (case-sensitive). */

			/* <keyword> */
			if (all_items == NULL)
			{
				all_items = g_string_new (NULL);

				if (prefix != NULL)
					g_string_append (all_items, prefix);
				else
					g_string_append (all_items,
							 parser_state->opening_delimiter);

				g_string_append (all_items, "(");
				g_string_append (all_items, (gchar*) child->children->content);
			}
			else
			{
				g_string_append (all_items, "|");
				g_string_append (all_items, (gchar*) child->children->content);
			}
		}
	}

	if (all_items != NULL)
	{
		g_string_append (all_items, ")");

		if (suffix != NULL)
			g_string_append (all_items, suffix);
		else
			g_string_append (all_items,
					 parser_state->closing_delimiter);

		match = g_string_free (all_items, FALSE);
		match_flags = parser_state->regex_compile_flags;
	}

	DEBUG (g_message ("start: '%s'", start ? start : "(null)"));
	DEBUG (g_message ("end: '%s'", end ? end : "(null)"));
	DEBUG (g_message ("match: '%s'", match ? match : "(null)"));


	if (tmp_error == NULL && start != NULL)
	{
		gchar *tmp = start;
		start = expand_regex (parser_state, start, start_flags,
				      TRUE, FALSE, &tmp_error);
		g_free (tmp);
	}
	if (tmp_error == NULL && end != NULL)
	{
		gchar *tmp = end;
		end = expand_regex (parser_state, end, end_flags,
				    TRUE, FALSE, &tmp_error);
		g_free (tmp);
	}
	if (tmp_error == NULL && match != NULL)
	{
		gchar *tmp = match;
		match = expand_regex (parser_state, match, match_flags,
				      TRUE, FALSE, &tmp_error);
		g_free (tmp);
	}

	if (tmp_error == NULL)
	{
		_gtk_source_context_data_define_context (parser_state->ctx_data,
							 id,
							 parent_id,
							 match,
							 start,
							 end,
							 style,
							 context_classes,
							 flags,
							 &tmp_error);
	}

	g_free (match);
	g_free (start);
	g_free (end);
	g_free (prefix);
	g_free (suffix);

	if (tmp_error != NULL)
	{
		g_propagate_error (error, tmp_error);
		return FALSE;
	}

	return TRUE;
}