static gboolean gconf_backend_verify_vtable (GConfBackendVTable *vtable, GConfBackendVTable *vtable_copy, const char *backend_name, GError **err) { int i; struct { char *name; gsize offset; } required_vtable_functions[] = { { "shutdown", G_STRUCT_OFFSET(GConfBackendVTable, shutdown) }, { "resolve_address", G_STRUCT_OFFSET(GConfBackendVTable, resolve_address) }, { "query_value", G_STRUCT_OFFSET(GConfBackendVTable, query_value) }, { "query_metainfo", G_STRUCT_OFFSET(GConfBackendVTable, query_metainfo) }, { "set_value", G_STRUCT_OFFSET(GConfBackendVTable, set_value) }, { "all_entries", G_STRUCT_OFFSET(GConfBackendVTable, all_entries) }, { "all_subdirs", G_STRUCT_OFFSET(GConfBackendVTable, all_subdirs) }, { "unset_value", G_STRUCT_OFFSET(GConfBackendVTable, unset_value) }, { "dir_exists", G_STRUCT_OFFSET(GConfBackendVTable, dir_exists) }, { "remove_dir", G_STRUCT_OFFSET(GConfBackendVTable, remove_dir) }, { "set_schema", G_STRUCT_OFFSET(GConfBackendVTable, set_schema) }, { "sync_all", G_STRUCT_OFFSET(GConfBackendVTable, sync_all) }, { "destroy_source", G_STRUCT_OFFSET(GConfBackendVTable, destroy_source) }, { "blow_away_locks", G_STRUCT_OFFSET(GConfBackendVTable, blow_away_locks) } }; if (!vtable) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Backend `%s' failed to return a vtable\n"), backend_name); return FALSE; } /* Create a copy in case vtable size doesn't match */ memcpy(vtable_copy, vtable, MIN(vtable->vtable_size, sizeof(GConfBackendVTable))); vtable_copy->vtable_size = sizeof(GConfBackendVTable); for (i = 0; i < G_N_ELEMENTS(required_vtable_functions); i++) { if (G_STRUCT_MEMBER_P(vtable_copy, required_vtable_functions[i].offset) == NULL) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Backend `%s' missing required vtable member `%s'\n"), backend_name, required_vtable_functions[i].name); return FALSE; } } return TRUE; }
static char* get_dir_from_address (const char *address, GError **err) { char *root_dir; int len; root_dir = gconf_address_resource (address); if (root_dir == NULL) { gconf_set_error (err, GCONF_ERROR_BAD_ADDRESS, _("Couldn't find the XML root directory in the address `%s'"), address); return NULL; } /* Chop trailing '/' to canonicalize */ len = strlen (root_dir); if (root_dir[len-1] == '/') root_dir[len-1] = '\0'; return root_dir; }
/** Type checking helper */ static gboolean gconf_require_type(const char *key, const GConfValue *value, GConfValueType type, GError **err) { if( value->type == type ) { return TRUE; } gconf_set_error(err, GCONF_ERROR_TYPE_MISMATCH, "%s: is %s, not %s", key, gconf_type_repr(value->type), gconf_type_repr(type)); return FALSE; }
gboolean cache_sync (Cache *cache, GError **err) { SyncData sd = { FALSE, NULL, FALSE }; GSList *list; sd.dc = cache; gconf_log (GCL_DEBUG, "Syncing the dir cache"); redo: sd.failed = FALSE; sd.deleted_some = FALSE; /* get a list of everything; we can't filter by * whether a sync is pending since we may make parents * of removed directories dirty when we sync their child * dir. */ list = NULL; g_hash_table_foreach (cache->cache, (GHFunc)listify_foreach, &list); /* sort subdirs before parents */ list = g_slist_sort (list, dircmp); /* sync it all */ g_slist_foreach (list, (GFunc) cache_sync_foreach, &sd); g_slist_free (list); /* If we deleted some subdirs, we may now be able to delete * more parent dirs. So go ahead and do the sync again. * Yeah this could be more efficient. */ if (!sd.failed && sd.deleted_some) goto redo; if (sd.failed && err && *err == NULL) { gconf_set_error (err, GCONF_ERROR_FAILED, _("Failed to sync XML cache contents to disk")); } return !sd.failed; }
static GConfSource* resolve_address (const gchar* address, GError** err) { struct stat statbuf; gchar* root_dir; XMLSource* xsource; GConfSource* source; gint flags = 0; GConfLock* lock = NULL; guint dir_mode = 0700; guint file_mode = 0600; gchar** address_flags; gchar** iter; gboolean force_readonly; root_dir = get_dir_from_address (address, err); if (root_dir == NULL) return NULL; if (g_stat (root_dir, &statbuf) == 0) { /* Already exists, base our dir_mode on it */ dir_mode = _gconf_mode_t_to_mode (statbuf.st_mode); /* dir_mode without search bits */ file_mode = dir_mode & (~0111); } else if (g_mkdir (root_dir, dir_mode) < 0) { /* Error out even on EEXIST - shouldn't happen anyway */ gconf_set_error (err, GCONF_ERROR_FAILED, _("Could not make directory `%s': %s"), (gchar *)root_dir, g_strerror (errno)); g_free (root_dir); return NULL; } force_readonly = FALSE; address_flags = gconf_address_flags (address); if (address_flags) { iter = address_flags; while (*iter) { if (strcmp (*iter, "readonly") == 0) { force_readonly = TRUE; break; } ++iter; } } g_strfreev (address_flags); { /* See if we're writable */ gboolean writable; int fd; gchar* testfile; writable = FALSE; if (!force_readonly) { testfile = g_strconcat(root_dir, "/.testing.writeability", NULL); fd = g_open(testfile, O_CREAT|O_WRONLY, S_IRWXU); if (fd >= 0) { writable = TRUE; close(fd); } g_unlink(testfile); g_free(testfile); } if (writable) flags |= GCONF_SOURCE_ALL_WRITEABLE; /* We only do locking if it's writable, * and if not using local locks, * which is sort of broken but close enough */ if (writable && !gconf_use_local_locks ()) { gchar* lockdir; lockdir = get_lock_dir_from_root_dir (root_dir); lock = gconf_get_lock(lockdir, err); if (lock != NULL) gconf_log(GCL_DEBUG, "Acquired lock directory `%s'", lockdir); g_free(lockdir); if (lock == NULL) { g_free(root_dir); return NULL; } } } { /* see if we're readable */ gboolean readable = FALSE; GDir* d; d = g_dir_open(root_dir, 0, NULL); if (d != NULL) { readable = TRUE; g_dir_close(d); } if (readable) flags |= GCONF_SOURCE_ALL_READABLE; } if (!(flags & GCONF_SOURCE_ALL_READABLE) && !(flags & GCONF_SOURCE_ALL_WRITEABLE)) { gconf_set_error(err, GCONF_ERROR_BAD_ADDRESS, _("Can't read from or write to the XML root directory in the address \"%s\""), address); g_free(root_dir); return NULL; } /* Create the new source */ xsource = xs_new(root_dir, dir_mode, file_mode, lock); gconf_log(GCL_DEBUG, _("Directory/file permissions for XML source at root %s are: %o/%o"), root_dir, dir_mode, file_mode); source = (GConfSource*)xsource; source->flags = flags; g_free(root_dir); return source; }
static void dir_load_doc(Dir* d, GError** err) { gboolean xml_already_exists = TRUE; gboolean need_backup = FALSE; struct stat statbuf; g_return_if_fail(d->doc == NULL); if (stat(d->xml_filename, &statbuf) < 0) { switch (errno) { case ENOENT: xml_already_exists = FALSE; break; case ENOTDIR: #ifdef ELOOP case ELOOP: #endif case EFAULT: case EACCES: case ENOMEM: case ENAMETOOLONG: default: /* These are all fatal errors */ gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to stat `%s': %s"), d->xml_filename, g_strerror(errno)); return; break; } } if (statbuf.st_size == 0) { xml_already_exists = FALSE; } if (xml_already_exists) { GError *tmp_err; gboolean error_was_fatal; error_was_fatal = FALSE; tmp_err = NULL; d->doc = my_xml_parse_file (d->xml_filename, &tmp_err); if (tmp_err != NULL) { gconf_log (GCL_WARNING, "%s", tmp_err->message); /* file errors are assumed to be some kind of * blowup, like out of file descriptors, so * we play it safe and don't touch anything */ if (tmp_err->domain == G_FILE_ERROR) error_was_fatal = TRUE; g_error_free (tmp_err); } if (error_was_fatal) return; } /* We recover from parse errors instead of passing them up */ /* This has the potential to just blow away an entire corrupted * config file; but I think that is better than the alternatives * (disabling config for a directory because the document is mangled). * * Parse errors really should not happen from an XML file we created * ourselves anyway... */ /* Also we create empty %gconf.xml files when we create a new dir, * and those return a parse error, though they should be trapped * by the statbuf.st_size == 0 check above. */ if (d->doc == NULL) { if (xml_already_exists) need_backup = TRUE; /* rather uselessly save whatever broken stuff was in the file */ /* Create a new doc */ d->doc = xmlNewDoc((xmlChar *)"1.0"); } if (d->doc->xmlRootNode == NULL) { /* fill it in */ d->doc->xmlRootNode = xmlNewDocNode(d->doc, NULL, "gconf", NULL); } else if (strcmp((char*)d->doc->xmlRootNode->name, "gconf") != 0) { xmlFreeDoc(d->doc); d->doc = xmlNewDoc((xmlChar*)"1.0"); d->doc->xmlRootNode = xmlNewDocNode(d->doc, NULL, (xmlChar *)"gconf", NULL); need_backup = TRUE; /* save broken stuff */ } else { /* We had an initial doc with a valid root */ /* Fill child_cache from entries */ dir_fill_cache_from_doc(d); } if (need_backup) { /* Back up the file we failed to parse, if it exists, we aren't going to be able to do anything if this call fails */ gchar* backup = g_strconcat(d->xml_filename, ".bak", NULL); int fd; g_rename(d->xml_filename, backup); /* Recreate %gconf.xml to maintain our integrity and be sure all_subdirs works */ /* If we failed to rename, we just give up and truncate the file */ fd = g_open(d->xml_filename, O_CREAT | O_WRONLY | O_TRUNC, d->file_mode); if (fd >= 0) close(fd); g_free(backup); } g_assert(d->doc != NULL); g_assert(d->doc->xmlRootNode != NULL); }
gboolean dir_sync (Dir *d, gboolean *deleted, GError **err) { gboolean retval = TRUE; if (deleted) *deleted = FALSE; if (!d->dirty) return TRUE; gconf_log (GCL_DEBUG, "Syncing dir \"%s\"", d->key); d->last_access = time (NULL); if (dir_useless (d)) { gconf_log (GCL_DEBUG, "Deleting useless dir \"%s\"", d->key); if (g_unlink (d->xml_filename) != 0) { gconf_set_error (err, GCONF_ERROR_FAILED, _("Failed to delete \"%s\": %s"), d->xml_filename, g_strerror (errno)); return FALSE; } if (strcmp (d->key, "/") != 0) /* don't delete root dir */ { if (g_rmdir (d->fs_dirname) != 0) { gconf_set_error (err, GCONF_ERROR_FAILED, _("Failed to delete \"%s\": %s"), d->fs_dirname, g_strerror (errno)); return FALSE; } } if (deleted) *deleted = TRUE; } else { gboolean old_existed = FALSE; gchar* tmp_filename; gchar* old_filename; FILE* outfile; /* We should have a doc if deleted is FALSE */ g_assert(d->doc != NULL); /* First make sure entry values are synced to their XML nodes */ g_hash_table_foreach(d->entry_cache, (GHFunc)entry_sync_foreach, NULL); tmp_filename = g_strconcat(d->fs_dirname, "/%gconf.xml.tmp", NULL); old_filename = g_strconcat(d->fs_dirname, "/%gconf.xml.old", NULL); outfile = g_fopen (tmp_filename, "w"); if (outfile == NULL) { /* Try to solve the problem by creating the FS dir */ if (!g_file_test (d->fs_dirname, G_FILE_TEST_EXISTS)) { if (create_fs_dir(d->fs_dirname, d->xml_filename, d->root_dir_len, d->dir_mode, d->file_mode, err)) outfile = g_fopen (tmp_filename, "w"); } if (outfile == NULL) { /* Don't set error if it's already set by some * earlier failure. */ if (err && *err == NULL) gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to write file `%s': %s"), tmp_filename, g_strerror(errno)); retval = FALSE; goto failed_end_of_sync; } } #ifdef HAVE_FCHMOD /* Set permissions on the new file */ if (fchmod (fileno (outfile), d->file_mode) != 0) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to set mode on `%s': %s"), tmp_filename, g_strerror(errno)); retval = FALSE; goto failed_end_of_sync; } #endif if (gconf_xml_doc_dump (outfile, d->doc) < 0) { gconf_set_error (err, GCONF_ERROR_FAILED, _("Failed to write XML data to `%s': %s"), tmp_filename, g_strerror (errno)); retval = FALSE; goto failed_end_of_sync; } if (fclose (outfile) < 0) { gconf_set_error (err, GCONF_ERROR_FAILED, _("Failed to close file `%s': %s"), tmp_filename, g_strerror (errno)); retval = FALSE; outfile = NULL; goto failed_end_of_sync; } outfile = NULL; #ifndef HAVE_FCHMOD /* Set permissions on the new file */ if (chmod (tmp_filename, d->file_mode) != 0) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to set mode on `%s': %s"), tmp_filename, g_strerror(errno)); retval = FALSE; goto failed_end_of_sync; } #endif old_existed = g_file_test (d->xml_filename, G_FILE_TEST_EXISTS); if (old_existed) { if (g_rename(d->xml_filename, old_filename) < 0) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to rename `%s' to `%s': %s"), d->xml_filename, old_filename, g_strerror(errno)); retval = FALSE; goto failed_end_of_sync; } } if (g_rename(tmp_filename, d->xml_filename) < 0) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to rename `%s' to `%s': %s"), tmp_filename, d->xml_filename, g_strerror(errno)); /* Put the original file back, so this isn't a total disaster. */ if (g_rename(old_filename, d->xml_filename) < 0) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to restore `%s' from `%s': %s"), d->xml_filename, old_filename, g_strerror(errno)); } retval = FALSE; goto failed_end_of_sync; } if (old_existed) { if (g_unlink(old_filename) < 0) { gconf_log(GCL_WARNING, _("Failed to delete old file `%s': %s"), old_filename, g_strerror(errno)); /* Not a failure, just leaves cruft around. */ } } failed_end_of_sync: g_free(old_filename); g_free(tmp_filename); if (outfile) fclose (outfile); } if (retval) d->dirty = FALSE; return retval; }
Dir* dir_load (const gchar* key, const gchar* xml_root_dir, GError** err) { Dir* d; gchar* fs_dirname; gchar* xml_filename; guint dir_mode = 0700; guint file_mode = 0600; g_return_val_if_fail(gconf_valid_key(key, NULL), NULL); fs_dirname = gconf_concat_dir_and_key(xml_root_dir, key); xml_filename = g_strconcat(fs_dirname, "/%gconf.xml", NULL); { struct stat s; gboolean notfound = FALSE; if (g_stat(xml_filename, &s) != 0) { if (errno != ENOENT) { gconf_set_error (err, GCONF_ERROR_FAILED, _("Could not stat `%s': %s"), xml_filename, g_strerror(errno)); } notfound = TRUE; } else if (S_ISDIR(s.st_mode)) { gconf_set_error (err, GCONF_ERROR_FAILED, _("XML filename `%s' is a directory"), xml_filename); notfound = TRUE; } if (notfound) { gconf_log(GCL_DEBUG, "dir file %s not found", xml_filename); g_free(fs_dirname); g_free(xml_filename); return NULL; } else { /* Take directory mode from the xml_root_dir, if possible */ if (g_stat (xml_root_dir, &s) == 0) { dir_mode = _gconf_mode_t_to_mode (s.st_mode); } file_mode = dir_mode & ~0111; /* turn off search bits */ } } d = dir_blank(key); /* sync with dir_new() */ d->fs_dirname = fs_dirname; d->xml_filename = xml_filename; d->root_dir_len = strlen(xml_root_dir); d->dir_mode = dir_mode; d->file_mode = file_mode; gconf_log (GCL_DEBUG, "loaded dir %s", fs_dirname); return d; }
static gboolean create_fs_dir(const gchar* dir, const gchar* xml_filename, guint root_dir_len, guint dir_mode, guint file_mode, GError** err) { g_return_val_if_fail(xml_filename != NULL, FALSE); gconf_log(GCL_DEBUG, "Enter create_fs_dir: %s", dir); if (g_file_test(xml_filename, G_FILE_TEST_IS_REGULAR)) { gconf_log(GCL_DEBUG, "XML backend file %s already exists", xml_filename); return TRUE; } /* Don't create anything above the root directory */ if (strlen(dir) > root_dir_len) { gchar* parent; parent = _gconf_parent_dir (dir); gconf_log (GCL_DEBUG, "Parent dir is %s", parent); if (parent != NULL) { gchar* parent_xml = NULL; gboolean success = FALSE; if (xml_filename) parent_xml = g_strconcat(parent, "/%gconf.xml", NULL); success = create_fs_dir(parent, parent_xml, root_dir_len, dir_mode, file_mode, err); if (success) gconf_log(GCL_DEBUG, "created parent: %s", parent); else gconf_log(GCL_DEBUG, "failed parent: %s", parent); g_free(parent); g_free(parent_xml); if (!success) return FALSE; } else { gconf_log(GCL_DEBUG, "%s has no parent", dir); } } gconf_log(GCL_DEBUG, "Making directory %s", dir); if (g_mkdir(dir, dir_mode) < 0) { if (errno != EEXIST) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Could not make directory \"%s\": %s"), (gchar*)dir, g_strerror(errno)); return FALSE; } } if (xml_filename != NULL) { int fd; /* don't truncate the file, it may well already exist */ fd = g_open(xml_filename, O_CREAT | O_WRONLY, file_mode); gconf_log(GCL_DEBUG, "Creating XML file %s", xml_filename); if (fd < 0) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to create file `%s': %s"), xml_filename, g_strerror(errno)); return FALSE; } if (close(fd) < 0) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to close file `%s': %s"), xml_filename, g_strerror(errno)); return FALSE; } } else { gconf_log(GCL_DEBUG, "No XML filename passed to create_fs_dir() for %s", dir); } return TRUE; }
GConfBackend* gconf_get_backend(const gchar* address, GError** err) { GConfBackend* backend; gchar* name; gchar* why_invalid; if (loaded_backends == NULL) { loaded_backends = g_hash_table_new(g_str_hash, g_str_equal); } why_invalid = NULL; if (!gconf_address_valid (address, &why_invalid)) { g_assert (why_invalid != NULL); gconf_set_error (err, GCONF_ERROR_BAD_ADDRESS, _("Bad address `%s': %s"), address, why_invalid); g_free (why_invalid); return NULL; } name = gconf_address_backend(address); if (name == NULL) { gconf_set_error(err, GCONF_ERROR_BAD_ADDRESS, _("Bad address `%s'"), address); return NULL; } backend = g_hash_table_lookup(loaded_backends, name); if (backend != NULL) { /* Returning a "copy" */ gconf_backend_ref(backend); g_free(name); return backend; } else { gchar* file; file = gconf_backend_file(address); if (file != NULL) { GModule* module; GConfBackendVTable* (*get_vtable)(void); if (!g_module_supported()) g_error(_("GConf won't work without dynamic module support (gmodule)")); module = g_module_open(file, G_MODULE_BIND_LAZY); g_free(file); if (module == NULL) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Error opening module `%s': %s\n"), name, g_module_error()); g_free(name); return NULL; } if (!g_module_symbol(module, "gconf_backend_get_vtable", (gpointer*)&get_vtable)) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Error initializing module `%s': %s\n"), name, g_module_error()); g_module_close(module); g_free(name); return NULL; } backend = g_new0(GConfBackend, 1); backend->module = module; if (!gconf_backend_verify_vtable((*get_vtable)(), &backend->vtable, name, err)) { g_module_close(module); g_free(name); g_free(backend); return NULL; } backend->name = name; g_hash_table_insert(loaded_backends, (gchar*)backend->name, backend); /* Returning a "copy" */ gconf_backend_ref(backend); return backend; } else { gconf_set_error(err, GCONF_ERROR_FAILED, _("Couldn't locate backend module for `%s'"), address); return NULL; } } }
/* this actually works on any node, not just <entry>, such as the <car> and <cdr> nodes and the <li> nodes and the <default> node */ static GConfValue* node_extract_value(xmlNodePtr node, const gchar** locales, GError** err) { GConfValue* value = NULL; gchar* type_str; GConfValueType type = GCONF_VALUE_INVALID; const gchar* default_locales[] = { "C", NULL }; if (locales == NULL) locales = default_locales; type_str = my_xmlGetProp(node, "type"); if (type_str == NULL) { gconf_set_error(err, GCONF_ERROR_PARSE_ERROR, _("No \"type\" attribute for <%s> node"), (node->name ? (char*)node->name : "(nil)")); return NULL; } type = gconf_value_type_from_string(type_str); xmlFree(type_str); switch (type) { case GCONF_VALUE_INVALID: { gconf_set_error(err, GCONF_ERROR_PARSE_ERROR, _("A node has unknown \"type\" attribute `%s', ignoring"), type_str); return NULL; } break; case GCONF_VALUE_INT: case GCONF_VALUE_BOOL: case GCONF_VALUE_FLOAT: { gchar* value_str; value_str = my_xmlGetProp(node, "value"); if (value_str == NULL) { gconf_set_error(err, GCONF_ERROR_PARSE_ERROR, _("No \"value\" attribute for node")); return NULL; } value = gconf_value_new_from_string(type, value_str, err); xmlFree(value_str); g_return_val_if_fail( (value != NULL) || (err == NULL) || (*err != NULL), NULL ); return value; } break; case GCONF_VALUE_STRING: { xmlNodePtr iter; iter = node->xmlChildrenNode; while (iter != NULL) { if (iter->type == XML_ELEMENT_NODE) { GConfValue* v = NULL; if (strcmp((char *)iter->name, "stringvalue") == 0) { gchar* s; s = (gchar *)xmlNodeGetContent(iter); v = gconf_value_new(GCONF_VALUE_STRING); /* strdup() caused purely by g_free()/free() difference */ gconf_value_set_string(v, s ? s : ""); if (s) xmlFree(s); return v; } else { /* What the hell is this? */ gconf_log(GCL_WARNING, _("Didn't understand XML node <%s> inside an XML list node"), iter->name ? iter->name : (guchar*)"???"); } } iter = iter->next; } return NULL; } break; case GCONF_VALUE_SCHEMA: return schema_node_extract_value(node, locales); break; case GCONF_VALUE_LIST: { xmlNodePtr iter; GSList* values = NULL; GConfValueType list_type = GCONF_VALUE_INVALID; { gchar* s; s = my_xmlGetProp(node, "ltype"); if (s != NULL) { list_type = gconf_value_type_from_string(s); xmlFree(s); } } switch (list_type) { case GCONF_VALUE_INVALID: case GCONF_VALUE_LIST: case GCONF_VALUE_PAIR: gconf_set_error(err, GCONF_ERROR_PARSE_ERROR, _("Invalid type (list, pair, or unknown) in a list node")); return NULL; default: break; } iter = node->xmlChildrenNode; while (iter != NULL) { if (iter->type == XML_ELEMENT_NODE) { GConfValue* v = NULL; if (strcmp((char*)iter->name, "li") == 0) { v = node_extract_value(iter, locales, err); if (v == NULL) { if (err && *err) { gconf_log(GCL_WARNING, _("Bad XML node: %s"), (*err)->message); /* avoid pile-ups */ g_clear_error(err); } } else if (v->type != list_type) { gconf_log(GCL_WARNING, _("List contains a badly-typed node (%s, should be %s)"), gconf_value_type_to_string(v->type), gconf_value_type_to_string(list_type)); gconf_value_free(v); v = NULL; } } else { /* What the hell is this? */ gconf_log(GCL_WARNING, _("Didn't understand XML node <%s> inside an XML list node"), iter->name ? iter->name : (guchar*)"???"); } if (v != NULL) values = g_slist_prepend(values, v); } iter = iter->next; } /* put them in order, set the value */ values = g_slist_reverse(values); value = gconf_value_new(GCONF_VALUE_LIST); gconf_value_set_list_type(value, list_type); gconf_value_set_list_nocopy(value, values); return value; } break; case GCONF_VALUE_PAIR: { GConfValue* car = NULL; GConfValue* cdr = NULL; xmlNodePtr iter; iter = node->xmlChildrenNode; while (iter != NULL) { if (iter->type == XML_ELEMENT_NODE) { if (car == NULL && strcmp((char *)iter->name, "car") == 0) { car = node_extract_value(iter, locales, err); if (car == NULL) { if (err && *err) { gconf_log(GCL_WARNING, _("Ignoring bad car from XML pair: %s"), (*err)->message); /* prevent pile-ups */ g_clear_error(err); } } else if (car->type == GCONF_VALUE_LIST || car->type == GCONF_VALUE_PAIR) { gconf_log(GCL_WARNING, _("parsing XML file: lists and pairs may not be placed inside a pair")); gconf_value_free(car); car = NULL; } } else if (cdr == NULL && strcmp((char *)iter->name, "cdr") == 0) { cdr = node_extract_value(iter, locales, err); if (cdr == NULL) { if (err && *err) { gconf_log(GCL_WARNING, _("Ignoring bad cdr from XML pair: %s"), (*err)->message); /* avoid pile-ups */ g_clear_error(err); } } else if (cdr->type == GCONF_VALUE_LIST || cdr->type == GCONF_VALUE_PAIR) { gconf_log(GCL_WARNING, _("parsing XML file: lists and pairs may not be placed inside a pair")); gconf_value_free(cdr); cdr = NULL; } } else { /* What the hell is this? */ gconf_log(GCL_WARNING, _("Didn't understand XML node <%s> inside an XML pair node"), iter->name ? (gchar*)iter->name : "???"); } } iter = iter->next; } /* Return the pair if we got both halves */ if (car && cdr) { value = gconf_value_new(GCONF_VALUE_PAIR); gconf_value_set_car_nocopy(value, car); gconf_value_set_cdr_nocopy(value, cdr); return value; } else { gconf_log(GCL_WARNING, _("Didn't find car and cdr for XML pair node")); if (car) { g_assert(cdr == NULL); gconf_value_free(car); gconf_set_error(err, GCONF_ERROR_PARSE_ERROR, _("Missing cdr from pair of values in XML file")); } else if (cdr) { g_assert(car == NULL); gconf_value_free(cdr); gconf_set_error(err, GCONF_ERROR_PARSE_ERROR, _("Missing car from pair of values in XML file")); } else { gconf_set_error(err, GCONF_ERROR_PARSE_ERROR, _("Missing both car and cdr values from pair in XML file")); } return NULL; } } break; default: g_assert_not_reached(); return NULL; break; } }