/* * mooutils/moomarkup.c * * Copyright (C) 2004-2006 by Yevgen Muntyan * * 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 "mooutils/mooutils-fs.h" #include "mooutils/mooutils-gobject.h" #include "mooutils/moocompat.h" #include #include 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 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) { 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, error) || !g_markup_parse_context_end_parse (context, error)) { 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) { char *content; MooMarkupDoc *doc; g_return_val_if_fail (filename != NULL, NULL); if (!g_file_get_contents (filename, &content, NULL, error)) return NULL; doc = moo_markup_parse_memory (content, -1, error); g_free (content); 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); g_assert (elm->parent == state->current); 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; } MooMarkupDoc* moo_markup_doc_new (const char *name) { return moo_markup_doc_new_priv (name); } 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); if (elm->attr_names) for (elm->n_attrs = 0; attribute_names[elm->n_attrs]; ++elm->n_attrs) ; else elm->n_attrs = 0; 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) { g_assert (MOO_MARKUP_IS_DOC (doc)); g_assert (parent != NULL); g_assert (node != NULL); node->doc = doc; node->parent = parent; if (parent->last) { parent->last->next = node; node->prev = parent->last; parent->last = node; } else { g_assert (parent->children == NULL); g_assert (parent->last == NULL); parent->children = node; parent->last = node; } } static void moo_markup_text_node_add_text (MooMarkupText *node, const char *text, gsize text_len) { char *tmp = g_new (char, node->size + text_len + 1); memcpy (tmp, node->text, node->size); memcpy (tmp + node->size, text, text_len); g_free (node->text); node->text = tmp; node->size += text_len; node->text[node->size] = 0; } static void collect_text_content (MooMarkupElement *node) { MooMarkupNode *child; GString *text = NULL; for (child = node->children; child != NULL; child = child->next) { if (MOO_MARKUP_IS_TEXT (child)) { if (text) { g_string_append_len (text, MOO_MARKUP_TEXT(child)->text, MOO_MARKUP_TEXT(child)->size); } else { text = g_string_new_len (MOO_MARKUP_TEXT(child)->text, MOO_MARKUP_TEXT(child)->size); } } } g_free (node->content); if (text) node->content = g_string_free (text, FALSE); else node->content = NULL; } static void moo_markup_node_free (MooMarkupNode *node) { MooMarkupNode *child; GSList *children = NULL, *l; g_return_if_fail (node != NULL); for (child = node->children; child != NULL; child = child->next) children = g_slist_prepend (children, child); for (l = children; l != NULL; l = l->next) moo_markup_node_free (l->data); g_free (node->name); 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 (); } g_slist_free (children); g_free (node); } 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) { if (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_node_get_string (MooMarkupNode *node) { MooMarkupNode *child; GString *str = g_string_new (""); g_return_val_if_fail (node != NULL, NULL); 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 (); } 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, "", node->name); } else g_string_append (str, "/>"); } static void moo_markup_text_node_print (MooMarkupNode *node, GString *str) { char *p, *escaped; MooMarkupText *text = (MooMarkupText*) node; if (!g_utf8_validate (text->text, -1, (const char**) &p)) { g_critical ("%s: invalid UTF8", G_STRLOC); *p = 0; } escaped = g_markup_escape_text (text->text, -1); g_string_append (str, escaped); g_free (escaped); } const char* moo_markup_get_content (MooMarkupNode *node) { g_return_val_if_fail (MOO_MARKUP_IS_ELEMENT (node), NULL); return MOO_MARKUP_ELEMENT(node)->content; } static MooMarkupElement* get_element (MooMarkupNode *node, char **path) { MooMarkupNode *child; if (!path[0]) return MOO_MARKUP_ELEMENT (node); for (child = node->children; child; child = child->next) { if (MOO_MARKUP_IS_ELEMENT (child)) { if (!strcmp (path[0], child->name)) return get_element (child, ++path); } } return NULL; } MooMarkupNode* moo_markup_get_element (MooMarkupNode *node, const char *path) { char **p; MooMarkupElement *elm; g_return_val_if_fail (path != NULL, NULL); g_return_val_if_fail (MOO_MARKUP_IS_ELEMENT (node) || MOO_MARKUP_IS_DOC (node), NULL); p = g_strsplit (path, "/", 0); g_return_val_if_fail (p != NULL, NULL); elm = get_element (node, p); g_strfreev (p); return (MooMarkupNode*)elm; } const char* moo_markup_get_prop (MooMarkupNode *node, const char *prop_name) { guint i; MooMarkupElement *elm; g_return_val_if_fail (node != NULL && prop_name != NULL, NULL); g_return_val_if_fail (MOO_MARKUP_IS_ELEMENT (node), NULL); elm = MOO_MARKUP_ELEMENT(node); for (i = 0 ; i < elm->n_attrs; ++i) if (!strcmp (prop_name, elm->attr_names[i])) return elm->attr_vals[i]; return NULL; } void moo_markup_set_prop (MooMarkupNode *elm, const char *prop_name, const char *val) { guint i; char **attr_names, **attr_vals; MooMarkupElement *node; g_return_if_fail (elm != NULL && prop_name != NULL); g_return_if_fail (MOO_MARKUP_IS_ELEMENT (elm)); g_return_if_fail (!val || g_utf8_validate (val, -1, NULL)); /* XXX validate prop_name and val */ node = MOO_MARKUP_ELEMENT (elm); for (i = 0 ; i < node->n_attrs; ++i) if (!strcmp (prop_name, node->attr_names[i])) { if (val) { g_free (node->attr_vals[i]); node->attr_vals[i] = g_strdup (val); return; } else if (node->n_attrs == 1) { g_strfreev (node->attr_names); g_strfreev (node->attr_vals); node->attr_names = NULL; node->attr_vals = NULL; node->n_attrs = 0; return; } else { guint j; char **attr_names, **attr_vals; /* XXX just move them */ attr_names = g_new (char*, node->n_attrs); attr_vals = g_new (char*, node->n_attrs); for (j = 0; j < i; ++j) { attr_names[j] = node->attr_names[j]; attr_vals[j] = node->attr_vals[j]; } for (j = i + 1; j < node->n_attrs; ++j) { attr_names[j-1] = node->attr_names[j]; attr_vals[j-1] = node->attr_vals[j]; } attr_names[node->n_attrs - 1] = NULL; attr_vals[node->n_attrs - 1] = NULL; g_free (node->attr_names[i]); g_free (node->attr_vals[i]); g_free (node->attr_names); g_free (node->attr_vals); node->attr_names = attr_names; node->attr_vals = attr_vals; node->n_attrs--; return; } } if (!val) return; g_return_if_fail (g_utf8_validate (prop_name, -1, NULL)); attr_names = g_new (char*, node->n_attrs + 2); attr_vals = g_new (char*, node->n_attrs + 2); for (i = 0; i < node->n_attrs; ++i) { attr_names[i] = node->attr_names[i]; attr_vals[i] = node->attr_vals[i]; } attr_names[node->n_attrs] = g_strdup (prop_name); attr_vals[node->n_attrs] = g_strdup (val); attr_names[node->n_attrs + 1] = NULL; attr_vals[node->n_attrs + 1] = NULL; g_free (node->attr_names); g_free (node->attr_vals); node->attr_names = attr_names; node->attr_vals = attr_vals; node->n_attrs++; } int moo_markup_get_int_prop (MooMarkupNode *node, const char *prop_name, int default_val) { g_return_val_if_fail (MOO_MARKUP_IS_ELEMENT (node), default_val); g_return_val_if_fail (prop_name != NULL, default_val); return moo_convert_string_to_int (moo_markup_get_prop (node, prop_name), default_val); } void moo_markup_set_int_prop (MooMarkupNode *node, const char *prop_name, int val) { moo_markup_set_prop (node, prop_name, moo_convert_int_to_string (val)); } gboolean moo_markup_get_bool_prop (MooMarkupNode *node, const char *prop_name, gboolean default_val) { g_return_val_if_fail (MOO_MARKUP_IS_ELEMENT (node), default_val); g_return_val_if_fail (prop_name != NULL, default_val); return moo_convert_string_to_bool (moo_markup_get_prop (node, prop_name), default_val); } void moo_markup_set_bool_prop (MooMarkupNode *node, const char *prop_name, gboolean val) { moo_markup_set_prop (node, prop_name, moo_convert_bool_to_string (val)); } MooMarkupNode* moo_markup_get_root_element (MooMarkupDoc *doc, const char *name) { MooMarkupNode *child; for (child = doc->children; child; child = child->next) { if (MOO_MARKUP_IS_ELEMENT (child)) { if (!name || !strcmp (child->name, name)) return child; } } return NULL; } char* moo_markup_element_get_path (MooMarkupElement *elm) { GString *path; MooMarkupNode *node; g_return_val_if_fail (elm != NULL, NULL); path = g_string_new (elm->name); for (node = elm->parent; node != NULL && MOO_MARKUP_IS_ELEMENT (node); node = node->parent) { g_string_prepend (path, "/"); g_string_prepend (path, node->name); } return g_string_free (path, FALSE); } void moo_markup_delete_node (MooMarkupNode *node) { MooMarkupNode *parent, *child, *next, *prev; g_return_if_fail (node != NULL); g_return_if_fail (node->parent != NULL); parent = node->parent; for (child = parent->children; child != NULL && child != node; child = child->next) ; g_return_if_fail (child == node); next = node->next; prev = node->prev; if (parent->children == node) { g_assert (node->prev == NULL); parent->children = next; } if (parent->last == node) { g_assert (node->next == NULL); parent->last = prev; } if (prev) prev->next = next; if (next) next->prev = prev; moo_markup_node_free (node); } MooMarkupNode* moo_markup_create_root_element (MooMarkupDoc *doc, const char *name) { MooMarkupNode *elm; g_return_val_if_fail (MOO_MARKUP_IS_DOC (doc), NULL); g_return_val_if_fail (name != NULL, NULL); elm = moo_markup_get_root_element (doc, name); if (elm) return elm; elm = moo_markup_element_new (doc, MOO_MARKUP_NODE (doc), name, NULL, NULL); return elm; } static MooMarkupNode* create_element (MooMarkupNode *node, char **path) { g_return_val_if_fail (MOO_MARKUP_IS_ELEMENT (node) || MOO_MARKUP_IS_DOC (node), NULL); g_return_val_if_fail (path && path[0], NULL); if (path[1]) { MooMarkupNode *child = NULL; for (child = node->children; child != NULL; child = child->next) if (MOO_MARKUP_IS_ELEMENT (child) && child->name && !strcmp (child->name, path[0])) break; if (!child) child = moo_markup_element_new (node->doc, MOO_MARKUP_NODE (node), path[0], NULL, NULL); return create_element (child, ++path); } else { return moo_markup_element_new (node->doc, MOO_MARKUP_NODE (node), path[0], NULL, NULL); } } MooMarkupNode* moo_markup_create_element (MooMarkupNode *parent, const char *path) { char **pieces; MooMarkupNode *elm; g_return_val_if_fail (MOO_MARKUP_IS_ELEMENT (parent) || MOO_MARKUP_IS_DOC (parent), NULL); g_return_val_if_fail (path != NULL, NULL); pieces = g_strsplit (path, "/", 0); g_return_val_if_fail (pieces != NULL, NULL); elm = create_element (parent, pieces); g_strfreev (pieces); return elm; } MooMarkupNode* moo_markup_create_text_element (MooMarkupNode *parent, const char *path, const char *content) { MooMarkupElement *elm; MooMarkupNode *child; g_return_val_if_fail (MOO_MARKUP_IS_ELEMENT (parent) || MOO_MARKUP_IS_DOC (parent), NULL); g_return_val_if_fail (path != NULL, NULL); g_return_val_if_fail (content != NULL, NULL); g_return_val_if_fail (g_utf8_validate (content, -1, NULL), NULL); elm = (MooMarkupElement*) moo_markup_create_element (parent, path); if (!elm) return NULL; child = moo_markup_text_node_new (MOO_MARKUP_TEXT_NODE, elm->doc, MOO_MARKUP_NODE (elm), content, strlen (content)); g_free (elm->content); elm->content = g_strdup (content); return MOO_MARKUP_NODE (elm); } MooMarkupNode* moo_markup_create_file_element (MooMarkupNode *parent, const char *path, const char *filename) { char *filename_utf8; MooMarkupNode *elm; g_return_val_if_fail (filename != NULL, NULL); filename_utf8 = g_filename_display_name (filename); if (!filename_utf8) { g_warning ("%s: could not convert '%s' to utf8", G_STRLOC, filename); return NULL; } elm = moo_markup_create_text_element (parent, path, filename_utf8); g_free (filename_utf8); return elm; } char* moo_markup_get_file_content (MooMarkupNode *node) { g_return_val_if_fail (MOO_MARKUP_IS_ELEMENT (node), NULL); if (!MOO_MARKUP_ELEMENT(node)->content) return NULL; return g_filename_from_utf8 (MOO_MARKUP_ELEMENT(node)->content, -1, NULL, NULL, NULL); } gboolean moo_markup_save (MooMarkupDoc *doc, const char *filename, GError **error) { char *text; gboolean result; g_return_val_if_fail (MOO_MARKUP_IS_DOC (doc), FALSE); g_return_val_if_fail (filename != NULL, FALSE); text = moo_markup_node_get_string (MOO_MARKUP_NODE (doc)); g_return_val_if_fail (text != NULL, FALSE); result = _moo_save_file_utf8 (filename, text, -1, error); g_free (text); return result; } #define INDENT_CHAR ' ' #ifdef __WIN32__ #define LINE_SEPARATOR "\r\n" #elif defined(MOO_OS_DARWIN) #define LINE_SEPARATOR "\r" #else #define LINE_SEPARATOR "\n" #endif static void format_pretty_element (MooMarkupElement *elm, GString *str, guint indent, guint indent_size) { gboolean isdir = FALSE; gboolean empty = TRUE; MooMarkupNode *child; char *fill; guint i; g_return_if_fail (MOO_MARKUP_IS_ELEMENT (elm)); g_return_if_fail (str != NULL); for (child = elm->children; child != NULL; child = child->next) { if (elm->content) empty = FALSE; if (MOO_MARKUP_IS_ELEMENT (child)) { isdir = TRUE; empty = FALSE; break; } } fill = g_strnfill (indent, INDENT_CHAR); g_string_append_len (str, fill, indent); g_string_append_printf (str, "<%s", elm->name); for (i = 0; i < elm->n_attrs; ++i) g_string_append_printf (str, " %s=\"%s\"", elm->attr_names[i], elm->attr_vals[i]); if (empty) { g_string_append (str, "/>" LINE_SEPARATOR); } else if (isdir) { g_string_append (str, ">" LINE_SEPARATOR); for (child = elm->children; child != NULL; child = child->next) if (MOO_MARKUP_IS_ELEMENT (child)) format_pretty_element (MOO_MARKUP_ELEMENT (child), str, indent + indent_size, indent_size); g_string_append_printf (str, "%s" LINE_SEPARATOR, fill, elm->name); } else { char *escaped = g_markup_escape_text (elm->content, -1); g_string_append_printf (str, ">%s" LINE_SEPARATOR, escaped, elm->name); g_free (escaped); } g_free (fill); } #undef INDENT_CHAR #undef LINE_SEPARATOR gboolean moo_markup_save_pretty (MooMarkupDoc *doc, const char *filename, guint indent, GError **error) { GString *str; MooMarkupNode *child; gboolean result; g_return_val_if_fail (MOO_MARKUP_IS_DOC (doc), FALSE); g_return_val_if_fail (filename != NULL, FALSE); str = g_string_new ("\n"); for (child = doc->children; child != NULL; child = child->next) if (MOO_MARKUP_IS_ELEMENT (child)) format_pretty_element (MOO_MARKUP_ELEMENT (child), str, 0, indent); result = _moo_save_file_utf8 (filename, str->str, -1, error); g_string_free (str, TRUE); return result; } char * moo_markup_node_get_pretty_string (MooMarkupNode *node, guint indent) { GString *str; MooMarkupNode *child; g_return_val_if_fail (node != NULL, NULL); str = g_string_new (NULL); for (child = node->children; child != NULL; child = child->next) if (MOO_MARKUP_IS_ELEMENT (child)) format_pretty_element (MOO_MARKUP_ELEMENT (child), str, 0, indent); return g_string_free (str, FALSE); } MooMarkupNode* MOO_MARKUP_NODE_CHECK_CAST (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_CHECK_CAST (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_CHECK_CAST (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_CHECK_CAST (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_CHECK_CAST (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; }