diff options
author | Havoc Pennington <hp@pobox.com> | 2002-05-27 05:06:31 +0000 |
---|---|---|
committer | Havoc Pennington <hp@src.gnome.org> | 2002-05-27 05:06:31 +0000 |
commit | a751a6ea18b96a593859a8df9f95418feef18cf0 (patch) | |
tree | e468456415ee4a0b0a2779dd802d9b4eb1966120 | |
parent | 682a3bd9776f1c732a338303f5d6273094343c7a (diff) | |
download | gconf-a751a6ea18b96a593859a8df9f95418feef18cf0.tar.gz |
implement all the tree reading stuff, need to implement
2002-05-27 Havoc Pennington <hp@pobox.com>
* backends/markup-tree.c: implement all the tree reading stuff,
need to implement modification/writing and implement
markup-backend.c. (Backend is totally unused and unusable
until post-GNOME2, don't worry.)
* gconf/gconf-internals.h (gconf_value_set_string_nocopy):
new function
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | backends/Makefile.am | 10 | ||||
-rw-r--r-- | backends/markup-tree.c | 1822 | ||||
-rw-r--r-- | backends/markup-tree.h | 38 | ||||
-rw-r--r-- | gconf/gconf-internals.h | 3 | ||||
-rw-r--r-- | gconf/gconf-value.c | 13 |
6 files changed, 1655 insertions, 241 deletions
@@ -1,3 +1,13 @@ +2002-05-27 Havoc Pennington <hp@pobox.com> + + * backends/markup-tree.c: implement all the tree reading stuff, + need to implement modification/writing and implement + markup-backend.c. (Backend is totally unused and unusable + until post-GNOME2, don't worry.) + + * gconf/gconf-internals.h (gconf_value_set_string_nocopy): + new function + 2002-05-15 Pablo Saratxaga <pablo@mandrakesoft.com> * configure.in: Added Vietnamese (vi) to ALL_LINGUAS diff --git a/backends/Makefile.am b/backends/Makefile.am index c70a8498..abf4d999 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -12,7 +12,7 @@ else BDBBACKEND= endif -backend_LTLIBRARIES = libgconfbackend-xml.la $(BDBBACKEND) +backend_LTLIBRARIES = libgconfbackend-xml.la $(BDBBACKEND) libgconfbackend-markup.la libgconfbackend_xml_la_SOURCES = \ xml-cache.h \ @@ -32,6 +32,14 @@ libgconfbackend_bdb_la_SOURCES = bdb.c bdb.h bdb-backend.c val-encode.c val-enco libgconfbackend_bdb_la_LDFLAGS = -avoid-version -module libgconfbackend_bdb_la_LIBADD = $(DEPENDENT_LIBS) $(BDB_LIBS) +libgconfbackend_markup_la_SOURCES = \ + markup-tree.h \ + markup-tree.c + +libgconfbackend_markup_la_LDFLAGS = -avoid-version -module +libgconfbackend_markup_la_LIBADD = $(DEPENDENT_LIBS) + + noinst_PROGRAMS = xml-test xml_test_SOURCES= $(libgconfbackend_xml_la_SOURCES) xml-test.c diff --git a/backends/markup-tree.c b/backends/markup-tree.c index 84de1b82..d3978daf 100644 --- a/backends/markup-tree.c +++ b/backends/markup-tree.c @@ -18,7 +18,43 @@ */ #include <glib.h> +#include <gconf/gconf-internals.h> +#include <gconf/gconf-schema.h> #include "markup-tree.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <dirent.h> +#include <limits.h> + + +typedef struct +{ + char *locale; + char *short_desc; + char *long_desc; + GConfValue *default_value; +} LocalSchemaInfo; + +struct _MarkupEntry +{ + MarkupDir *dir; + char *name; + GConfValue *value; + /* list of LocalSchemaInfo */ + GSList *local_schemas; + char *schema_name; + char *mod_user; + GTime mod_time; +}; + +static LocalSchemaInfo* local_schema_info_new (void); +static void local_schema_info_free (LocalSchemaInfo *info); + static MarkupDir* markup_dir_new (MarkupTree *tree, MarkupDir *parent, @@ -30,10 +66,15 @@ static gboolean markup_dir_sync (MarkupDir *dir, static char* markup_dir_build_path (MarkupDir *dir, gboolean with_data_file); -static MarkupEntry* markup_entry_new (MarkupDir *dir, - const char *name); -static void markup_entry_free (MarkupEntry *entry); - +static MarkupEntry* markup_entry_new (MarkupDir *dir, + const char *name); +static void markup_entry_free (MarkupEntry *entry); +static void markup_entry_set_mod_user (MarkupEntry *entry, + const char *muser); +static void markup_entry_set_mod_time (MarkupEntry *entry, + GTime mtime); +static void markup_entry_set_schema_name (MarkupEntry *entry, + const char *schema_name); static GSList* parse_entries (const char *filename, GError **err); @@ -45,12 +86,15 @@ struct _MarkupTree guint file_mode; MarkupDir *root; -} + + guint read_only : 1; +}; MarkupTree* markup_tree_new (const char *root_dir, guint dir_mode, - guint file_mode) + guint file_mode, + gboolean read_only) { MarkupTree *tree; @@ -59,9 +103,10 @@ markup_tree_new (const char *root_dir, tree->dirname = g_strdup (root_dir); tree->dir_mode = dir_mode; tree->file_mode = file_mode; + tree->read_only = read_only; + + tree->root = markup_dir_new (tree, NULL, "/"); - root = markup_dir_new (tree, NULL, "/"); - return tree; } @@ -81,7 +126,7 @@ struct _MarkupDir GSList *entries; GSList *subdirs; - + /* Have read the existing XML file */ guint entries_loaded : 1; /* Need to rewrite the XML file since we changed @@ -91,6 +136,9 @@ struct _MarkupDir /* Have read the existing directories */ guint subdirs_loaded : 1; + + /* Some subdirs added/removed */ + guint subdirs_added_or_removed : 1; }; static MarkupDir* @@ -105,7 +153,7 @@ markup_dir_new (MarkupTree *tree, dir->name = g_strdup (name); dir->tree = tree; dir->parent = parent; - + return dir; } @@ -113,24 +161,234 @@ static void markup_dir_free (MarkupDir *dir) { g_free (dir->name); - + g_free (dir); } -MarkupDir* -markup_tree_lookup_dir (MarkupTree *tree, - const char *full_key) +static MarkupDir* +markup_tree_get_dir_internal (MarkupTree *tree, + const char *full_key, + gboolean create_if_not_found, + GError **err) { + char **components; + int i; + MarkupDir *dir; + + g_return_val_if_fail (*full_key == '/', NULL); + + /* Split without leading '/' */ + components = g_strsplit (full_key + 1, "/", -1); + + dir = tree->root; + + if (components) /* if components == NULL the root dir was requested */ + { + i = 0; + while (components[i]) + { + MarkupDir *subdir; + GError *tmp_err; + + tmp_err = NULL; + if (create_if_not_found) + subdir = markup_dir_ensure_subdir (dir, components[i], &tmp_err); + else + subdir = markup_dir_lookup_subdir (dir, components[i], &tmp_err); + if (tmp_err != NULL) + { + dir = NULL; + g_propagate_error (err, tmp_err); + goto out; + } + + if (subdir) + { + /* Descend one level */ + dir = subdir; + } + else + { + dir = NULL; + goto out; + } + + ++i; + } + } + + out: + g_strfreev (components); + + return dir; +} + +MarkupDir* +markup_tree_lookup_dir (MarkupTree *tree, + const char *full_key, + GError **err) + +{ + return markup_tree_get_dir_internal (tree, full_key, FALSE, err); } MarkupDir* markup_tree_ensure_dir (MarkupTree *tree, - const char *full_key) + const char *full_key, + GError **err) +{ + return markup_tree_get_dir_internal (tree, full_key, TRUE, err); +} + +static gboolean +load_entries (MarkupDir *dir) { + /* Load the entries in this directory */ + char *markup_file; + GSList *entries; + GError *tmp_err; + GSList *tmp; + + if (dir->entries_loaded) + return TRUE; + + /* We mark it loaded even if the next stuff + * fails, because we don't want to keep trying and + * failing, plus we have invariants + * that assume entries_loaded is TRUE once we've + * called load_entries() + */ + dir->entries_loaded = TRUE; + + markup_file = markup_dir_build_path (dir, TRUE); + + tmp_err = NULL; + entries = parse_entries (markup_file, &tmp_err); + + if (tmp_err) + { + g_assert (entries == NULL); + + gconf_log (GCL_WARNING, + _("Failed to load file \"%s\": %s"), + markup_file, tmp_err->message); + g_error_free (tmp_err); + g_free (markup_file); + return FALSE; + } + + g_free (markup_file); + + g_assert (dir->entries == NULL); + dir->entries = entries; + + /* Fill in entry->dir */ + tmp = dir->entries; + while (tmp != NULL) + { + MarkupEntry *entry = tmp->data; + + entry->dir = dir; + + tmp = tmp->next; + } + + return TRUE; +} + +static gboolean +load_subdirs (MarkupDir *dir) +{ + DIR* dp; + struct dirent* dent; + struct stat statbuf; + GSList* retval = NULL; + gchar* fullpath; + gchar* fullpath_end; + guint len; + guint subdir_len; + char *markup_dir; + + if (dir->subdirs_loaded) + return TRUE; + + /* We mark it loaded even if the next stuff + * fails, because we don't want to keep trying and + * failing, plus we have invariants + * that assume subdirs_loaded is TRUE once we've + * called load_subdirs() + */ + dir->subdirs_loaded = TRUE; + + g_assert (dir->subdirs == NULL); + + markup_dir = markup_dir_build_path (dir, FALSE); + + dp = opendir (markup_dir); + + if (dp == NULL) + { + gconf_log (GCL_WARNING, + _("Could not open directory \"%s\": %s\n"), + /* strerror, in locale encoding */ + markup_dir, strerror (errno)); + g_free (markup_dir); + return FALSE; + } + len = strlen (markup_dir); + + subdir_len = PATH_MAX - len; + + fullpath = g_new0 (char, subdir_len + len + 2); /* ensure null termination */ + strcpy (fullpath, markup_dir); + + fullpath_end = fullpath + len; + if (*(fullpath_end - 1) != '/') + { + *fullpath_end = '/'; + ++fullpath_end; + } + while ((dent = readdir (dp)) != NULL) + { + /* ignore ., .., and all dot-files */ + if (dent->d_name[0] == '.') + continue; + + len = strlen (dent->d_name); + + if (len < subdir_len) + { + strcpy (fullpath_end, dent->d_name); + strncpy (fullpath_end+len, "/%gconf.xml", subdir_len - len); + } + else + continue; /* Shouldn't ever happen since PATH_MAX is available */ + + if (stat (fullpath, &statbuf) < 0) + { + /* This is some kind of cruft, not an XML directory */ + continue; + } + + retval = g_slist_prepend (retval, + markup_dir_new (dir->tree, dir, dent->d_name)); + } + + /* if this fails, we really can't do a thing about it + * and it's not a meaningful error + */ + closedir (dp); + + dir->subdirs = retval; + + g_free (fullpath); + g_free (markup_dir); + + return TRUE; } MarkupEntry* @@ -138,8 +396,22 @@ markup_dir_lookup_entry (MarkupDir *dir, const char *relative_key, GError **err) { + GSList *tmp; + load_entries (dir); + + tmp = dir->entries; + while (tmp != NULL) + { + MarkupEntry *entry = tmp->data; + if (strcmp (relative_key, entry->name) == 0) + return entry; + + tmp = tmp->next; + } + + return NULL; } MarkupEntry* @@ -147,39 +419,117 @@ markup_dir_ensure_entry (MarkupDir *dir, const char *relative_key, GError **err) { + MarkupEntry *entry; + GError *tmp_err; + + tmp_err = NULL; + entry = markup_dir_lookup_entry (dir, relative_key, &tmp_err); + if (tmp_err != NULL) + { + g_propagate_error (err, tmp_err); + return NULL; + } + + if (entry != NULL) + return entry; + + /* Create a new entry */ + entry = markup_entry_new (dir, relative_key); + dir->entries = g_slist_prepend (dir->entries, entry); + + /* Need to save this */ + dir->entries_need_save = TRUE; + + return entry; +} + +MarkupDir* +markup_dir_lookup_subdir (MarkupDir *dir, + const char *relative_key, + GError **err) +{ + GSList *tmp; + + load_subdirs (dir); + + tmp = dir->subdirs; + while (tmp != NULL) + { + MarkupDir *subdir = tmp->data; + + if (strcmp (subdir->name, relative_key) == 0) + return subdir; + + tmp = tmp->next; + } + + return NULL; +} + +MarkupDir* +markup_dir_ensure_subdir (MarkupDir *dir, + const char *relative_key, + GError **err) +{ + MarkupDir *subdir; + GError *tmp_err; + + tmp_err = NULL; + subdir = markup_dir_lookup_subdir (dir, relative_key, &tmp_err); + if (tmp_err != NULL) + { + g_propagate_error (err, tmp_err); + return NULL; + } + if (subdir == NULL) + { + subdir = markup_dir_new (dir->tree, dir, relative_key); + dir->subdirs = g_slist_prepend (dir->subdirs, + subdir); + } + return subdir; } GSList* markup_dir_list_entries (MarkupDir *dir, GError **err) { + load_entries (dir); - + return dir->entries; } GSList* markup_dir_list_subdirs (MarkupDir *dir, GError **err) { + load_subdirs (dir); - - + return dir->subdirs; } static gboolean -markup_dir_needs_sync (MarkupDir *dir) +markup_dir_needs_sync (MarkupDir *dir) { - + /* Never write to read-only tree + * (it shouldn't get marked dirty, but this + * is here as a safeguard) + */ + if (dir->tree->read_only) + return FALSE; + + return dir->entries_need_save || dir->subdirs_added_or_removed; } static gboolean markup_dir_sync (MarkupDir *dir, GError **err) { - + /* - Clean up local_schema */ + /* - never delete root dir, always delete other empty dirs */ } @@ -191,7 +541,6 @@ markup_dir_build_path (MarkupDir *dir, GSList *components; GSList *tmp; MarkupDir *iter; - int i; components = NULL; iter = dir; @@ -200,7 +549,7 @@ markup_dir_build_path (MarkupDir *dir, components = g_slist_prepend (components, iter->name); iter = iter->parent; } - + name = g_string_new (dir->tree->dirname); tmp = components; while (tmp != NULL) @@ -217,22 +566,15 @@ markup_dir_build_path (MarkupDir *dir, g_slist_free (components); - if (with_data_file) + if (with_data_file) g_string_append (name, "/%gconf.xml"); return g_string_free (name, FALSE); } -struct _MarkupEntry -{ - MarkupDir *dir; - char *name; - GConfValue *value; - GSList *localized_values; - char *schema_name; - char *mod_user; - GTime mod_time; -}; +/* + * MarkupEntry + */ static MarkupEntry* markup_entry_new (MarkupDir *dir, @@ -242,9 +584,11 @@ markup_entry_new (MarkupDir *dir, entry = g_new0 (MarkupEntry, 1); + /* "dir" may be NULL during %gconf.xml parsing */ + entry->dir = dir; entry->name = g_strdup (name); - + return entry; } @@ -256,25 +600,14 @@ markup_entry_free (MarkupEntry *entry) gconf_value_free (entry->value); g_free (entry->schema_name); g_free (entry->mod_user); - - g_free (entry); -} -static const char* -get_value_locale (const GConfValue *value) -{ - if (value->type == GCONF_VALUE_SCHEMA) - { - GConfSchema *schema; + g_slist_foreach (entry->local_schemas, + (GFunc) local_schema_info_free, + NULL); - schema = gconf_value_get_schema (value); - g_assert (schema); - return schema_get_locale (schema); - } - else - { - return NULL; - } + g_slist_free (entry->local_schemas); + + g_free (entry); } void @@ -282,96 +615,326 @@ markup_entry_set_value (MarkupEntry *entry, const GConfValue *value, GError **err) { - const char *locale; - - g_assert (entry->dir->entries_loaded); - - locale = get_value_locale (value); + /* We have to have loaded entries, because + * someone called ensure_entry to get this + * entry. + */ + g_return_if_fail (entry->dir != NULL); + g_return_if_fail (entry->dir->entries_loaded); - if (locale == NULL || strcmp (locale, "C") == 0) + if (value->type != GCONF_VALUE_SCHEMA) { if (entry->value == value) return; - + if (entry->value) gconf_value_free (entry->value); - + entry->value = gconf_value_copy (value); + + /* Dump these if they exist, we aren't a schema anymore */ + if (entry->local_schemas) + { + g_slist_foreach (entry->local_schemas, + (GFunc) local_schema_info_free, + NULL); + g_slist_free (entry->local_schemas); + entry->local_schemas = NULL; + } } else { + /* For schema entries, we put the localized info + * in a LocalSchemaInfo, and the other info + * in the schema in the GConfValue + */ GSList *tmp; + LocalSchemaInfo *local_schema; + GConfSchema *schema; + const char *locale; + GConfSchema *current_schema; + GConfValue *def_value; + + schema = gconf_value_get_schema (value); + g_assert (schema); + + locale = gconf_schema_get_locale (schema); + if (locale == NULL) + locale = "C"; - tmp = entry->localized_values; + local_schema = NULL; + tmp = entry->local_schemas; while (tmp != NULL) { - const GConfValue *v; - const char *l; - - v = tmp->data; - l = get_value_locale (v); + LocalSchemaInfo *lsi; - g_assert (l != NULL); + lsi = tmp->data; - if (strcmp (l, locale) == 0) + if (strcmp (lsi->locale, locale) == 0) { - gconf_value_free (tmp->data); - tmp->data = gconf_value_copy (value); + local_schema = lsi; break; } tmp = tmp->next; } - if (tmp == NULL) + if (local_schema == NULL) { /* Didn't find a value for locale, make a new entry in the list */ - entry->localized_values = - g_slist_prepend (entry->localized_values, - gconf_value_copy (value)); + local_schema = local_schema_info_new (); + local_schema->locale = g_strdup (locale); + entry->local_schemas = + g_slist_prepend (entry->local_schemas, local_schema); } - } + + if (local_schema->short_desc) + g_free (local_schema->short_desc); + if (local_schema->long_desc) + g_free (local_schema->long_desc); + if (local_schema->default_value) + gconf_value_free (local_schema->default_value); + + local_schema->short_desc = g_strdup (gconf_schema_get_short_desc (schema)); + local_schema->long_desc = g_strdup (gconf_schema_get_long_desc (schema)); + def_value = gconf_schema_get_default_value (schema); + if (def_value) + local_schema->default_value = gconf_value_copy (def_value); + else + local_schema->default_value = NULL; + + /* When saving, we will check that the type of default_value is + * consistent with the type in the entry->value schema, so that + * we don't save something we can't load. We'll drop any + * LocalSchemaInfo with the wrong type default value at that + * time, more efficient than dropping it now. + */ + if (entry->value->type != GCONF_VALUE_SCHEMA) + { + gconf_value_free (entry->value); + entry->value = NULL; + } + + if (entry->value == NULL) + { + entry->value = gconf_value_new (GCONF_VALUE_SCHEMA); + current_schema = gconf_schema_new (); + gconf_value_set_schema_nocopy (entry->value, current_schema); + } + else + { + current_schema = gconf_value_get_schema (entry->value); + } + + /* Don't save localized info in the main schema */ + gconf_schema_set_locale (current_schema, NULL); + gconf_schema_set_short_desc (current_schema, NULL); + gconf_schema_set_long_desc (current_schema, NULL); + + /* But everything else goes in the main schema */ + gconf_schema_set_list_type (current_schema, + gconf_schema_get_list_type (schema)); + gconf_schema_set_car_type (current_schema, + gconf_schema_get_car_type (schema)); + gconf_schema_set_cdr_type (current_schema, + gconf_schema_get_cdr_type (schema)); + gconf_schema_set_type (current_schema, + gconf_schema_get_type (schema)); + gconf_schema_set_owner (current_schema, + gconf_schema_get_owner (schema)); + } entry->dir->entries_need_save = TRUE; } +GConfValue* +markup_entry_get_value (MarkupEntry *entry, + const char **locales, + GError **err) +{ + /* We have to have loaded entries, because + * someone called ensure_entry to get this + * entry. + */ + g_return_val_if_fail (entry->dir != NULL, NULL); + g_return_val_if_fail (entry->dir->entries_loaded, NULL); + + if (entry->value == NULL) + { + return NULL; + } + else if (entry->value->type != GCONF_VALUE_SCHEMA) + { + return gconf_value_copy (entry->value); + } + else + { + GConfValue *retval; + GConfSchema *schema; + int n_locales; + static const char *fallback_locales[2] = { + "C", NULL + }; + LocalSchemaInfo **local_schemas; + LocalSchemaInfo *best; + LocalSchemaInfo *c_local_schema; + GSList *tmp; + int i; + + retval = gconf_value_copy (entry->value); + schema = gconf_value_get_schema (retval); + g_return_val_if_fail (schema != NULL, NULL); + + /* Find the best local schema */ + + if (locales == NULL && locales[0] == NULL) + locales = fallback_locales; + + n_locales = 0; + while (locales[n_locales]) + ++n_locales; + + local_schemas = g_new0 (LocalSchemaInfo*, n_locales); + c_local_schema = NULL; + + tmp = entry->local_schemas; + while (tmp != NULL) + { + LocalSchemaInfo *lsi = tmp->data; + + if (strcmp (lsi->locale, "C") == 0) + c_local_schema = lsi; + + i = 0; + while (locales[i]) + { + if (strcmp (locales[i], lsi->locale) == 0) + { + local_schemas[i] = lsi; + break; + } + ++i; + } + + /* Quit as soon as we have the best possible locale */ + if (local_schemas[0] != NULL) + break; + + tmp = tmp->next; + } + + i = 0; + best = local_schemas[i]; + while (best == NULL && i < n_locales) + { + best = local_schemas[i]; + ++i; + } + + g_free (local_schemas); + + /* If we found localized info, add it to the return value, + * fall back to C locale if we can + */ + + if (best->default_value) + gconf_schema_set_default_value (schema, best->default_value); + else if (c_local_schema && c_local_schema->default_value) + gconf_schema_set_default_value (schema, c_local_schema->default_value); + + if (best->short_desc) + gconf_schema_set_short_desc (schema, best->short_desc); + else if (c_local_schema && c_local_schema->short_desc) + gconf_schema_set_short_desc (schema, c_local_schema->short_desc); + + if (best->long_desc) + gconf_schema_set_long_desc (schema, best->long_desc); + else if (c_local_schema && c_local_schema->long_desc) + gconf_schema_set_long_desc (schema, c_local_schema->long_desc); + + return retval; + } +} + +static void +markup_entry_set_mod_user (MarkupEntry *entry, + const char *muser) +{ + if (muser == entry->mod_user) + return; + + g_free (entry->mod_user); + entry->mod_user = g_strdup (muser); +} + +static void +markup_entry_set_mod_time (MarkupEntry *entry, + GTime mtime) +{ + entry->mod_time = mtime; +} + +static void +markup_entry_set_schema_name (MarkupEntry *entry, + const char *schema_name) +{ + if (schema_name == entry->schema_name) + return; + + g_free (entry->schema_name); + entry->schema_name = g_strdup (schema_name); +} /* * Parser */ +/* The GConf XML format is on a lot of crack. When I wrote it, + * I didn't know what I was doing, and now we're stuck with it. + * Apologies. + */ + typedef enum { STATE_START, STATE_GCONF, - STATE_EMPTY_ENTRY, - STATE_STRING_ENTRY, - STATE_SCHEMA_ENTRY, - STATE_LIST_ENTRY, - STATE_PAIR_ENTRY, - STATE_STRINGVALUE, - STATE_DEFAULT, - STATE_SHORTDESC, + STATE_ENTRY, + STATE_STRINGVALUE, STATE_LONGDESC, - STATE_LOCAL_SCHEMA + + STATE_LOCAL_SCHEMA, + + /* these all work just like <entry> in storing a value but have no + * name/muser/mtime/owner and in the case of car/cdr/li can only + * store primitive values. + */ + + STATE_DEFAULT, + STATE_CAR, + STATE_CDR, + STATE_LI } ParseState; typedef struct { GSList *states; + MarkupEntry *current_entry; + GSList *value_stack; + GSList *value_freelist; + + /* Collected while parsing a schema entry */ + GSList *local_schemas; + + GSList *complete_entries; - GConfValueType current_entry_type; - MarkupEntry *current_entry; - } ParseInfo; static void set_error (GError **err, GMarkupParseContext *context, - int error_domain, int error_code, const char *format, - ...) G_GNUC_PRINTF (5, 6); + ...) G_GNUC_PRINTF (4, 5); static void add_context_to_error (GError **err, GMarkupParseContext *context); @@ -384,12 +947,11 @@ static void push_state (ParseInfo *info, static void pop_state (ParseInfo *info); static ParseState peek_state (ParseInfo *info); -static void parse_foo_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - ParseInfo *info, - GError **error); +static void value_stack_push (ParseInfo *info, + GConfValue *value, + gboolean add_to_freelist); +static GConfValue* value_stack_peek (ParseInfo *info); +static void value_stack_pop (ParseInfo *info); static void start_element_handler (GMarkupParseContext *context, @@ -419,7 +981,6 @@ static GMarkupParser gconf_parser = { static void set_error (GError **err, GMarkupParseContext *context, - int error_domain, int error_code, const char *format, ...) @@ -427,14 +988,14 @@ set_error (GError **err, int line, ch; va_list args; char *str; - + g_markup_parse_context_get_position (context, &line, &ch); va_start (args, format); str = g_strdup_vprintf (format, args); va_end (args); - g_set_error (err, error_domain, error_code, + g_set_error (err, GCONF_ERROR, error_code, _("Line %d character %d: %s"), line, ch, str); @@ -463,11 +1024,36 @@ static void parse_info_init (ParseInfo *info) { info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START)); + info->current_entry = NULL; + info->value_stack = NULL; + info->value_freelist = NULL; + info->local_schemas = NULL; + info->complete_entries = NULL; } static void parse_info_free (ParseInfo *info) -{ +{ + if (info->current_entry) + markup_entry_free (info->current_entry); + + g_slist_foreach (info->local_schemas, + (GFunc) local_schema_info_free, + NULL); + g_slist_free (info->local_schemas); + + g_slist_foreach (info->complete_entries, + (GFunc) markup_entry_free, + NULL); + g_slist_free (info->complete_entries); + + /* only free values on the freelist, not those on the stack, + * but all values in the freelist are also in the stack. + */ + g_slist_foreach (info->value_freelist, (GFunc) gconf_value_free, NULL); + g_slist_free (info->value_freelist); + g_slist_free (info->value_stack); + g_slist_free (info->states); } @@ -482,7 +1068,7 @@ static void pop_state (ParseInfo *info) { g_return_if_fail (info->states != NULL); - + info->states = g_slist_remove (info->states, info->states->data); } @@ -494,6 +1080,35 @@ peek_state (ParseInfo *info) return GPOINTER_TO_INT (info->states->data); } + +/* add_to_freelist means that if the parse is aborted + * while the value is on the stack, free that value + */ +static void +value_stack_push (ParseInfo *info, + GConfValue *value, + gboolean add_to_freelist) +{ + info->value_stack = g_slist_prepend (info->value_stack, value); + if (add_to_freelist) + info->value_freelist = g_slist_prepend (info->value_freelist, value); +} + +static GConfValue* +value_stack_peek (ParseInfo *info) +{ + return info->value_stack ? info->value_stack->data : NULL; +} + +static void +value_stack_pop (ParseInfo *info) +{ + info->value_freelist = g_slist_remove (info->value_freelist, + info->value_stack->data); + info->value_stack = g_slist_remove (info->value_stack, + info->value_stack->data); +} + #define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0) typedef struct @@ -530,7 +1145,7 @@ locate_attributes (GMarkupParseContext *context, attrs[0].name = first_attribute_name; attrs[0].retloc = first_attribute_retloc; *first_attribute_retloc = NULL; - + va_start (args, first_attribute_retloc); name = va_arg (args, const char*); @@ -541,11 +1156,11 @@ locate_attributes (GMarkupParseContext *context, g_return_val_if_fail (retloc != NULL, FALSE); g_assert (n_attrs < MAX_ATTRS); - + attrs[n_attrs].name = name; attrs[n_attrs].retloc = retloc; n_attrs += 1; - *retloc = NULL; + *retloc = NULL; name = va_arg (args, const char*); retloc = va_arg (args, const char**); @@ -573,8 +1188,7 @@ locate_attributes (GMarkupParseContext *context, if (*retloc != NULL) { set_error (error, context, - G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, + GCONF_ERROR_PARSE_ERROR, _("Attribute \"%s\" repeated twice on the same <%s> element"), attrs[j].name, element_name); retval = FALSE; @@ -591,8 +1205,7 @@ locate_attributes (GMarkupParseContext *context, if (!found) { set_error (error, context, - G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, + GCONF_ERROR_PARSE_ERROR, _("Attribute \"%s\" is invalid on <%s> element in this context"), attribute_names[i], element_name); retval = FALSE; @@ -616,8 +1229,7 @@ check_no_attributes (GMarkupParseContext *context, if (attribute_names[0] != NULL) { set_error (error, context, - G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, + GCONF_ERROR_PARSE_ERROR, _("Attribute \"%s\" is invalid on <%s> element in this context"), attribute_names[0], element_name); return FALSE; @@ -626,176 +1238,798 @@ check_no_attributes (GMarkupParseContext *context, return TRUE; } +static gboolean +int_from_string (GMarkupParseContext *context, + const char *str, + int *val, + GError **error) +{ + char* endptr = NULL; + glong result; + + *val = 0; + + errno = 0; + result = strtol (str, &endptr, 10); + + if (endptr == str) + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Didn't understand `%s' (expected integer)"), + str); + return FALSE; + } + else if (errno == ERANGE || result < G_MININT || result > G_MAXINT) + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Integer `%s' is too large or small"), + str); + return FALSE; + } + else + { + *val = result; + return TRUE; + } +} + +static gboolean +bool_from_string (GMarkupParseContext *context, + const char *str, + gboolean *val, + GError **error) +{ + if (strcmp (str, "true") == 0) + { + *val = TRUE; + return TRUE; + } + else if (strcmp (str, "false") == 0) + { + *val = FALSE; + return TRUE; + } + else + { + *val = FALSE; + + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Didn't understand `%s' (expected true or false)"), + str); + return FALSE; + } +} + + +static gboolean +float_from_string (GMarkupParseContext *context, + const char *str, + double *val, + GError **error) +{ + double num; + + if (gconf_string_to_double (str, &num)) + { + *val = num; + return TRUE; + } + else + { + *val = 0.0; + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Didn't understand `%s' (expected real number)"), + str); + return FALSE; + } +} + static void -parse_entry_element (GMarkupParseContext *context, +parse_value_element (GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, - ParseInfo *info, + GConfValue **retval, GError **error) { - const char *name; - const char *muser; - const char *mtime; const char *type; const char *stype; - const char *list_type; const char *car_type; const char *cdr_type; - const char *owner; const char *value; - - g_return_if_fail (peek_state (info) == STATE_GCONF); + /* check out the crack; "ltype" is for nodes storing a list, + * and "list_type" is for nodes storing a schema + */ + const char *ltype; + const char *list_type; + const char *owner; + GConfValueType vtype; - g_assert (ELEMENT_IS ("entry")); +#if 0 + g_assert (ELEMENT_IS ("entry") || + ELEMENT_IS ("default") || + ELEMENT_IS ("cdr") || + ELEMENT_IS ("car") || + ELEMENT_IS ("li")); +#endif + + *retval = NULL; - name = NULL; value = NULL; - muser = NULL; - mtime = NULL; type = NULL; stype = NULL; + ltype = NULL; list_type = NULL; car_type = NULL; cdr_type = NULL; owner = NULL; - + if (!locate_attributes (context, element_name, attribute_names, attribute_values, error, - "name", &name, "value", &value, - "muser", &muser, - "mtime", &mtime, "type", &type, "stype", &stype, + "ltype", <ype, "list_type", &list_type, "car_type", &car_type, "cdr_type", &cdr_type, "owner", &owner, NULL)) return; - - if (name == NULL) - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("No \"%s\" attribute on element <%s>"), - "name", element_name); - return; - } if (type == NULL) { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + set_error (error, context, GCONF_ERROR_PARSE_ERROR, _("No \"%s\" attribute on element <%s>"), "type", element_name); return; - } + } - if (strcmp (type, "string") == 0) + vtype = gconf_value_type_from_string (type); + if (vtype == GCONF_VALUE_INVALID) { - push_state (info, STATE_STRING_ENTRY); - + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Unknown value \"%s\" for \"%s\" attribute on element <%s>"), + type, "type", element_name); + return; } - else if (strcmp (type, "list") == 0) + + switch (vtype) { - push_state (info, STATE_LIST_ENTRY); + case GCONF_VALUE_STRING: + { + *retval = gconf_value_new (GCONF_VALUE_STRING); + } + break; + + case GCONF_VALUE_LIST: + { + GConfValueType lvtype; + + if (ltype == NULL) + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("No \"%s\" attribute on element <%s>"), + "ltype", element_name); + return; + } + + lvtype = gconf_value_type_from_string (ltype); + + switch (lvtype) + { + case GCONF_VALUE_INVALID: + case GCONF_VALUE_LIST: + case GCONF_VALUE_PAIR: + case GCONF_VALUE_SCHEMA: + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Invalid ltype \"%s\" on <%s>"), + ltype, element_name); + return; + break; + default: + break; + } + + *retval = gconf_value_new (GCONF_VALUE_LIST); + + gconf_value_set_list_type (*retval, + lvtype); + } + break; + + case GCONF_VALUE_SCHEMA: + { + GConfValueType schema_vtype; + GConfSchema *schema; + GConfValueType car_vtype; + GConfValueType cdr_vtype; + GConfValueType list_vtype; + + if (stype == NULL) + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("No \"%s\" attribute on element <%s>"), + "stype", element_name); + return; + } + + /* init for compiler warnings */ + car_vtype = GCONF_VALUE_INVALID; + cdr_vtype = GCONF_VALUE_INVALID; + list_vtype = GCONF_VALUE_INVALID; + + schema_vtype = gconf_value_type_from_string (stype); + + if (schema_vtype == GCONF_VALUE_PAIR) + { + if (car_type == NULL) + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("No \"%s\" attribute on element <%s>"), + "car_type", element_name); + return; + } + + if (cdr_type == NULL) + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("No \"%s\" attribute on element <%s>"), + "cdr_type", element_name); + return; + } + + car_vtype = gconf_value_type_from_string (car_type); + cdr_vtype = gconf_value_type_from_string (cdr_type); + + switch (car_vtype) + { + case GCONF_VALUE_INVALID: + case GCONF_VALUE_LIST: + case GCONF_VALUE_PAIR: + case GCONF_VALUE_SCHEMA: + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Invalid car_type \"%s\" on <%s>"), + car_type, element_name); + return; + break; + default: + break; + } + + switch (cdr_vtype) + { + case GCONF_VALUE_INVALID: + case GCONF_VALUE_LIST: + case GCONF_VALUE_PAIR: + case GCONF_VALUE_SCHEMA: + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Invalid cdr_type \"%s\" on <%s>"), + cdr_type, element_name); + return; + break; + default: + break; + } + } + else if (schema_vtype == GCONF_VALUE_LIST) + { + if (list_type == NULL) + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("No \"%s\" attribute on element <%s>"), + "list_type", element_name); + return; + } + + list_vtype = gconf_value_type_from_string (list_type); + + switch (list_vtype) + { + case GCONF_VALUE_INVALID: + case GCONF_VALUE_LIST: + case GCONF_VALUE_PAIR: + case GCONF_VALUE_SCHEMA: + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Invalid list_type \"%s\" on <%s>"), + list_type, element_name); + return; + break; + default: + break; + } + } + + *retval = gconf_value_new (GCONF_VALUE_SCHEMA); + + schema = gconf_schema_new (); + gconf_schema_set_type (schema, schema_vtype); + + if (schema_vtype == GCONF_VALUE_PAIR) + { + gconf_schema_set_car_type (schema, car_vtype); + gconf_schema_set_cdr_type (schema, cdr_vtype); + } + else if (schema_vtype == GCONF_VALUE_LIST) + { + gconf_schema_set_list_type (schema, list_vtype); + } + + if (owner) + gconf_schema_set_owner (schema, owner); + + gconf_value_set_schema_nocopy (*retval, schema); + } + break; + + case GCONF_VALUE_PAIR: + { + *retval = gconf_value_new (GCONF_VALUE_PAIR); + } + break; + + case GCONF_VALUE_INT: + case GCONF_VALUE_BOOL: + case GCONF_VALUE_FLOAT: + { + double fval; + gboolean bval; + int ival; + + if (value == NULL) + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("No \"%s\" attribute on element <%s>"), + "value", element_name); + return; + } + + switch (vtype) + { + case GCONF_VALUE_INT: + if (!int_from_string (context, value, &ival, error)) + return; + break; + + case GCONF_VALUE_BOOL: + if (!bool_from_string (context, value, &bval, error)) + return; + break; + + case GCONF_VALUE_FLOAT: + if (!float_from_string (context, value, &fval, error)) + return; + break; + + default: + g_assert_not_reached (); + } + + *retval = gconf_value_new (vtype); + + switch (vtype) + { + case GCONF_VALUE_INT: + gconf_value_set_int (*retval, ival); + break; + + case GCONF_VALUE_BOOL: + gconf_value_set_bool (*retval, bval); + break; + + case GCONF_VALUE_FLOAT: + gconf_value_set_float (*retval, fval); + break; + + default: + g_assert_not_reached (); + } + } + break; + case GCONF_VALUE_INVALID: + g_assert_not_reached (); + break; } - else if (strcmp (type, "schema") == 0) +} + +static void +parse_entry_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseInfo *info, + GError **error) +{ + const char *name; + const char *muser; + const char *mtime; + const char *schema; + GConfValue *value; + + g_return_if_fail (peek_state (info) == STATE_GCONF); + g_return_if_fail (ELEMENT_IS ("entry")); + g_return_if_fail (info->current_entry == NULL); + + push_state (info, STATE_ENTRY); + + name = NULL; + muser = NULL; + mtime = NULL; + schema = NULL; + + if (!locate_attributes (context, element_name, attribute_names, attribute_values, + error, + "name", &name, + "muser", &muser, + "mtime", &mtime, + "schema", &schema, + NULL)) + return; + + if (name == NULL) { - push_state (info, STATE_SCHEMA_ENTRY); + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("No \"%s\" attribute on element <%s>"), + "name", element_name); + return; } - else if (strcmp (type, "pair") == 0) - { - push_state (info, STATE_PAIR_ENTRY); - } - else if (strcmp (type, "int") == 0 || - strcmp (type, "bool") == 0 || - strcmp (type, "float") == 0) + value = NULL; + parse_value_element (context, element_name, attribute_names, + attribute_values, &value, + error); + if (value == NULL) + return; + + info->current_entry = markup_entry_new (NULL, name); + info->current_entry->value = value; + value_stack_push (info, value, FALSE); /* FALSE since current_entry owns it */ + + if (muser) + markup_entry_set_mod_user (info->current_entry, + muser); + if (mtime) { - push_state (info, STATE_EMPTY_ENTRY); + GTime vmtime; + + vmtime = gconf_string_to_gulong (mtime); + markup_entry_set_mod_time (info->current_entry, + vmtime); } - else - { - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Unknown value \"%s\" for \"%s\" attribute on element <%s>"), - type, "type", element_name); - return; - } + + if (schema) + markup_entry_set_schema_name (info->current_entry, + schema); } -parse_entry_child_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - ParseInfo *info, - GError **error) +static void +parse_local_schema_child_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseInfo *info, + GError **error) { - g_return_if_fail (peek_state (info) == STATE_ENTRY); + LocalSchemaInfo *local_schema; - if (ELEMENT_IS ("stringvalue")) + g_return_if_fail (peek_state (info) == STATE_LOCAL_SCHEMA); + + local_schema = info->local_schemas->data; + + if (ELEMENT_IS ("default")) { - if (!check_no_attributes (context, element_name, - attribute_names, attribute_values, - error)) + GConfValue *value; + + push_state (info, STATE_DEFAULT); + + value = NULL; + parse_value_element (context, element_name, attribute_names, + attribute_values, &value, + error); + if (value == NULL) return; - push_state (info, STATE_MERGE_DIR); + if (local_schema->default_value != NULL) + { + gconf_value_free (value); + set_error (error, context, + GCONF_ERROR_PARSE_ERROR, + _("Two <default> elements below a <local_schema>")); + return; + } + + local_schema->default_value = value; + value_stack_push (info, value, FALSE); /* local_schema owns it */ } - else if (ELEMENT_IS ("local_schema")) + else if (ELEMENT_IS ("longdesc")) { - if (!check_no_attributes (context, element_name, - attribute_names, attribute_values, - error)) - return; - - push_state (info, STATE_DESKTOP_DIR); + push_state (info, STATE_LONGDESC); + + if (local_schema->long_desc != NULL) + { + set_error (error, context, + GCONF_ERROR_PARSE_ERROR, + _("Two <longdesc> elements below a <local_schema>")); + } } else { set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + GCONF_ERROR_PARSE_ERROR, _("Element <%s> is not allowed below <%s>"), - element_name, "entry"); + element_name, "local_schema"); } } -parse_schema_child_element (GMarkupParseContext *context, +static void +parse_local_schema_element (GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, ParseInfo *info, GError **error) { - g_return_if_fail (peek_state (info) == STATE_ENTRY); + const char *locale; + const char *short_desc; + LocalSchemaInfo *local_schema; + GConfValue *value; - if (ELEMENT_IS ("default")) + g_return_if_fail (ELEMENT_IS ("local_schema")); + + value = value_stack_peek (info); + if (value == NULL || value->type != GCONF_VALUE_SCHEMA) { - if (!check_no_attributes (context, element_name, - attribute_names, attribute_values, - error)) - return; - - push_state (info, STATE_DESKTOP_DIR); + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("<%s> provided but current element does not have type %s"), + "local_schema", "schema"); + return; + } + + push_state (info, STATE_LOCAL_SCHEMA); + + locale = NULL; + short_desc = NULL; + + if (!locate_attributes (context, element_name, attribute_names, attribute_values, + error, + "locale", &locale, + "short_desc", &short_desc, + NULL)) + return; + + if (locale == NULL) + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("No \"%s\" attribute on element <%s>"), + "locale", element_name); + return; } - else if (ELEMENT_IS ("shortdesc")) + + local_schema = local_schema_info_new (); + local_schema->locale = g_strdup (locale); + local_schema->short_desc = g_strdup (short_desc); + + info->local_schemas = g_slist_prepend (info->local_schemas, + local_schema); +} + +static void +parse_car_or_cdr_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseInfo *info, + GError **error) +{ + ParseState current_state; + GConfValue *value; + GConfValue *pair; + + current_state = ELEMENT_IS ("car") ? STATE_CAR : STATE_CDR; + push_state (info, current_state); + + value = NULL; + parse_value_element (context, element_name, attribute_names, + attribute_values, &value, + error); + if (value == NULL) + return; + + pair = value_stack_peek (info); + + if (pair->type == GCONF_VALUE_PAIR) { - parse_folder_element (context, element_name, - attribute_names, attribute_values, - info, error); + if (current_state == STATE_CAR) + { + if (gconf_value_get_car (pair) == NULL) + { + gconf_value_set_car_nocopy (pair, value); + value_stack_push (info, value, FALSE); /* pair owns it */ + } + else + { + gconf_value_free (value); + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Two <car> elements given for same pair")); + } + } + else + { + if (gconf_value_get_cdr (pair) == NULL) + { + gconf_value_set_cdr_nocopy (pair, value); + value_stack_push (info, value, FALSE); /* pair owns it */ + } + else + { + gconf_value_free (value); + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Two <cdr> elements given for same pair")); + } + } } - else if (ELEMENT_IS ("longdesc")) + else { + gconf_value_free (value); + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("<%s> provided but current element does not have type %s"), + current_state == STATE_CAR ? "car" : "cdr", "pair"); + } +} +static void +parse_li_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseInfo *info, + GError **error) +{ + ParseState current_state; + GConfValue *value; + GConfValue *list; + + current_state = peek_state (info); + + value = NULL; + parse_value_element (context, element_name, attribute_names, + attribute_values, &value, + error); + if (value == NULL) + return; + + list = value_stack_peek (info); + + if (list->type == GCONF_VALUE_LIST) + { + if (value->type == gconf_value_get_list_type (list)) + { + GSList *slist; + + slist = gconf_value_steal_list (list); + slist = g_slist_append (slist, value); + gconf_value_set_list_nocopy (list, slist); + } + else + { + gconf_value_free (value); + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("<li> has wrong type %s"), + gconf_value_type_to_string (value->type)); + } } else { - set_error (error, context, - G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("Element <%s> is not allowed below <%s>"), - element_name, "local_schema"); + gconf_value_free (value); + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("<%s> provided but current element does not have type %s"), + "li", "list"); + } +} + +static void +parse_value_child_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseInfo *info, + GError **error) +{ + ParseState current_state; + + current_state = peek_state (info); + + if (ELEMENT_IS ("stringvalue")) + { + GConfValue *value; + + value = value_stack_peek (info); + + if (value->type == GCONF_VALUE_STRING) + { + push_state (info, STATE_STRINGVALUE); + } + else + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("<%s> provided but current element does not have type %s"), + "stringvalue", "string"); + } + } + else if (ELEMENT_IS ("local_schema")) + { + switch (current_state) + { + case STATE_CAR: + case STATE_CDR: + case STATE_LI: + case STATE_DEFAULT: + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Element <%s> is not allowed inside current element"), + element_name); + break; + case STATE_ENTRY: + parse_local_schema_element (context, element_name, + attribute_names, attribute_values, + info, error); + break; + default: + g_assert_not_reached (); + break; + } + } + else if (ELEMENT_IS ("car") || + ELEMENT_IS ("cdr")) + { + switch (current_state) + { + case STATE_CAR: + case STATE_CDR: + case STATE_LI: + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Element <%s> is not allowed inside current element"), + element_name); + break; + case STATE_DEFAULT: + case STATE_ENTRY: + parse_car_or_cdr_element (context, element_name, + attribute_names, attribute_values, + info, error); + break; + default: + g_assert_not_reached (); + break; + } + } + else if (ELEMENT_IS ("li")) + { + switch (current_state) + { + case STATE_CAR: + case STATE_CDR: + case STATE_LI: + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Element <%s> is not allowed inside current element"), + element_name); + break; + case STATE_DEFAULT: + case STATE_ENTRY: + parse_li_element (context, element_name, + attribute_names, attribute_values, + info, error); + break; + default: + g_assert_not_reached (); + break; + } + } + else + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Element <%s> is not allowed inside current element"), + element_name); } } @@ -808,8 +2042,11 @@ start_element_handler (GMarkupParseContext *context, GError **error) { ParseInfo *info = user_data; + ParseState current_state; - switch (peek_state (info)) + current_state = peek_state (info); + + switch (current_state) { case STATE_START: if (ELEMENT_IS ("gconf")) @@ -818,11 +2055,11 @@ start_element_handler (GMarkupParseContext *context, attribute_names, attribute_values, error)) return; - + push_state (info, STATE_GCONF); } else - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + set_error (error, context, GCONF_ERROR_PARSE_ERROR, _("Outermost element in menu file must be <gconf> not <%s>"), element_name); break; @@ -835,15 +2072,36 @@ start_element_handler (GMarkupParseContext *context, info, error); } else - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + set_error (error, context, GCONF_ERROR_PARSE_ERROR, _("Element <%s> is not allowed inside a <%s> element"), element_name, "gconf"); break; + + case STATE_ENTRY: + case STATE_DEFAULT: + case STATE_CAR: + case STATE_CDR: + case STATE_LI: + parse_value_child_element (context, element_name, + attribute_names, attribute_values, + info, error); + break; + + case STATE_LOCAL_SCHEMA: + parse_local_schema_child_element (context, element_name, + attribute_names, attribute_values, + info, error); + break; - case STATE_MERGE_DIR: - set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + case STATE_STRINGVALUE: + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Element <%s> is not allowed inside a <%s> element"), + element_name, "stringvalue"); + break; + case STATE_LONGDESC: + set_error (error, context, GCONF_ERROR_PARSE_ERROR, _("Element <%s> is not allowed inside a <%s> element"), - element_name, "MergeDir"); + element_name, "longdesc"); break; } } @@ -859,15 +2117,58 @@ end_element_handler (GMarkupParseContext *context, switch (peek_state (info)) { case STATE_START: - break; + break; + + case STATE_ENTRY: + g_assert (info->current_entry); + g_assert (info->current_entry->local_schemas == NULL); - case STATE_ONLY_UNALLOCATED: + info->current_entry->local_schemas = info->local_schemas; + info->local_schemas = NULL; + + info->complete_entries = g_slist_prepend (info->complete_entries, + info->current_entry); + info->current_entry = NULL; + + value_stack_pop (info); + pop_state (info); + break; + + case STATE_DEFAULT: + { + GConfValue *value; + LocalSchemaInfo *local_schema; + + local_schema = info->local_schemas->data; + + /* Default should already be in a LocalSchemaInfo */ + value = value_stack_peek (info); + + g_assert (value == local_schema->default_value); + + value_stack_pop (info); + + pop_state (info); + } + break; + + case STATE_CAR: + case STATE_CDR: + case STATE_LI: + value_stack_pop (info); + pop_state (info); + break; + + case STATE_GCONF: + case STATE_LOCAL_SCHEMA: + case STATE_LONGDESC: + case STATE_STRINGVALUE: pop_state (info); break; } } -#define NO_TEXT(element_name) set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, _("No text is allowed inside element <%s>"), element_name) +#define NO_TEXT(element_name) set_error (error, context, GCONF_ERROR_PARSE_ERROR, _("No text is allowed inside element <%s>"), element_name) static gboolean all_whitespace (const char *text, @@ -875,10 +2176,10 @@ all_whitespace (const char *text, { const char *p; const char *end; - + p = text; end = text + text_len; - + while (p != end) { if (!g_ascii_isspace (*p)) @@ -898,11 +2199,10 @@ text_handler (GMarkupParseContext *context, GError **error) { ParseInfo *info = user_data; - Vfolder *folder; - + if (all_whitespace (text, text_len)) return; - + /* FIXME http://bugzilla.gnome.org/show_bug.cgi?id=70448 would * allow a nice cleanup here. */ @@ -912,8 +2212,55 @@ text_handler (GMarkupParseContext *context, case STATE_START: g_assert_not_reached (); /* gmarkup shouldn't do this */ break; - case STATE_VFOLDER_INFO: - NO_TEXT ("VFolderInfo"); + case STATE_STRINGVALUE: + { + GConfValue *value; + + value = value_stack_peek (info); + g_assert (value->type == GCONF_VALUE_STRING); + + if (gconf_value_get_string (value) != NULL) + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("Element <%s> is not allowed inside current element"), + "stringvalue"); + } + else + { + gconf_value_set_string_nocopy (value, + g_strndup (text, text_len)); + } + } + break; + case STATE_LONGDESC: + { + LocalSchemaInfo *local_schema; + + local_schema = info->local_schemas->data; + + local_schema->long_desc = g_strndup (text, text_len); + } + break; + case STATE_GCONF: + NO_TEXT ("gconf"); + break; + case STATE_ENTRY: + NO_TEXT ("entry"); + break; + case STATE_LOCAL_SCHEMA: + NO_TEXT ("local_schema"); + break; + case STATE_DEFAULT: + NO_TEXT ("default"); + break; + case STATE_CAR: + NO_TEXT ("car"); + break; + case STATE_CDR: + NO_TEXT ("cdr"); + break; + case STATE_LI: + NO_TEXT ("li"); break; } } @@ -932,7 +2279,7 @@ parse_entries (const char *filename, text = NULL; length = 0; retval = NULL; - + if (!g_file_get_contents (filename, &text, &length, @@ -942,7 +2289,7 @@ parse_entries (const char *filename, g_assert (text); parse_info_init (&info); - + context = g_markup_parse_context_new (&gconf_parser, 0, &info, NULL); @@ -964,26 +2311,49 @@ parse_entries (const char *filename, out: g_free (text); - + if (error) { g_propagate_error (err, error); } -#if 0 - else if (info.root_folder) + else if (info.complete_entries) { - retval = info.root_folder; - info.root_folder = NULL; + retval = info.complete_entries; + info.complete_entries = NULL; } -#endif else { - g_set_error (err, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - _("File %s did not contain a root <gconf> element"), - filename); + /* No entries in here, but that's not an error, + * empty files are allowed. + */ } parse_info_free (&info); - + return retval; } + +/* + * Local schema + */ + +static LocalSchemaInfo* +local_schema_info_new (void) +{ + LocalSchemaInfo *info; + + info = g_new0 (LocalSchemaInfo, 1); + + return info; +} + +static void +local_schema_info_free (LocalSchemaInfo *info) +{ + g_free (info->locale); + g_free (info->short_desc); + g_free (info->long_desc); + if (info->default_value) + gconf_value_free (info->default_value); + g_free (info); +} diff --git a/backends/markup-tree.h b/backends/markup-tree.h index 36be273e..37b13749 100644 --- a/backends/markup-tree.h +++ b/backends/markup-tree.h @@ -21,6 +21,7 @@ #define MARKUP_TREE_H #include <glib.h> +#include <gconf/gconf-value.h> typedef struct _MarkupTree MarkupTree; typedef struct _MarkupDir MarkupDir; @@ -30,29 +31,42 @@ typedef struct _MarkupEntry MarkupEntry; MarkupTree* markup_tree_new (const char *root_dir, guint dir_mode, - guint file_mode); + guint file_mode, + gboolean read_only); void markup_tree_free (MarkupTree *tree); MarkupDir* markup_tree_lookup_dir (MarkupTree *tree, - const char *full_key); + const char *full_key, + GError **err); MarkupDir* markup_tree_ensure_dir (MarkupTree *tree, - const char *full_key); + const char *full_key, + GError **err); /* Directories in the tree */ -MarkupEntry* markup_dir_lookup_entry (MarkupDir *dir, - const char *relative_key, - GError **err); -MarkupEntry* markup_dir_ensure_entry (MarkupDir *dir, - const char *relative_key, - GError **err); +MarkupEntry* markup_dir_lookup_entry (MarkupDir *dir, + const char *relative_key, + GError **err); +MarkupEntry* markup_dir_ensure_entry (MarkupDir *dir, + const char *relative_key, + GError **err); +MarkupDir* markup_dir_lookup_subdir (MarkupDir *dir, + const char *relative_key, + GError **err); +MarkupDir* markup_dir_ensure_subdir (MarkupDir *dir, + const char *relative_key, + GError **err); GSList* markup_dir_list_entries (MarkupDir *dir, GError **err); GSList* markup_dir_list_subdirs (MarkupDir *dir, GError **err); /* Value entries in the directory */ -void markup_entry_set_value (MarkupEntry *entry, - const GConfValue *value, - GError **err); +void markup_entry_set_value (MarkupEntry *entry, + const GConfValue *value, + GError **err); +GConfValue* markup_entry_get_value (MarkupEntry *entry, + const char **locales, + GError **err); + #endif diff --git a/gconf/gconf-internals.h b/gconf/gconf-internals.h index 79f55585..f6e4b09f 100644 --- a/gconf/gconf-internals.h +++ b/gconf/gconf-internals.h @@ -242,6 +242,9 @@ int gconf_value_compare (const GConfValue *value_a, GConfValue* gconf_schema_steal_default_value (GConfSchema *schema); +void gconf_value_set_string_nocopy (GConfValue *value, + char *str); + #endif /* GCONF_ENABLE_INTERNALS */ #endif /* GCONF_GCONF_INTERNALS_H */ diff --git a/gconf/gconf-value.c b/gconf/gconf-value.c index 32222ef1..2f8064a3 100644 --- a/gconf/gconf-value.c +++ b/gconf/gconf-value.c @@ -966,6 +966,14 @@ gconf_value_set_int(GConfValue* value, gint the_int) void gconf_value_set_string(GConfValue* value, const gchar* the_str) +{ + gconf_value_set_string_nocopy (value, + g_strdup (the_str)); +} + +void +gconf_value_set_string_nocopy (GConfValue *value, + char *str) { GConfRealValue *real; @@ -973,8 +981,9 @@ gconf_value_set_string(GConfValue* value, const gchar* the_str) g_return_if_fail(value->type == GCONF_VALUE_STRING); real = REAL_VALUE (value); - - set_string(&real->d.string_data, the_str); + + g_free (real->d.string_data); + real->d.string_data = str; } void |