diff options
Diffstat (limited to '')
-rw-r--r-- | libgimpconfig/gimpconfig-deserialize.c | 1019 |
1 files changed, 1019 insertions, 0 deletions
diff --git a/libgimpconfig/gimpconfig-deserialize.c b/libgimpconfig/gimpconfig-deserialize.c new file mode 100644 index 0000000..55cbf50 --- /dev/null +++ b/libgimpconfig/gimpconfig-deserialize.c @@ -0,0 +1,1019 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * Object properties deserialization routines + * Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org> + * + * 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 + * <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <cairo.h> +#include <gegl.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "gimpconfigtypes.h" + +#include "gimpconfigwriter.h" +#include "gimpconfig-iface.h" +#include "gimpconfig-deserialize.h" +#include "gimpconfig-params.h" +#include "gimpconfig-path.h" +#include "gimpscanner.h" + +#include "libgimp/libgimp-intl.h" + + +/** + * SECTION: gimpconfig-deserialize + * @title: GimpConfig-deserialize + * @short_description: Deserializing code for libgimpconfig. + * + * Deserializing code for libgimpconfig. + **/ + + +/* + * All functions return G_TOKEN_RIGHT_PAREN on success, + * the GTokenType they would have expected but didn't get + * or G_TOKEN_NONE if they got the expected token but + * couldn't parse it. + */ + +static GTokenType gimp_config_deserialize_value (GValue *value, + GimpConfig *config, + GParamSpec *prop_spec, + GScanner *scanner); +static GTokenType gimp_config_deserialize_fundamental (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner); +static GTokenType gimp_config_deserialize_enum (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner); +static GTokenType gimp_config_deserialize_memsize (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner); +static GTokenType gimp_config_deserialize_path (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner); +static GTokenType gimp_config_deserialize_rgb (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner); +static GTokenType gimp_config_deserialize_matrix2 (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner); +static GTokenType gimp_config_deserialize_object (GValue *value, + GimpConfig *config, + GParamSpec *prop_spec, + GScanner *scanner, + gint nest_level); +static GTokenType gimp_config_deserialize_value_array (GValue *value, + GimpConfig *config, + GParamSpec *prop_spec, + GScanner *scanner); +static GTokenType gimp_config_deserialize_unit (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner); +static GTokenType gimp_config_deserialize_file_value (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner); +static GTokenType gimp_config_deserialize_any (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner); +static GTokenType gimp_config_skip_unknown_property (GScanner *scanner); + +static inline gboolean scanner_string_utf8_valid (GScanner *scanner, + const gchar *token_name); + +static inline gboolean +scanner_string_utf8_valid (GScanner *scanner, + const gchar *token_name) +{ + if (g_utf8_validate (scanner->value.v_string, -1, NULL)) + return TRUE; + + g_scanner_error (scanner, + _("value for token %s is not a valid UTF-8 string"), + token_name); + + return FALSE; +} + +/** + * gimp_config_deserialize_properties: + * @config: a #GimpConfig. + * @scanner: a #GScanner. + * @nest_level: the nest level + * + * This function uses the @scanner to configure the properties of @config. + * + * Return value: %TRUE on success, %FALSE otherwise. + * + * Since: 2.4 + **/ +gboolean +gimp_config_deserialize_properties (GimpConfig *config, + GScanner *scanner, + gint nest_level) +{ + GObjectClass *klass; + GParamSpec **property_specs; + guint n_property_specs; + guint i; + guint scope_id; + guint old_scope_id; + GTokenType token; + + g_return_val_if_fail (GIMP_IS_CONFIG (config), FALSE); + + klass = G_OBJECT_GET_CLASS (config); + property_specs = g_object_class_list_properties (klass, &n_property_specs); + + if (! property_specs) + return TRUE; + + scope_id = g_type_qname (G_TYPE_FROM_INSTANCE (config)); + old_scope_id = g_scanner_set_scope (scanner, scope_id); + + for (i = 0; i < n_property_specs; i++) + { + GParamSpec *prop_spec = property_specs[i]; + + if (prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE) + { + g_scanner_scope_add_symbol (scanner, scope_id, + prop_spec->name, prop_spec); + } + } + + g_free (property_specs); + + g_object_freeze_notify (G_OBJECT (config)); + + token = G_TOKEN_LEFT_PAREN; + + while (TRUE) + { + GTokenType next = g_scanner_peek_next_token (scanner); + + if (next == G_TOKEN_EOF) + break; + + if (G_UNLIKELY (next != token && + ! (token == G_TOKEN_SYMBOL && + next == G_TOKEN_IDENTIFIER))) + { + break; + } + + token = g_scanner_get_next_token (scanner); + + switch (token) + { + case G_TOKEN_LEFT_PAREN: + token = G_TOKEN_SYMBOL; + break; + + case G_TOKEN_IDENTIFIER: + token = gimp_config_skip_unknown_property (scanner); + break; + + case G_TOKEN_SYMBOL: + token = gimp_config_deserialize_property (config, + scanner, nest_level); + break; + + case G_TOKEN_RIGHT_PAREN: + token = G_TOKEN_LEFT_PAREN; + break; + + default: /* do nothing */ + break; + } + } + + g_scanner_set_scope (scanner, old_scope_id); + + g_object_thaw_notify (G_OBJECT (config)); + + if (token == G_TOKEN_NONE) + return FALSE; + + return gimp_config_deserialize_return (scanner, token, nest_level); +} + +/** + * gimp_config_deserialize_property: + * @config: a #GimpConfig. + * @scanner: a #GScanner. + * @nest_level: the nest level + * + * This function deserializes a single property of @config. You + * shouldn't need to call this function directly. If possible, use + * gimp_config_deserialize_properties() instead. + * + * Return value: %G_TOKEN_RIGHT_PAREN on success, otherwise the + * expected #GTokenType or %G_TOKEN_NONE if the expected token was + * found but couldn't be parsed. + * + * Since: 2.4 + **/ +GTokenType +gimp_config_deserialize_property (GimpConfig *config, + GScanner *scanner, + gint nest_level) +{ + GimpConfigInterface *config_iface = NULL; + GimpConfigInterface *parent_iface = NULL; + GParamSpec *prop_spec; + GTokenType token = G_TOKEN_RIGHT_PAREN; + GValue value = G_VALUE_INIT; + guint old_scope_id; + + old_scope_id = g_scanner_set_scope (scanner, 0); + + prop_spec = G_PARAM_SPEC (scanner->value.v_symbol); + + g_value_init (&value, prop_spec->value_type); + + if (G_TYPE_IS_OBJECT (prop_spec->owner_type)) + { + GTypeClass *owner_class = g_type_class_peek (prop_spec->owner_type); + + config_iface = g_type_interface_peek (owner_class, GIMP_TYPE_CONFIG); + + /* We must call deserialize_property() *only* if the *exact* class + * which implements it is param_spec->owner_type's class. + * + * Therefore, we ask param_spec->owner_type's immediate parent class + * for it's GimpConfigInterface and check if we get a different + * pointer. + * + * (if the pointers are the same, param_spec->owner_type's + * GimpConfigInterface is inherited from one of it's parent classes + * and thus not able to handle param_spec->owner_type's properties). + */ + if (config_iface) + { + GTypeClass *owner_parent_class; + + owner_parent_class = g_type_class_peek_parent (owner_class); + + parent_iface = g_type_interface_peek (owner_parent_class, + GIMP_TYPE_CONFIG); + } + } + + if (config_iface && + config_iface != parent_iface && /* see comment above */ + config_iface->deserialize_property && + config_iface->deserialize_property (config, + prop_spec->param_id, + &value, + prop_spec, + scanner, + &token)) + { + /* nop */ + } + else + { + if (G_VALUE_HOLDS_OBJECT (&value) && + G_VALUE_TYPE (&value) != G_TYPE_FILE) + { + token = gimp_config_deserialize_object (&value, + config, prop_spec, + scanner, nest_level); + } + else + { + token = gimp_config_deserialize_value (&value, + config, prop_spec, scanner); + } + } + + if (token == G_TOKEN_RIGHT_PAREN && + g_scanner_peek_next_token (scanner) == token) + { + if (! (G_VALUE_HOLDS_OBJECT (&value) && + (prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE))) + g_object_set_property (G_OBJECT (config), prop_spec->name, &value); + } +#ifdef CONFIG_DEBUG + else + { + g_warning ("%s: couldn't deserialize property %s::%s of type %s", + G_STRFUNC, + g_type_name (G_TYPE_FROM_INSTANCE (config)), + prop_spec->name, + g_type_name (prop_spec->value_type)); + } +#endif + + g_value_unset (&value); + + g_scanner_set_scope (scanner, old_scope_id); + + return token; +} + +static GTokenType +gimp_config_deserialize_value (GValue *value, + GimpConfig *config, + GParamSpec *prop_spec, + GScanner *scanner) +{ + if (G_TYPE_FUNDAMENTAL (prop_spec->value_type) == G_TYPE_ENUM) + { + return gimp_config_deserialize_enum (value, prop_spec, scanner); + } + else if (G_TYPE_IS_FUNDAMENTAL (prop_spec->value_type)) + { + return gimp_config_deserialize_fundamental (value, prop_spec, scanner); + } + else if (prop_spec->value_type == GIMP_TYPE_MEMSIZE) + { + return gimp_config_deserialize_memsize (value, prop_spec, scanner); + } + else if (prop_spec->value_type == GIMP_TYPE_CONFIG_PATH) + { + return gimp_config_deserialize_path (value, prop_spec, scanner); + } + else if (prop_spec->value_type == GIMP_TYPE_RGB) + { + return gimp_config_deserialize_rgb (value, prop_spec, scanner); + } + else if (prop_spec->value_type == GIMP_TYPE_MATRIX2) + { + return gimp_config_deserialize_matrix2 (value, prop_spec, scanner); + } + else if (prop_spec->value_type == GIMP_TYPE_VALUE_ARRAY) + { + return gimp_config_deserialize_value_array (value, + config, prop_spec, scanner); + } + else if (prop_spec->value_type == GIMP_TYPE_UNIT) + { + return gimp_config_deserialize_unit (value, prop_spec, scanner); + } + else if (prop_spec->value_type == G_TYPE_FILE) + { + return gimp_config_deserialize_file_value (value, prop_spec, scanner); + } + + /* This fallback will only work for value_types that + * can be transformed from a string value. + */ + return gimp_config_deserialize_any (value, prop_spec, scanner); +} + +static GTokenType +gimp_config_deserialize_fundamental (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner) +{ + GTokenType token; + GTokenType next_token; + GType value_type; + gboolean negate = FALSE; + + value_type = G_TYPE_FUNDAMENTAL (prop_spec->value_type); + + switch (value_type) + { + case G_TYPE_STRING: + token = G_TOKEN_STRING; + break; + + case G_TYPE_BOOLEAN: + token = G_TOKEN_IDENTIFIER; + break; + + case G_TYPE_INT: + case G_TYPE_LONG: + case G_TYPE_INT64: + if (g_scanner_peek_next_token (scanner) == '-') + { + negate = TRUE; + g_scanner_get_next_token (scanner); + } + /* fallthrough */ + case G_TYPE_UINT: + case G_TYPE_ULONG: + case G_TYPE_UINT64: + token = G_TOKEN_INT; + break; + + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + if (g_scanner_peek_next_token (scanner) == '-') + { + negate = TRUE; + g_scanner_get_next_token (scanner); + } + token = G_TOKEN_FLOAT; + break; + + default: + g_assert_not_reached (); + break; + } + + next_token = g_scanner_peek_next_token (scanner); + + /* we parse integers into floats too, because g_ascii_dtostr() + * serialized whole number without decimal point + */ + if (next_token != token && + ! (token == G_TOKEN_FLOAT && next_token == G_TOKEN_INT)) + { + return token; + } + + g_scanner_get_next_token (scanner); + + switch (value_type) + { + case G_TYPE_STRING: + if (scanner_string_utf8_valid (scanner, prop_spec->name)) + g_value_set_string (value, scanner->value.v_string); + else + return G_TOKEN_NONE; + break; + + case G_TYPE_BOOLEAN: + if (! g_ascii_strcasecmp (scanner->value.v_identifier, "yes") || + ! g_ascii_strcasecmp (scanner->value.v_identifier, "true")) + g_value_set_boolean (value, TRUE); + else if (! g_ascii_strcasecmp (scanner->value.v_identifier, "no") || + ! g_ascii_strcasecmp (scanner->value.v_identifier, "false")) + g_value_set_boolean (value, FALSE); + else + { + g_scanner_error + (scanner, + /* please don't translate 'yes' and 'no' */ + _("expected 'yes' or 'no' for boolean token %s, got '%s'"), + prop_spec->name, scanner->value.v_identifier); + return G_TOKEN_NONE; + } + break; + + case G_TYPE_INT: + g_value_set_int (value, (negate ? + - (gint) scanner->value.v_int64 : + (gint) scanner->value.v_int64)); + break; + case G_TYPE_UINT: + g_value_set_uint (value, scanner->value.v_int64); + break; + + case G_TYPE_LONG: + g_value_set_long (value, (negate ? + - (glong) scanner->value.v_int64 : + (glong) scanner->value.v_int64)); + break; + case G_TYPE_ULONG: + g_value_set_ulong (value, scanner->value.v_int64); + break; + + case G_TYPE_INT64: + g_value_set_int64 (value, (negate ? + - (gint64) scanner->value.v_int64 : + (gint64) scanner->value.v_int64)); + break; + case G_TYPE_UINT64: + g_value_set_uint64 (value, scanner->value.v_int64); + break; + + case G_TYPE_FLOAT: + if (next_token == G_TOKEN_FLOAT) + g_value_set_float (value, negate ? + - scanner->value.v_float : + scanner->value.v_float); + else + g_value_set_float (value, negate ? + - (gfloat) scanner->value.v_int : + (gfloat) scanner->value.v_int); + break; + + case G_TYPE_DOUBLE: + if (next_token == G_TOKEN_FLOAT) + g_value_set_double (value, negate ? + - scanner->value.v_float: + scanner->value.v_float); + else + g_value_set_double (value, negate ? + - (gdouble) scanner->value.v_int: + (gdouble) scanner->value.v_int); + break; + + default: + g_assert_not_reached (); + break; + } + + return G_TOKEN_RIGHT_PAREN; +} + +static GTokenType +gimp_config_deserialize_enum (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner) +{ + GEnumClass *enum_class; + GEnumValue *enum_value; + + enum_class = g_type_class_peek (G_VALUE_TYPE (value)); + + switch (g_scanner_peek_next_token (scanner)) + { + case G_TOKEN_IDENTIFIER: + g_scanner_get_next_token (scanner); + + enum_value = g_enum_get_value_by_nick (enum_class, + scanner->value.v_identifier); + if (! enum_value) + enum_value = g_enum_get_value_by_name (enum_class, + scanner->value.v_identifier); + if (! enum_value) + { + /* if the value was not found, check if we have a compat + * enum to find the ideitifier + */ + GQuark quark = g_quark_from_static_string ("gimp-compat-enum"); + GType compat_type = (GType) g_type_get_qdata (G_VALUE_TYPE (value), + quark); + + if (compat_type) + { + GEnumClass *compat_class = g_type_class_ref (compat_type); + + enum_value = g_enum_get_value_by_nick (compat_class, + scanner->value.v_identifier); + if (! enum_value) + enum_value = g_enum_get_value_by_name (compat_class, + scanner->value.v_identifier); + + /* finally, if we found a compat value, make sure the + * same value exists in the original enum + */ + if (enum_value) + enum_value = g_enum_get_value (enum_class, enum_value->value); + + g_type_class_unref (compat_class); + } + } + + if (! enum_value) + { + g_scanner_error (scanner, + _("invalid value '%s' for token %s"), + scanner->value.v_identifier, prop_spec->name); + return G_TOKEN_NONE; + } + break; + + case G_TOKEN_INT: + g_scanner_get_next_token (scanner); + + enum_value = g_enum_get_value (enum_class, + (gint) scanner->value.v_int64); + + if (! enum_value) + { + g_scanner_error (scanner, + _("invalid value '%ld' for token %s"), + (glong) scanner->value.v_int64, prop_spec->name); + return G_TOKEN_NONE; + } + break; + + default: + return G_TOKEN_IDENTIFIER; + } + + g_value_set_enum (value, enum_value->value); + + return G_TOKEN_RIGHT_PAREN; +} + +static GTokenType +gimp_config_deserialize_memsize (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner) +{ + gchar *orig_cset_first = scanner->config->cset_identifier_first; + gchar *orig_cset_nth = scanner->config->cset_identifier_nth; + guint64 memsize; + + scanner->config->cset_identifier_first = G_CSET_DIGITS; + scanner->config->cset_identifier_nth = G_CSET_DIGITS "gGmMkKbB"; + + if (g_scanner_peek_next_token (scanner) != G_TOKEN_IDENTIFIER) + return G_TOKEN_IDENTIFIER; + + g_scanner_get_next_token (scanner); + + scanner->config->cset_identifier_first = orig_cset_first; + scanner->config->cset_identifier_nth = orig_cset_nth; + + if (! gimp_memsize_deserialize (scanner->value.v_identifier, &memsize)) + return G_TOKEN_NONE; + + g_value_set_uint64 (value, memsize); + + return G_TOKEN_RIGHT_PAREN; +} + +static GTokenType +gimp_config_deserialize_path (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner) +{ + GError *error = NULL; + + if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING) + return G_TOKEN_STRING; + + g_scanner_get_next_token (scanner); + + if (!scanner_string_utf8_valid (scanner, prop_spec->name)) + return G_TOKEN_NONE; + + if (scanner->value.v_string) + { + /* Check if the string can be expanded + * and converted to the filesystem encoding. + */ + gchar *expand = gimp_config_path_expand (scanner->value.v_string, + TRUE, &error); + + if (!expand) + { + g_scanner_error (scanner, + _("while parsing token '%s': %s"), + prop_spec->name, error->message); + g_error_free (error); + + return G_TOKEN_NONE; + } + + g_free (expand); + + g_value_set_static_string (value, scanner->value.v_string); + } + + return G_TOKEN_RIGHT_PAREN; +} + +static GTokenType +gimp_config_deserialize_rgb (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner) +{ + GimpRGB rgb; + + if (! gimp_scanner_parse_color (scanner, &rgb)) + return G_TOKEN_NONE; + + g_value_set_boxed (value, &rgb); + + return G_TOKEN_RIGHT_PAREN; +} + +static GTokenType +gimp_config_deserialize_matrix2 (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner) +{ + GimpMatrix2 matrix; + + if (! gimp_scanner_parse_matrix2 (scanner, &matrix)) + return G_TOKEN_NONE; + + g_value_set_boxed (value, &matrix); + + return G_TOKEN_RIGHT_PAREN; +} + +static GTokenType +gimp_config_deserialize_object (GValue *value, + GimpConfig *config, + GParamSpec *prop_spec, + GScanner *scanner, + gint nest_level) +{ + GimpConfigInterface *config_iface; + GimpConfig *prop_object; + + g_object_get_property (G_OBJECT (config), prop_spec->name, value); + + prop_object = g_value_get_object (value); + + if (! prop_object) + { + /* if the object property is not GIMP_CONFIG_PARAM_AGGREGATE, read + * the type of the object and create it + */ + if (! (prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE)) + { + gchar *type_name; + GType type; + + if (! gimp_scanner_parse_string (scanner, &type_name)) + return G_TOKEN_STRING; + + if (! (type_name && *type_name)) + { + g_scanner_error (scanner, "Type name is empty"); + g_free (type_name); + return G_TOKEN_NONE; + } + + type = g_type_from_name (type_name); + g_free (type_name); + + if (! g_type_is_a (type, prop_spec->value_type)) + return G_TOKEN_STRING; + + prop_object = g_object_new (type, NULL); + + g_value_take_object (value, prop_object); + } + else + { + return G_TOKEN_RIGHT_PAREN; + } + } + + config_iface = GIMP_CONFIG_GET_INTERFACE (prop_object); + + if (! config_iface) + return gimp_config_deserialize_any (value, prop_spec, scanner); + + if (! config_iface->deserialize (prop_object, scanner, nest_level + 1, NULL)) + return G_TOKEN_NONE; + + return G_TOKEN_RIGHT_PAREN; +} + +static GTokenType +gimp_config_deserialize_value_array (GValue *value, + GimpConfig *config, + GParamSpec *prop_spec, + GScanner *scanner) +{ + GimpParamSpecValueArray *array_spec; + GimpValueArray *array; + GValue array_value = G_VALUE_INIT; + gint n_values; + GTokenType token; + gint i; + + array_spec = GIMP_PARAM_SPEC_VALUE_ARRAY (prop_spec); + + if (! gimp_scanner_parse_int (scanner, &n_values)) + return G_TOKEN_INT; + + array = gimp_value_array_new (n_values); + + for (i = 0; i < n_values; i++) + { + g_value_init (&array_value, array_spec->element_spec->value_type); + + token = gimp_config_deserialize_value (&array_value, + config, + array_spec->element_spec, + scanner); + + if (token == G_TOKEN_RIGHT_PAREN) + gimp_value_array_append (array, &array_value); + + g_value_unset (&array_value); + + if (token != G_TOKEN_RIGHT_PAREN) + return token; + } + + g_value_take_boxed (value, array); + + return G_TOKEN_RIGHT_PAREN; +} + +/* This function is entirely sick, so is our method of serializing + * units, which we write out as (unit foo bar) instead of + * (unit "foo bar"). The assumption that caused this shit was that a + * unit's "identifier" is really an identifier in the C-ish sense, + * when in fact it's just a random user entered string. + * + * Here, we try to parse at least the default units shipped with gimp, + * and we add code to parse (unit "foo bar") in order to be compatible + * with future correct unit serializing. + */ +static GTokenType +gimp_config_deserialize_unit (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner) +{ + gchar *old_cset_skip_characters; + gchar *old_cset_identifier_first; + gchar *old_cset_identifier_nth; + GString *buffer; + GValue src = G_VALUE_INIT; + GTokenType token; + + /* parse the next token *before* reconfiguring the scanner, so it + * skips whitespace first + */ + token = g_scanner_peek_next_token (scanner); + + if (token == G_TOKEN_STRING) + return gimp_config_deserialize_any (value, prop_spec, scanner); + + old_cset_skip_characters = scanner->config->cset_skip_characters; + old_cset_identifier_first = scanner->config->cset_identifier_first; + old_cset_identifier_nth = scanner->config->cset_identifier_nth; + + scanner->config->cset_skip_characters = ""; + scanner->config->cset_identifier_first = ( G_CSET_a_2_z G_CSET_A_2_Z "." ); + scanner->config->cset_identifier_nth = ( G_CSET_a_2_z G_CSET_A_2_Z + G_CSET_DIGITS "-_." ); + + buffer = g_string_new (""); + + while (g_scanner_peek_next_token (scanner) != G_TOKEN_RIGHT_PAREN) + { + token = g_scanner_peek_next_token (scanner); + + if (token == G_TOKEN_IDENTIFIER) + { + g_scanner_get_next_token (scanner); + g_string_append (buffer, scanner->value.v_identifier); + } + else if (token == G_TOKEN_CHAR) + { + g_scanner_get_next_token (scanner); + g_string_append_c (buffer, scanner->value.v_char); + } + else if (token == ' ') + { + g_scanner_get_next_token (scanner); + g_string_append_c (buffer, token); + } + else + { + token = G_TOKEN_IDENTIFIER; + goto cleanup; + } + } + + g_value_init (&src, G_TYPE_STRING); + g_value_set_static_string (&src, buffer->str); + g_value_transform (&src, value); + g_value_unset (&src); + + token = G_TOKEN_RIGHT_PAREN; + + cleanup: + + g_string_free (buffer, TRUE); + + scanner->config->cset_skip_characters = old_cset_skip_characters; + scanner->config->cset_identifier_first = old_cset_identifier_first; + scanner->config->cset_identifier_nth = old_cset_identifier_nth; + + return token; +} + +static GTokenType +gimp_config_deserialize_file_value (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner) +{ + GTokenType token; + + token = g_scanner_peek_next_token (scanner); + + if (token != G_TOKEN_IDENTIFIER && + token != G_TOKEN_STRING) + { + return G_TOKEN_STRING; + } + + g_scanner_get_next_token (scanner); + + if (token == G_TOKEN_IDENTIFIER) + { + /* this is supposed to parse a literal "NULL" only, but so what... */ + g_value_set_object (value, NULL); + } + else + { + gchar *path = gimp_config_path_expand (scanner->value.v_string, TRUE, + NULL); + + if (path) + { + GFile *file = g_file_new_for_path (path); + + g_value_take_object (value, file); + g_free (path); + } + else + { + g_value_set_object (value, NULL); + } + } + + return G_TOKEN_RIGHT_PAREN; +} + +static GTokenType +gimp_config_deserialize_any (GValue *value, + GParamSpec *prop_spec, + GScanner *scanner) +{ + GValue src = G_VALUE_INIT; + GTokenType token; + + if (!g_value_type_transformable (G_TYPE_STRING, prop_spec->value_type)) + { + g_warning ("%s: %s can not be transformed from a string", + G_STRFUNC, g_type_name (prop_spec->value_type)); + return G_TOKEN_NONE; + } + + token = g_scanner_peek_next_token (scanner); + + if (token != G_TOKEN_IDENTIFIER && + token != G_TOKEN_STRING) + { + return G_TOKEN_IDENTIFIER; + } + + g_scanner_get_next_token (scanner); + + g_value_init (&src, G_TYPE_STRING); + + if (token == G_TOKEN_IDENTIFIER) + g_value_set_static_string (&src, scanner->value.v_identifier); + else + g_value_set_static_string (&src, scanner->value.v_string); + + g_value_transform (&src, value); + g_value_unset (&src); + + return G_TOKEN_RIGHT_PAREN; +} + +static GTokenType +gimp_config_skip_unknown_property (GScanner *scanner) +{ + gint open_paren = 0; + + while (TRUE) + { + GTokenType token = g_scanner_peek_next_token (scanner); + + switch (token) + { + case G_TOKEN_LEFT_PAREN: + open_paren++; + g_scanner_get_next_token (scanner); + break; + + case G_TOKEN_RIGHT_PAREN: + if (open_paren == 0) + return token; + + open_paren--; + g_scanner_get_next_token (scanner); + break; + + case G_TOKEN_EOF: + return token; + + default: + g_scanner_get_next_token (scanner); + break; + } + } +} |