aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--e-util/e-meta.c517
-rw-r--r--e-util/e-meta.h60
2 files changed, 577 insertions, 0 deletions
diff --git a/e-util/e-meta.c b/e-util/e-meta.c
new file mode 100644
index 0000000000..56e8d3bca9
--- /dev/null
+++ b/e-util/e-meta.c
@@ -0,0 +1,517 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include <glib.h>
+
+#include "e-meta.h"
+
+#include <gal/util/e-util.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlmemory.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+static GObjectClass *e_meta_parent_class;
+
+struct _meta_data {
+ struct _meta_data *next;
+
+ char *md_key;
+ char *md_value;
+};
+
+struct _EMetaPrivate {
+ char *path;
+ struct _meta_data *data;
+ gulong sync_id;
+ /* if set, we wont try and save/etc */
+ unsigned int deleted:1;
+};
+
+static int meta_save(EMeta *em)
+{
+ struct _EMetaPrivate *p = em->priv;
+ xmlDocPtr doc;
+ xmlNodePtr root, work;
+ struct _meta_data *md;
+ int res;
+ char *dir;
+ struct stat st;
+
+ if (p->deleted)
+ return 0;
+
+ /* since we can, build the full path if we need to */
+ dir = g_path_get_dirname(p->path);
+ if (stat(dir, &st) == -1) {
+ e_mkdir_hier(dir, 0777);
+ g_free(dir);
+ }
+
+ /* of course, saving in xml is overkill, but everyone loves this shit ... */
+
+ doc = xmlNewDoc("1.0");
+ root = xmlNewDocNode(doc, NULL, "e-meta-data", NULL);
+ xmlDocSetRootElement(doc, root);
+
+ md = p->data;
+ while (md) {
+ work = xmlNewChild(root, NULL, "item", NULL);
+ xmlSetProp(work, "name", md->md_key);
+ xmlSetProp(work, "value", md->md_value);
+ md = md->next;
+ }
+
+ res = e_xml_save_file(p->path, doc);
+ if (res != 0)
+ g_warning("Could not save folder meta-data `%s': %s", p->path, g_strerror(errno));
+
+ xmlFreeDoc(doc);
+
+ return res;
+}
+
+static int meta_load(EMeta *em)
+{
+ struct _EMetaPrivate *p = em->priv;
+ struct _meta_data *tail, *md;
+ xmlDocPtr doc;
+ xmlNodePtr root, work;
+ char *name, *val;
+
+ doc = xmlParseFile(p->path);
+ if (doc == NULL)
+ return -1;
+
+ root = xmlDocGetRootElement(doc);
+ if (root == NULL || strcmp(root->name, "e-meta-data")) {
+ xmlFreeDoc(doc);
+ errno = EINVAL;
+ return -1;
+ }
+
+ work = root->children;
+ tail = (struct _meta_data *)&p->data;
+ while (work) {
+ if (strcmp(work->name, "item") == 0) {
+ name = xmlGetProp(work, "name");
+ val = xmlGetProp(work, "value");
+ if (name && val) {
+ md = g_malloc(sizeof(*md));
+ md->md_key = g_strdup(name);
+ md->md_value = g_strdup(val);
+ md->next = NULL;
+ tail->next = md;
+ tail = md;
+ }
+ if (name)
+ xmlFree(name);
+ if (val)
+ xmlFree(val);
+ }
+ work = work->next;
+ }
+
+ xmlFreeDoc(doc);
+
+ return 0;
+}
+
+static struct _meta_data *meta_find(EMeta *em, const char *key, struct _meta_data **mpp)
+{
+ struct _meta_data *mp = (struct _meta_data *)&em->priv->data;
+ struct _meta_data *md = mp->next;
+
+ while (md && strcmp(md->md_key, key) != 0) {
+ mp = md;
+ md = md->next;
+ }
+
+ *mpp = mp;
+
+ return md;
+}
+
+static void meta_free(struct _meta_data *md)
+{
+ g_free(md->md_key);
+ g_free(md->md_value);
+ g_free(md);
+}
+
+static void
+e_meta_init (EMeta *em)
+{
+ em->priv = g_malloc0(sizeof(*em->priv));
+}
+
+static void
+e_meta_finalise(GObject *crap)
+{
+ EMeta *em = (EMeta *)crap;
+ struct _EMetaPrivate *p = em->priv;
+ struct _meta_data *md, *mn;
+
+ if (p->sync_id != 0)
+ e_meta_sync(em);
+
+ md = p->data;
+ while (md) {
+ mn = md->next;
+ meta_free(md);
+ md = mn;
+ }
+
+ g_free(p->path);
+ g_free(p);
+ e_meta_parent_class->finalize((GObject *)em);
+}
+
+static void
+e_meta_class_init (EMetaClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS(klass);
+
+ e_meta_parent_class = g_type_class_ref (G_TYPE_OBJECT);
+
+ ((GObjectClass *)klass)->finalize = e_meta_finalise;
+}
+
+static GTypeInfo e_meta_type_info = {
+ sizeof (EMetaClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) e_meta_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (EMeta),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) e_meta_init
+};
+
+static GType e_meta_type;
+
+GType
+e_meta_get_type (void)
+{
+ return e_meta_type?e_meta_type:(e_meta_type = g_type_register_static (G_TYPE_OBJECT, "EMeta", &e_meta_type_info, 0));
+}
+
+/**
+ * e_meta_new:
+ * @path: full path to meta-data storage object.
+ *
+ * Create a new meta-data storage object. Any existing meta-data stored for
+ * this key will be loaded.
+ *
+ * Return value:
+ **/
+EMeta *
+e_meta_new(const char *path)
+{
+ EMeta *em;
+
+ em = g_object_new(e_meta_get_type(), NULL);
+ em->priv->path = g_strdup(path);
+ meta_load(em);
+
+ return em;
+}
+
+static gboolean
+meta_flush(EMeta *em)
+{
+ em->priv->sync_id = 0;
+ meta_save(em);
+
+ return FALSE;
+}
+
+/* returns TRUE if the value changed */
+static int meta_set(EMeta *em, const char *key, const char *val)
+{
+ struct _EMetaPrivate *p = em->priv;
+ struct _meta_data *md, *mp;
+
+ md = meta_find(em, key, &mp);
+ if (md == NULL) {
+ /* already unset / or new case */
+ if (val == NULL)
+ return FALSE;
+ md = g_malloc0(sizeof(*md));
+ md->md_key = g_strdup(key);
+ md->next = p->data;
+ p->data = md;
+ } else if (val == NULL) {
+ /* unset case */
+ mp->next = md->next;
+ meta_free(md);
+ return TRUE;
+ } else if (strcmp(md->md_value, val) == 0) {
+ /* unchanged value */
+ return FALSE;
+ } else {
+ /* changed value */
+ g_free(md->md_value);
+ }
+ md->md_value = g_strdup(val);
+
+ return TRUE;
+}
+
+/* get a value, returns NULL if it doesn't exist */
+static const char *meta_get(EMeta *em, const char *key)
+{
+ struct _meta_data *md, *mp;
+
+ md = meta_find(em, key, &mp);
+
+ return md?md->md_value:NULL;
+}
+
+/**
+ * e_meta_set:
+ * @em:
+ * @key:
+ * @...: value, key, value, ..., NULL.
+ *
+ * Set any number of meta-data key-value pairs.
+ * Unset a key by passing a value of NULL.
+ *
+ * If the meta-data set changes as a result of this
+ * call, then a sync will be implicitly queued for
+ * a later time.
+ **/
+void
+e_meta_set(EMeta *em, const char *key, ...)
+{
+ struct _EMetaPrivate *p = em->priv;
+ const char *val;
+ va_list ap;
+ int changed = FALSE;
+
+ va_start(ap, key);
+ while (key != NULL) {
+ val = va_arg(ap, const char *);
+ changed = meta_set(em, key, val);
+ key = va_arg(ap, const char *);
+ }
+ va_end(ap);
+
+ /* todo: could do changed events ? */
+
+ if (changed && p->sync_id == 0)
+ p->sync_id = g_timeout_add(2000, (GSourceFunc)meta_flush, em);
+}
+
+/**
+ * e_meta_get:
+ * @em:
+ * @key:
+ * @...: value, key, value, ..., NULL.
+ *
+ * Get any number of meta-data key-value pairs.
+ **/
+void
+e_meta_get(EMeta *em, const char *key, ...)
+{
+ const char **valp;
+ va_list ap;
+
+ va_start(ap, key);
+ while (key) {
+ valp = va_arg(ap, const char **);
+ *valp = meta_get(em, key);
+ key = va_arg(ap, const char *);
+ }
+ va_end(ap);
+}
+
+/**
+ * e_meta_get_bool:
+ * @em:
+ * @key:
+ * @def:
+ *
+ * Get a boolean value at @key, with a default fallback @def.
+ *
+ * If the default value is used, then it will become the persistent
+ * new value for the key.
+ *
+ * Return value: The value of the key, or if the key was not
+ * previously set, then the new value of the key, @def.
+ **/
+gboolean
+e_meta_get_bool(EMeta *em, const char *key, gboolean def)
+{
+ const char *v;
+
+ v = meta_get(em, key);
+ /* this forces the value to become 'static' from first use */
+ if (v == NULL) {
+ e_meta_set_bool(em, key, def);
+ return def;
+ }
+
+ return atoi(v);
+}
+
+/**
+ * e_meta_set_bool:
+ * @em:
+ * @key:
+ * @val:
+ *
+ * Helper to set a boolean value. Boolean TRUE is mapped to
+ * the string "1", FALSE to "0".
+ **/
+void
+e_meta_set_bool(EMeta *em, const char *key, gboolean val)
+{
+ e_meta_set(em, key, val?"1":"0", NULL);
+}
+
+/**
+ * e_meta_sync:
+ * @em:
+ *
+ * Force an explicit and immediate sync of the meta-data to disk.
+ *
+ * This is not normally required unless part of transactional
+ * processing, as updates will always be flushed to disk automatically.
+ *
+ * Return value: 0 on success.
+ **/
+int
+e_meta_sync(EMeta *em)
+{
+ struct _EMetaPrivate *p = em->priv;
+
+ if (p->sync_id != 0) {
+ g_source_remove(p->sync_id);
+ p->sync_id = 0;
+ }
+
+ return meta_save(em);
+}
+
+static GHashTable *e_meta_table;
+
+static char *meta_filename(const char *base, const char *key)
+{
+ const char *p;
+ char *keyp, *o, c;
+
+ p = key;
+ o = keyp = alloca(strlen(key)+8);
+
+ while ( (c = *p++) ) {
+ if (c == '/')
+ c == '_';
+ *o++ = c;
+ }
+ strcpy(o, ".emeta");
+ o = g_build_filename(base, keyp, NULL);
+
+ return o;
+}
+
+static void
+meta_weak_notify(char *path, void *o)
+{
+ g_hash_table_remove(e_meta_table, path);
+ g_free(path);
+}
+
+/**
+ * e_meta_data_lookup:
+ * @base: Base storage directory.
+ * @key: key for file.
+ *
+ * Lookup a meta-data object from a storage directory.
+ *
+ * Return value: The metadata object.
+ **/
+EMeta *e_meta_data_find(const char *base, const char *key)
+{
+ EMeta *em;
+ char *path;
+
+ if (e_meta_table == NULL)
+ e_meta_table = g_hash_table_new(g_str_hash, g_str_equal);
+
+ path = meta_filename(base, key);
+ em = g_hash_table_lookup(e_meta_table, path);
+ if (em) {
+ g_free(path);
+ g_object_ref(em);
+ return em;
+ }
+
+ em = e_meta_new(path);
+ g_hash_table_insert(e_meta_table, path, em);
+ g_object_weak_ref((GObject *)em, (GWeakNotify)meta_weak_notify, path);
+
+ return em;
+}
+
+/**
+ * e_meta_data_delete:
+ * @base:
+ * @key:
+ *
+ * Delete a key from storage. If the key is still cached, it will be
+ * marked as deleted, and will not be saved from then on.
+ **/
+void e_meta_data_delete(const char *base, const char *key)
+{
+ EMeta *em;
+ char *path;
+
+ path = meta_filename(base, key);
+
+ if (e_meta_table && (em = g_hash_table_lookup(e_meta_table, path))) {
+ if (em->priv->sync_id) {
+ g_source_remove(em->priv->sync_id);
+ em->priv->sync_id = 0;
+ }
+ em->priv->deleted = TRUE;
+ }
+
+ unlink(path);
+ g_free(path);
+}
diff --git a/e-util/e-meta.h b/e-util/e-meta.h
new file mode 100644
index 0000000000..19c23e3a06
--- /dev/null
+++ b/e-util/e-meta.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *
+ *
+ * A simple persistent meta-data api.
+ *
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _E_META_H
+#define _E_META_H 1
+
+#include <glib.h>
+#include <glib-object.h>
+
+typedef struct _EMeta EMeta;
+typedef struct _EMetaClass EMetaClass;
+
+struct _EMeta {
+ GObject object;
+
+ struct _EMetaPrivate *priv;
+};
+
+struct _EMetaClass {
+ GObjectClass object;
+};
+
+GType e_meta_get_type (void);
+
+/* 'trivial' meta-data api */
+EMeta *e_meta_new(const char *path);
+void e_meta_set(EMeta *em, const char *key, ...);
+void e_meta_get(EMeta *em, const char *key, ...);
+int e_meta_sync(EMeta *em);
+
+/* helpers */
+gboolean e_meta_get_bool(EMeta *, const char *key, gboolean def);
+void e_meta_set_bool(EMeta *, const char *key, gboolean val);
+
+/* 'class' methods */
+EMeta *e_meta_data_find(const char *base, const char *key);
+void e_meta_data_delete(const char *base, const char *key);
+
+#endif /* ! _E_META_H */