medit/moo/mooutils/moomarkup.c

669 lines
22 KiB
C

/*
* mooutils/moomarkup.c
*
* Copyright (C) 2004-2005 by Yevgen Muntyan <muntyan@math.tamu.edu>
*
* 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 2 of the License, or
* (at your option) any later version.
*
* See COPYING file that comes with this distribution.
*/
#include "mooutils/moomarkup.h"
#include <string.h>
#include <glib.h>
GType moo_markup_doc_get_type (void)
{
static GType type = 0;
if (type == 0)
type = g_boxed_type_register_static ("MooMarkupDoc",
(GBoxedCopyFunc)moo_markup_doc_ref,
(GBoxedFreeFunc)moo_markup_doc_unref);
return type;
}
typedef void (*markup_start_element_func) (GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data,
GError **error);
typedef void (*markup_end_element_func) (GMarkupParseContext *context,
const gchar *element_name,
gpointer user_data,
GError **error);
typedef void (*markup_text_func) (GMarkupParseContext *context,
const gchar *text,
gsize text_len,
gpointer user_data,
GError **error);
typedef void (*markup_passthrough_func) (GMarkupParseContext *context,
const gchar *passthrough_text,
gsize text_len,
gpointer user_data,
GError **error);
typedef struct {
MooMarkupDoc *doc;
MooMarkupNode *current;
} ParserState;
#define ELEMENT_TEXT_SEPARATOR " "
#define BUFSIZE 1024
static MooMarkupDoc *moo_markup_doc_new_priv (const char *name);
static MooMarkupNode *moo_markup_element_new (MooMarkupDoc *doc,
MooMarkupNode *parent,
const char *name,
const char **attribute_names,
const char **attribute_values);
static MooMarkupNode *moo_markup_text_node_new (MooMarkupNodeType type,
MooMarkupDoc *doc,
MooMarkupNode *parent,
const char *text,
gsize text_len);
static void add_node (MooMarkupDoc *doc,
MooMarkupNode *parent,
MooMarkupNode *node);
static void moo_markup_doc_unref_private (MooMarkupDoc *doc);
static void moo_markup_element_free (MooMarkupElement *node);
static void moo_markup_text_node_free (MooMarkupNode *node);
static void moo_markup_node_free (MooMarkupNode *node);
static void collect_text_content (MooMarkupElement *node);
static void moo_markup_text_node_add_text (MooMarkupText *node,
const char *text,
gsize text_len);
static void moo_markup_element_print (MooMarkupElement *node,
GString *dest);
static void moo_markup_text_node_print (MooMarkupNode *node,
GString *dest);
static void start_element (GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
ParserState *state,
GError **error);
static void end_element (GMarkupParseContext *context,
const gchar *element_name,
ParserState *state,
GError **error);
static void text (GMarkupParseContext *context,
const gchar *text,
gsize text_len,
ParserState *state,
GError **error);
static void passthrough (GMarkupParseContext *context,
const gchar *passthrough_text,
gsize text_len,
ParserState *state,
GError **error);
MooMarkupDoc *moo_markup_parse_memory (const char *buffer,
int size,
GError **error)
{
GError *err = NULL;
GMarkupParser parser = {(markup_start_element_func)start_element,
(markup_end_element_func)end_element,
(markup_text_func)text,
(markup_passthrough_func)passthrough,
NULL};
MooMarkupDoc *doc;
ParserState state;
GMarkupParseContext *context;
if (size < 0) size = strlen (buffer);
doc = moo_markup_doc_new_priv (NULL);
state.doc = doc;
state.current = MOO_MARKUP_NODE (doc);
context = g_markup_parse_context_new (&parser, (GMarkupParseFlags)0, &state, NULL);
if (!g_markup_parse_context_parse (context, buffer, size, &err) ||
!g_markup_parse_context_end_parse (context, &err))
{
if (err)
{
if (error) *error = err;
else g_error_free (err);
}
g_markup_parse_context_free (context);
moo_markup_doc_unref (doc);
return NULL;
}
g_markup_parse_context_free (context);
return doc;
}
MooMarkupDoc *moo_markup_parse_file (const char *filename,
GError **error)
{
GMarkupParser parser = {(markup_start_element_func)start_element,
(markup_end_element_func)end_element,
(markup_text_func)text,
(markup_passthrough_func)passthrough,
NULL};
GError *err = NULL;
MooMarkupDoc *doc;
ParserState state;
GMarkupParseContext *context;
GIOChannel *file;
file = g_io_channel_new_file (filename, "r", &err);
if (!file)
{
if (err)
{
if (error) *error = err;
else g_error_free (err);
}
return NULL;
}
doc = moo_markup_doc_new_priv (filename);
state.doc = doc;
state.current = MOO_MARKUP_NODE (doc);
context = g_markup_parse_context_new (&parser, (GMarkupParseFlags)0, &state, NULL);
while (TRUE)
{
char buf[BUFSIZE];
gsize len = 0;
GIOStatus status = g_io_channel_read_chars (file, buf, BUFSIZE, &len, &err);
if (status == G_IO_STATUS_ERROR)
{
if (err)
{
if (error) *error = err;
else g_error_free (err);
}
g_markup_parse_context_free (context);
moo_markup_doc_unref (doc);
return NULL;
}
if (len > 0)
{
if (!g_markup_parse_context_parse (context, buf, len, &err))
{
if (err)
{
if (error) *error = err;
else g_error_free (err);
}
g_markup_parse_context_free (context);
moo_markup_doc_unref (doc);
return NULL;
}
}
if (status == G_IO_STATUS_EOF)
{
if (!g_markup_parse_context_end_parse (context, &err))
{
if (err)
{
if (error) *error = err;
else g_error_free (err);
}
g_markup_parse_context_free (context);
moo_markup_doc_unref (doc);
return NULL;
}
break;
}
}
g_markup_parse_context_free (context);
return doc;
}
static void start_element (G_GNUC_UNUSED GMarkupParseContext *ctx,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
ParserState *state,
G_GNUC_UNUSED GError **error)
{
MooMarkupNode *elm = moo_markup_element_new (state->doc,
state->current,
element_name,
attribute_names,
attribute_values);
state->current = elm;
}
static void end_element (G_GNUC_UNUSED GMarkupParseContext *ctx,
G_GNUC_UNUSED const gchar *elm,
ParserState *state,
G_GNUC_UNUSED GError **error)
{
g_assert (state->current->type == MOO_MARKUP_ELEMENT_NODE);
collect_text_content (MOO_MARKUP_ELEMENT (state->current));
g_assert (state->current->parent != NULL);
state->current = state->current->parent;
}
static void text (G_GNUC_UNUSED GMarkupParseContext *ctx,
const gchar *text,
gsize text_len,
ParserState *state,
G_GNUC_UNUSED GError **error)
{
if (MOO_MARKUP_IS_TEXT (state->current->last))
moo_markup_text_node_add_text (MOO_MARKUP_TEXT (state->current->last),
text, text_len);
else
moo_markup_text_node_new (MOO_MARKUP_TEXT_NODE,
state->doc, state->current,
text, text_len);
}
static void passthrough (G_GNUC_UNUSED GMarkupParseContext *ctx,
const gchar *passthrough_text,
gsize text_len,
ParserState *state,
G_GNUC_UNUSED GError **error)
{
if (MOO_MARKUP_IS_COMMENT (state->current->last))
moo_markup_text_node_add_text (MOO_MARKUP_COMMENT (state->current->last),
passthrough_text, text_len);
else
moo_markup_text_node_new (MOO_MARKUP_COMMENT_NODE,
state->doc, state->current,
passthrough_text, text_len);
}
static MooMarkupDoc *moo_markup_doc_new_priv (const char *name)
{
MooMarkupDoc *doc = g_new0 (MooMarkupDoc, 1);
doc->type = MOO_MARKUP_DOC_NODE;
doc->name = g_strdup (name);
doc->doc = doc;
doc->ref_count = 1;
return doc;
}
static MooMarkupNode *moo_markup_element_new (MooMarkupDoc *doc,
MooMarkupNode *parent,
const char *name,
const char **attribute_names,
const char **attribute_values)
{
MooMarkupElement *elm = g_new0 (MooMarkupElement, 1);
add_node (doc, parent, MOO_MARKUP_NODE (elm));
elm->type = MOO_MARKUP_ELEMENT_NODE;
elm->name = g_strdup (name);
elm->attr_names = g_strdupv ((char**)attribute_names);
elm->attr_vals = g_strdupv ((char**)attribute_values);
for (elm->n_attrs = 0; attribute_names[elm->n_attrs]; ++elm->n_attrs) ;
return MOO_MARKUP_NODE (elm);
}
static MooMarkupNode *moo_markup_text_node_new(MooMarkupNodeType type,
MooMarkupDoc *doc,
MooMarkupNode *parent,
const char *text,
gsize text_len)
{
MooMarkupText *node;
g_assert (type == MOO_MARKUP_TEXT_NODE || type == MOO_MARKUP_COMMENT_NODE);
node = g_new0 (MooMarkupText, 1);
add_node (doc, parent, MOO_MARKUP_NODE (node));
node->type = type;
if (type == MOO_MARKUP_TEXT_NODE)
node->name = g_strdup ("TEXT");
else
node->name = g_strdup ("COMMENT");
node->text = g_strndup (text, text_len);
node->size = text_len;
return MOO_MARKUP_NODE (node);
}
static void add_node (MooMarkupDoc *doc,
MooMarkupNode *parent,
MooMarkupNode *node)
{
node->doc = doc;
node->parent = parent;
if (parent->last)
{
parent->last->next = node;
node->prev = parent->last;
parent->last = node;
}
else
{
parent->children = node;
parent->last = node;
}
}
static void moo_markup_text_node_add_text (MooMarkupText *node,
const char *text,
gsize text_len)
{
char *tmp = (char*)g_memdup (node->text, sizeof(char) * (node->size + text_len));
g_memmove (tmp + node->size, text, sizeof(char) * text_len);
tmp[node->size + text_len] = 0;
g_free (node->text);
node->text = tmp;
}
static void collect_text_content (MooMarkupElement *node)
{
MooMarkupNode *child;
char *text = NULL;
for (child = node->children; child != NULL; child = child->next)
{
if (MOO_MARKUP_IS_TEXT (child))
{
if (text)
text = g_strconcat (text, ELEMENT_TEXT_SEPARATOR,
MOO_MARKUP_TEXT(child)->text, NULL);
else
text = g_strndup (MOO_MARKUP_TEXT(child)->text,
MOO_MARKUP_TEXT(child)->size);
}
}
node->content = text;
}
static void moo_markup_node_free (MooMarkupNode *node)
{
MooMarkupNode *child;
g_return_if_fail (node != NULL);
g_free (node->name);
for (child = node->children; child != NULL; child = child->next)
moo_markup_node_free (child);
switch (node->type)
{
case MOO_MARKUP_DOC_NODE:
moo_markup_doc_unref_private (MOO_MARKUP_DOC (node));
break;
case MOO_MARKUP_ELEMENT_NODE:
moo_markup_element_free (MOO_MARKUP_ELEMENT (node));
break;
case MOO_MARKUP_TEXT_NODE:
case MOO_MARKUP_COMMENT_NODE:
moo_markup_text_node_free (node);
break;
default:
g_assert_not_reached ();
}
}
static void moo_markup_element_free (MooMarkupElement *node)
{
g_free (node->content);
g_strfreev (node->attr_names);
g_strfreev (node->attr_vals);
}
static void moo_markup_text_node_free (MooMarkupNode *node)
{
g_assert (node->type == MOO_MARKUP_TEXT_NODE || node->type == MOO_MARKUP_COMMENT_NODE);
g_free (((MooMarkupText*)node)->text);
}
void moo_markup_doc_unref (MooMarkupDoc *doc)
{
g_return_if_fail (MOO_MARKUP_IS_DOC (doc));
if (--(doc->ref_count)) return;
moo_markup_node_free (MOO_MARKUP_NODE (doc));
}
MooMarkupDoc *moo_markup_doc_ref (MooMarkupDoc *doc)
{
g_return_val_if_fail (MOO_MARKUP_IS_DOC (doc), NULL);
++(doc->ref_count);
return doc;
}
static void moo_markup_doc_unref_private (G_GNUC_UNUSED MooMarkupDoc *doc)
{
}
char *moo_markup_doc_get_string (MooMarkupDoc *doc)
{
MooMarkupNode *child;
GString *str = g_string_new ("");
for (child = doc->children; child != NULL; child = child->next)
switch (child->type)
{
case MOO_MARKUP_ELEMENT_NODE:
moo_markup_element_print (MOO_MARKUP_ELEMENT (child), str);
break;
case MOO_MARKUP_TEXT_NODE:
case MOO_MARKUP_COMMENT_NODE:
moo_markup_text_node_print (child, str);
break;
default:
g_assert_not_reached ();
}
return g_string_free (str, FALSE);
}
static void moo_markup_element_print (MooMarkupElement *node,
GString *str)
{
guint i;
g_string_append_printf (str, "<%s", node->name);
for (i = 0; i < node->n_attrs; ++i)
g_string_append_printf (str, " %s=\"%s\"",
node->attr_names[i],
node->attr_vals[i]);
if (node->children)
{
MooMarkupNode *child;
g_string_append (str, ">");
for (child = node->children; child != NULL; child = child->next)
switch (child->type)
{
case MOO_MARKUP_ELEMENT_NODE:
moo_markup_element_print (MOO_MARKUP_ELEMENT (child), str);
break;
case MOO_MARKUP_TEXT_NODE:
case MOO_MARKUP_COMMENT_NODE:
moo_markup_text_node_print (child, str);
break;
default:
g_assert_not_reached ();
}
g_string_append_printf (str, "</%s>", node->name);
}
else
g_string_append (str, "/>");
}
static void moo_markup_text_node_print (MooMarkupNode *node,
GString *str)
{
MooMarkupText *text = (MooMarkupText*) node;
g_string_append (str, text->text);
}
static MooMarkupElement *get_element (MooMarkupNode *node,
char **path)
{
MooMarkupNode *child;
if (!path || !path[0] || !path[0][0]) return MOO_MARKUP_ELEMENT (node);
/* TODO: fix this */
for (child = node->children; child; child = child->next)
{
if (MOO_MARKUP_IS_ELEMENT (child)) {
const char *name = moo_markup_get_prop (MOO_MARKUP_ELEMENT (child),
"name");
if (name && !strcmp (path[0], name))
return get_element (child, ++path);
}
}
return NULL;
}
MooMarkupElement *moo_markup_get_element (MooMarkupNode *node,
const char *path)
{
char **p;
MooMarkupElement *elm;
g_return_val_if_fail (path != NULL, NULL);
p = g_strsplit (path, "/", 0);
elm = get_element (node, p);
g_strfreev (p);
return elm;
}
const char *moo_markup_get_prop (MooMarkupElement *node,
const char *prop_name)
{
guint i;
g_return_val_if_fail (node != NULL && prop_name != NULL, NULL);
for (i = 0 ; i < node->n_attrs; ++i)
if (!strcmp (prop_name, node->attr_names[i]))
return node->attr_vals[i];
return NULL;
}
MooMarkupElement*moo_markup_get_root_element(MooMarkupDoc *doc,
const char *name)
{
MooMarkupNode *child;
for (child = doc->children; child; child = child->next)
{
MooMarkupElement *elm;
if (!MOO_MARKUP_IS_ELEMENT (child)) continue;
elm = MOO_MARKUP_ELEMENT (child);
if (!name || !strcmp (elm->name, name))
return elm;
}
return NULL;
}
#ifdef MOO_MARKUP_NODE
#undef MOO_MARKUP_NODE
#endif
#ifdef MOO_MARKUP_DOC
#undef MOO_MARKUP_DOC
#endif
#ifdef MOO_MARKUP_ELEMENT
#undef MOO_MARKUP_ELEMENT
#endif
#ifdef MOO_MARKUP_TEXT
#undef MOO_MARKUP_TEXT
#endif
#ifdef MOO_MARKUP_COMMENT
#undef MOO_MARKUP_COMMENT
#endif
MooMarkupNode *MOO_MARKUP_NODE (gpointer n)
{
MooMarkupNode *node = (MooMarkupNode*) n;
g_return_val_if_fail (n != NULL, NULL);
g_return_val_if_fail (node->type == MOO_MARKUP_DOC_NODE ||
node->type == MOO_MARKUP_ELEMENT_NODE ||
node->type == MOO_MARKUP_TEXT_NODE ||
node->type == MOO_MARKUP_COMMENT_NODE,
NULL);
return node;
}
MooMarkupDoc *MOO_MARKUP_DOC (gpointer n)
{
MooMarkupNode *node = (MooMarkupNode*) n;
g_return_val_if_fail (n != NULL, NULL);
g_return_val_if_fail (node->type == MOO_MARKUP_DOC_NODE, NULL);
return (MooMarkupDoc*) n;
}
MooMarkupElement *MOO_MARKUP_ELEMENT (gpointer n)
{
MooMarkupNode *node = (MooMarkupNode*) n;
g_return_val_if_fail (n != NULL, NULL);
g_return_val_if_fail (node->type == MOO_MARKUP_ELEMENT_NODE, NULL);
return (MooMarkupElement*) n;
}
MooMarkupText *MOO_MARKUP_TEXT (gpointer n)
{
MooMarkupNode *node = (MooMarkupNode*) n;
g_return_val_if_fail (n != NULL, NULL);
g_return_val_if_fail (node->type == MOO_MARKUP_TEXT_NODE, NULL);
return (MooMarkupText*) n;
}
MooMarkupComment *MOO_MARKUP_COMMENT (gpointer n)
{
MooMarkupNode *node = (MooMarkupNode*) n;
g_return_val_if_fail (n != NULL, NULL);
g_return_val_if_fail (node->type == MOO_MARKUP_COMMENT_NODE, NULL);
return (MooMarkupComment*) n;
}