Commit d4b90098 authored by Sven Neumann's avatar Sven Neumann Committed by Sven Neumann

app/config/gimpconfig-deserialize.[ch]

2002-03-23  Sven Neumann  <sven@gimp.org>

	* app/config/gimpconfig-deserialize.[ch]
	* app/config/gimpconfig-serialize.[ch]
	* app/config/gimpconfig.[ch]
	* app/config/gimprc.[ch]
	* app/config/test-config.c: added better error reporting using GError.
parent 76e903c4
2002-03-23 Sven Neumann <sven@gimp.org>
* app/config/gimpconfig-deserialize.[ch]
* app/config/gimpconfig-serialize.[ch]
* app/config/gimpconfig.[ch]
* app/config/gimprc.[ch]
* app/config/test-config.c: added better error reporting using GError.
2002-03-22 Michael Natterer <mitch@gimp.org>
* app/widgets/Makefile.am
......
......@@ -2,7 +2,7 @@
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Object properties deserialization routines
* Copyright (C) 2001 Sven Neumann <sven@gimp.org>
* Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
*
* 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
......@@ -71,10 +71,26 @@ static inline gboolean scanner_string_utf8_valid (GScanner *scanner,
const gchar *token_name);
/**
* gimp_config_deserialize_properties:
* @object: a #GObject.
* @scanner: a #GScanner.
* @store_unknown_tokens: %TRUE if you want to store unknown tokens.
*
* This function uses the @scanner to configure the properties of @object.
*
* The store_unknown_tokens parameter is a special feature for #GimpRc.
* If it set to %TRUE, unknown tokens (e.g. tokens that don't refer to
* a property of @object) with string values are attached to @object as
* unknown tokens. GimpConfig has a couple of functions to handle the
* attached key/value pairs.
*
* Return value:
**/
gboolean
gimp_config_deserialize_properties (GObject *object,
GScanner *scanner,
gboolean store_unknown_tokens)
gimp_config_deserialize_properties (GObject *object,
GScanner *scanner,
gboolean store_unknown_tokens)
{
GObjectClass *klass;
GParamSpec **property_specs;
......@@ -148,16 +164,15 @@ gimp_config_deserialize_properties (GObject *object,
}
while (token != G_TOKEN_EOF);
if (token != G_TOKEN_LEFT_PAREN && token != G_TOKEN_NONE)
if (next != G_TOKEN_EOF && next != token && token != G_TOKEN_NONE)
{
g_scanner_get_next_token (scanner);
g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
_("fatal parse error"), TRUE);
g_scanner_unexp_token (scanner, token, NULL, NULL, NULL, NULL, TRUE);
}
g_scanner_set_scope (scanner, old_scope_id);
return (token == G_TOKEN_EOF);
return (next == G_TOKEN_EOF || next == token);
}
static GTokenType
......@@ -224,6 +239,7 @@ gimp_config_deserialize_property (GObject *object,
{
g_object_set_property (object, prop_spec->name, &value);
}
#if CONFIG_DEBUG
else
{
g_warning ("couldn't deserialize property %s::%s of type %s",
......@@ -231,6 +247,7 @@ gimp_config_deserialize_property (GObject *object,
prop_spec->name,
g_type_name (prop_spec->value_type));
}
#endif
g_value_unset (&value);
......@@ -296,7 +313,7 @@ gimp_config_deserialize_fundamental (GValue *value,
else
{
/* don't translate 'yes' and 'no' */
g_scanner_warn
g_scanner_error
(scanner,
_("expected 'yes' or 'no' for boolean token %s, got '%s'"),
prop_spec->name, scanner->value.v_identifier);
......@@ -353,9 +370,9 @@ gimp_config_deserialize_enum (GValue *value,
if (!enum_value)
{
g_scanner_warn (scanner,
_("invalid value '%s' for token %s"),
scanner->value.v_identifier, prop_spec->name);
g_scanner_error (scanner,
_("invalid value '%s' for token %s"),
scanner->value.v_identifier, prop_spec->name);
return G_TOKEN_NONE;
}
......@@ -449,9 +466,9 @@ scanner_string_utf8_valid (GScanner *scanner,
if (g_utf8_validate (scanner->value.v_string, -1, NULL))
return TRUE;
g_scanner_warn (scanner,
_("value for token %s is not a valid UTF-8 string"),
token_name);
g_scanner_error (scanner,
_("value for token %s is not a valid UTF-8 string"),
token_name);
return FALSE;
}
......@@ -2,7 +2,7 @@
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Object properties deserialization routines
* Copyright (C) 2001 Sven Neumann <sven@gimp.org>
* Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
*
* 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
......
......@@ -2,7 +2,7 @@
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Object properties serialization routines
* Copyright (C) 2001 Sven Neumann <sven@gimp.org>
* Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
*
* 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
......@@ -34,6 +34,20 @@
#include "gimpconfig-types.h"
static gboolean gimp_values_equal (const GValue *a,
const GValue *b);
static void serialize_unknown_token (const gchar *key,
const gchar *value,
gpointer data);
/**
* gimp_config_serialize_properties:
* @object: a #GObject.
* @fd: a file descriptor to write to.
*
* This function writes all object properties to the file descriptor @fd.
**/
void
gimp_config_serialize_properties (GObject *object,
gint fd)
......@@ -91,6 +105,92 @@ gimp_config_serialize_properties (GObject *object,
g_string_free (str, TRUE);
}
/**
* gimp_config_serialize_properties:
* @new: a #GObject.
* @old: a #GObject of the same type as @new.
* @fd: a file descriptor to write to.
*
* This function compares the objects @new and @old and writes all properties
* of @new that have different values than @old to the file descriptor @fd.
**/
void
gimp_config_serialize_changed_properties (GObject *new,
GObject *old,
gint fd)
{
GObjectClass *klass;
GParamSpec **property_specs;
guint n_property_specs;
guint i;
GString *str;
g_return_if_fail (G_IS_OBJECT (new));
g_return_if_fail (G_IS_OBJECT (old));
g_return_if_fail (G_TYPE_FROM_INSTANCE (new) == G_TYPE_FROM_INSTANCE (old));
klass = G_OBJECT_GET_CLASS (new);
property_specs = g_object_class_list_properties (klass, &n_property_specs);
if (!property_specs)
return;
str = g_string_new (NULL);
for (i = 0; i < n_property_specs; i++)
{
GParamSpec *prop_spec;
GValue new_value = { 0, };
GValue old_value = { 0, };
prop_spec = property_specs[i];
if (! (prop_spec->flags & G_PARAM_READWRITE))
continue;
g_value_init (&new_value, prop_spec->value_type);
g_value_init (&old_value, prop_spec->value_type);
g_object_get_property (new, prop_spec->name, &new_value);
g_object_get_property (old, prop_spec->name, &old_value);
if (!gimp_values_equal (&new_value, &old_value))
{
g_string_assign (str, "(");
g_string_append (str, prop_spec->name);
if (gimp_config_serialize_value (&new_value, str))
{
g_string_append (str, ")\n");
write (fd, str->str, str->len);
}
else if (prop_spec->value_type != G_TYPE_STRING)
{
g_warning ("couldn't serialize property %s::%s of type %s",
g_type_name (G_TYPE_FROM_INSTANCE (new)),
prop_spec->name,
g_type_name (prop_spec->value_type));
}
}
g_value_unset (&new_value);
g_value_unset (&old_value);
}
g_free (property_specs);
g_string_free (str, TRUE);
}
/**
* gimp_config_serialize_value:
* @value: a #GValue.
* @str: a #Gstring.
*
* This utility function appends a string representation of #GValue to @str.
*
* Return value: %TRUE if serialization succeeded, %FALSE otherwise.
**/
gboolean
gimp_config_serialize_value (const GValue *value,
GString *str)
......@@ -158,12 +258,14 @@ gimp_config_serialize_value (const GValue *value,
return FALSE;
}
static void serialize_unknown_token (const gchar *key,
const gchar *value,
gpointer data);
/**
* gimp_config_serialize_unknown_tokens:
* @object: a #GObject.
* @fd: a file descriptor to write to.
*
* Writes all unknown tokens attached to #object to the file descriptor @fd.
* See gimp_config_add_unknown_token().
**/
void
gimp_config_serialize_unknown_tokens (GObject *object,
gint fd)
......@@ -190,3 +292,37 @@ serialize_unknown_token (const gchar *key,
g_free (escaped);
}
static gboolean
gimp_values_equal (const GValue *a,
const GValue *b)
{
g_return_val_if_fail (G_VALUE_TYPE (a) == G_VALUE_TYPE (b), FALSE);
if (g_value_fits_pointer (a))
{
if (a->data[0].v_pointer == b->data[0].v_pointer)
return TRUE;
if (G_VALUE_HOLDS_STRING (a))
{
const gchar *a_str = g_value_get_string (a);
const gchar *b_str = g_value_get_string (b);
if (a_str && b_str)
return (strcmp (a_str, b_str) == 0);
else
return FALSE;
}
else
{
g_warning ("%s: Can not compare values of type %s.",
G_STRLOC, G_VALUE_TYPE_NAME (a));
return FALSE;
}
}
else
{
return (a->data[0].v_uint64 == b->data[0].v_uint64);
}
}
......@@ -2,7 +2,7 @@
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Object properties serialization routines
* Copyright (C) 2001 Sven Neumann <sven@gimp.org>
* Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
*
* 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
......@@ -23,12 +23,14 @@
#define __GIMP_CONFIG_SERIALIZE_H__
void gimp_config_serialize_properties (GObject *object,
gint fd);
void gimp_config_serialize_unknown_tokens (GObject *object,
gint fd);
gboolean gimp_config_serialize_value (const GValue *value,
GString *str);
void gimp_config_serialize_properties (GObject *object,
gint fd);
void gimp_config_serialize_changed_properties (GObject *new,
GObject *old,
gint fd);
void gimp_config_serialize_unknown_tokens (GObject *object,
gint fd);
gboolean gimp_config_serialize_value (const GValue *value,
GString *str);
#endif /* __GIMP_CONFIG_SERIALIZE_H__ */
......@@ -2,7 +2,7 @@
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Config file serialization and deserialization interface
* Copyright (C) 2001 Sven Neumann <sven@gimp.org>
* Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
*
* 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
......@@ -53,6 +53,9 @@ static void gimp_config_iface_serialize (GObject *object,
gint fd);
static gboolean gimp_config_iface_deserialize (GObject *object,
GScanner *scanner);
static void gimp_config_scanner_message (GScanner *scanner,
gchar *message,
gboolean is_error);
GType
......@@ -102,15 +105,30 @@ gimp_config_iface_deserialize (GObject *object,
return gimp_config_deserialize_properties (object, scanner, FALSE);
}
/**
* gimp_config_serialize:
* @object: a #GObject that implements the #GimpConfigInterface.
* @filename: the name of the file to write the configuration to.
* @error:
*
* Serializes the object properties of @object to the file specified
* by @filename. If a file with that name already exists, it is
* overwritten. Basically this function opens @filename for you and
* calls the serialize function of the @object's #GimpConfigInterface.
*
* Return value: %TRUE if serialization succeeded, %FALSE otherwise.
**/
gboolean
gimp_config_serialize (GObject *object,
const gchar *filename)
const gchar *filename,
GError **error)
{
GimpConfigInterface *gimp_config_iface;
gint fd;
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
g_return_val_if_fail (filename != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
gimp_config_iface = GIMP_GET_CONFIG_INTERFACE (object);
......@@ -118,15 +136,18 @@ gimp_config_serialize (GObject *object,
fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC,
#ifndef G_OS_WIN32
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
#else
_S_IREAD | _S_IWRITE);
_S_IREAD | _S_IWRITE
#endif
);
if (fd == -1)
{
g_message (_("Failed to open file '%s': %s"),
filename, g_strerror (errno));
g_set_error (error,
GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_FILE,
_("Failed to open file: '%s': %s"),
filename, g_strerror (errno));
return FALSE;
}
......@@ -137,9 +158,23 @@ gimp_config_serialize (GObject *object,
return TRUE;
}
/**
* gimp_config_deserialize:
* @object: a #GObject that implements the #GimpConfigInterface.
* @filename: the name of the file to read configuration from.
* @error:
*
* Opens the file specified by @filename, reads configuration data
* from it and configures @object accordingly. Basically this function
* creates a properly configured #GScanner for you and calls the
* deserialize function of the @object's #GimpConfigInterface.
*
* Return value: %TRUE if deserialization succeeded, %FALSE otherwise.
**/
gboolean
gimp_config_deserialize (GObject *object,
const gchar *filename)
const gchar *filename,
GError **error)
{
GimpConfigInterface *gimp_config_iface;
gint fd;
......@@ -148,6 +183,7 @@ gimp_config_deserialize (GObject *object,
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
g_return_val_if_fail (filename != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
gimp_config_iface = GIMP_GET_CONFIG_INTERFACE (object);
......@@ -157,13 +193,20 @@ gimp_config_deserialize (GObject *object,
if (fd == -1)
{
g_message (_("Failed to open file '%s': %s"),
filename, g_strerror (errno));
g_set_error (error,
GIMP_CONFIG_ERROR,
(errno == ENOENT ?
GIMP_CONFIG_ERROR_FILE_ENOENT : GIMP_CONFIG_ERROR_FILE),
_("Failed to open file: '%s': %s"),
filename, g_strerror (errno));
return FALSE;
}
scanner = g_scanner_new (NULL);
scanner->user_data = error;
scanner->msg_handler = gimp_config_scanner_message;
scanner->config->cset_identifier_first = ( G_CSET_a_2_z );
scanner->config->cset_identifier_nth = ( G_CSET_a_2_z "-_" );
......@@ -178,6 +221,31 @@ gimp_config_deserialize (GObject *object,
return success;
}
GQuark
gimp_config_error_quark (void)
{
static GQuark q = 0;
if (q == 0)
q = g_quark_from_static_string ("gimp-config-error-quark");
return q;
}
static void
gimp_config_scanner_message (GScanner *scanner,
gchar *message,
gboolean is_error)
{
GError **error = scanner->user_data;
/* we don't expect warnings */
g_return_if_fail (is_error);
g_set_error (error,
GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
_("Error while parsing '%s' in line %d:\n %s"),
scanner->input_name, scanner->line, message);
}
/*
* Code to store and lookup unknown tokens (string key/value pairs).
......@@ -194,6 +262,20 @@ typedef struct
static void gimp_config_destroy_unknown_tokens (GSList *unknown_tokens);
/**
* gimp_config_add_unknown_token:
* @object: a #GObject.
* @key: a nul-terminated string to identify the value.
* @value: a nul-terminated string representing the value.
*
* This function allows to add arbitrary key/value pairs to a GObject.
* It's purpose is to attach additional data to a #GimpConfig object
* that is stored along with the object properties when serializing
* the object to a configuration file.
*
* If you want to remove a key/value pair from the object, call this
* function with a %NULL @value.
**/
void
gimp_config_add_unknown_token (GObject *object,
const gchar *key,
......@@ -206,7 +288,6 @@ gimp_config_add_unknown_token (GObject *object,
g_return_if_fail (G_IS_OBJECT (object));
g_return_if_fail (key != NULL);
g_return_if_fail (value != NULL);
unknown_tokens = (GSList *) g_object_get_data (object,
GIMP_CONFIG_UNKNOWN_TOKENS);
......@@ -219,9 +300,20 @@ gimp_config_add_unknown_token (GObject *object,
if (strcmp (token->key, key) == 0)
{
/* FIXME: should we emit a warning here ?? */
g_free (token->value);
token->value = g_strdup (value);
if (value)
{
token->value = g_strdup (value);
}
else
{
g_free (token->key);
unknown_tokens = g_slist_remove (unknown_tokens, token);
g_object_set_data_full (object, GIMP_CONFIG_UNKNOWN_TOKENS,
unknown_tokens,
(GDestroyNotify) gimp_config_destroy_unknown_tokens);
}
return;
}
}
......@@ -244,6 +336,15 @@ gimp_config_add_unknown_token (GObject *object,
}
}
/**
* gimp_config_lookup_unknown_token:
* @object: a #GObject.
* @key: a nul-terminated string to identify the value.
*
* This function retrieves data that was previously attached using
* gimp_config_add_unknown_token(). You should not free or modify
* the returned string.
**/
const gchar *
gimp_config_lookup_unknown_token (GObject *object,
const gchar *key)
......@@ -269,6 +370,15 @@ gimp_config_lookup_unknown_token (GObject *object,
return NULL;
}
/**
* gimp_config_foreach_unknown_token:
* @object: a #GObject.
* @func: a function to call for each key/value pair.
* @user_data: data to pass to @func.
*
* Calls @func for each key/value stored with the @object using
* gimp_config_add_unknown_token().
**/
void
gimp_config_foreach_unknown_token (GObject *object,
GimpConfigForeachFunc func,
......@@ -309,3 +419,59 @@ gimp_config_destroy_unknown_tokens (GSList *unknown_tokens)
g_slist_free (unknown_tokens);
}
/*
* Generic code useful when working with config objects.
*/
/**
* gimp_config_duplicate:
* @object: a #GObject object to duplicate.
*
* Creates a copy of the passed object by copying all object properties.
* This only works for objects that are completely defined by their
* properties.
*
* Return value: the duplicated #GObject.
**/
GObject *
gimp_config_duplicate (GObject *object)
{
GObject *dup;
GObjectClass *klass;
GParamSpec **property_specs;
guint n_property_specs;
guint i;
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
dup = g_object_new (G_TYPE_FROM_INSTANCE (object), NULL);
klass = G_OBJECT_GET_CLASS (object);
property_specs = g_object_class_list_properties (klass, &n_property_specs);
if (!property_specs)
return dup;
for (i = 0; i < n_property_specs; i++)
{
GParamSpec *prop_spec;
GValue value = { 0, };
prop_spec = property_specs[i];
if (! (prop_spec->flags & G_PARAM_READWRITE))
continue;
g_value_init (&value, prop_spec->value_type);
g_object_get_property (object, prop_spec->name, &value);
g_object_set_property (dup, prop_spec->name, &value);
}
return dup;
}
......@@ -2,7 +2,7 @@
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
*
* Config file serialization and deserialization interface
* Copyright (C) 2001 Sven Neumann <sven@gimp.org>
* Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
*
* 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
......@@ -32,10 +32,10 @@ struct _GimpConfigInterface
{
GTypeInterface base_iface;
void (* serialize) (GObject *object,
gint fd);
gboolean (* deserialize) (GObject *object,
GScanner *scanner);
void (* serialize) (GObject *object,
gint fd);
gboolean (* deserialize) (GObject *object,
GScanner *scanner);
};
typedef void (* GimpConfigForeachFunc) (const gchar *key,
......@@ -45,19 +45,35 @@ typedef void (* GimpConfigForeachFunc) (const gchar *key,
GType gimp_config_interface_get_type (void) G_GNUC_CONST;
gboolean gimp_config_serialize (GObject *object,
const gchar *filename);
gboolean gimp_config_deserialize (GObject *object,
const gchar *filename);
void gimp_config_add_unknown_token (GObject *object,
const gchar *key,
const gchar *value);
const gchar * gimp_config_lookup_unknown_token (GObject *object,
const gchar *key);
void gimp_config_foreach_unknown_token (GObject *object,
gboolean gimp_config_serialize (GObject *object,
const gchar *filename,
GError **error);
gboolean gimp_config_deserialize (GObject *object,
const gchar *filename,
GError **error);
GObject * gimp_config_duplicate (GObject *object);
void gimp_config_add_unknown_token (GObject *object,
const gchar *key,
const gchar *value);
const gchar * gimp_config_lookup_unknown_token (GObject *object,
const gchar *key);
void gimp_config_foreach_unknown_token (GObject *object,
GimpConfigForeachFunc func,
gpointer user_data);
gpointer user_data);
#define GIMP_CONFIG_ERROR (gimp_config_error_quark ())
GQuark gimp_config_error_quark (void) G_GNUC_CONST;
typedef enum
{
GIMP_CONFIG_ERROR_FILE_ENOENT, /* config file does not exist */
GIMP_CONFIG_ERROR_FILE, /* config file could not be opened */
GIMP_CONFIG_ERROR_PARSE /* config file could not be parsed */
} GimpConfigError;
#endif /* __GIMP_CONFIG_H__ */
/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* GimpRc
* Copyright (C) 2001 Sven Neumann <sven@gimp.org>
* GimpRc, the object for GIMPs user configuration file gimprc.
* Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
*
* 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
......@@ -46,19 +46,13 @@
#include "libgimp/gimpintl.h"
static void gimp_rc_config_iface_init (gpointer iface,
static void gimp_rc_config_iface_init (gpointer iface,
gpointer iface_data);
static void gimp_rc_serialize (GObject *object,
gint fd);
static gboolean gimp_rc_deserialize (GObject *object,
GScanner *scanner);
static void gimp_rc_serialize_changed_properties (GimpRc *new,
GimpRc *old,
gint fd);
static void gimp_rc_write_header (gint fd);
static gboolean gimp_values_equal (const GValue *a,
const GValue *b);
static void gimp_rc_serialize (GObject *object,
gint fd);
static gboolean gimp_rc_deserialize (GObject *object,
GScanner *scanner);
static void gimp_rc_write_header (gint fd);
GType
......@@ -137,54 +131,6 @@ gimp_rc_new (void)
return GIMP_RC (g_object_new (GIMP_TYPE_RC, NULL));