gimp/app/config/gimpconfig-deserialize.c
Sven Neumann 023c76978f configure.in etc/Makefile.am etc/gimprc.in removed templates for gimprc
2002-12-29  Sven Neumann  <sven@gimp.org>

	* configure.in
	* etc/Makefile.am
	* etc/gimprc.in
	* etc/gimprc_user.in: removed templates for gimprc files.

	* etc/gimprc: added this file as generated by gimp-config-dump.

	* app/gui/user-install-dialog.c
	* data/misc/user_install: don't install an empty user gimprc.

	* app/config/Makefile.am
	* app/config/gimpconfig-substitute.[ch]: removed these files.
	* app/config/gimpconfig-path.[ch]: and added them again with
	reduced functionality. Paths found in config files are now
	basically handled like standard strings by the config system.
	Users of the GimpConfig path variables need to expand the path
	themselves.

	* app/config/gimpbaseconfig.c
	* app/config/gimpconfig-deserialize.c
	* app/config/gimpconfig-dump.c
	* app/config/gimpconfig-utils.c
	* app/config/gimpconfig.c
	* app/config/gimpcoreconfig.c
	* app/config/gimprc.c:
	* app/base/base.c
	* app/base/temp-buf.c
	* app/core/gimp.c
	* app/core/gimpdatafactory.c
	* app/core/gimpmodules.c
	* app/gui/user-install-dialog.c
	* app/plug-in/plug-in.c
	* app/tools/tools.c
	* app/widgets/gimppropwidgets.c: changed accordingly.
2002-12-29 18:58:24 +00:00

650 lines
20 KiB
C

/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Object properties deserialization routines
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <glib-object.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "gimpconfig.h"
#include "gimpconfig-deserialize.h"
#include "gimpconfig-params.h"
#include "gimpconfig-path.h"
#include "gimpconfig-types.h"
#include "gimpscanner.h"
#include "libgimp/gimpintl.h"
/*
* 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_unknown (GObject *object,
GScanner *scanner);
static GTokenType gimp_config_deserialize_property (GObject *object,
GScanner *scanner);
static GTokenType gimp_config_deserialize_value (GValue *value,
GObject *object,
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_color (GValue *value,
GParamSpec *prop_spec,
GScanner *scanner);
static GTokenType gimp_config_deserialize_value_array (GValue *value,
GObject *object,
GParamSpec *prop_spec,
GScanner *scanner);
static GTokenType gimp_config_deserialize_any (GValue *value,
GParamSpec *prop_spec,
GScanner *scanner);
static inline gboolean scanner_string_utf8_valid (GScanner *scanner,
const gchar *token_name);
/**
* gimp_config_deserialize_properties:
* @object: a #GObject.
* @scanner: a #GScanner.
* @embedded_scope: %TRUE if a trailing ')' should not trigger a parse error.
* @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,
gint nest_level,
gboolean store_unknown_tokens)
{
GObjectClass *klass;
GParamSpec **property_specs;
guint n_property_specs;
guint i;
guint scope_id;
guint old_scope_id;
GTokenType token;
GTokenType next;
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
klass = G_OBJECT_GET_CLASS (object);
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 (object));
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_PARAM_SERIALIZE)
{
g_scanner_scope_add_symbol (scanner, scope_id,
prop_spec->name, prop_spec);
}
}
g_free (property_specs);
token = G_TOKEN_LEFT_PAREN;
while (TRUE)
{
next = g_scanner_peek_next_token (scanner);
if (next != token &&
! (store_unknown_tokens &&
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_deserialize_unknown (object, scanner);
break;
case G_TOKEN_SYMBOL:
token = gimp_config_deserialize_property (object, scanner);
break;
case G_TOKEN_RIGHT_PAREN:
token = G_TOKEN_LEFT_PAREN;
break;
default: /* do nothing */
break;
}
}
g_scanner_set_scope (scanner, old_scope_id);
/* If store_unknown_tokens is TRUE but the unknown token value couldn't
be parsed the default error message is rather confusing.
We try to produce something more meaningful here ... */
if ((store_unknown_tokens &&
token == G_TOKEN_STRING && next == G_TOKEN_IDENTIFIER))
{
g_scanner_unexp_token (scanner, G_TOKEN_SYMBOL, NULL, NULL, NULL,
_("fatal parse error"), TRUE);
return FALSE;
}
return gimp_config_deserialize_return (scanner, token, nest_level);
}
static GTokenType
gimp_config_deserialize_unknown (GObject *object,
GScanner *scanner)
{
gchar *key;
if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING)
return G_TOKEN_STRING;
key = g_strdup (scanner->value.v_identifier);
g_scanner_get_next_token (scanner);
if (!scanner_string_utf8_valid (scanner, key))
{
g_free (key);
return G_TOKEN_NONE;
}
gimp_config_add_unknown_token (object, key, scanner->value.v_string);
g_free (key);
return G_TOKEN_RIGHT_PAREN;
}
static GTokenType
gimp_config_deserialize_property (GObject *object,
GScanner *scanner)
{
GTypeClass *owner_class;
GimpConfigInterface *gimp_config_iface;
GimpConfigInterface *parent_iface;
GParamSpec *prop_spec;
GTokenType token = G_TOKEN_RIGHT_PAREN;
GValue value = { 0, };
prop_spec = G_PARAM_SPEC (scanner->value.v_symbol);
g_value_init (&value, prop_spec->value_type);
owner_class = g_type_class_peek (prop_spec->owner_type);
gimp_config_iface = g_type_interface_peek (owner_class,
GIMP_TYPE_CONFIG_INTERFACE);
/* 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 (gimp_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_INTERFACE);
}
if (gimp_config_iface &&
gimp_config_iface != parent_iface && /* see comment above */
gimp_config_iface->deserialize_property &&
gimp_config_iface->deserialize_property (object,
prop_spec->param_id,
&value,
prop_spec,
scanner,
&token))
{
/* nop */
}
else
{
token = gimp_config_deserialize_value (&value, object, prop_spec, scanner);
}
if (token == G_TOKEN_RIGHT_PAREN &&
g_scanner_peek_next_token (scanner) == token)
{
g_object_set_property (object, prop_spec->name, &value);
}
#if CONFIG_DEBUG
else
{
g_warning ("couldn't deserialize property %s::%s of type %s",
g_type_name (G_TYPE_FROM_INSTANCE (object)),
prop_spec->name,
g_type_name (prop_spec->value_type));
}
#endif
g_value_unset (&value);
return token;
}
static GTokenType
gimp_config_deserialize_value (GValue *value,
GObject *object,
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_PATH)
{
return gimp_config_deserialize_path (value, prop_spec, scanner);
}
else if (prop_spec->value_type == GIMP_TYPE_COLOR)
{
return gimp_config_deserialize_color (value, prop_spec, scanner);
}
else if (prop_spec->value_type == G_TYPE_VALUE_ARRAY)
{
return gimp_config_deserialize_value_array (value,
object, 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;
switch (G_TYPE_FUNDAMENTAL (prop_spec->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_UINT:
case G_TYPE_LONG:
case G_TYPE_ULONG:
token = G_TOKEN_INT;
break;
case G_TYPE_FLOAT:
case G_TYPE_DOUBLE:
token = G_TOKEN_FLOAT;
break;
default:
token = G_TOKEN_NONE;
g_assert_not_reached ();
break;
}
if (g_scanner_peek_next_token (scanner) != token)
return token;
g_scanner_get_next_token (scanner);
switch (G_TYPE_FUNDAMENTAL (prop_spec->value_type))
{
case G_TYPE_STRING:
if (scanner_string_utf8_valid (scanner, prop_spec->name))
g_value_set_static_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, scanner->value.v_int);
break;
case G_TYPE_UINT:
g_value_set_uint (value, scanner->value.v_int);
break;
case G_TYPE_LONG:
g_value_set_int (value, scanner->value.v_int);
break;
case G_TYPE_ULONG:
g_value_set_uint (value, scanner->value.v_int);
break;
case G_TYPE_FLOAT:
g_value_set_float (value, scanner->value.v_float);
break;
case G_TYPE_DOUBLE:
g_value_set_double (value, scanner->value.v_float);
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 (G_ENUM_CLASS (enum_class),
scanner->value.v_identifier);
if (!enum_value)
enum_value = g_enum_get_value_by_name (G_ENUM_CLASS (enum_class),
scanner->value.v_identifier);
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, scanner->value.v_int);
if (!enum_value)
{
g_scanner_error (scanner,
_("invalid value '%ld' for token %s"),
scanner->value.v_int, 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;
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_set_from_string (value, scanner->value.v_identifier))
return G_TOKEN_RIGHT_PAREN;
else
return G_TOKEN_NONE;
}
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 thde 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_color (GValue *value,
GParamSpec *prop_spec,
GScanner *scanner)
{
GimpRGB color;
if (! gimp_scanner_parse_color (scanner, &color))
return G_TOKEN_NONE;
g_value_set_boxed (value, &color);
return G_TOKEN_RIGHT_PAREN;
}
static GTokenType
gimp_config_deserialize_value_array (GValue *value,
GObject *object,
GParamSpec *prop_spec,
GScanner *scanner)
{
GParamSpecValueArray *array_spec;
GValueArray *array;
GValue array_value = { 0, };
gint n_values;
GTokenType token;
gint i;
array_spec = G_PARAM_SPEC_VALUE_ARRAY (prop_spec);
if (! gimp_scanner_parse_int (scanner, &n_values))
return G_TOKEN_INT;
array = g_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,
object,
array_spec->element_spec,
scanner);
if (token == G_TOKEN_RIGHT_PAREN)
g_value_array_append (array, &array_value);
g_value_unset (&array_value);
if (token != G_TOKEN_RIGHT_PAREN)
return token;
}
g_value_set_boxed_take_ownership (value, array);
return G_TOKEN_RIGHT_PAREN;
}
static GTokenType
gimp_config_deserialize_any (GValue *value,
GParamSpec *prop_spec,
GScanner *scanner)
{
GValue src = { 0, };
if (!g_value_type_transformable (G_TYPE_STRING, prop_spec->value_type))
{
g_warning ("%s: %s can not be transformed from a string",
G_STRLOC, g_type_name (prop_spec->value_type));
return G_TOKEN_NONE;
}
if (g_scanner_peek_next_token (scanner) != G_TOKEN_IDENTIFIER)
return G_TOKEN_IDENTIFIER;
g_scanner_get_next_token (scanner);
g_value_init (&src, G_TYPE_STRING);
g_value_set_static_string (&src, scanner->value.v_identifier);
g_value_transform (&src, value);
g_value_unset (&src);
return G_TOKEN_RIGHT_PAREN;
}
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;
}