/* LIBPIKA - The PIKA Library * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis * * Config file serialization and deserialization interface * Copyright (C) 2001-2002 Sven Neumann * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include "config.h" #include #include #include "libpikabase/pikabase.h" #include "pikaconfigtypes.h" #include "pikaconfigwriter.h" #include "pikaconfig-iface.h" #include "pikaconfig-deserialize.h" #include "pikaconfig-serialize.h" #include "pikaconfig-params.h" #include "pikaconfig-utils.h" #include "pikascanner.h" #include "libpika/libpika-intl.h" /* * PikaConfigIface: * * The [struct@Config] serialization and deserialization interface. */ /* local function prototypes */ static void pika_config_iface_default_init (PikaConfigInterface *iface); static void pika_config_iface_base_init (PikaConfigInterface *iface); static gboolean pika_config_iface_serialize (PikaConfig *config, PikaConfigWriter *writer, gpointer data); static gboolean pika_config_iface_deserialize (PikaConfig *config, GScanner *scanner, gint nest_level, gpointer data); static PikaConfig * pika_config_iface_duplicate (PikaConfig *config); static gboolean pika_config_iface_equal (PikaConfig *a, PikaConfig *b); static void pika_config_iface_reset (PikaConfig *config); static gboolean pika_config_iface_copy (PikaConfig *src, PikaConfig *dest, GParamFlags flags); /* private functions */ GType pika_config_get_type (void) { static GType config_iface_type = 0; if (! config_iface_type) { const GTypeInfo config_iface_info = { sizeof (PikaConfigInterface), (GBaseInitFunc) pika_config_iface_base_init, (GBaseFinalizeFunc) NULL, (GClassInitFunc) pika_config_iface_default_init, (GClassFinalizeFunc) NULL, }; config_iface_type = g_type_register_static (G_TYPE_INTERFACE, "PikaConfigInterface", &config_iface_info, 0); g_type_interface_add_prerequisite (config_iface_type, G_TYPE_OBJECT); } return config_iface_type; } static void pika_config_iface_default_init (PikaConfigInterface *iface) { iface->serialize = pika_config_iface_serialize; iface->deserialize = pika_config_iface_deserialize; iface->duplicate = pika_config_iface_duplicate; iface->equal = pika_config_iface_equal; iface->reset = pika_config_iface_reset; iface->copy = pika_config_iface_copy; } static void pika_config_iface_base_init (PikaConfigInterface *iface) { /* always set these to NULL since we don't want to inherit them * from parent classes */ iface->serialize_property = NULL; iface->deserialize_property = NULL; } static gboolean pika_config_iface_serialize (PikaConfig *config, PikaConfigWriter *writer, gpointer data) { return pika_config_serialize_properties (config, writer); } static gboolean pika_config_iface_deserialize (PikaConfig *config, GScanner *scanner, gint nest_level, gpointer data) { return pika_config_deserialize_properties (config, scanner, nest_level); } static PikaConfig * pika_config_iface_duplicate (PikaConfig *config) { GObject *object = G_OBJECT (config); GObjectClass *klass = G_OBJECT_GET_CLASS (object); GParamSpec **property_specs; guint n_property_specs; gint n_construct_properties = 0; const gchar **construct_names = NULL; GValue *construct_values = NULL; guint i; GObject *dup; property_specs = g_object_class_list_properties (klass, &n_property_specs); construct_names = g_new0 (const gchar *, n_property_specs); construct_values = g_new0 (GValue, n_property_specs); for (i = 0; i < n_property_specs; i++) { GParamSpec *prop_spec = property_specs[i]; if ((prop_spec->flags & G_PARAM_READABLE) && (prop_spec->flags & G_PARAM_WRITABLE) && (prop_spec->flags & G_PARAM_CONSTRUCT_ONLY)) { construct_names[n_construct_properties] = prop_spec->name; g_value_init (&construct_values[n_construct_properties], prop_spec->value_type); g_object_get_property (object, prop_spec->name, &construct_values[n_construct_properties]); n_construct_properties++; } } g_free (property_specs); dup = g_object_new_with_properties (G_TYPE_FROM_INSTANCE (object), n_construct_properties, (const gchar **) construct_names, (const GValue *) construct_values); for (i = 0; i < n_construct_properties; i++) g_value_unset (&construct_values[i]); g_free (construct_names); g_free (construct_values); pika_config_copy (config, PIKA_CONFIG (dup), 0); return PIKA_CONFIG (dup); } static gboolean pika_config_iface_equal (PikaConfig *a, PikaConfig *b) { GObjectClass *klass; GParamSpec **property_specs; guint n_property_specs; guint i; gboolean equal = TRUE; klass = G_OBJECT_GET_CLASS (a); property_specs = g_object_class_list_properties (klass, &n_property_specs); for (i = 0; equal && i < n_property_specs; i++) { GParamSpec *prop_spec; GValue a_value = G_VALUE_INIT; GValue b_value = G_VALUE_INIT; prop_spec = property_specs[i]; if (! (prop_spec->flags & G_PARAM_READABLE) || (prop_spec->flags & PIKA_CONFIG_PARAM_DONT_COMPARE)) { continue; } g_value_init (&a_value, prop_spec->value_type); g_value_init (&b_value, prop_spec->value_type); g_object_get_property (G_OBJECT (a), prop_spec->name, &a_value); g_object_get_property (G_OBJECT (b), prop_spec->name, &b_value); if (g_param_values_cmp (prop_spec, &a_value, &b_value)) { if ((prop_spec->flags & PIKA_CONFIG_PARAM_AGGREGATE) && G_IS_PARAM_SPEC_OBJECT (prop_spec) && g_type_interface_peek (g_type_class_peek (prop_spec->value_type), PIKA_TYPE_CONFIG)) { if (! pika_config_is_equal_to (g_value_get_object (&a_value), g_value_get_object (&b_value))) { equal = FALSE; } } else { equal = FALSE; } } g_value_unset (&a_value); g_value_unset (&b_value); } g_free (property_specs); return equal; } static void pika_config_iface_reset (PikaConfig *config) { pika_config_reset_properties (G_OBJECT (config)); } static gboolean pika_config_iface_copy (PikaConfig *src, PikaConfig *dest, GParamFlags flags) { return pika_config_sync (G_OBJECT (src), G_OBJECT (dest), flags); } /* public functions */ /** * pika_config_serialize_to_file: * @config: an object that implements [iface@ConfigInterface]. * @file: the file to write the configuration to. * @header: (nullable): optional file header (must be ASCII only) * @footer: (nullable): optional file footer (must be ASCII only) * @data: user data passed to the serialize implementation. * @error: return location for a possible error * * Serializes the object properties of @config to the file specified * by @file. If a file with that name already exists, it is * overwritten. Basically this function opens @file for you and calls * the serialize function of the @config's [iface@ConfigInterface]. * * Returns: %TRUE if serialization succeeded, %FALSE otherwise. * * Since: 2.10 **/ gboolean pika_config_serialize_to_file (PikaConfig *config, GFile *file, const gchar *header, const gchar *footer, gpointer data, GError **error) { PikaConfigWriter *writer; g_return_val_if_fail (PIKA_IS_CONFIG (config), FALSE); g_return_val_if_fail (G_IS_FILE (file), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); writer = pika_config_writer_new_from_file (file, TRUE, header, error); if (!writer) return FALSE; PIKA_CONFIG_GET_IFACE (config)->serialize (config, writer, data); return pika_config_writer_finish (writer, footer, error); } /** * pika_config_serialize_to_stream: * @config: an object that implements [iface@ConfigInterface]. * @output: the #GOutputStream to write the configuration to. * @header: (nullable): optional file header (must be ASCII only) * @footer: (nullable): optional file footer (must be ASCII only) * @data: user data passed to the serialize implementation. * @error: return location for a possible error * * Serializes the object properties of @config to the stream specified * by @output. * * Returns: Whether serialization succeeded. * * Since: 2.10 **/ gboolean pika_config_serialize_to_stream (PikaConfig *config, GOutputStream *output, const gchar *header, const gchar *footer, gpointer data, GError **error) { PikaConfigWriter *writer; g_return_val_if_fail (PIKA_IS_CONFIG (config), FALSE); g_return_val_if_fail (G_IS_OUTPUT_STREAM (output), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); writer = pika_config_writer_new_from_stream (output, header, error); if (!writer) return FALSE; PIKA_CONFIG_GET_IFACE (config)->serialize (config, writer, data); return pika_config_writer_finish (writer, footer, error); } /** * pika_config_serialize_to_fd: * @config: an object that implements [iface@ConfigInterface]. * @fd: a file descriptor, opened for writing * @data: user data passed to the serialize implementation. * * Serializes the object properties of @config to the given file * descriptor. * * Returns: %TRUE if serialization succeeded, %FALSE otherwise. * * Since: 2.4 **/ gboolean pika_config_serialize_to_fd (PikaConfig *config, gint fd, gpointer data) { PikaConfigWriter *writer; g_return_val_if_fail (PIKA_IS_CONFIG (config), FALSE); g_return_val_if_fail (fd > 0, FALSE); writer = pika_config_writer_new_from_fd (fd); if (!writer) return FALSE; PIKA_CONFIG_GET_IFACE (config)->serialize (config, writer, data); return pika_config_writer_finish (writer, NULL, NULL); } /** * pika_config_serialize_to_string: * @config: an object that implements the [iface@ConfigInterface]. * @data: user data passed to the serialize implementation. * * Serializes the object properties of @config to a string. * * Returns: a newly allocated NUL-terminated string. * * Since: 2.4 **/ gchar * pika_config_serialize_to_string (PikaConfig *config, gpointer data) { PikaConfigWriter *writer; GString *str; g_return_val_if_fail (PIKA_IS_CONFIG (config), NULL); str = g_string_new (NULL); writer = pika_config_writer_new_from_string (str); PIKA_CONFIG_GET_IFACE (config)->serialize (config, writer, data); pika_config_writer_finish (writer, NULL, NULL); return g_string_free (str, FALSE); } /** * pika_config_serialize_to_parasite: * @config: an object that implements the [iface@ConfigInterface]. * @parasite_name: the new parasite's name * @parasite_flags: the new parasite's flags * @data: user data passed to the serialize implementation. * * Serializes the object properties of @config to a [struct@Parasite]. * * Returns: (transfer full): the newly allocated parasite. * * Since: 3.0 **/ PikaParasite * pika_config_serialize_to_parasite (PikaConfig *config, const gchar *parasite_name, guint parasite_flags, gpointer data) { PikaParasite *parasite; gchar *str; g_return_val_if_fail (PIKA_IS_CONFIG (config), NULL); g_return_val_if_fail (parasite_name != NULL, NULL); str = pika_config_serialize_to_string (config, data); if (! str) return NULL; parasite = pika_parasite_new (parasite_name, parasite_flags, 0, NULL); parasite->size = strlen (str) + 1; parasite->data = str; return parasite; } /** * pika_config_deserialize_file: * @config: an object that implements the #PikaConfigInterface. * @file: the file to read configuration from. * @data: user data passed to the deserialize implementation. * @error: return location for a possible error * * Opens the file specified by @file, reads configuration data from it * and configures @config accordingly. Basically this function creates * a properly configured [struct@GLib.Scanner] for you and calls the deserialize * function of the @config's [iface@ConfigInterface]. * * Returns: Whether deserialization succeeded. * * Since: 2.10 **/ gboolean pika_config_deserialize_file (PikaConfig *config, GFile *file, gpointer data, GError **error) { GScanner *scanner; gboolean success; g_return_val_if_fail (PIKA_IS_CONFIG (config), FALSE); g_return_val_if_fail (G_IS_FILE (file), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); scanner = pika_scanner_new_file (file, error); if (! scanner) return FALSE; g_object_freeze_notify (G_OBJECT (config)); success = PIKA_CONFIG_GET_IFACE (config)->deserialize (config, scanner, 0, data); g_object_thaw_notify (G_OBJECT (config)); pika_scanner_unref (scanner); if (! success) /* If we get this assert, it means we have a bug in one of the * deserialize() implementations. Any failure case should report the * error condition with g_scanner_error() which will populate the * error object passed in pika_scanner_new*(). */ g_assert (error == NULL || *error != NULL); return success; } /** * pika_config_deserialize_stream: * @config: an object that implements the #PikaConfigInterface. * @input: the input stream to read configuration from. * @data: user data passed to the deserialize implementation. * @error: return location for a possible error * * Reads configuration data from @input and configures @config * accordingly. Basically this function creates a properly configured * #GScanner for you and calls the deserialize function of the * @config's #PikaConfigInterface. * * Returns: Whether deserialization succeeded. * * Since: 2.10 **/ gboolean pika_config_deserialize_stream (PikaConfig *config, GInputStream *input, gpointer data, GError **error) { GScanner *scanner; gboolean success; g_return_val_if_fail (PIKA_IS_CONFIG (config), FALSE); g_return_val_if_fail (G_IS_INPUT_STREAM (input), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); scanner = pika_scanner_new_stream (input, error); if (! scanner) return FALSE; g_object_freeze_notify (G_OBJECT (config)); success = PIKA_CONFIG_GET_IFACE (config)->deserialize (config, scanner, 0, data); g_object_thaw_notify (G_OBJECT (config)); pika_scanner_unref (scanner); if (! success) g_assert (error == NULL || *error != NULL); return success; } /** * pika_config_deserialize_string: * @config: a #GObject that implements the #PikaConfigInterface. * @text: (array length=text_len): string to deserialize (in UTF-8 encoding) * @text_len: length of @text in bytes or -1 * @data: client data * @error: return location for a possible error * * Configures @config from @text. Basically this function creates a * properly configured #GScanner for you and calls the deserialize * function of the @config's #PikaConfigInterface. * * Returns: %TRUE if deserialization succeeded, %FALSE otherwise. * * Since: 2.4 **/ gboolean pika_config_deserialize_string (PikaConfig *config, const gchar *text, gint text_len, gpointer data, GError **error) { GScanner *scanner; gboolean success; g_return_val_if_fail (PIKA_IS_CONFIG (config), FALSE); g_return_val_if_fail (text != NULL || text_len == 0, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); scanner = pika_scanner_new_string (text, text_len, error); g_object_freeze_notify (G_OBJECT (config)); success = PIKA_CONFIG_GET_IFACE (config)->deserialize (config, scanner, 0, data); g_object_thaw_notify (G_OBJECT (config)); pika_scanner_unref (scanner); if (! success) g_assert (error == NULL || *error != NULL); return success; } /** * pika_config_deserialize_parasite: * @config: a #GObject that implements the #PikaConfigInterface. * @parasite: parasite containing a serialized config string * @data: client data * @error: return location for a possible error * * Configures @config from @parasite. Basically this function creates * a properly configured #GScanner for you and calls the deserialize * function of the @config's #PikaConfigInterface. * * Returns: %TRUE if deserialization succeeded, %FALSE otherwise. * * Since: 3.0 **/ gboolean pika_config_deserialize_parasite (PikaConfig *config, const PikaParasite *parasite, gpointer data, GError **error) { const gchar *parasite_data; guint32 parasite_size; g_return_val_if_fail (PIKA_IS_CONFIG (config), FALSE); g_return_val_if_fail (parasite != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); parasite_data = pika_parasite_get_data (parasite, ¶site_size); if (! parasite_data) return TRUE; return pika_config_deserialize_string (config, parasite_data, parasite_size, data, error); } /** * pika_config_deserialize_return: * @scanner: a #GScanner * @expected_token: the expected token * @nest_level: the nest level * * Returns: * * Since: 2.4 **/ gboolean pika_config_deserialize_return (GScanner *scanner, GTokenType expected_token, gint nest_level) { GTokenType next_token; g_return_val_if_fail (scanner != NULL, FALSE); next_token = g_scanner_peek_next_token (scanner); if (expected_token != G_TOKEN_LEFT_PAREN) { g_scanner_get_next_token (scanner); g_scanner_unexp_token (scanner, expected_token, NULL, NULL, NULL, _("fatal parse error"), TRUE); return FALSE; } else { if (nest_level > 0 && next_token == G_TOKEN_RIGHT_PAREN) { return TRUE; } else if (next_token != G_TOKEN_EOF) { g_scanner_get_next_token (scanner); g_scanner_unexp_token (scanner, expected_token, NULL, NULL, NULL, _("fatal parse error"), TRUE); return FALSE; } } return TRUE; } /** * pika_config_serialize: * @config: an object that implements the #PikaConfigInterface. * @writer: the #PikaConfigWriter to use. * @data: client data * * Serialize the #PikaConfig object. * * Returns: Whether serialization succeeded. * * Since: 2.8 **/ gboolean pika_config_serialize (PikaConfig *config, PikaConfigWriter *writer, gpointer data) { g_return_val_if_fail (PIKA_IS_CONFIG (config), FALSE); return PIKA_CONFIG_GET_IFACE (config)->serialize (config, writer, data); } /** * pika_config_deserialize: * @config: a #GObject that implements the #PikaConfigInterface. * @scanner: the #GScanner to use. * @nest_level: the nest level. * @data: client data. * * Deserialize the #PikaConfig object. * * Returns: Whether serialization succeeded. * * Since: 2.8 **/ gboolean pika_config_deserialize (PikaConfig *config, GScanner *scanner, gint nest_level, gpointer data) { g_return_val_if_fail (PIKA_IS_CONFIG (config), FALSE); return PIKA_CONFIG_GET_IFACE (config)->deserialize (config, scanner, nest_level, data); } /** * pika_config_duplicate: * @config: a #GObject that implements the #PikaConfigInterface. * * Creates a copy of the passed object by copying all object * properties. The default implementation of the #PikaConfigInterface * only works for objects that are completely defined by their * properties. * * Returns: the duplicated #PikaConfig object * * Since: 2.4 **/ gpointer pika_config_duplicate (PikaConfig *config) { g_return_val_if_fail (PIKA_IS_CONFIG (config), NULL); return PIKA_CONFIG_GET_IFACE (config)->duplicate (config); } /** * pika_config_is_equal_to: * @a: a #GObject that implements the #PikaConfigInterface. * @b: another #GObject of the same type as @a. * * Compares the two objects. The default implementation of the * #PikaConfigInterface compares the object properties and thus only * works for objects that are completely defined by their * properties. * * Returns: %TRUE if the two objects are equal. * * Since: 2.4 **/ gboolean pika_config_is_equal_to (PikaConfig *a, PikaConfig *b) { g_return_val_if_fail (PIKA_IS_CONFIG (a), FALSE); g_return_val_if_fail (PIKA_IS_CONFIG (b), FALSE); g_return_val_if_fail (G_TYPE_FROM_INSTANCE (a) == G_TYPE_FROM_INSTANCE (b), FALSE); return PIKA_CONFIG_GET_IFACE (a)->equal (a, b); } /** * pika_config_reset: * @config: a #GObject that implements the #PikaConfigInterface. * * Resets the object to its default state. The default implementation of the * #PikaConfigInterface only works for objects that are completely defined by * their properties. * * Since: 2.4 **/ void pika_config_reset (PikaConfig *config) { g_return_if_fail (PIKA_IS_CONFIG (config)); g_object_freeze_notify (G_OBJECT (config)); PIKA_CONFIG_GET_IFACE (config)->reset (config); g_object_thaw_notify (G_OBJECT (config)); } /** * pika_config_copy: * @src: a #GObject that implements the #PikaConfigInterface. * @dest: another #GObject of the same type as @a. * @flags: a mask of GParamFlags * * Compares all read- and write-able properties from @src and @dest * that have all @flags set. Differing values are then copied from * @src to @dest. If @flags is 0, all differing read/write properties. * * Properties marked as "construct-only" are not touched. * * Returns: %TRUE if @dest was modified, %FALSE otherwise * * Since: 2.6 **/ gboolean pika_config_copy (PikaConfig *src, PikaConfig *dest, GParamFlags flags) { gboolean changed; g_return_val_if_fail (PIKA_IS_CONFIG (src), FALSE); g_return_val_if_fail (PIKA_IS_CONFIG (dest), FALSE); g_return_val_if_fail (G_TYPE_FROM_INSTANCE (src) == G_TYPE_FROM_INSTANCE (dest), FALSE); g_object_freeze_notify (G_OBJECT (dest)); changed = PIKA_CONFIG_GET_IFACE (src)->copy (src, dest, flags); g_object_thaw_notify (G_OBJECT (dest)); return changed; }