From f4c43bff55658daafa2646bbaaa2ff0dca0c64f8 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Sun, 11 Dec 2005 15:38:08 +0000 Subject: re-work the logic for finding the best schema for the given locales. 2005-12-11 Mark McLoughlin * backends/markup-tree.c: (markup_entry_get_value): re-work the logic for finding the best schema for the given locales. 2005-12-11 Mark McLoughlin * backends/markup-tree.c: - Introduce the concept of a "subtree root" - a point in the tree where we save a subtree in a merged file. - When loading a subtree (%gconf-tree.xml), take note of any available translations (%gconf-tree-$(locale).xml) - When getting/setting/unsetting a schema, ensure that the appropriate translations are loaded - When parsing a translations file, allow for missing attributes and elements and read the translations of descriptions - When syncing a subtree, write out all translations of descriptions which are currently cached in memory to separate files. * backends/gconf-merge-tree.c: (merge_tree): update for markup_dir_build_file_path() change --- backends/gconf-merge-tree.c | 2 +- backends/markup-tree.c | 1282 +++++++++++++++++++++++++++++++++---------- 2 files changed, 984 insertions(+), 300 deletions(-) (limited to 'backends') diff --git a/backends/gconf-merge-tree.c b/backends/gconf-merge-tree.c index abad0b4a..1f5fc965 100644 --- a/backends/gconf-merge-tree.c +++ b/backends/gconf-merge-tree.c @@ -91,7 +91,7 @@ merge_tree (const char *root_dir) { char *markup_file; - markup_file = markup_dir_build_file_path (tree->root, TRUE); + markup_file = markup_dir_build_file_path (tree->root, TRUE, NULL); fprintf (stderr, _("Error saving GConf tree to '%s': %s\n"), markup_file, error->message); diff --git a/backends/markup-tree.c b/backends/markup-tree.c index 6d0c49c0..1f6908b5 100644 --- a/backends/markup-tree.c +++ b/backends/markup-tree.c @@ -1,5 +1,6 @@ /* GConf * Copyright (C) 2002 Red Hat Inc. + * Copyright (C) 2005 Mark McLoughlin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -64,20 +65,23 @@ static gboolean markup_dir_sync (MarkupDir *dir); static char* markup_dir_build_path (MarkupDir *dir, gboolean filesystem_path, gboolean with_data_file, - gboolean subtree_data_file); + gboolean subtree_data_file, + const char *locale); static void markup_dir_set_entries_need_save (MarkupDir *dir); +static void markup_dir_setup_as_subtree_root (MarkupDir *dir); static MarkupEntry* markup_entry_new (MarkupDir *dir, const char *name); static void markup_entry_free (MarkupEntry *entry); -static void parse_tree (MarkupDir *root, - gboolean parse_subtree, - GError **err); -static void save_tree (MarkupDir *root, - gboolean save_as_subtree, - guint file_mode, - GError **err); +static void parse_tree (MarkupDir *root, + gboolean parse_subtree, + const char *locale, + GError **err); +static void save_tree (MarkupDir *root, + gboolean save_as_subtree, + guint file_mode, + GError **err); struct _MarkupTree @@ -166,11 +170,15 @@ struct _MarkupDir { MarkupTree *tree; MarkupDir *parent; + MarkupDir *subtree_root; char *name; GSList *entries; GSList *subdirs; + /* Available %gconf-tree-$(locale).xml files */ + GHashTable *available_local_descs; + /* Have read the existing XML file */ guint entries_loaded : 1; /* Need to rewrite the XML file since we changed @@ -192,6 +200,15 @@ struct _MarkupDir /* Save to %gconf-tree.xml when syncing */ guint save_as_subtree : 1; + + /* We've loaded all locales in @available_local_descs */ + guint all_local_descs_loaded : 1; + + /* This is a temporary directory used only during parsing */ + guint is_parser_dummy : 1; + + /* Temporary flag used only when writing */ + guint is_dir_empty : 1; }; static MarkupDir* @@ -209,8 +226,13 @@ markup_dir_new (MarkupTree *tree, if (parent) { + dir->subtree_root = parent->subtree_root; parent->subdirs = g_slist_prepend (parent->subdirs, dir); } + else + { + markup_dir_setup_as_subtree_root (dir); + } return dir; } @@ -220,6 +242,12 @@ markup_dir_free (MarkupDir *dir) { GSList *tmp; + if (dir->available_local_descs != NULL) + { + g_hash_table_destroy (dir->available_local_descs); + dir->available_local_descs = NULL; + } + tmp = dir->entries; while (tmp) { @@ -259,17 +287,18 @@ markup_dir_queue_sync (MarkupDir *dir) } static inline char * -markup_dir_build_file_path (MarkupDir *dir, - gboolean subtree_data_file) +markup_dir_build_file_path (MarkupDir *dir, + gboolean subtree_data_file, + const char *locale) { - return markup_dir_build_path (dir, TRUE, TRUE, subtree_data_file); + return markup_dir_build_path (dir, TRUE, TRUE, subtree_data_file, locale); } static inline char * markup_dir_build_dir_path (MarkupDir *dir, gboolean filesystem_path) { - return markup_dir_build_path (dir, filesystem_path, FALSE, FALSE); + return markup_dir_build_path (dir, filesystem_path, FALSE, FALSE, NULL); } static MarkupDir* @@ -367,6 +396,90 @@ markup_tree_sync (MarkupTree *tree, return TRUE; } +static void +markup_dir_setup_as_subtree_root (MarkupDir *dir) +{ + if (dir->subtree_root != dir) + { + dir->subtree_root = dir; + + dir->available_local_descs = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + dir->all_local_descs_loaded = TRUE; + } +} + +static void +markup_dir_list_available_local_descs (MarkupDir *dir) +{ +#define LOCALE_FILE_PREFIX "%gconf-tree-" +#define LOCALE_FILE_SUFFIX ".xml" +#define LOCALE_FILE_PREFIX_LEN (sizeof (LOCALE_FILE_PREFIX) - 1) +#define LOCALE_FILE_SUFFIX_LEN (sizeof (LOCALE_FILE_SUFFIX) - 1) + + GDir *dp; + char *dir_path; + const char *dent; + + dir_path = markup_dir_build_dir_path (dir, TRUE); + + if ((dp = g_dir_open (dir_path, 0, NULL)) == NULL) + { + /* This is debug-only since it usually happens when creating a + * new directory + */ + gconf_log (GCL_DEBUG, + "Could not open directory \"%s\": %s\n", + dir_path, g_strerror (errno)); + g_free (dir_path); + return; + } + + g_assert (dir->available_local_descs != NULL); + g_assert (g_hash_table_size (dir->available_local_descs) == 0); + + while ((dent = g_dir_read_name (dp)) != NULL) + { + gsize dent_len; + char *locale; + + dent_len = strlen (dent); + + if (dent_len <= LOCALE_FILE_PREFIX_LEN + LOCALE_FILE_SUFFIX_LEN) + continue; + + if (strncmp (dent, LOCALE_FILE_PREFIX, LOCALE_FILE_PREFIX_LEN) != 0) + continue; + + if (strcmp (dent + dent_len - LOCALE_FILE_SUFFIX_LEN, LOCALE_FILE_SUFFIX) != 0) + continue; + + locale = g_strndup (dent + LOCALE_FILE_PREFIX_LEN, + dent_len - LOCALE_FILE_PREFIX_LEN - LOCALE_FILE_SUFFIX_LEN); + + g_hash_table_replace (dir->available_local_descs, + locale, + GINT_TO_POINTER (FALSE)); + } + + if (g_hash_table_size (dir->available_local_descs) != 0) + dir->all_local_descs_loaded = FALSE; + + /* if this fails, we really can't do a thing about it + * and it's not a meaningful error + */ + g_dir_close (dp); + + g_free (dir_path); + +#undef LOCALE_FILE_SUFFIX_LEN +#undef LOCALE_FILE_PREFIX_LEN +#undef LOCALE_FILE_SUFFIX +#undef LOCALE_FILE_PREFIX +} + static gboolean load_subtree (MarkupDir *dir) { @@ -375,7 +488,7 @@ load_subtree (MarkupDir *dir) GError *tmp_err = NULL; char *markup_file; - markup_file = markup_dir_build_file_path (dir, TRUE); + markup_file = markup_dir_build_file_path (dir, TRUE, NULL); if (!gconf_file_exists (markup_file)) { g_free (markup_file); @@ -386,7 +499,10 @@ load_subtree (MarkupDir *dir) dir->entries_loaded = TRUE; dir->save_as_subtree = TRUE; - parse_tree (dir, TRUE, &tmp_err); + markup_dir_setup_as_subtree_root (dir); + markup_dir_list_available_local_descs (dir); + + parse_tree (dir, TRUE, NULL, &tmp_err); if (tmp_err) { /* note that tmp_err may be a G_MARKUP_ERROR while only @@ -428,7 +544,7 @@ load_entries (MarkupDir *dir) { GError *tmp_err = NULL; - parse_tree (dir, FALSE, &tmp_err); + parse_tree (dir, FALSE, NULL, &tmp_err); if (tmp_err) { char *markup_file; @@ -441,7 +557,7 @@ load_entries (MarkupDir *dir) /* this message is debug-only because it usually happens * when creating a new directory */ - markup_file = markup_dir_build_file_path (dir, FALSE); + markup_file = markup_dir_build_file_path (dir, FALSE, NULL); gconf_log (GCL_DEBUG, "Failed to load file \"%s\": %s", markup_file, tmp_err->message); @@ -828,7 +944,9 @@ delete_useless_subdirs (MarkupDir *dir) char *fs_filename; fs_dirname = markup_dir_build_dir_path (subdir, TRUE); - fs_filename = markup_dir_build_file_path (subdir, subdir->save_as_subtree); + fs_filename = markup_dir_build_file_path (subdir, + subdir->save_as_subtree, + NULL); if (g_unlink (fs_filename) < 0) { @@ -975,8 +1093,8 @@ markup_dir_sync (MarkupDir *dir) clean_old_local_schemas_recurse (dir, dir->save_as_subtree); fs_dirname = markup_dir_build_dir_path (dir, TRUE); - fs_filename = markup_dir_build_file_path (dir, FALSE); - fs_subtree = markup_dir_build_file_path (dir, TRUE); + fs_filename = markup_dir_build_file_path (dir, FALSE, NULL); + fs_subtree = markup_dir_build_file_path (dir, TRUE, NULL); /* For a dir to be loaded as a subdir, it must have a * %gconf.xml file, even if it has no entries in that @@ -1116,7 +1234,8 @@ static char* markup_dir_build_path (MarkupDir *dir, gboolean filesystem_path, gboolean with_data_file, - gboolean subtree_data_file) + gboolean subtree_data_file, + const char *locale) { GString *name; GSList *components; @@ -1152,7 +1271,19 @@ markup_dir_build_path (MarkupDir *dir, g_slist_free (components); if (with_data_file) - g_string_append (name, !subtree_data_file ? "/%gconf.xml" : "/%gconf-tree.xml"); + { + if (locale == NULL) + { + g_string_append (name, + subtree_data_file ? "/%gconf-tree.xml" : "/%gconf.xml"); + } + else + { + g_assert (subtree_data_file); + + g_string_append_printf (name, "/%%gconf-tree-%s.xml", locale); + } + } return g_string_free (name, FALSE); } @@ -1195,6 +1326,106 @@ markup_entry_free (MarkupEntry *entry) g_free (entry); } +static void +load_schema_descs_for_locale (MarkupDir *dir, + const char *locale) +{ + GError *error; + + error = NULL; + parse_tree (dir, TRUE, locale, &error); + if (error != NULL) + { + char *markup_file; + + markup_file = markup_dir_build_file_path (dir, TRUE, locale); + + gconf_log (GCL_ERR, + _("Failed to load file \"%s\": %s"), + markup_file, + error->message); + + g_free (markup_file); + g_error_free (error); + } + + g_hash_table_replace (dir->available_local_descs, + g_strdup (locale), + GINT_TO_POINTER (TRUE)); +} + +static void +load_schema_descs_foreach (const char *locale, + gpointer value, + MarkupDir *dir) +{ + if (value != NULL) + return; /* already loaded */ + + load_schema_descs_for_locale (dir, locale); +} + +static gboolean +find_unloaded_locale (const char *locale, + gpointer value, + gboolean *any_unloaded) +{ + if (value != NULL) + return FALSE; + + *any_unloaded = TRUE; + + return TRUE; +} + +static void +ensure_schema_descs_loaded (MarkupEntry *entry, + const char *locale) +{ + MarkupDir *subtree_root; + + subtree_root = entry->dir->subtree_root; + + if (subtree_root->all_local_descs_loaded) + return; + + if (locale == NULL) + { + g_hash_table_foreach (subtree_root->available_local_descs, + (GHFunc) load_schema_descs_foreach, + subtree_root); + + subtree_root->all_local_descs_loaded = TRUE; + + return; + } + else + { + gpointer value; + gboolean any_unloaded; + + value = NULL; + if (!g_hash_table_lookup_extended (subtree_root->available_local_descs, + locale, + NULL, + &value)) + return; /* locale isn't available */ + + if (value != NULL) + return; /* already loaded */ + + load_schema_descs_for_locale (subtree_root, locale); + + any_unloaded = FALSE; + g_hash_table_find (subtree_root->available_local_descs, + (GHRFunc) find_unloaded_locale, + &any_unloaded); + + if (!any_unloaded) + subtree_root->all_local_descs_loaded = TRUE; + } +} + void markup_entry_set_value (MarkupEntry *entry, const GConfValue *value) @@ -1247,6 +1478,8 @@ markup_entry_set_value (MarkupEntry *entry, if (locale == NULL) locale = "C"; + ensure_schema_descs_loaded (entry, locale); + local_schema = NULL; tmp = entry->local_schemas; while (tmp != NULL) @@ -1361,6 +1594,8 @@ markup_entry_unset_value (MarkupEntry *entry, gconf_value_free (entry->value); entry->value = NULL; + ensure_schema_descs_loaded (entry, NULL); + g_slist_foreach (entry->local_schemas, (GFunc) local_schema_info_free, NULL); @@ -1374,6 +1609,8 @@ markup_entry_unset_value (MarkupEntry *entry, /* Just blow away any matching local schema */ GSList *tmp; + ensure_schema_descs_loaded (entry, locale); + tmp = entry->local_schemas; while (tmp != NULL) { @@ -1454,14 +1691,11 @@ markup_entry_get_value (MarkupEntry *entry, { 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); @@ -1473,49 +1707,47 @@ markup_entry_get_value (MarkupEntry *entry, 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); + best = NULL; c_local_schema = NULL; - tmp = entry->local_schemas; - while (tmp != NULL) + i = 0; + while (locales[i] != NULL) { - LocalSchemaInfo *lsi = tmp->data; + GSList *tmp; - if (strcmp (lsi->locale, "C") == 0) - c_local_schema = lsi; + ensure_schema_descs_loaded (entry, locales[i]); - i = 0; - while (locales[i]) + tmp = entry->local_schemas; + while (tmp != NULL) { - if (strcmp (locales[i], lsi->locale) == 0) + LocalSchemaInfo *lsi = tmp->data; + + if (c_local_schema == NULL && + strcmp (lsi->locale, "C") == 0) { - local_schemas[i] = lsi; - break; + c_local_schema = lsi; + if (best != NULL) + break; } - ++i; + + if (best == NULL && + strcmp (locales[i], lsi->locale) == 0) + { + best = lsi; + if (c_local_schema != NULL) + break; + } + + tmp = tmp->next; } /* Quit as soon as we have the best possible locale */ - if (local_schemas[0] != NULL && c_local_schema != NULL) + if (best != NULL && c_local_schema != 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 */ @@ -1644,7 +1876,10 @@ typedef struct /* Collected while parsing a schema entry */ GSList *local_schemas; + char *locale; + guint allow_subdirs : 1; + guint parsing_local_descs : 1; } ParseInfo; static void set_error (GError **err, @@ -1705,9 +1940,10 @@ set_error (GError **err, } static void -parse_info_init (ParseInfo *info, - MarkupDir *root, - gboolean allow_subdirs) +parse_info_init (ParseInfo *info, + MarkupDir *root, + gboolean allow_subdirs, + const char *locale) { info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START)); @@ -1720,7 +1956,10 @@ parse_info_init (ParseInfo *info, info->local_schemas = NULL; + info->locale = g_strdup (locale); + info->allow_subdirs = allow_subdirs != FALSE; + info->parsing_local_descs = info->locale != NULL; dir_stack_push (info, root); } @@ -1728,6 +1967,8 @@ parse_info_init (ParseInfo *info, static void parse_info_free (ParseInfo *info) { + g_free (info->locale); + g_slist_free (info->dir_stack); /* Don't free current_entry - always owned by tree */ @@ -2393,102 +2634,152 @@ parse_entry_element (GMarkupParseContext *context, ParseInfo *info, GError **error) { - const char *name; - const char *muser; - const char *mtime; - const char *schema; - const char *type; - const char *dummy1, *dummy2, *dummy3, *dummy4; - const char *dummy5, *dummy6, *dummy7; - GConfValue *value; - GError *tmp_err; - + MarkupEntry *entry; + g_return_if_fail (peek_state (info) == STATE_GCONF || peek_state (info) == STATE_DIR); 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; - type = NULL; - - if (!locate_attributes (context, element_name, attribute_names, attribute_values, - error, - "name", &name, - "muser", &muser, - "mtime", &mtime, - "schema", &schema, - "type", &type, + if (!info->parsing_local_descs) + { + const char *name; + const char *muser; + const char *mtime; + const char *schema; + const char *type; + const char *dummy1, *dummy2, *dummy3, *dummy4; + const char *dummy5, *dummy6, *dummy7; + GConfValue *value; + GError *tmp_err; + + name = NULL; + muser = NULL; + mtime = NULL; + schema = NULL; + type = NULL; + + if (!locate_attributes (context, element_name, attribute_names, attribute_values, + error, + "name", &name, + "muser", &muser, + "mtime", &mtime, + "schema", &schema, + "type", &type, - /* These are allowed but we don't use them until - * parse_value_element - */ - "value", &dummy1, - "stype", &dummy2, - "ltype", &dummy3, - "list_type", &dummy4, - "car_type", &dummy5, - "cdr_type", &dummy6, - "owner", &dummy7, - NULL)) - return; + /* These are allowed but we don't use them until + * parse_value_element + */ + "value", &dummy1, + "stype", &dummy2, + "ltype", &dummy3, + "list_type", &dummy4, + "car_type", &dummy5, + "cdr_type", &dummy6, + "owner", &dummy7, + NULL)) + return; - if (name == NULL) - { - set_error (error, context, GCONF_ERROR_PARSE_ERROR, - _("No \"%s\" attribute on element <%s>"), - "name", element_name); - return; - } + if (name == NULL) + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("No \"%s\" attribute on element <%s>"), + "name", element_name); + return; + } - /* Entries can exist just for the schema name, - * lacking a value element. But if the entry has a type - * attribute, it's supposed to have a value. - */ - value = NULL; - tmp_err = NULL; - parse_value_element (context, element_name, attribute_names, - attribute_values, &value, - &tmp_err); + /* Entries can exist just for the schema name, + * lacking a value element. But if the entry has a type + * attribute, it's supposed to have a value. + */ + value = NULL; + tmp_err = NULL; + parse_value_element (context, element_name, attribute_names, + attribute_values, &value, + &tmp_err); - if (tmp_err) - { - if (type != NULL) + if (tmp_err) { - g_propagate_error (error, tmp_err); - return; + if (type != NULL) + { + g_propagate_error (error, tmp_err); + return; + } + else + g_error_free (tmp_err); } - else - g_error_free (tmp_err); - } - info->current_entry = markup_entry_new (dir_stack_peek (info), name); - if (value != NULL) - { - info->current_entry->value = value; - value_stack_push (info, value, FALSE); /* FALSE since current_entry owns it */ - } + entry = markup_entry_new (dir_stack_peek (info), name); + if (value != NULL) + { + entry->value = value; + value_stack_push (info, value, FALSE); /* FALSE since entry owns it */ + } - if (muser) - markup_entry_set_mod_user (info->current_entry, muser); + if (muser) + markup_entry_set_mod_user (entry, muser); - if (mtime) - { - GTime vmtime; + if (mtime) + { + GTime vmtime; - vmtime = gconf_string_to_gulong (mtime); + vmtime = gconf_string_to_gulong (mtime); - markup_entry_set_mod_time (info->current_entry, vmtime); + markup_entry_set_mod_time (entry, vmtime); + } + + /* don't use markup_entry_set_schema_name because it would + * mess up the modtime + */ + if (schema) + entry->schema_name = g_strdup (schema); } + else + { + MarkupDir *dir; + GSList *tmp; + const char *name; + + name = NULL; - /* don't use markup_entry_set_schema_name because it would - * mess up the modtime - */ - if (schema) - info->current_entry->schema_name = g_strdup (schema); + if (!locate_attributes (context, element_name, attribute_names, attribute_values, + error, + "name", &name, + NULL)) + return; + + if (name == NULL) + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("No \"%s\" attribute on element <%s>"), + "name", element_name); + return; + } + + dir = dir_stack_peek (info); + + entry = NULL; + + tmp = dir->entries; + while (tmp != NULL) + { + entry = tmp->data; + + if (strcmp (entry->name, name) == 0) + break; + else + entry = NULL; + + tmp = tmp->next; + } + + /* Note: entry can be NULL here, in which case we'll discard + * the LocalSchemaInfo once we've finished parsing this entry + */ + } + + info->current_entry = entry; } static void @@ -2499,7 +2790,8 @@ parse_dir_element (GMarkupParseContext *context, ParseInfo *info, GError **error) { - MarkupDir *dir; + MarkupDir *parent; + MarkupDir *dir; const char *name; g_return_if_fail (peek_state (info) == STATE_GCONF || peek_state (info) == STATE_DIR); @@ -2508,7 +2800,7 @@ parse_dir_element (GMarkupParseContext *context, push_state (info, STATE_DIR); name = NULL; - + if (!locate_attributes (context, element_name, attribute_names, attribute_values, error, "name", &name, @@ -2523,11 +2815,46 @@ parse_dir_element (GMarkupParseContext *context, return; } - dir = markup_dir_new (info->root->tree, dir_stack_peek (info), name); + dir = NULL; + parent = dir_stack_peek (info); - dir->not_in_filesystem = TRUE; - dir->entries_loaded = TRUE; - dir->subdirs_loaded = TRUE; + if (!info->parsing_local_descs) + { + dir = markup_dir_new (info->root->tree, parent, name); + + dir->not_in_filesystem = TRUE; + dir->entries_loaded = TRUE; + dir->subdirs_loaded = TRUE; + } + else + { + GSList *tmp; + + tmp = parent->subdirs; + while (tmp != NULL) + { + dir = tmp->data; + + if (strcmp (dir->name, name) == 0) + break; + else + dir = NULL; + + tmp = tmp->next; + } + + if (dir == NULL) + { + dir = markup_dir_new (info->root->tree, parent, name); + + /* This is a dummy directory which will be deleted when + * we've finised parsing the contents of this element. + */ + dir->is_parser_dummy = TRUE; + } + } + + g_assert (dir != NULL); dir_stack_push (info, dir); } @@ -2546,7 +2873,7 @@ parse_local_schema_child_element (GMarkupParseContext *context, local_schema = info->local_schemas->data; - if (ELEMENT_IS ("default")) + if (ELEMENT_IS ("default") && !info->parsing_local_descs) { GConfValue *value; @@ -2602,12 +2929,13 @@ parse_local_schema_element (GMarkupParseContext *context, const char *locale; const char *short_desc; LocalSchemaInfo *local_schema; - GConfValue *value; g_return_if_fail (ELEMENT_IS ("local_schema")); - value = value_stack_peek (info); - if (value == NULL || value->type != GCONF_VALUE_SCHEMA) + if (!info->parsing_local_descs && + (info->current_entry == NULL || + info->current_entry->value == NULL || + info->current_entry->value->type != GCONF_VALUE_SCHEMA)) { set_error (error, context, GCONF_ERROR_PARSE_ERROR, _("<%s> provided but current element does not have type %s"), @@ -2620,19 +2948,32 @@ parse_local_schema_element (GMarkupParseContext *context, 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 (!info->parsing_local_descs) + { + if (!locate_attributes (context, element_name, attribute_names, attribute_values, + error, + "locale", &locale, + "short_desc", &short_desc, + NULL)) + return; - if (locale == NULL) + if (locale == NULL) + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("No \"%s\" attribute on element <%s>"), + "locale", element_name); + return; + } + } + else { - set_error (error, context, GCONF_ERROR_PARSE_ERROR, - _("No \"%s\" attribute on element <%s>"), - "locale", element_name); - return; + if (!locate_attributes (context, element_name, attribute_names, attribute_values, + error, + "short_desc", &short_desc, + NULL)) + return; + + locale = info->locale; } local_schema = local_schema_info_new (); @@ -2773,20 +3114,23 @@ parse_value_child_element (GMarkupParseContext *context, current_state = peek_state (info); - if (current_state == STATE_ENTRY && - info->current_entry->value == NULL) + if (!info->parsing_local_descs) { - set_error (error, context, GCONF_ERROR_PARSE_ERROR, - _("<%s> provided but parent does not have a value"), - element_name); - return; - } - else if (current_state == STATE_ENTRY) - { - g_assert (info->current_entry->value == value_stack_peek (info)); + if (current_state == STATE_ENTRY && + info->current_entry->value == NULL) + { + set_error (error, context, GCONF_ERROR_PARSE_ERROR, + _("<%s> provided but parent does not have a value"), + element_name); + return; + } + else if (current_state == STATE_ENTRY) + { + g_assert (info->current_entry->value == value_stack_peek (info)); + } } - if (ELEMENT_IS ("stringvalue")) + if (ELEMENT_IS ("stringvalue") && !info->parsing_local_descs) { GConfValue *value; @@ -2830,8 +3174,9 @@ parse_value_child_element (GMarkupParseContext *context, break; } } - else if (ELEMENT_IS ("car") || - ELEMENT_IS ("cdr")) + else if ((ELEMENT_IS ("car") || + ELEMENT_IS ("cdr")) && + !info->parsing_local_descs) { switch (current_state) { @@ -2853,7 +3198,7 @@ parse_value_child_element (GMarkupParseContext *context, break; } } - else if (ELEMENT_IS ("li")) + else if (ELEMENT_IS ("li") && !info->parsing_local_descs) { switch (current_state) { @@ -2977,14 +3322,69 @@ end_element_handler (GMarkupParseContext *context, break; case STATE_ENTRY: - g_assert (info->current_entry); - g_assert (info->current_entry->local_schemas == NULL); + if (!info->parsing_local_descs) + { + g_assert (info->current_entry); + g_assert (info->current_entry->local_schemas == NULL); - info->current_entry->local_schemas = g_slist_reverse (info->local_schemas); - info->local_schemas = NULL; + info->current_entry->local_schemas = g_slist_reverse (info->local_schemas); + info->local_schemas = NULL; - if (info->current_entry->value != NULL) - value_stack_pop (info); + if (info->current_entry->value != NULL) + value_stack_pop (info); + } + else if (info->local_schemas != NULL) + { + LocalSchemaInfo *local_schema; + + g_assert (g_slist_length (info->local_schemas) == 1); + + local_schema = info->local_schemas->data; + + g_slist_free (info->local_schemas); + info->local_schemas = NULL; + + if (info->current_entry != NULL && + info->current_entry->value != NULL && + info->current_entry->value->type == GCONF_VALUE_SCHEMA) + { + GSList *tmp; + + tmp = info->current_entry->local_schemas; + while (tmp != NULL) + { + LocalSchemaInfo *lsi = tmp->data; + + if (strcmp (local_schema->locale, lsi->locale) == 0) + { + g_free (lsi->short_desc); + lsi->short_desc = local_schema->short_desc; + local_schema->short_desc = NULL; + + g_free (lsi->long_desc); + lsi->long_desc = local_schema->short_desc; + local_schema->long_desc = NULL; + + local_schema_info_free (local_schema); + + break; + } + + tmp = tmp->next; + } + + if (tmp == NULL) + { + info->current_entry->local_schemas = + g_slist_append (info->current_entry->local_schemas, + local_schema); + } + } + else + { + local_schema_info_free (local_schema); + } + } info->current_entry = NULL; @@ -3022,8 +3422,17 @@ end_element_handler (GMarkupParseContext *context, MarkupDir *dir; dir = dir_stack_pop (info); - dir->entries = g_slist_reverse (dir->entries); - dir->subdirs = g_slist_reverse (dir->subdirs); + + if (!info->parsing_local_descs) + { + dir->entries = g_slist_reverse (dir->entries); + dir->subdirs = g_slist_reverse (dir->subdirs); + } + else if (dir->is_parser_dummy) + { + dir->parent->subdirs = g_slist_remove (dir->parent->subdirs, dir); + markup_dir_free (dir); + } pop_state (info); } @@ -3129,9 +3538,10 @@ text_handler (GMarkupParseContext *context, } static void -parse_tree (MarkupDir *root, - gboolean parse_subtree, - GError **err) +parse_tree (MarkupDir *root, + gboolean parse_subtree, + const char *locale, + GError **err) { GMarkupParseContext *context; GError *error; @@ -3139,9 +3549,12 @@ parse_tree (MarkupDir *root, char *filename; FILE *f; - filename = markup_dir_build_file_path (root, parse_subtree); + if (!parse_subtree) + g_assert (locale == NULL); + + filename = markup_dir_build_file_path (root, parse_subtree, locale); - parse_info_init (&info, root, parse_subtree); + parse_info_init (&info, root, parse_subtree, locale); error = NULL; @@ -3223,16 +3636,18 @@ static gboolean write_pair_children (GConfValue *value, FILE *f, int indent); static gboolean write_schema_children (GConfValue *value, - GSList *local_schemas, FILE *f, - int indent); + int indent, + GSList *local_schemas, + gboolean save_as_subtree); static gboolean -write_value_element (GConfValue *value, - GSList *local_schemas, - const char *closing_element, - FILE *f, - int indent) +write_value_element (GConfValue *value, + const char *closing_element, + FILE *f, + int indent, + GSList *local_schemas, + gboolean save_as_subtree) { char *whitespace; @@ -3379,17 +3794,21 @@ write_value_element (GConfValue *value, break; case GCONF_VALUE_LIST: - if (!write_list_children (value, f, indent)) + if (!write_list_children (value, f, indent + INDENT_SPACES)) return FALSE; break; case GCONF_VALUE_PAIR: - if (!write_pair_children (value, f, indent)) + if (!write_pair_children (value, f, indent + INDENT_SPACES)) return FALSE; break; case GCONF_VALUE_SCHEMA: - if (!write_schema_children (value, local_schemas, f, indent)) + if (!write_schema_children (value, + f, + indent + INDENT_SPACES, + local_schemas, + save_as_subtree)) return FALSE; break; @@ -3433,7 +3852,7 @@ write_list_children (GConfValue *value, if (fputs ("next; @@ -3469,7 +3888,7 @@ write_pair_children (GConfValue *value, if (fputs (" and and have locale and short_desc - * attributes - */ - GSList *tmp; gboolean retval; char *whitespace1, *whitespace2; + char *s; + + if (!write_descs && local_schema->default_value == NULL) + return TRUE; + + retval = FALSE; whitespace1 = g_strnfill (indent, ' '); whitespace2 = g_strnfill (indent + INDENT_SPACES, ' '); - - tmp = local_schemas; - while (tmp != NULL) - { - LocalSchemaInfo *local_schema = tmp->data; - char *s; - if (fputs (whitespace1, f) < 0) - goto out; + if (fputs (whitespace1, f) < 0) + goto out; - if (fputs ("locale); s = g_markup_escape_text (local_schema->locale, -1); @@ -3532,69 +3949,70 @@ write_schema_children (GConfValue *value, if (fprintf (f, " locale=\"%s\"", s) < 0) { g_free (s); - goto out; + goto out; } g_free (s); + } - if (local_schema->short_desc) - { - s = g_markup_escape_text (local_schema->short_desc, -1); + if (write_descs && local_schema->short_desc) + { + s = g_markup_escape_text (local_schema->short_desc, -1); - if (fprintf (f, " short_desc=\"%s\"", s) < 0) - { - g_free (s); - goto out; - } - + if (fprintf (f, " short_desc=\"%s\"", s) < 0) + { g_free (s); + goto out; } + + g_free (s); + } - if (fputs (">\n", f) < 0) - goto out; - - if (local_schema->default_value) - { - if (fputs (whitespace2, f) < 0) - goto out; - - if (fputs ("\n", f) < 0) + goto out; - if (!write_value_element (local_schema->default_value, NULL, - "default", f, - indent + INDENT_SPACES)) - goto out; - } + if (!is_locale_file && local_schema->default_value) + { + if (fputs (whitespace2, f) < 0) + goto out; + + if (fputs ("default_value, + "default", + f, + indent + INDENT_SPACES, + NULL, + FALSE)) + goto out; + } - if (local_schema->long_desc) - { - if (fprintf (f, "%s", whitespace2) < 0) - goto out; + if (write_descs && local_schema->long_desc) + { + if (fprintf (f, "%s", whitespace2) < 0) + goto out; - s = g_markup_escape_text (local_schema->long_desc, -1); - - if (fputs (s, f) < 0) - { - g_free (s); - goto out; - } + s = g_markup_escape_text (local_schema->long_desc, -1); + if (fputs (s, f) < 0) + { g_free (s); - - if (fputs ("\n", f) < 0) - goto out; + goto out; } + + g_free (s); - if (fputs (whitespace1, f) < 0) - goto out; + if (fputs ("\n", f) < 0) + goto out; + } - if (fputs ("\n", f) < 0) - goto out; + if (fputs (whitespace1, f) < 0) + goto out; + + if (fputs ("\n", f) < 0) + goto out; - tmp = tmp->next; - } - retval = TRUE; out: @@ -3602,50 +4020,176 @@ write_schema_children (GConfValue *value, g_free (whitespace1); g_free (whitespace2); + return retval; +} + +static gboolean +write_schema_children (GConfValue *value, + FILE *f, + int indent, + GSList *local_schemas, + gboolean save_as_subtree) +{ + /* Here we write each local_schema, in turn a local_schema can + * contain and and have locale and short_desc + * attributes + */ + GSList *tmp; + + tmp = local_schemas; + while (tmp != NULL) + { + LocalSchemaInfo *local_schema = tmp->data; + gboolean write_descs; + + write_descs = TRUE; + + if (save_as_subtree && + strcmp (local_schema->locale, "C") != 0) + write_descs = FALSE; + + if (!write_local_schema_info (local_schema, + f, + indent, + FALSE, + write_descs)) + return FALSE; + + tmp = tmp->next; + } + return TRUE; } +static void +get_non_c_desc_locales (MarkupEntry *entry, + GHashTable *non_c_desc_locales) +{ + GSList *tmp; + + tmp = entry->local_schemas; + while (tmp != NULL) + { + LocalSchemaInfo *local_schema = tmp->data; + + if (strcmp (local_schema->locale, "C") != 0 && + local_schema->short_desc != NULL && + local_schema->long_desc != NULL) + { + g_hash_table_replace (non_c_desc_locales, + (char *) local_schema->locale, + GINT_TO_POINTER (TRUE)); + } + + tmp = tmp->next; + } +} + +static LocalSchemaInfo * +get_local_schema_info (MarkupEntry *entry, + const char *locale) +{ + GSList *tmp; + + tmp = entry->local_schemas; + while (tmp != NULL) + { + LocalSchemaInfo *local_schema = tmp->data; + + if (strcmp (local_schema->locale, locale) == 0) + { + return local_schema; + } + + tmp = tmp->next; + } + + return NULL; +} + static gboolean write_entry (MarkupEntry *entry, FILE *f, - int indent) + int indent, + gboolean save_as_subtree, + const char *locale, + GHashTable *other_locales) { - gboolean retval = FALSE; - char *whitespace; + LocalSchemaInfo *local_schema_info; + gboolean retval; + char *whitespace; - whitespace = g_strnfill (indent, ' '); + retval = FALSE; + local_schema_info = NULL; - if (fprintf (f, "%sname != NULL); - if (fprintf (f, " name=\"%s\" mtime=\"%lu\"", - entry->name, - (unsigned long) entry->mod_time) < 0) + if (fprintf (f, "%sname) < 0) goto out; - - if (entry->schema_name) - { - if (fprintf (f, " schema=\"%s\"", entry->schema_name) < 0) - goto out; - } - if (entry->mod_user) + if (local_schema_info == NULL) { - if (fprintf (f, " muser=\"%s\"", entry->mod_user) < 0) + if (fprintf (f, " mtime=\"%lu\"", (unsigned long) entry->mod_time) < 0) goto out; - } + + if (entry->schema_name) + { + if (fprintf (f, " schema=\"%s\"", entry->schema_name) < 0) + goto out; + } - if (entry->value != NULL) - { - if (!write_value_element (entry->value, entry->local_schemas, "entry", f, indent)) - goto out; - } + if (entry->mod_user) + { + if (fprintf (f, " muser=\"%s\"", entry->mod_user) < 0) + goto out; + } + + if (entry->value != NULL) + { + if (!write_value_element (entry->value, + "entry", + f, + indent, + entry->local_schemas, + save_as_subtree)) + goto out; + } + else + { + if (fputs ("/>\n", f) < 0) + goto out; + } + } else { - if (fputs ("/>\n", f) < 0) - goto out; + if (fputs (">\n", f) < 0) + goto out; + + if (!write_local_schema_info (local_schema_info, + f, + indent + INDENT_SPACES, + TRUE, + TRUE)) + goto out; + + if (fprintf (f, "%s\n", whitespace) < 0) + goto out; } retval = TRUE; @@ -3658,9 +4202,12 @@ write_entry (MarkupEntry *entry, } static gboolean -write_dir (MarkupDir *dir, - FILE *f, - int indent) +write_dir (MarkupDir *dir, + FILE *f, + int indent, + gboolean save_as_subtree, + const char *locale, + GHashTable *other_locales) { GSList *tmp; gboolean retval = FALSE; @@ -3668,10 +4215,7 @@ write_dir (MarkupDir *dir, dir->not_in_filesystem = TRUE; - /* This dir will be deleted from the - * MarkupTree after syncing anyway ... - */ - if (!dir->entries && !dir->subdirs) + if (save_as_subtree && locale != NULL && dir->is_dir_empty) return TRUE; whitespace = g_strnfill (indent, ' '); @@ -3686,7 +4230,12 @@ write_dir (MarkupDir *dir, { MarkupEntry *entry = tmp->data; - if (!write_entry (entry, f, indent + INDENT_SPACES)) + if (!write_entry (entry, + f, + indent + INDENT_SPACES, + save_as_subtree, + locale, + other_locales)) goto out; tmp = tmp->next; @@ -3697,7 +4246,12 @@ write_dir (MarkupDir *dir, { MarkupDir *subdir = tmp->data; - if (!write_dir (subdir, f, indent + INDENT_SPACES)) + if (!write_dir (subdir, + f, + indent + INDENT_SPACES, + save_as_subtree, + locale, + other_locales)) goto out; tmp = tmp->next; @@ -3715,11 +4269,49 @@ write_dir (MarkupDir *dir, return retval; } +static gboolean +init_is_dir_empty_flags (MarkupDir *dir, + const char *locale) +{ + GSList *tmp; + + dir->is_dir_empty = TRUE; + + tmp = dir->entries; + while (tmp != NULL) + { + MarkupEntry *entry = tmp->data; + + if (get_local_schema_info (entry, locale) != NULL) + { + dir->is_dir_empty = FALSE; + break; + } + + tmp = tmp->next; + } + + tmp = dir->subdirs; + while (tmp != NULL) + { + MarkupDir *subdir = tmp->data; + + if (!init_is_dir_empty_flags (subdir, locale)) + dir->is_dir_empty = FALSE; + + tmp = tmp->next; + } + + return dir->is_dir_empty; +} + static void -save_tree (MarkupDir *dir, - gboolean save_as_subtree, - guint file_mode, - GError **err) +save_tree_with_locale (MarkupDir *dir, + gboolean save_as_subtree, + const char *locale, + GHashTable *other_locales, + guint file_mode, + GError **err) { /* We save to a secondary file then copy over, to handle * out-of-disk-space robustly @@ -3741,7 +4333,7 @@ save_tree (MarkupDir *dir, new_fd = -1; f = NULL; - filename = markup_dir_build_file_path (dir, save_as_subtree); + filename = markup_dir_build_file_path (dir, save_as_subtree, locale); new_filename = g_strconcat (filename, ".new", NULL); #ifdef G_OS_WIN32 @@ -3795,7 +4387,12 @@ save_tree (MarkupDir *dir, { MarkupEntry *entry = tmp->data; - if (!write_entry (entry, f, INDENT_SPACES)) + if (!write_entry (entry, + f, + INDENT_SPACES, + save_as_subtree, + locale, + other_locales)) { write_failed = TRUE; goto done_writing; @@ -3806,12 +4403,20 @@ save_tree (MarkupDir *dir, if (save_as_subtree) { + if (locale != NULL) + init_is_dir_empty_flags (dir, locale); + tmp = dir->subdirs; while (tmp != NULL) { MarkupDir *dir = tmp->data; - if (!write_dir (dir, f, INDENT_SPACES)) + if (!write_dir (dir, + f, + INDENT_SPACES, + save_as_subtree, + locale, + other_locales)) { write_failed = TRUE; goto done_writing; @@ -3891,6 +4496,85 @@ save_tree (MarkupDir *dir, fclose (f); } +typedef struct +{ + MarkupDir *dir; + guint file_mode; + GError *first_error; +} OtherLocalesForeachData; + +static void +other_locales_foreach (const char *locale, + gpointer dummy, + OtherLocalesForeachData *data) +{ + GError *error; + + error = NULL; + save_tree_with_locale (data->dir, + TRUE, + locale, + NULL, + data->file_mode, + &error); + if (error != NULL) + { + if (data->first_error != NULL) + data->first_error = error; + else + g_error_free (error); + } +} + +static void +save_tree (MarkupDir *dir, + gboolean save_as_subtree, + guint file_mode, + GError **err) +{ + if (!save_as_subtree) + { + save_tree_with_locale (dir, FALSE, NULL, NULL, file_mode, err); + } + else + { + OtherLocalesForeachData other_locales_foreach_data; + GHashTable *other_locales; + + /* First save %gconf-tree.xml with all values and C locale + * schema descriptions; then save schema descriptions for + * all other locales in %gconf-tree-$(locale).xml + */ + + other_locales = g_hash_table_new (g_str_hash, g_str_equal); + + save_tree_with_locale (dir, + TRUE, + NULL, + other_locales, + file_mode, + err); + + other_locales_foreach_data.dir = dir; + other_locales_foreach_data.file_mode = file_mode; + other_locales_foreach_data.first_error = NULL; + + g_hash_table_foreach (other_locales, + (GHFunc) other_locales_foreach, + &other_locales_foreach_data); + + if (other_locales_foreach_data.first_error != NULL) + { + if (err != NULL && *err == NULL) + *err = other_locales_foreach_data.first_error; + else + g_error_free (other_locales_foreach_data.first_error); + } + + g_hash_table_destroy (other_locales); + } +} + /* * Local schema */ -- cgit v1.2.1