#include <stdlib.h>
#include <string.h>
#include "../gskerror.h"
#include "gskxmlvaluereader.h"
/*
* XXX: GMarkup assumes UTF-8, so this isn't 8-bit clean (not to mention
* string-escaping issues in GskXmlValueWriter).
*/
/*
*
* Helpers.
*
*/
static __inline__ void
g_value_set_string_len (GValue *value, const char *str, guint len)
{
char *tmp;
tmp = g_alloca (len + 1);
memcpy (tmp, str, len);
tmp[len] = 0;
g_value_set_string (value, tmp);
}
static __inline__ void
g_value_move (GValue *dst, GValue *src)
{
memcpy (dst, src, sizeof (*dst));
memset (src, 0, sizeof (*src));
}
/* Compare canonical versions of property names. */
static gboolean
property_names_equal (const char *pa, const char *pb)
{
g_return_val_if_fail (pa && pb, FALSE);
for ( ; ; ++pa, ++pb)
{
char a = *pa;
char b = *pb;
if (a == '\0')
return b ? FALSE : TRUE;
else if (b == '\0')
return FALSE;
else if (a != b)
{
/* Only if both a and b are equivalent to '-', are they still
* equal, so if either one is in the non-equivalent class,
* they are not equal.
*/
if (g_ascii_isalnum (a) || g_ascii_isalnum (b))
return FALSE;
}
}
}
static gboolean
parse_text_value (GValue *value, const char *str, int len, GError **error)
{
GType type = G_VALUE_TYPE (value);
switch (type)
{
case G_TYPE_CHAR:
if (len == 0) return FALSE;
g_value_set_char (value, *str);
return TRUE;
case G_TYPE_UCHAR:
if (len == 0) return FALSE;
g_value_set_uchar (value, *str);
return TRUE;
case G_TYPE_BOOLEAN:
{
if (len == 0) return FALSE;
switch (*str)
{
case 'y': case 'Y': case 't': case 'T': case '1':
g_value_set_boolean (value, TRUE);
return TRUE;
case 'n': case 'N': case 'f': case 'F': case '0':
g_value_set_boolean (value, FALSE);
return TRUE;
}
return FALSE;
}
#define STRTOX_TYPE_CASE(T,t,fct) \
case G_TYPE_##T: \
{ \
char *tmp, *end; \
int base; \
g##t parsed_val; \
if (len > 1023) \
len = 1023; \
tmp = g_alloca (len + 1); \
memcpy (tmp, str, len); \
tmp[len] = 0; \
base = (tmp[0] == '0' && tmp[1] == 'x') ? 16 : 10; \
parsed_val = (g##t) fct (tmp, &end, base); \
if (tmp == end) \
return FALSE; \
g_value_set_##t (value, parsed_val); \
} \
break;
STRTOX_TYPE_CASE (INT, int, strtol)
STRTOX_TYPE_CASE (UINT, uint, strtoul)
STRTOX_TYPE_CASE (LONG, long, strtol)
STRTOX_TYPE_CASE (ULONG, ulong, strtoul)
STRTOX_TYPE_CASE (INT64, int64, strtoll)
STRTOX_TYPE_CASE (UINT64, uint64, strtoull)
#undef STRTOX_TYPE_CASE
case G_TYPE_FLOAT:
case G_TYPE_DOUBLE:
{
char *tmp;
char *end;
if (len > 1023)
len = 1023;
tmp = g_alloca (len + 1);
memcpy (tmp, str, len);
tmp[len] = 0;
switch (type)
{
case G_TYPE_FLOAT:
g_value_set_float (value, (gfloat) strtod (tmp, &end));
break;
case G_TYPE_DOUBLE:
g_value_set_double (value, (gdouble) strtod (tmp, &end));
break;
}
if (tmp == end)
return FALSE;
}
break;
case G_TYPE_STRING:
g_value_set_string_len (value, str, len);
break;
/* TODO: FLAGS, ENUMS */
default:
{
/* See if we can use g_value_transform to parse type
* from a string.
*/
if (g_value_type_transformable (G_TYPE_STRING, type))
{
GValue tmp_value = { 0, { { 0 }, { 0 } } };
gboolean result;
g_value_init (&tmp_value, G_TYPE_STRING);
g_value_set_string_len (&tmp_value, str, len);
result = g_value_transform (&tmp_value, value);
if (!result)
{
g_set_error (error,
GSK_G_ERROR_DOMAIN,
0, /* TODO: error code */
"error transforming string '%s' to a %s",
g_value_get_string (&tmp_value),
g_type_name (type));
}
g_value_unset (&tmp_value);
return result;
}
else
{
g_set_error (error,
GSK_G_ERROR_DOMAIN,
0, /* TODO: error code */
"cannot parse value of type %s",
g_type_name (type));
return FALSE;
}
}
}
return TRUE;
}
/*
*
* XmlStackFrame
*
*
*/
/* Our little grammar/state diagram:
*
* VALUE: <CLASS> OBJECT-BODY </CLASS>
* | <TYPE> VALUE-TEXT </TYPE>
* | TEXT
*
* OBJECT-BODY: <PROPERTY> [push] VALUE [pop] </PROPERTY> OBJECT-BODY
* | empty
*
* VALUE-TEXT: TEXT
*/
typedef enum _XmlState
{
STATE_VALUE, /* expecting VALUE */
STATE_OBJECT_BODY, /* expecting OBJECT-BODY */
STATE_PROPERTY_CLOSE, /* expecting </PROPERTY> */
STATE_VALUE_TEXT, /* expecting TEXT */
STATE_VALUE_CLOSE /* expecting </TYPE> */
}
XmlState;
typedef struct _XmlStackFrame XmlStackFrame;
struct _XmlStackFrame
{
/* Current scanning state. */
XmlState state;
/* What type we're supposed to instantiate in this frame
* (or G_TYPE_INVALID for a top-level value of unspecified type).
*/
GType type;
/* The value being constructed. */
GValue value;
/* If value is an object, properties to be set at construction: */
GArray *properties; /* of GParameter */
/* If value is an object and we've pushed a new stack frame for
* one of its properties, this is the corresponding GParamSpec.
* (We assume this cannot go away, so we don't reference it.)
*/
GParamSpec *param_spec;
/* Parent stack frame. */
XmlStackFrame *parent;
};
static GMemChunk *xml_stack_frame_chunk = NULL;
G_LOCK_DEFINE_STATIC (xml_stack_frame_chunk);
static __inline__ XmlStackFrame *
xml_stack_frame_alloc0 (void)
{
XmlStackFrame *frame;
G_LOCK (xml_stack_frame_chunk);
if (xml_stack_frame_chunk == NULL)
{
xml_stack_frame_chunk =
g_mem_chunk_create (XmlStackFrame, 64, G_ALLOC_AND_FREE);
}
frame = g_mem_chunk_alloc0 (xml_stack_frame_chunk);
G_UNLOCK (xml_stack_frame_chunk);
return frame;
}
static __inline__ void
xml_stack_frame_free (XmlStackFrame *frame)
{
G_LOCK (xml_stack_frame_chunk);
g_mem_chunk_free (xml_stack_frame_chunk, frame);
G_UNLOCK (xml_stack_frame_chunk);
}
/* Push a stack frame for a value of the given type. */
static XmlStackFrame *
xml_stack_push (GType type, XmlStackFrame *parent)
{
XmlStackFrame *frame;
frame = xml_stack_frame_alloc0 ();
frame->state = STATE_VALUE;
frame->type = type;
frame->parent = parent;
if (type)
g_value_init (&frame->value, type);
return frame;
}
/* Push a stack frame for an object property. */
static XmlStackFrame *
xml_stack_push_property (GParamSpec *param_spec, XmlStackFrame *parent)
{
GType type;
parent->param_spec = param_spec;
/* For GValueArray properties, we instantiate the element type. */
if (G_PARAM_SPEC_VALUE_TYPE (param_spec) == G_TYPE_VALUE_ARRAY)
{
GParamSpecValueArray *param_spec_value_array;
g_return_val_if_fail (G_IS_PARAM_SPEC_VALUE_ARRAY (param_spec), NULL);
param_spec_value_array = G_PARAM_SPEC_VALUE_ARRAY (param_spec);
type = G_PARAM_SPEC_VALUE_TYPE (param_spec_value_array->element_spec);
}
else
type = G_PARAM_SPEC_VALUE_TYPE (param_spec);
return xml_stack_push (type, parent);
}
static void
xml_stack_frame_destroy_one (XmlStackFrame *frame)
{
if (G_VALUE_TYPE (&frame->value))
g_value_unset (&frame->value);
/* (param_spec is static) */
if (frame->properties)
{
GArray *properties = frame->properties;
guint i;
for (i = 0; i < properties->len; ++i)
{
/* (param->name is copy of static param_spec->name) */
GValue *value = & g_array_index (properties, GParameter, i).value;
if (G_VALUE_TYPE (value))
g_value_unset (value);
}
g_array_free (properties, TRUE);
}
xml_stack_frame_free (frame);
}
static void
xml_stack_frame_destroy_stack (XmlStackFrame *top)
{
while (top)
{
XmlStackFrame *parent = top->parent;
xml_stack_frame_destroy_one (top);
top = parent;
}
}
/*
*
* GskXmlValueReader
*
*/
struct _GskXmlValueReader
{
GMarkupParseContext *parse_context;
GskGtypeLoader *type_loader;
XmlStackFrame *stack;
/* Position data for error reporting. */
char *filename;
gint line_start, line_offset, char_offset;
/* What type we must output. */
GType output_type;
/* Callback to report output to client. */
GskXmlValueFunc value_callback;
gpointer value_callback_data;
GDestroyNotify value_callback_destroy;
guint had_error : 1;
};
#define GSK_XML_VALUE_READER(obj) ((GskXmlValueReader *) (obj))
/*
*
* Error handling
*
*/
void
gsk_xml_value_reader_set_error (GskXmlValueReader *reader,
GError **error,
gint error_code,
const char *format,
...)
{
va_list args;
char *message;
gint line_no;
gint char_no;
reader->had_error = 1;
va_start (args, format);
message = g_strdup_vprintf (format, args);
va_end (args);
g_markup_parse_context_get_position (reader->parse_context,
&line_no,
&char_no);
if (line_no == reader->line_start)
char_no += reader->char_offset;
line_no += reader->line_offset;
if (reader->filename)
g_set_error (error,
GSK_G_ERROR_DOMAIN,
error_code,
"%s, line %d, character %d: %s",
reader->filename, line_no, char_no, message);
else
g_set_error (error,
GSK_G_ERROR_DOMAIN,
error_code,
"line %d, character %d: %s",
line_no, char_no, message);
g_free (message);
}
/* format describes what we got; add a description of what we expected. */
static void
gsk_xml_value_reader_set_error_mismatch (GskXmlValueReader *reader,
GError **error,
gint error_code,
const char *format,
...)
{
va_list args;
XmlStackFrame *top = reader->stack;
gchar *got, *expected;
va_start (args, format);
got = g_strdup_vprintf (format, args);
va_end (args);
g_return_if_fail (top);
switch (top->state)
{
case STATE_VALUE:
expected = g_strdup ("<CLASS> or text");
break;
case STATE_OBJECT_BODY:
expected = g_strdup_printf ("<PROPERTY>, or </%s>",
g_type_name (top->type));
break;
case STATE_PROPERTY_CLOSE:
g_return_if_fail (top->param_spec);
g_return_if_fail (top->param_spec->name);
expected = g_strdup_printf ("</%s>", top->param_spec->name);
break;
case STATE_VALUE_TEXT:
expected = g_strdup ("text");
break;
case STATE_VALUE_CLOSE:
expected = g_strdup_printf ("</%s>", g_type_name (top->type));
break;
default:
g_return_if_reached ();
}
gsk_xml_value_reader_set_error (reader,
error,
error_code,
"got %s; expected %s",
got, expected);
g_free (expected);
g_free (got);
}
/* A value has been instantiated and stored in stack->value,
* by <CLASS> ... </CLASS> or text.
* Deal with this value according to the parent stack frame.
*/
static void
gsk_xml_value_reader_pop_value (GskXmlValueReader *reader)
{
XmlStackFrame *top = reader->stack;
XmlStackFrame *parent = top->parent;
GValue *value = &top->value;
if (parent)
{
/* value is an object property. */
GArray *properties = parent->properties;
GParamSpec *param_spec = parent->param_spec;
GParameter *param;
guint index;
g_return_if_fail (parent->state == STATE_PROPERTY_CLOSE);
g_return_if_fail (param_spec);
if (properties == NULL)
{
properties = parent->properties =
g_array_new (FALSE, FALSE, sizeof (GParameter));
}
/* Special case for GValueArray properties. */
if (G_PARAM_SPEC_VALUE_TYPE (param_spec) == G_TYPE_VALUE_ARRAY)
{
GValueArray *value_array;
/* Check whether the array has already been created. */
for (index = 0; index < properties->len; ++index)
{
param = & g_array_index (properties, GParameter, index);
if (property_names_equal (param->name, param_spec->name))
{
value_array = g_value_get_boxed (¶m->value);
goto ADD;
}
}
/* If not, create the array. */
value_array = g_value_array_new (1);
g_array_set_size (properties, index + 1);
param = & g_array_index (properties, GParameter, index);
param->name = param_spec->name;
memset (¶m->value, 0, sizeof (param->value));
g_value_init (¶m->value, G_TYPE_VALUE_ARRAY);
g_value_set_boxed_take_ownership (¶m->value, value_array);
ADD:
g_value_array_append (value_array, value);
}
else
{
index = properties->len;
g_array_set_size (properties, index + 1);
param = & g_array_index (properties, GParameter, index);
param->name = param_spec->name;
g_value_move (¶m->value, value);
}
reader->stack = parent;
}
else
{
/* No parent stack frame; done with a top-level value. Invoke user
* callback.
*/
if (reader->value_callback)
(*reader->value_callback) (value, reader->value_callback_data);
/* Replace top stack frame for next value. */
reader->stack = xml_stack_push (reader->output_type, NULL);
}
xml_stack_frame_destroy_one (top);
}
/* Text is either a simple type to be deserialized, or a
* representation for g_value_transform.
*/
static gboolean
instantiate_value_from_text (GskXmlValueReader *reader,
const char *text,
gsize text_len,
GError **error)
{
XmlStackFrame *top = reader->stack;
GValue *value = &top->value;
GError *tmp_error = NULL;
if (G_VALUE_TYPE (value) == G_TYPE_INVALID)
{
gsk_xml_value_reader_set_error (reader,
error,
0, /* TODO: error_code */
"can't parse a value of "
"unspecified type from text");
return FALSE;
}
if (!parse_text_value (value, text, text_len, &tmp_error))
{
const char *msg = tmp_error ? tmp_error->message : "unknown error";
gsk_xml_value_reader_set_error (reader,
error,
0, /* TODO: error_code */
"error parsing %s from text: %s",
g_type_name (G_VALUE_TYPE (value)),
msg);
if (tmp_error)
g_error_free (tmp_error);
return FALSE;
}
return TRUE;
}
static void
handle_start_element (GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data,
GError **error)
{
GskXmlValueReader *reader = GSK_XML_VALUE_READER (user_data);
XmlStackFrame *top = reader->stack;
(void) context;
(void) attribute_names;
(void) attribute_values;
if (reader->had_error)
return;
g_return_if_fail (top);
switch (top->state)
{
case STATE_VALUE:
{
/* Load the type and make sure it's allowed by the
* current configuration.
*/
GError *tmp_error = NULL;
GType type;
type = gsk_gtype_loader_load_type (reader->type_loader,
element_name,
&tmp_error);
if (type == G_TYPE_INVALID)
{
const char *msg =
tmp_error ? tmp_error->message : "unknown error";
gsk_xml_value_reader_set_error (reader,
error,
0, /* TODO: error_code */
"couldn't load type %s: %s",
element_name,
msg);
g_error_free (tmp_error);
return;
}
if (!gsk_gtype_loader_test_type (reader->type_loader, type))
{
gsk_xml_value_reader_set_error (reader,
error,
0, /* TODO: error_code */
"%s is not an allowed type",
g_type_name (type));
return;
}
if (top->type)
{
/* XXX: do we really handle g_value_type_transformable? */
if (!(g_type_is_a (type, top->type) ||
g_value_type_transformable (type, top->type)))
{
gsk_xml_value_reader_set_error (reader,
error,
0, /* TODO: error_code */
"%s is not a %s",
g_type_name (type),
g_type_name (top->type));
return;
}
}
else
{
/* Only the top-level output type can be unspecified. */
g_return_if_fail (top->parent == NULL);
g_value_init (&top->value, type);
}
/* Now scan for properties, if this is an object type,
* or parse text if this is a fundamental type.
*/
top->type = type;
if (g_type_is_a (type, G_TYPE_OBJECT))
top->state = STATE_OBJECT_BODY;
else
top->state = STATE_VALUE_TEXT;
return;
}
case STATE_OBJECT_BODY:
{
/* Expecting <PROPERTY>. Is element_name a property? */
GObjectClass *klass;
GParamSpec *param_spec;
klass = G_OBJECT_CLASS (g_type_class_ref (top->type));
g_return_if_fail (klass);
param_spec = g_object_class_find_property (klass, element_name);
g_type_class_unref (klass);
if (param_spec)
{
/* When we pop, we'll scan for </PROPERTY>. */
top->state = STATE_PROPERTY_CLOSE;
reader->stack = xml_stack_push_property (param_spec, top);
return;
}
/* element_name is not a property, error. */
gsk_xml_value_reader_set_error (reader,
error,
0, /* TODO: error_code */
"%s is not a property of %s",
element_name,
g_type_name (top->type));
return;
}
default:
break;
}
gsk_xml_value_reader_set_error_mismatch (reader,
error,
0, /* TODO: error_code */
"tag <%s>",
element_name);
}
static void
handle_end_element (GMarkupParseContext *context,
const gchar *element_name,
gpointer user_data,
GError **error)
{
GskXmlValueReader *reader = GSK_XML_VALUE_READER (user_data);
XmlStackFrame *top = reader->stack;
(void) context;
(void) error;
if (reader->had_error)
return;
REDO:
switch (top->state)
{
case STATE_OBJECT_BODY:
{
/* Expecting </CLASS>. */
const char *class_name;
class_name = g_type_name (top->type);
if (strcmp (element_name, class_name) == 0)
{
/* Try to construct the object. */
GParameter *parameters = NULL;
int n_parameters = 0;
GObject *object;
if (top->properties)
{
parameters = (GParameter *) top->properties->data;
n_parameters = top->properties->len;
}
object = g_object_newv (top->type, n_parameters, parameters);
if (object == NULL)
{
gsk_xml_value_reader_set_error (reader,
error,
0, /* TODO: error_code */
"error constructing a %s",
class_name);
return;
}
g_value_set_object (&top->value, object);
/* value has referenced object */
g_object_unref (object);
gsk_xml_value_reader_pop_value (reader);
return;
}
}
break;
case STATE_PROPERTY_CLOSE:
{
/* Expecting </PROPERTY>. */
GArray *properties = top->properties;
const char *property_name;
g_return_if_fail (properties);
property_name =
g_array_index (properties, GParameter, properties->len - 1).name;
g_return_if_fail (property_name);
if (property_names_equal (element_name, property_name))
{
/* Scan for next property. */
top->state = STATE_OBJECT_BODY;
return;
}
}
break;
case STATE_VALUE:
case STATE_VALUE_TEXT:
{
/* Might get a close tag when we're expecting a length 0 string. */
if (!instantiate_value_from_text (reader, "", 0, error))
return;
if (top->state == STATE_VALUE)
{
gsk_xml_value_reader_pop_value (reader);
top = reader->stack;
}
else
top->state = STATE_VALUE_CLOSE;
goto REDO;
}
break;
case STATE_VALUE_CLOSE:
{
/* Expecting </TYPE>. */
const char *type_name;
type_name = g_type_name (top->type);
g_return_if_fail (type_name);
if (strcmp (element_name, type_name) == 0)
{
gsk_xml_value_reader_pop_value (reader);
return;
}
}
break;
default:
break;
}
gsk_xml_value_reader_set_error_mismatch (reader,
error,
0, /* TODO: error_code */
"tag </%s>",
element_name);
}
static void
handle_text (GMarkupParseContext *context,
const gchar *text,
gsize text_len,
gpointer user_data,
GError **error)
{
GskXmlValueReader *reader = GSK_XML_VALUE_READER (user_data);
XmlStackFrame *top = reader->stack;
(void) context;
g_return_if_fail (top);
if (reader->had_error)
return;
/* Eat whitespace. */
while (text_len > 0)
{
char c = *text;
if (!g_ascii_isspace (c))
break;
--text_len;
++text;
}
if (text_len <= 0)
return;
if (top->state == STATE_VALUE || top->state == STATE_VALUE_TEXT)
{
if (instantiate_value_from_text (reader, text, text_len, error))
{
if (top->state == STATE_VALUE)
gsk_xml_value_reader_pop_value (reader);
else
top->state = STATE_VALUE_CLOSE;
}
return;
}
{
char *terminated_text;
terminated_text = g_strndup (text, text_len);
gsk_xml_value_reader_set_error_mismatch (reader,
error,
0, /* TODO: error_code */
"text '%s'",
terminated_text);
g_free (terminated_text);
}
}
/* text is not nul-terminated. */
static void
handle_passthrough (GMarkupParseContext *context,
const gchar *passthrough_text,
gsize text_len,
gpointer user_data,
GError **error)
{
GskXmlValueReader *reader = GSK_XML_VALUE_READER (user_data);
XmlStackFrame *top = reader->stack;
(void) context;
if (reader->had_error)
return;
g_return_if_fail (top);
/* Simply ignore any passthrough except CDATA. */
if (text_len < 12 ||
(strncmp (passthrough_text, "<![CDATA[", 9) != 0) ||
(strncmp (passthrough_text + text_len - 3, "]]>", 3) != 0))
return;
passthrough_text += 9;
text_len -= 12;
/* TODO: perhaps CDATA should be merged with plain text blocks? */
if (top->state == STATE_VALUE || top->state == STATE_VALUE_TEXT)
{
if (instantiate_value_from_text (reader,
passthrough_text,
text_len,
error))
{
if (top->state == STATE_VALUE)
gsk_xml_value_reader_pop_value (reader);
else
top->state = STATE_VALUE_CLOSE;
}
return;
}
{
char *terminated_text;
terminated_text = g_strndup (passthrough_text, text_len);
gsk_xml_value_reader_set_error_mismatch (reader,
error,
0, /* TODO: error_code */
"text '%s'",
terminated_text);
g_free (terminated_text);
}
}
/* Create the GMarkupParseContext and the stack. */
static void
gsk_xml_value_reader_create_parser (GskXmlValueReader *reader)
{
static const GMarkupParser g_markup_parser =
{
handle_start_element,
handle_end_element,
handle_text,
handle_passthrough,
NULL, /* error */
};
g_return_if_fail (reader->parse_context == NULL);
reader->parse_context =
g_markup_parse_context_new (&g_markup_parser,
0, /* flags */
reader, /* user_data */
NULL); /* user_data_dnotify */
g_return_if_fail (reader->stack == NULL);
reader->stack = xml_stack_push (reader->output_type, NULL);
}
/*
*
* Public interface.
*
*/
GskXmlValueReader *
gsk_xml_value_reader_new (GskGtypeLoader *type_loader,
GType output_type,
GskXmlValueFunc value_func,
gpointer value_func_data,
GDestroyNotify value_func_destroy)
{
GskXmlValueReader *reader;
g_return_val_if_fail (type_loader, NULL);
reader = g_new0 (GskXmlValueReader, 1);
reader->type_loader = type_loader;
gsk_gtype_loader_ref (type_loader);
reader->output_type = output_type;
g_return_val_if_fail (output_type == G_TYPE_INVALID ||
g_type_name (output_type),
NULL);
reader->value_callback = value_func;
reader->value_callback_data = value_func_data;
reader->value_callback_destroy = value_func_destroy;
return reader;
}
void
gsk_xml_value_reader_free (GskXmlValueReader *reader)
{
if (reader->value_callback_destroy)
(*reader->value_callback_destroy) (reader->value_callback_data);
if (reader->parse_context)
{
/* XXX: this isn't reentrant... */
g_markup_parse_context_free (reader->parse_context);
reader->parse_context = NULL;
}
xml_stack_frame_destroy_stack (reader->stack);
if (reader->type_loader)
{
gsk_gtype_loader_unref (reader->type_loader);
reader->type_loader = NULL;
}
g_free (reader);
}
gboolean
gsk_xml_value_reader_input (GskXmlValueReader *reader,
const char *input,
guint len,
GError **error)
{
const char *start = input;
if (reader->had_error)
return FALSE;
if (reader->parse_context == NULL)
{
/* Only create parser if we get non-whitespace. */
for (;;)
{
if (len == 0)
return TRUE;
if (!g_ascii_isspace (*start))
break;
if (*start == '\n')
{
++reader->line_offset;
reader->char_offset = 0;
}
else
++reader->char_offset;
++start;
--len;
}
gsk_xml_value_reader_create_parser (reader);
}
/* Feed the data to the parser. */
return g_markup_parse_context_parse (reader->parse_context,
start,
len,
error);
}
void
gsk_xml_value_reader_set_pos (GskXmlValueReader *reader,
const char *filename,
gint line_no,
gint char_no)
{
gint parser_line_no, parser_char_no;
if (reader->filename)
g_free (reader->filename);
reader->filename = filename ? g_strdup (filename) : NULL;
if (reader->parse_context == NULL)
gsk_xml_value_reader_create_parser (reader);
g_markup_parse_context_get_position (reader->parse_context,
&parser_line_no,
&parser_char_no);
reader->line_start = parser_line_no;
reader->line_offset = line_no - parser_line_no;
reader->char_offset = char_no - parser_char_no;
}
gboolean
gsk_xml_value_reader_had_error (GskXmlValueReader *reader)
{
return reader->had_error;
}
/*
*
* gsk_load_object_from_xml_file
*
*/
/* GskXmlValueFunc */
static void
set_object_ptr (const GValue *value, gpointer user_data)
{
*((GObject **) user_data) = g_value_dup_object (value);
}
GObject *
gsk_load_object_from_xml_file (const char *path,
GskGtypeLoader *type_loader,
GType object_type,
GError **error)
{
GskXmlValueReader *xml_value_reader = NULL;
char *file_contents = NULL;
gsize file_contents_len;
GObject *object = NULL;
if (!g_file_get_contents (path, &file_contents, &file_contents_len, error))
goto FAIL;
g_return_val_if_fail (file_contents, NULL);
xml_value_reader = gsk_xml_value_reader_new (type_loader,
object_type,
set_object_ptr,
&object,
NULL);
g_return_val_if_fail (xml_value_reader, NULL);
if (!gsk_xml_value_reader_input (xml_value_reader,
file_contents,
file_contents_len,
error))
goto FAIL;
if (object == NULL || !g_type_is_a (G_OBJECT_TYPE (object), object_type))
goto FAIL;
gsk_xml_value_reader_free (xml_value_reader);
g_free (file_contents);
return object;
FAIL:
if (object)
g_object_unref (object);
if (file_contents)
g_free (file_contents);
if (xml_value_reader)
gsk_xml_value_reader_free (xml_value_reader);
return NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1