summaryrefslogtreecommitdiff
path: root/libedataserver
diff options
context:
space:
mode:
authorJP Rosevear <jpr@src.gnome.org>2003-11-03 18:25:44 +0000
committerJP Rosevear <jpr@src.gnome.org>2003-11-03 18:25:44 +0000
commitc85a60e4e50b6a38db7297a84834cf3252d003c5 (patch)
tree0fcb86057ac9eb601b1bf4b56f281723cc4ac847 /libedataserver
parent27f7db1c9baeb3b1011ab490881f11d789d4cdda (diff)
downloadevolution-data-server-c85a60e4e50b6a38db7297a84834cf3252d003c5.tar.gz
Initial revision
Diffstat (limited to 'libedataserver')
-rw-r--r--libedataserver/Makefile.am59
-rw-r--r--libedataserver/e-account-list.c472
-rw-r--r--libedataserver/e-account-list.h75
-rw-r--r--libedataserver/e-account.c561
-rw-r--r--libedataserver/e-account.h106
-rw-r--r--libedataserver/e-component-listener.c171
-rw-r--r--libedataserver/e-component-listener.h47
-rw-r--r--libedataserver/e-data-server-marshal.c54
-rw-r--r--libedataserver/e-data-server-marshal.h20
-rw-r--r--libedataserver/e-db3-utils.c184
-rw-r--r--libedataserver/e-db3-utils.h29
-rw-r--r--libedataserver/e-dbhash.c227
-rw-r--r--libedataserver/e-dbhash.h45
-rw-r--r--libedataserver/e-iterator.c183
-rw-r--r--libedataserver/e-iterator.h71
-rw-r--r--libedataserver/e-list-iterator.c249
-rw-r--r--libedataserver/e-list-iterator.h46
-rw-r--r--libedataserver/e-list.c191
-rw-r--r--libedataserver/e-list.h71
-rw-r--r--libedataserver/e-memory.c1306
-rw-r--r--libedataserver/e-memory.h74
-rw-r--r--libedataserver/e-msgport.c1041
-rw-r--r--libedataserver/e-msgport.h88
-rw-r--r--libedataserver/e-sexp.c1379
-rw-r--r--libedataserver/e-sexp.h172
-rw-r--r--libedataserver/e-uid.c61
-rw-r--r--libedataserver/e-uid.h28
-rw-r--r--libedataserver/e-url.c341
-rw-r--r--libedataserver/e-url.h56
-rw-r--r--libedataserver/e-xml-hash-utils.c252
-rw-r--r--libedataserver/e-xml-hash-utils.h69
-rw-r--r--libedataserver/ename/Makefile.am45
-rw-r--r--libedataserver/ename/TODO2
-rw-r--r--libedataserver/ename/e-address-western.c444
-rw-r--r--libedataserver/ename/e-address-western.h21
-rw-r--r--libedataserver/ename/e-name-western-tables.h74
-rw-r--r--libedataserver/ename/e-name-western.c982
-rw-r--r--libedataserver/ename/e-name-western.h21
-rw-r--r--libedataserver/libedataserver-1.0.pc.in14
-rw-r--r--libedataserver/md5-utils.c363
-rw-r--r--libedataserver/md5-utils.h52
41 files changed, 9746 insertions, 0 deletions
diff --git a/libedataserver/Makefile.am b/libedataserver/Makefile.am
new file mode 100644
index 000000000..a118a6de8
--- /dev/null
+++ b/libedataserver/Makefile.am
@@ -0,0 +1,59 @@
+SUBDIRS = ename
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ -DG_LOG_DOMAIN=\"e-data-server\" \
+ -DG_DISABLE_DEPRECATED \
+ $(DB3_CFLAGS) \
+ $(E_DATA_SERVER_CFLAGS)
+
+# The marshallers
+MARSHAL_GENERATED = e-data-server-marshal.c e-data-server-marshal.h
+@EVO_MARSHAL_RULE@
+
+lib_LTLIBRARIES = libedataserver.la
+
+libedataserver_la_SOURCES = \
+ $(MARSHAL_GENERATED) \
+ e-account-list.c \
+ e-account.c \
+ e-component-listener.c \
+ e-dbhash.c \
+ e-db3-utils.c \
+ e-iterator.c \
+ e-list.c \
+ e-list-iterator.c \
+ e-memory.c \
+ e-msgport.c \
+ e-sexp.c \
+ e-uid.c \
+ e-url.c \
+ e-xml-hash-utils.c \
+ md5-utils.c
+
+libedataserver_la_LIBADD = \
+ $(DB3_LDADD) \
+ $(E_DATA_SERVER_LIBS)
+
+libedataserver_la_LDFLAGS = \
+ -version-info $(LIBEDATASERVER_CURRENT):$(LIBEDATASERVER_REVISION):$(LIBEDATASERVER_AGE) \
+ -no-undefined
+
+libedataserverincludedir = $(includedir)/evolution-data-server-1.0/libedataserver
+
+libedataserverinclude_HEADERS = \
+ e-account-list.h \
+ e-account.h \
+ e-component-listener.h \
+ e-db3-utils.h \
+ e-dbhash.h \
+ e-iterator.h \
+ e-list.h \
+ e-list-iterator.h \
+ e-memory.h \
+ e-msgport.h \
+ e-sexp.h \
+ e-uid.h \
+ e-url.h \
+ e-xml-hash-utils.h \
+ md5-utils.h \ No newline at end of file
diff --git a/libedataserver/e-account-list.c b/libedataserver/e-account-list.c
new file mode 100644
index 000000000..b9fbb8de3
--- /dev/null
+++ b/libedataserver/e-account-list.c
@@ -0,0 +1,472 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2003 Ximian, Inc.
+ *
+ * This library 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 library; 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
+
+#include "e-account-list.h"
+#include "e-account.h"
+#include "e-data-server-marshal.h"
+
+#include <string.h>
+#include <gal/util/e-util.h>
+
+struct EAccountListPrivate {
+ GConfClient *gconf;
+ guint notify_id;
+};
+
+enum {
+ ACCOUNT_ADDED,
+ ACCOUNT_CHANGED,
+ ACCOUNT_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
+#define PARENT_TYPE E_TYPE_LIST
+static EListClass *parent_class = NULL;
+
+static void dispose (GObject *);
+static void finalize (GObject *);
+
+static void
+class_init (GObjectClass *object_class)
+{
+ parent_class = g_type_class_ref (PARENT_TYPE);
+
+ /* virtual method override */
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ /* signals */
+ signals[ACCOUNT_ADDED] =
+ g_signal_new ("account-added",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EAccountListClass, account_added),
+ NULL, NULL,
+ e_data_server_marshal_NONE__OBJECT,
+ G_TYPE_NONE, 1,
+ E_TYPE_ACCOUNT);
+ signals[ACCOUNT_CHANGED] =
+ g_signal_new ("account-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EAccountListClass, account_changed),
+ NULL, NULL,
+ e_data_server_marshal_NONE__OBJECT,
+ G_TYPE_NONE, 1,
+ E_TYPE_ACCOUNT);
+ signals[ACCOUNT_REMOVED] =
+ g_signal_new ("account-removed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EAccountListClass, account_removed),
+ NULL, NULL,
+ e_data_server_marshal_NONE__OBJECT,
+ G_TYPE_NONE, 1,
+ E_TYPE_ACCOUNT);
+}
+
+static void
+init (GObject *object)
+{
+ EAccountList *account_list = E_ACCOUNT_LIST (object);
+
+ account_list->priv = g_new0 (EAccountListPrivate, 1);
+}
+
+static void
+dispose (GObject *object)
+{
+ EAccountList *account_list = E_ACCOUNT_LIST (object);
+
+ if (account_list->priv->gconf) {
+ if (account_list->priv->notify_id) {
+ gconf_client_notify_remove (account_list->priv->gconf,
+ account_list->priv->notify_id);
+ }
+ g_object_unref (account_list->priv->gconf);
+ account_list->priv->gconf = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ EAccountList *account_list = E_ACCOUNT_LIST (object);
+
+ g_free (account_list->priv);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+E_MAKE_TYPE (e_account_list, "EAccountList", EAccountList, class_init, init, PARENT_TYPE)
+
+
+static void
+gconf_accounts_changed (GConfClient *client, guint cnxn_id,
+ GConfEntry *entry, gpointer user_data)
+{
+ EAccountList *account_list = user_data;
+ GSList *list, *l, *new_accounts = NULL;
+ EAccount *account;
+ EList *old_accounts;
+ EIterator *iter;
+ char *uid;
+
+ old_accounts = e_list_duplicate (E_LIST (account_list));
+
+ list = gconf_client_get_list (client, "/apps/evolution/mail/accounts",
+ GCONF_VALUE_STRING, NULL);
+ for (l = list; l; l = l->next) {
+ uid = e_account_uid_from_xml (l->data);
+ if (!uid)
+ continue;
+
+ /* See if this is an existing account */
+ for (iter = e_list_get_iterator (old_accounts);
+ e_iterator_is_valid (iter);
+ e_iterator_next (iter)) {
+ account = (EAccount *)e_iterator_get (iter);
+ if (!strcmp (account->uid, uid)) {
+ /* The account still exists, so remove
+ * it from "old_accounts" and update it.
+ */
+ e_iterator_delete (iter);
+ if (e_account_set_from_xml (account, l->data))
+ g_signal_emit (account_list, signals[ACCOUNT_CHANGED], 0, account);
+ goto next;
+ }
+ }
+
+ /* Must be a new account */
+ account = e_account_new_from_xml (l->data);
+ e_list_append (E_LIST (account_list), account);
+ new_accounts = g_slist_prepend (new_accounts, account);
+
+ next:
+ g_free (uid);
+ g_object_unref (iter);
+ }
+
+ /* Now emit signals for each added account. (We do this after
+ * adding all of them because otherwise if the signal handler
+ * calls e_account_list_get_default_account() it will end up
+ * causing the first account in the list to become the
+ * default.)
+ */
+ for (l = new_accounts; l; l = l->next) {
+ account = l->data;
+ g_signal_emit (account_list, signals[ACCOUNT_ADDED], 0, account);
+ g_object_unref (account);
+ }
+ g_slist_free (new_accounts);
+
+ /* Anything left in old_accounts must have been deleted */
+ for (iter = e_list_get_iterator (old_accounts);
+ e_iterator_is_valid (iter);
+ e_iterator_next (iter)) {
+ account = (EAccount *)e_iterator_get (iter);
+ e_list_remove (E_LIST (account_list), account);
+ g_signal_emit (account_list, signals[ACCOUNT_REMOVED], 0, account);
+ }
+ g_object_unref (iter);
+ g_object_unref (old_accounts);
+}
+
+static void *
+copy_func (const void *data, void *closure)
+{
+ GObject *object = (GObject *)data;
+
+ g_object_ref (object);
+ return object;
+}
+
+static void
+free_func (void *data, void *closure)
+{
+ g_object_unref (data);
+}
+
+/**
+ * e_account_list_new:
+ * @gconf: a #GConfClient
+ *
+ * Reads the list of accounts from @gconf and listens for changes.
+ * Will emit %account_added, %account_changed, and %account_removed
+ * signals according to notifications from GConf.
+ *
+ * You can modify the list using e_list_append(), e_list_remove(), and
+ * e_iterator_delete(). After adding, removing, or changing accounts,
+ * you must call e_account_list_save() to push the changes back to
+ * GConf.
+ *
+ * Return value: the list of accounts
+ **/
+EAccountList *
+e_account_list_new (GConfClient *gconf)
+{
+ EAccountList *account_list;
+
+ g_return_val_if_fail (GCONF_IS_CLIENT (gconf), NULL);
+
+ account_list = g_object_new (E_TYPE_ACCOUNT_LIST, NULL);
+ e_account_list_construct (account_list, gconf);
+
+ return account_list;
+}
+
+void
+e_account_list_construct (EAccountList *account_list, GConfClient *gconf)
+{
+ g_return_if_fail (GCONF_IS_CLIENT (gconf));
+
+ e_list_construct (E_LIST (account_list), copy_func, free_func, NULL);
+ account_list->priv->gconf = gconf;
+ g_object_ref (gconf);
+
+ gconf_client_add_dir (account_list->priv->gconf,
+ "/apps/evolution/mail/accounts",
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+ account_list->priv->notify_id =
+ gconf_client_notify_add (account_list->priv->gconf,
+ "/apps/evolution/mail/accounts",
+ gconf_accounts_changed, account_list,
+ NULL, NULL);
+
+ gconf_accounts_changed (account_list->priv->gconf,
+ account_list->priv->notify_id,
+ NULL, account_list);
+}
+
+/**
+ * e_account_list_save:
+ * @account_list: an #EAccountList
+ *
+ * Saves @account_list to GConf. Signals will be emitted for changes.
+ **/
+void
+e_account_list_save (EAccountList *account_list)
+{
+ GSList *list = NULL;
+ EAccount *account;
+ EIterator *iter;
+ char *xmlbuf;
+
+ for (iter = e_list_get_iterator (E_LIST (account_list));
+ e_iterator_is_valid (iter);
+ e_iterator_next (iter)) {
+ account = (EAccount *)e_iterator_get (iter);
+
+ xmlbuf = e_account_to_xml (account);
+ if (xmlbuf)
+ list = g_slist_append (list, xmlbuf);
+ }
+ g_object_unref (iter);
+
+ gconf_client_set_list (account_list->priv->gconf,
+ "/apps/evolution/mail/accounts",
+ GCONF_VALUE_STRING, list, NULL);
+
+ while (list) {
+ g_free (list->data);
+ list = g_slist_remove (list, list->data);
+ }
+
+ gconf_client_suggest_sync (account_list->priv->gconf, NULL);
+}
+
+/**
+ * e_account_list_add:
+ * @accounts:
+ * @account:
+ *
+ * Add an account to the account list. Will emit the account-changed
+ * event.
+ **/
+void
+e_account_list_add(EAccountList *accounts, EAccount *account)
+{
+ /* FIXME: should we check for duplicate accounts? */
+
+ e_list_append ((EList *)accounts, account);
+ g_signal_emit(accounts, signals[ACCOUNT_ADDED], 0, account);
+}
+
+/**
+ * e_account_list_change:
+ * @accounts:
+ * @account:
+ *
+ * Signal that the details of an account have changed.
+ **/
+void
+e_account_list_change(EAccountList *accounts, EAccount *account)
+{
+ /* maybe the account should do this itself ... */
+ g_signal_emit(accounts, signals[ACCOUNT_CHANGED], 0, account);
+}
+
+/**
+ * e_account_list_remove:
+ * @accounts:
+ * @account:
+ *
+ * Remove an account from the account list, and emit the
+ * account-removed signal. If the account was the default account,
+ * then reset the default to the first account.
+ **/
+void
+e_account_list_remove(EAccountList *accounts, EAccount *account)
+{
+ if (account == e_account_list_get_default(accounts))
+ gconf_client_unset (accounts->priv->gconf, "/apps/evolution/mail/default_account", NULL);
+
+ /* not sure if need to ref but no harm */
+ g_object_ref (account);
+ e_list_remove ((EList *) accounts, account);
+ g_signal_emit(accounts, signals[ACCOUNT_REMOVED], 0, account);
+ g_object_unref (account);
+}
+
+/**
+ * e_account_list_get_default:
+ * @accounts:
+ *
+ * Get the default account. If no default is specified, or the default
+ * has become stale, then the first account is made the default.
+ *
+ * Return value: The account or NULL if no accounts are defined.
+ **/
+const EAccount *
+e_account_list_get_default(EAccountList *accounts)
+{
+ char *uid;
+ EIterator *it;
+ const EAccount *account = NULL;
+
+ uid = gconf_client_get_string (accounts->priv->gconf, "/apps/evolution/mail/default_account", NULL);
+ it = e_list_get_iterator ((EList *)accounts);
+
+ if (uid) {
+ for (;e_iterator_is_valid (it);e_iterator_next (it)) {
+ account = (const EAccount *)e_iterator_get (it);
+
+ if (!strcmp(uid, account->uid))
+ break;
+ account = NULL;
+ }
+ e_iterator_reset(it);
+ }
+
+ /* no uid or uid not found, @it will be at the first account */
+ if (account == NULL && e_iterator_is_valid(it)) {
+ account = (const EAccount *) e_iterator_get (it);
+ gconf_client_set_string (accounts->priv->gconf, "/apps/evolution/mail/default_account", account->uid, NULL);
+ }
+
+ g_object_unref(it);
+ g_free(uid);
+
+ return account;
+}
+
+/**
+ * e_account_list_set_default:
+ * @accounts:
+ * @account:
+ *
+ * Set the account @account to be the default account.
+ **/
+void
+e_account_list_set_default(EAccountList *accounts, EAccount *account)
+{
+ gconf_client_set_string (accounts->priv->gconf, "/apps/evolution/mail/default_account", account->uid, NULL);
+}
+
+/**
+ * e_account_list_find:
+ * @accounts:
+ * @type: Type of search.
+ * @key: Search key.
+ *
+ * Perform a search of the account list on a single key.
+ *
+ * @type must be set from one of the following search types:
+ * E_ACCOUNT_FIND_NAME - Find an account by account name.
+ * E_ACCOUNT_FIND_ID_NAME - Find an account by the owner's identity name.
+ * E_ACCOUNT_FIND_ID_ADDRESS - Find an account by the owner's identity address.
+ *
+ * Return value: The account or NULL if it doesn't exist.
+ **/
+const EAccount *
+e_account_list_find(EAccountList *accounts, e_account_find_t type, const char *key)
+{
+ char *val;
+ EIterator *it;
+ const EAccount *account = NULL;
+
+ /* this could use a callback for more flexibility ...
+ ... but this makes the common cases easier */
+
+ if (!key)
+ return NULL;
+
+ for (it = e_list_get_iterator ((EList *)accounts);
+ e_iterator_is_valid (it);
+ e_iterator_next (it)) {
+ int found = 0;
+
+ account = (const EAccount *)e_iterator_get (it);
+
+ val = NULL;
+ switch(type) {
+ case E_ACCOUNT_FIND_NAME:
+ found = strcmp(account->name, key) == 0;
+ break;
+ case E_ACCOUNT_FIND_UID:
+ found = strcmp(account->uid, key) == 0;
+ break;
+ case E_ACCOUNT_FIND_ID_NAME:
+ if (account->id)
+ found = strcmp(account->id->name, key) == 0;
+ break;
+ case E_ACCOUNT_FIND_ID_ADDRESS:
+ if (account->id)
+ found = g_ascii_strcasecmp(account->id->address, key) == 0;
+ break;
+ }
+
+ if (found)
+ break;
+
+ account = NULL;
+ }
+ g_object_unref(it);
+
+ return account;
+}
+
diff --git a/libedataserver/e-account-list.h b/libedataserver/e-account-list.h
new file mode 100644
index 000000000..b47efb42a
--- /dev/null
+++ b/libedataserver/e-account-list.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2003 Ximian, Inc.
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __E_ACCOUNT_LIST__
+#define __E_ACCOUNT_LIST__
+
+#include "e-list.h"
+#include "e-account.h"
+#include <gconf/gconf-client.h>
+
+#define E_TYPE_ACCOUNT_LIST (e_account_list_get_type ())
+#define E_ACCOUNT_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_ACCOUNT_LIST, EAccountList))
+#define E_ACCOUNT_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_ACCOUNT_LIST, EAccountListClass))
+#define E_IS_ACCOUNT_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_ACCOUNT_LIST))
+#define E_IS_ACCOUNT_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), E_TYPE_ACCOUNT_LIST))
+
+typedef struct EAccountListPrivate EAccountListPrivate;
+
+/* search options for the find command */
+typedef enum _e_account_find_t {
+ E_ACCOUNT_FIND_NAME,
+ E_ACCOUNT_FIND_UID,
+ E_ACCOUNT_FIND_ID_NAME,
+ E_ACCOUNT_FIND_ID_ADDRESS,
+} e_account_find_t;
+
+typedef struct {
+ EList parent_object;
+
+ EAccountListPrivate *priv;
+} EAccountList;
+
+typedef struct {
+ EListClass parent_class;
+
+ /* signals */
+ void (*account_added) (EAccountList *, EAccount *);
+ void (*account_changed) (EAccountList *, EAccount *);
+ void (*account_removed) (EAccountList *, EAccount *);
+} EAccountListClass;
+
+
+GType e_account_list_get_type (void);
+
+EAccountList *e_account_list_new (GConfClient *gconf);
+void e_account_list_construct (EAccountList *account_list,
+ GConfClient *gconf);
+
+void e_account_list_save (EAccountList *account_list);
+
+void e_account_list_add (EAccountList *, EAccount *);
+void e_account_list_change (EAccountList *, EAccount *);
+void e_account_list_remove (EAccountList *, EAccount *);
+
+const EAccount *e_account_list_get_default(EAccountList *);
+void e_account_list_set_default(EAccountList *, EAccount *);
+const EAccount *e_account_list_find (EAccountList *, e_account_find_t type, const char *key);
+
+#endif /* __E_ACCOUNT_LIST__ */
diff --git a/libedataserver/e-account.c b/libedataserver/e-account.c
new file mode 100644
index 000000000..19bd0eadd
--- /dev/null
+++ b/libedataserver/e-account.c
@@ -0,0 +1,561 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2003 Ximian, Inc.
+ *
+ * This library 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 library; 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
+
+#include "e-account.h"
+
+#include "e-uid.h"
+
+#include <string.h>
+
+#include <gal/util/e-util.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlmemory.h>
+
+#define PARENT_TYPE G_TYPE_OBJECT
+static GObjectClass *parent_class = NULL;
+
+static void finalize (GObject *);
+
+static void
+class_init (GObjectClass *object_class)
+{
+ parent_class = g_type_class_ref (PARENT_TYPE);
+
+ /* virtual method override */
+ object_class->finalize = finalize;
+}
+
+static void
+init (EAccount *account)
+{
+ account->id = g_new0 (EAccountIdentity, 1);
+ account->source = g_new0 (EAccountService, 1);
+ account->transport = g_new0 (EAccountService, 1);
+}
+
+static void
+identity_destroy (EAccountIdentity *id)
+{
+ if (!id)
+ return;
+
+ g_free (id->name);
+ g_free (id->address);
+ g_free (id->reply_to);
+ g_free (id->organization);
+
+ g_free (id);
+}
+
+static void
+service_destroy (EAccountService *service)
+{
+ if (!service)
+ return;
+
+ g_free (service->url);
+
+ g_free (service);
+}
+
+static void
+finalize (GObject *object)
+{
+ EAccount *account = E_ACCOUNT (object);
+
+ g_free (account->name);
+ g_free (account->uid);
+
+ identity_destroy (account->id);
+ service_destroy (account->source);
+ service_destroy (account->transport);
+
+ g_free (account->drafts_folder_uri);
+ g_free (account->sent_folder_uri);
+
+ g_free (account->cc_addrs);
+ g_free (account->bcc_addrs);
+
+ g_free (account->pgp_key);
+ g_free (account->smime_sign_key);
+ g_free (account->smime_encrypt_key);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+E_MAKE_TYPE (e_account, "EAccount", EAccount, class_init, init, PARENT_TYPE)
+
+
+/**
+ * e_account_new:
+ *
+ * Return value: a blank new account which can be filled in and
+ * added to an #EAccountList.
+ **/
+EAccount *
+e_account_new (void)
+{
+ EAccount *account;
+
+ account = g_object_new (E_TYPE_ACCOUNT, NULL);
+ account->uid = e_uid_new ();
+
+ return account;
+}
+
+/**
+ * e_account_new_from_xml:
+ * @xml: an XML account description
+ *
+ * Return value: a new #EAccount based on the data in @xml, or %NULL
+ * if @xml could not be parsed as valid account data.
+ **/
+EAccount *
+e_account_new_from_xml (const char *xml)
+{
+ EAccount *account;
+
+ account = g_object_new (E_TYPE_ACCOUNT, NULL);
+ if (!e_account_set_from_xml (account, xml)) {
+ g_object_unref (account);
+ return NULL;
+ }
+
+ return account;
+}
+
+
+static gboolean
+xml_set_bool (xmlNodePtr node, const char *name, gboolean *val)
+{
+ gboolean bool;
+ char *buf;
+
+ if ((buf = xmlGetProp (node, name))) {
+ bool = (!strcmp (buf, "true") || !strcmp (buf, "yes"));
+ xmlFree (buf);
+
+ if (bool != *val) {
+ *val = bool;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+xml_set_int (xmlNodePtr node, const char *name, int *val)
+{
+ int number;
+ char *buf;
+
+ if ((buf = xmlGetProp (node, name))) {
+ number = strtol (buf, NULL, 10);
+ xmlFree (buf);
+
+ if (number != *val) {
+ *val = number;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+xml_set_prop (xmlNodePtr node, const char *name, char **val)
+{
+ char *buf, *new_val;
+
+ buf = xmlGetProp (node, name);
+ new_val = g_strdup (buf);
+ xmlFree (buf);
+
+ /* We can use strcmp here whether the value is UTF8 or
+ * not, since we only care if the bytes changed.
+ */
+ if (!*val || strcmp (*val, new_val)) {
+ g_free (*val);
+ *val = new_val;
+ return TRUE;
+ } else {
+ g_free (new_val);
+ return FALSE;
+ }
+}
+
+static gboolean
+xml_set_content (xmlNodePtr node, char **val)
+{
+ char *buf, *new_val;
+
+ buf = xmlNodeGetContent (node);
+ new_val = g_strdup (buf);
+ xmlFree (buf);
+
+ /* We can use strcmp here whether the value is UTF8 or
+ * not, since we only care if the bytes changed.
+ */
+ if (!*val || strcmp (*val, new_val)) {
+ g_free (*val);
+ *val = new_val;
+ return TRUE;
+ } else {
+ g_free (new_val);
+ return FALSE;
+ }
+}
+
+static gboolean
+xml_set_identity (xmlNodePtr node, EAccountIdentity *id)
+{
+ gboolean changed = FALSE;
+
+ for (node = node->children; node; node = node->next) {
+ if (!strcmp (node->name, "name"))
+ changed |= xml_set_content (node, &id->name);
+ else if (!strcmp (node->name, "addr-spec"))
+ changed |= xml_set_content (node, &id->address);
+ else if (!strcmp (node->name, "reply-to"))
+ changed |= xml_set_content (node, &id->reply_to);
+ else if (!strcmp (node->name, "organization"))
+ changed |= xml_set_content (node, &id->organization);
+ else if (!strcmp (node->name, "signature")) {
+ changed |= xml_set_bool (node, "auto", &id->auto_signature);
+ changed |= xml_set_int (node, "default", &id->def_signature);
+ }
+ }
+
+ return changed;
+}
+
+static gboolean
+xml_set_service (xmlNodePtr node, EAccountService *service)
+{
+ gboolean changed = FALSE;
+
+ changed |= xml_set_bool (node, "save-passwd", &service->save_passwd);
+ changed |= xml_set_bool (node, "keep-on-server", &service->keep_on_server);
+
+ changed |= xml_set_bool (node, "auto-check", &service->auto_check);
+ changed |= xml_set_int (node, "auto-check-timeout", &service->auto_check_time);
+ if (service->auto_check && service->auto_check_time <= 0) {
+ service->auto_check = FALSE;
+ service->auto_check_time = 0;
+ }
+
+ for (node = node->children; node; node = node->next) {
+ if (!strcmp (node->name, "url")) {
+ changed |= xml_set_content (node, &service->url);
+ break;
+ }
+ }
+
+ return changed;
+}
+
+/**
+ * e_account_set_from_xml:
+ * @account: an #EAccount
+ * @xml: an XML account description.
+ *
+ * Changes @account to match @xml.
+ *
+ * Return value: %TRUE if @account was changed, %FALSE if @account
+ * already matched @xml or @xml could not be parsed
+ **/
+gboolean
+e_account_set_from_xml (EAccount *account, const char *xml)
+{
+ xmlNodePtr node, cur;
+ xmlDocPtr doc;
+ gboolean changed = FALSE;
+
+ if (!(doc = xmlParseDoc ((char *)xml)))
+ return FALSE;
+
+ node = doc->children;
+ if (strcmp (node->name, "account") != 0) {
+ xmlFreeDoc (doc);
+ return FALSE;
+ }
+
+ if (!account->uid)
+ xml_set_prop (node, "uid", &account->uid);
+
+ changed |= xml_set_prop (node, "name", &account->name);
+ changed |= xml_set_bool (node, "enabled", &account->enabled);
+
+ for (node = node->children; node; node = node->next) {
+ if (!strcmp (node->name, "identity")) {
+ changed |= xml_set_identity (node, account->id);
+ } else if (!strcmp (node->name, "source")) {
+ changed |= xml_set_service (node, account->source);
+ } else if (!strcmp (node->name, "transport")) {
+ changed |= xml_set_service (node, account->transport);
+ } else if (!strcmp (node->name, "drafts-folder")) {
+ changed |= xml_set_content (node, &account->drafts_folder_uri);
+ } else if (!strcmp (node->name, "sent-folder")) {
+ changed |= xml_set_content (node, &account->sent_folder_uri);
+ } else if (!strcmp (node->name, "auto-cc")) {
+ changed |= xml_set_bool (node, "always", &account->always_cc);
+ changed |= xml_set_content (node, &account->cc_addrs);
+ } else if (!strcmp (node->name, "auto-bcc")) {
+ changed |= xml_set_bool (node, "always", &account->always_bcc);
+ changed |= xml_set_content (node, &account->bcc_addrs);
+ } else if (!strcmp (node->name, "pgp")) {
+ changed |= xml_set_bool (node, "encrypt-to-self", &account->pgp_encrypt_to_self);
+ changed |= xml_set_bool (node, "always-trust", &account->pgp_always_trust);
+ changed |= xml_set_bool (node, "always-sign", &account->pgp_always_sign);
+ changed |= xml_set_bool (node, "no-imip-sign", &account->pgp_no_imip_sign);
+
+ if (node->children) {
+ for (cur = node->children; cur; cur = cur->next) {
+ if (!strcmp (cur->name, "key-id")) {
+ changed |= xml_set_content (cur, &account->pgp_key);
+ break;
+ }
+ }
+ }
+ } else if (!strcmp (node->name, "smime")) {
+ changed |= xml_set_bool (node, "sign-default", &account->smime_sign_default);
+ changed |= xml_set_bool (node, "encrypt-to-self", &account->smime_encrypt_to_self);
+ changed |= xml_set_bool (node, "encrypt-default", &account->smime_encrypt_default);
+
+ if (node->children) {
+ for (cur = node->children; cur; cur = cur->next) {
+ if (!strcmp (cur->name, "sign-key-id")) {
+ changed |= xml_set_content (cur, &account->smime_sign_key);
+ } else if (!strcmp (cur->name, "encrypt-key-id")) {
+ changed |= xml_set_content (cur, &account->smime_encrypt_key);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ xmlFreeDoc (doc);
+
+ return changed;
+}
+
+
+/**
+ * e_account_import:
+ * @dest: destination account object
+ * @src: source account object
+ *
+ * Import the settings from @src to @dest.
+ **/
+void
+e_account_import (EAccount *dest, EAccount *src)
+{
+ g_free (dest->name);
+ dest->name = g_strdup (src->name);
+
+ dest->enabled = src->enabled;
+
+ g_free (dest->id->name);
+ dest->id->name = g_strdup (src->id->name);
+ g_free (dest->id->address);
+ dest->id->address = g_strdup (src->id->address);
+ g_free (dest->id->reply_to);
+ dest->id->reply_to = g_strdup (src->id->reply_to);
+ g_free (dest->id->organization);
+ dest->id->organization = g_strdup (src->id->organization);
+ dest->id->def_signature = src->id->def_signature;
+ dest->id->auto_signature = src->id->auto_signature;
+
+ g_free (dest->source->url);
+ dest->source->url = g_strdup (src->source->url);
+ dest->source->keep_on_server = src->source->keep_on_server;
+ dest->source->auto_check = src->source->auto_check;
+ dest->source->auto_check_time = src->source->auto_check_time;
+ dest->source->save_passwd = src->source->save_passwd;
+
+ g_free (dest->transport->url);
+ dest->transport->url = g_strdup (src->transport->url);
+ dest->transport->save_passwd = src->transport->save_passwd;
+
+ g_free (dest->drafts_folder_uri);
+ dest->drafts_folder_uri = g_strdup (src->drafts_folder_uri);
+
+ g_free (dest->sent_folder_uri);
+ dest->sent_folder_uri = g_strdup (src->sent_folder_uri);
+
+ dest->always_cc = src->always_cc;
+ g_free (dest->cc_addrs);
+ dest->cc_addrs = g_strdup (src->cc_addrs);
+
+ dest->always_bcc = src->always_bcc;
+ g_free (dest->bcc_addrs);
+ dest->bcc_addrs = g_strdup (src->bcc_addrs);
+
+ g_free (dest->pgp_key);
+ dest->pgp_key = g_strdup (src->pgp_key);
+ dest->pgp_encrypt_to_self = src->pgp_encrypt_to_self;
+ dest->pgp_always_sign = src->pgp_always_sign;
+ dest->pgp_no_imip_sign = src->pgp_no_imip_sign;
+ dest->pgp_always_trust = src->pgp_always_trust;
+
+ dest->smime_sign_default = src->smime_sign_default;
+ g_free (dest->smime_sign_key);
+ dest->smime_sign_key = g_strdup (src->smime_sign_key);
+
+ dest->smime_encrypt_default = src->smime_encrypt_default;
+ dest->smime_encrypt_to_self = src->smime_encrypt_to_self;
+ g_free (dest->smime_encrypt_key);
+ dest->smime_encrypt_key = g_strdup (src->smime_encrypt_key);
+}
+
+
+/**
+ * e_account_to_xml:
+ * @account: an #EAccount
+ *
+ * Return value: an XML representation of @account, which the caller
+ * must free.
+ **/
+char *
+e_account_to_xml (EAccount *account)
+{
+ xmlNodePtr root, node, id, src, xport;
+ char *tmp, buf[20];
+ xmlChar *xmlbuf;
+ xmlDocPtr doc;
+ int n;
+
+ doc = xmlNewDoc ("1.0");
+
+ root = xmlNewDocNode (doc, NULL, "account", NULL);
+ xmlDocSetRootElement (doc, root);
+
+ xmlSetProp (root, "name", account->name);
+ xmlSetProp (root, "uid", account->uid);
+ xmlSetProp (root, "enabled", account->enabled ? "true" : "false");
+
+ id = xmlNewChild (root, NULL, "identity", NULL);
+ if (account->id->name)
+ xmlNewTextChild (id, NULL, "name", account->id->name);
+ if (account->id->address)
+ xmlNewTextChild (id, NULL, "addr-spec", account->id->address);
+ if (account->id->reply_to)
+ xmlNewTextChild (id, NULL, "reply-to", account->id->reply_to);
+ if (account->id->organization)
+ xmlNewTextChild (id, NULL, "organization", account->id->organization);
+
+ node = xmlNewChild (id, NULL, "signature",NULL);
+ xmlSetProp (node, "auto", account->id->auto_signature ? "true" : "false");
+ sprintf (buf, "%d", account->id->def_signature);
+ xmlSetProp (node, "default", buf);
+
+ src = xmlNewChild (root, NULL, "source", NULL);
+ xmlSetProp (src, "save-passwd", account->source->save_passwd ? "true" : "false");
+ xmlSetProp (src, "keep-on-server", account->source->keep_on_server ? "true" : "false");
+ xmlSetProp (src, "auto-check", account->source->auto_check ? "true" : "false");
+ sprintf (buf, "%d", account->source->auto_check_time);
+ xmlSetProp (src, "auto-check-timeout", buf);
+ if (account->source->url)
+ xmlNewTextChild (src, NULL, "url", account->source->url);
+
+ xport = xmlNewChild (root, NULL, "transport", NULL);
+ xmlSetProp (xport, "save-passwd", account->transport->save_passwd ? "true" : "false");
+ if (account->transport->url)
+ xmlNewTextChild (xport, NULL, "url", account->transport->url);
+
+ xmlNewTextChild (root, NULL, "drafts-folder", account->drafts_folder_uri);
+ xmlNewTextChild (root, NULL, "sent-folder", account->sent_folder_uri);
+
+ node = xmlNewChild (root, NULL, "auto-cc", NULL);
+ xmlSetProp (node, "always", account->always_cc ? "true" : "false");
+ if (account->cc_addrs)
+ xmlNewTextChild (node, NULL, "recipients", account->cc_addrs);
+
+ node = xmlNewChild (root, NULL, "auto-bcc", NULL);
+ xmlSetProp (node, "always", account->always_bcc ? "true" : "false");
+ if (account->bcc_addrs)
+ xmlNewTextChild (node, NULL, "recipients", account->bcc_addrs);
+
+ node = xmlNewChild (root, NULL, "pgp", NULL);
+ xmlSetProp (node, "encrypt-to-self", account->pgp_encrypt_to_self ? "true" : "false");
+ xmlSetProp (node, "always-trust", account->pgp_always_trust ? "true" : "false");
+ xmlSetProp (node, "always-sign", account->pgp_always_sign ? "true" : "false");
+ xmlSetProp (node, "no-imip-sign", account->pgp_no_imip_sign ? "true" : "false");
+ if (account->pgp_key)
+ xmlNewTextChild (node, NULL, "key-id", account->pgp_key);
+
+ node = xmlNewChild (root, NULL, "smime", NULL);
+ xmlSetProp (node, "sign-default", account->smime_sign_default ? "true" : "false");
+ xmlSetProp (node, "encrypt-default", account->smime_encrypt_default ? "true" : "false");
+ xmlSetProp (node, "encrypt-to-self", account->smime_encrypt_to_self ? "true" : "false");
+ if (account->smime_sign_key)
+ xmlNewTextChild (node, NULL, "sign-key-id", account->smime_sign_key);
+ if (account->smime_encrypt_key)
+ xmlNewTextChild (node, NULL, "encrypt-key-id", account->smime_encrypt_key);
+
+ xmlDocDumpMemory (doc, &xmlbuf, &n);
+ xmlFreeDoc (doc);
+
+ /* remap to glib memory */
+ tmp = g_malloc (n + 1);
+ memcpy (tmp, xmlbuf, n);
+ tmp[n] = '\0';
+ xmlFree (xmlbuf);
+
+ return tmp;
+}
+
+
+/**
+ * e_account_uid_from_xml:
+ * @xml: an XML account description
+ *
+ * Return value: the permanent UID of the account described by @xml
+ * (or %NULL if @xml could not be parsed or did not contain a uid).
+ * The caller must free this string.
+ **/
+char *
+e_account_uid_from_xml (const char *xml)
+{
+ xmlNodePtr node;
+ xmlDocPtr doc;
+ char *uid = NULL;
+
+ if (!(doc = xmlParseDoc ((char *)xml)))
+ return NULL;
+
+ node = doc->children;
+ if (strcmp (node->name, "account") != 0) {
+ xmlFreeDoc (doc);
+ return NULL;
+ }
+
+ xml_set_prop (node, "uid", &uid);
+ xmlFreeDoc (doc);
+
+ return uid;
+}
diff --git a/libedataserver/e-account.h b/libedataserver/e-account.h
new file mode 100644
index 000000000..e50c77399
--- /dev/null
+++ b/libedataserver/e-account.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2003 Ximian, Inc.
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __E_ACCOUNT__
+#define __E_ACCOUNT__
+
+#include <glib-object.h>
+
+#define E_TYPE_ACCOUNT (e_account_get_type ())
+#define E_ACCOUNT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_ACCOUNT, EAccount))
+#define E_ACCOUNT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_ACCOUNT, EAccountClass))
+#define E_IS_ACCOUNT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_ACCOUNT))
+#define E_IS_ACCOUNT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), E_TYPE_ACCOUNT))
+
+typedef struct {
+ char *name;
+ char *address;
+ char *reply_to;
+ char *organization;
+
+ int def_signature;
+ gboolean auto_signature;
+} EAccountIdentity;
+
+typedef struct {
+ char *url;
+ gboolean keep_on_server;
+ gboolean auto_check;
+ int auto_check_time;
+ gboolean save_passwd;
+} EAccountService;
+
+
+typedef struct {
+ GObject parent_object;
+
+ char *name;
+ char *uid;
+
+ gboolean enabled;
+
+ EAccountIdentity *id;
+ EAccountService *source;
+ EAccountService *transport;
+
+ char *drafts_folder_uri, *sent_folder_uri;
+
+ gboolean always_cc;
+ char *cc_addrs;
+ gboolean always_bcc;
+ char *bcc_addrs;
+
+ char *pgp_key;
+ gboolean pgp_encrypt_to_self;
+ gboolean pgp_always_sign;
+ gboolean pgp_no_imip_sign;
+ gboolean pgp_always_trust;
+
+ char *smime_sign_key;
+ char *smime_encrypt_key;
+ gboolean smime_sign_default;
+ gboolean smime_encrypt_to_self;
+ gboolean smime_encrypt_default;
+} EAccount;
+
+typedef struct {
+ GObjectClass parent_class;
+
+} EAccountClass;
+
+
+GType e_account_get_type (void);
+
+EAccount *e_account_new (void);
+
+EAccount *e_account_new_from_xml (const char *xml);
+
+gboolean e_account_set_from_xml (EAccount *account,
+ const char *xml);
+
+void e_account_import (EAccount *dest,
+ EAccount *src);
+
+char *e_account_to_xml (EAccount *account);
+
+
+char *e_account_uid_from_xml (const char *xml);
+
+
+#endif /* __E_ACCOUNT__ */
diff --git a/libedataserver/e-component-listener.c b/libedataserver/e-component-listener.c
new file mode 100644
index 000000000..1e7027f33
--- /dev/null
+++ b/libedataserver/e-component-listener.c
@@ -0,0 +1,171 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Component listener.
+ *
+ * Author:
+ * Rodrigo Moya <rodrigo@ximian.com>
+ *
+ * Copyright 2002, Ximian, Inc.
+ */
+
+#include <bonobo/bonobo-exception.h>
+#include <bonobo/bonobo-object.h>
+#include "e-component-listener.h"
+#include <libgnome/gnome-i18n.h>
+
+#define PARENT_TYPE GTK_TYPE_OBJECT
+
+struct _EComponentListenerPrivate {
+ Bonobo_Unknown component;
+};
+
+static void e_component_listener_class_init (EComponentListenerClass *klass);
+static void e_component_listener_init (EComponentListener *cl, EComponentListenerClass *klass);
+static void e_component_listener_finalize (GObject *object);
+
+static GObjectClass *parent_class = NULL;
+static GList *watched_connections = NULL;
+
+enum {
+ COMPONENT_DIED,
+ LAST_SIGNAL
+};
+
+static guint comp_listener_signals[LAST_SIGNAL];
+
+static void
+connection_listen_cb (gpointer object, gpointer user_data)
+{
+ GList *l, *next = NULL;
+ EComponentListener *cl;
+
+ for (l = watched_connections; l != NULL; l = next) {
+ next = l->next;
+ cl = l->data;
+
+ switch (ORBit_small_get_connection_status (cl->priv->component)) {
+ case ORBIT_CONNECTION_DISCONNECTED :
+ watched_connections = g_list_delete_link (watched_connections, l);
+
+ g_object_ref (cl);
+ g_signal_emit (cl, comp_listener_signals[COMPONENT_DIED], 0);
+ cl->priv->component = CORBA_OBJECT_NIL;
+ g_object_unref (cl);
+ break;
+ default :
+ break;
+ }
+ }
+}
+
+static void
+e_component_listener_class_init (EComponentListenerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->finalize = e_component_listener_finalize;
+ klass->component_died = NULL;
+
+ comp_listener_signals[COMPONENT_DIED] =
+ g_signal_new ("component_died",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EComponentListenerClass, component_died),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+e_component_listener_init (EComponentListener *cl, EComponentListenerClass *klass)
+{
+ /* allocate internal structure */
+ cl->priv = g_new (EComponentListenerPrivate, 1);
+ cl->priv->component = CORBA_OBJECT_NIL;
+}
+
+static void
+e_component_listener_finalize (GObject *object)
+{
+ EComponentListener *cl = (EComponentListener *) object;
+
+ g_return_if_fail (E_IS_COMPONENT_LISTENER (cl));
+
+ watched_connections = g_list_remove (watched_connections, cl);
+
+ if (cl->priv->component != CORBA_OBJECT_NIL)
+ cl->priv->component = CORBA_OBJECT_NIL;
+
+ /* free memory */
+ g_free (cl->priv);
+ cl->priv = NULL;
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ (* G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+GType
+e_component_listener_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ static GTypeInfo info = {
+ sizeof (EComponentListenerClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) e_component_listener_class_init,
+ NULL, NULL,
+ sizeof (EComponentListener),
+ 0,
+ (GInstanceInitFunc) e_component_listener_init
+ };
+ type = g_type_register_static (G_TYPE_OBJECT, "EComponentListener", &info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * e_component_listener_new
+ * @comp: Component to listen for.
+ *
+ * Create a new #EComponentListener object, which allows to listen
+ * for a given component and get notified when that component dies.
+ *
+ * Returns: a component listener object.
+ */
+EComponentListener *
+e_component_listener_new (Bonobo_Unknown comp)
+{
+ EComponentListener *cl;
+
+ g_return_val_if_fail (comp != NULL, NULL);
+
+ cl = g_object_new (E_COMPONENT_LISTENER_TYPE, NULL);
+ cl->priv->component = comp;
+
+ /* watch the connection */
+ ORBit_small_listen_for_broken (comp, G_CALLBACK (connection_listen_cb), cl);
+ watched_connections = g_list_prepend (watched_connections, cl);
+
+ return cl;
+}
+
+Bonobo_Unknown
+e_component_listener_get_component (EComponentListener *cl)
+{
+ g_return_val_if_fail (E_IS_COMPONENT_LISTENER (cl), CORBA_OBJECT_NIL);
+ return cl->priv->component;
+}
+
+void
+e_component_listener_set_component (EComponentListener *cl, Bonobo_Unknown comp)
+{
+ g_return_if_fail (E_IS_COMPONENT_LISTENER (cl));
+
+ cl->priv->component = comp;
+ ORBit_small_listen_for_broken (comp, G_CALLBACK (connection_listen_cb), cl);
+}
diff --git a/libedataserver/e-component-listener.h b/libedataserver/e-component-listener.h
new file mode 100644
index 000000000..3f5694ecd
--- /dev/null
+++ b/libedataserver/e-component-listener.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Component listener
+ *
+ * Author:
+ * Rodrigo Moya <rodrigo@ximian.com>
+ *
+ * Copyright 2002, Ximian, Inc.
+ */
+
+#ifndef __E_COMPONENT_LISTENER_H__
+#define __E_COMPONENT_LISTENER_H__
+
+#include <glib-object.h>
+#include <bonobo/Bonobo.h>
+
+G_BEGIN_DECLS
+
+#define E_COMPONENT_LISTENER_TYPE (e_component_listener_get_type ())
+#define E_COMPONENT_LISTENER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), E_COMPONENT_LISTENER_TYPE, EComponentListener))
+#define E_COMPONENT_LISTENER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), E_COMPONENT_LISTENER_TYPE, EComponentListenerClass))
+#define E_IS_COMPONENT_LISTENER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_COMPONENT_LISTENER_TYPE))
+#define E_IS_COMPONENT_LISTENER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), E_COMPONENT_LISTENER_TYPE))
+
+typedef struct _EComponentListenerPrivate EComponentListenerPrivate;
+
+typedef struct {
+ GObject object;
+ EComponentListenerPrivate *priv;
+} EComponentListener;
+
+typedef struct {
+ GObjectClass parent_class;
+
+ void (* component_died) (EComponentListener *cl);
+} EComponentListenerClass;
+
+GType e_component_listener_get_type (void);
+EComponentListener *e_component_listener_new (Bonobo_Unknown comp);
+
+Bonobo_Unknown e_component_listener_get_component (EComponentListener *cl);
+void e_component_listener_set_component (EComponentListener *cl,
+ Bonobo_Unknown comp);
+
+G_END_DECLS
+
+#endif
diff --git a/libedataserver/e-data-server-marshal.c b/libedataserver/e-data-server-marshal.c
new file mode 100644
index 000000000..daf7e3ff1
--- /dev/null
+++ b/libedataserver/e-data-server-marshal.c
@@ -0,0 +1,54 @@
+#include "e-data-server-marshal.h"
+
+#include <glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v) g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v) g_value_get_int (v)
+#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
+#define g_marshal_value_peek_long(v) g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
+#define g_marshal_value_peek_float(v) g_value_get_float (v)
+#define g_marshal_value_peek_double(v) g_value_get_double (v)
+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v) g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v) g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ * Do not access GValues directly in your code. Instead, use the
+ * g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
+#define g_marshal_value_peek_char(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v) (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v) (v)->data[0].v_int
+#define g_marshal_value_peek_flags(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_float(v) (v)->data[0].v_float
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* NONE:NONE (e-data-server-marshal.list:1) */
+
+/* NONE:OBJECT (e-data-server-marshal.list:2) */
+
diff --git a/libedataserver/e-data-server-marshal.h b/libedataserver/e-data-server-marshal.h
new file mode 100644
index 000000000..cc82af0f7
--- /dev/null
+++ b/libedataserver/e-data-server-marshal.h
@@ -0,0 +1,20 @@
+
+#ifndef __e_data_server_marshal_MARSHAL_H__
+#define __e_data_server_marshal_MARSHAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* NONE:NONE (e-data-server-marshal.list:1) */
+#define e_data_server_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID
+#define e_data_server_marshal_NONE__NONE e_data_server_marshal_VOID__VOID
+
+/* NONE:OBJECT (e-data-server-marshal.list:2) */
+#define e_data_server_marshal_VOID__OBJECT g_cclosure_marshal_VOID__OBJECT
+#define e_data_server_marshal_NONE__OBJECT e_data_server_marshal_VOID__OBJECT
+
+G_END_DECLS
+
+#endif /* __e_data_server_marshal_MARSHAL_H__ */
+
diff --git a/libedataserver/e-db3-utils.c b/libedataserver/e-db3-utils.c
new file mode 100644
index 000000000..3326f32a6
--- /dev/null
+++ b/libedataserver/e-db3-utils.c
@@ -0,0 +1,184 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include "config.h"
+
+#include "e-db3-utils.h"
+
+#include <db.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libgnome/gnome-util.h>
+
+#if DB_VERSION_MAJOR != 3 || \
+ DB_VERSION_MINOR != 1 || \
+ DB_VERSION_PATCH != 17
+#error Including wrong DB3. Need libdb 3.1.17.
+#endif
+
+static char *
+get_check_filename (const char *filename)
+{
+ return g_strdup_printf ("%s-upgrading", filename);
+}
+
+static char *
+get_copy_filename (const char *filename)
+{
+ return g_strdup_printf ("%s-copy", filename);
+}
+
+static int
+cp_file (const char *src, const char *dest)
+{
+ int i;
+ int o;
+ char buffer[1024];
+ int length;
+ int place;
+
+ i = open (src, O_RDONLY);
+ if (i == -1)
+ return -1;
+ o = creat (dest, S_IREAD | S_IWRITE);
+ if (o == -1) {
+ close (i);
+ return -1;
+ }
+ while (1) {
+ length = read (i, &buffer, sizeof (buffer));
+
+ if (length == 0)
+ break;
+
+ if (length == -1) {
+ if (errno == EINTR)
+ continue;
+ else {
+ close (i);
+ close (o);
+ unlink (dest);
+ return -1;
+ }
+ }
+
+ place = 0;
+ while (length != 0) {
+ int count;
+ count = write (o, buffer + place, length);
+ if (count == -1) {
+ if (errno == EINTR)
+ continue;
+ else {
+ close (i);
+ close (o);
+ unlink (dest);
+ return -1;
+ }
+ }
+
+ length -= count;
+ place += count;
+ }
+ }
+ if (close (i))
+ return -1;
+ if (close (o))
+ return -1;
+ return 0;
+}
+
+static int
+touch_file (const char *file)
+{
+ int o;
+ o = creat (file, S_IREAD | S_IWRITE);
+ if (o == -1)
+ return -1;
+
+ if (close (o) == -1)
+ return -1;
+
+ return 0;
+}
+
+static int
+resume_upgrade (const char *filename, const char *copy_filename, const char *check_filename)
+{
+ DB *db;
+ int ret_val;
+
+ ret_val = db_create (&db, NULL, 0);
+
+ if (ret_val == 0)
+ ret_val = cp_file (copy_filename, filename);
+
+ if (ret_val == 0)
+ ret_val = db->upgrade (db, filename, 0);
+
+ if (ret_val == 0)
+ ret_val = unlink (check_filename);
+ if (ret_val == 0)
+ ret_val = unlink (copy_filename);
+
+ db->close (db, 0);
+
+ return ret_val;
+}
+
+int
+e_db3_utils_maybe_recover (const char *filename)
+{
+ int ret_val = 0;
+ char *copy_filename;
+ char *check_filename;
+
+ copy_filename = get_copy_filename (filename);
+ check_filename = get_check_filename (filename);
+
+ if (g_file_exists (check_filename)) {
+ ret_val = resume_upgrade(filename, copy_filename, check_filename);
+ } else if (g_file_exists (copy_filename)) {
+ unlink (copy_filename);
+ }
+
+ g_free (copy_filename);
+ g_free (check_filename);
+ return ret_val;
+}
+
+int
+e_db3_utils_upgrade_format (const char *filename)
+{
+ char *copy_filename;
+ char *check_filename;
+ DB *db;
+ int ret_val;
+
+ ret_val = db_create (&db, NULL, 0);
+ if (ret_val != 0)
+ return ret_val;
+
+ copy_filename = get_copy_filename (filename);
+ check_filename = get_check_filename (filename);
+
+ ret_val = cp_file (filename, copy_filename);
+
+ if (ret_val == 0)
+ ret_val = touch_file (check_filename);
+ if (ret_val == 0)
+ ret_val = db->upgrade (db, filename, 0);
+ if (ret_val == 0)
+ ret_val = unlink (check_filename);
+
+ if (ret_val == 0)
+ ret_val = unlink (copy_filename);
+
+ db->close (db, 0);
+
+ g_free (check_filename);
+ g_free (copy_filename);
+ return ret_val;
+}
diff --git a/libedataserver/e-db3-utils.h b/libedataserver/e-db3-utils.h
new file mode 100644
index 000000000..a574e5917
--- /dev/null
+++ b/libedataserver/e-db3-utils.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * db3 utils.
+ *
+ * Author:
+ * Chris Lahey <clahey@ximian.com>
+ *
+ * Copyright 2001, Ximian, Inc.
+ */
+
+#ifndef __E_DB3_UTILS_H__
+#define __E_DB3_UTILS_H__
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+int e_db3_utils_maybe_recover (const char *filename);
+int e_db3_utils_upgrade_format (const char *filename);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ! __E_DB3_UTILS_H__ */
+
diff --git a/libedataserver/e-dbhash.c b/libedataserver/e-dbhash.c
new file mode 100644
index 000000000..209798fbc
--- /dev/null
+++ b/libedataserver/e-dbhash.c
@@ -0,0 +1,227 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Author:
+ * JP Rosevear (jpr@ximian.com)
+ *
+ * Copyright 2000, Ximian, Inc.
+ */
+
+#include <config.h>
+
+#include "e-dbhash.h"
+
+#include <string.h>
+#include <fcntl.h>
+#include <db.h>
+#include "md5-utils.h"
+
+#if DB_VERSION_MAJOR != 3 || \
+ DB_VERSION_MINOR != 1 || \
+ DB_VERSION_PATCH != 17
+#error Including wrong DB3. Need libdb 3.1.17.
+#endif
+
+struct _EDbHashPrivate
+{
+ DB *db;
+};
+
+EDbHash *
+e_dbhash_new (const char *filename)
+{
+ EDbHash *edbh;
+ DB *db;
+ int rv;
+
+ int major, minor, patch;
+
+ db_version (&major, &minor, &patch);
+
+ if (major != 3 ||
+ minor != 1 ||
+ patch != 17) {
+ g_warning ("Wrong version of libdb.");
+ return NULL;
+ }
+
+ /* Attempt to open the database */
+ rv = db_create (&db, NULL, 0);
+ if (rv != 0) {
+ return NULL;
+ }
+
+ rv = db->open (db, filename, NULL, DB_HASH, 0, 0666);
+ if (rv != 0) {
+ rv = db->open (db, filename, NULL, DB_HASH, DB_CREATE, 0666);
+
+ if (rv != 0)
+ return NULL;
+ }
+
+ edbh = g_new (EDbHash, 1);
+ edbh->priv = g_new (EDbHashPrivate, 1);
+ edbh->priv->db = db;
+
+ return edbh;
+}
+
+static void
+string_to_dbt(const char *str, DBT *dbt)
+{
+ memset (dbt, 0, sizeof (DBT));
+ dbt->data = (void*)str;
+ dbt->size = strlen (str) + 1;
+}
+
+static void
+md5_to_dbt(const char str[16], DBT *dbt)
+{
+ memset (dbt, 0, sizeof (DBT));
+ dbt->data = (void*)str;
+ dbt->size = 16;
+}
+
+void
+e_dbhash_add (EDbHash *edbh, const gchar *key, const gchar *data)
+{
+ DB *db;
+ DBT dkey;
+ DBT ddata;
+ guchar local_hash[16];
+
+ g_return_if_fail (edbh != NULL);
+ g_return_if_fail (edbh->priv != NULL);
+ g_return_if_fail (edbh->priv->db != NULL);
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (data != NULL);
+
+ db = edbh->priv->db;
+
+ /* Key dbt */
+ string_to_dbt (key, &dkey);
+
+ /* Data dbt */
+ md5_get_digest (data, strlen (data), local_hash);
+ md5_to_dbt (local_hash, &ddata);
+
+ /* Add to database */
+ db->put (db, NULL, &dkey, &ddata, 0);
+}
+
+void
+e_dbhash_remove (EDbHash *edbh, const char *key)
+{
+ DB *db;
+ DBT dkey;
+
+ g_return_if_fail (edbh != NULL);
+ g_return_if_fail (edbh->priv != NULL);
+ g_return_if_fail (key != NULL);
+
+ db = edbh->priv->db;
+
+ /* Key dbt */
+ string_to_dbt (key, &dkey);
+
+ /* Remove from database */
+ db->del (db, NULL, &dkey, 0);
+}
+
+void
+e_dbhash_foreach_key (EDbHash *edbh, EDbHashFunc func, gpointer user_data)
+{
+ DB *db;
+ DBT dkey;
+ DBT ddata;
+ DBC *dbc;
+ int db_error = 0;
+
+ g_return_if_fail (edbh != NULL);
+ g_return_if_fail (edbh->priv != NULL);
+ g_return_if_fail (func != NULL);
+
+ db = edbh->priv->db;
+
+ db_error = db->cursor (db, NULL, &dbc, 0);
+
+ if (db_error != 0) {
+ return;
+ }
+
+ memset(&dkey, 0, sizeof(DBT));
+ memset(&ddata, 0, sizeof(DBT));
+ db_error = dbc->c_get(dbc, &dkey, &ddata, DB_FIRST);
+
+ while (db_error == 0) {
+ (*func) ((const char *)dkey.data, user_data);
+
+ db_error = dbc->c_get(dbc, &dkey, &ddata, DB_NEXT);
+ }
+ dbc->c_close (dbc);
+}
+
+EDbHashStatus
+e_dbhash_compare (EDbHash *edbh, const char *key, const char *compare_data)
+{
+ DB *db;
+ DBT dkey;
+ DBT ddata;
+ guchar compare_hash[16];
+
+ g_return_val_if_fail (edbh != NULL, FALSE);
+ g_return_val_if_fail (edbh->priv != NULL, FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+ g_return_val_if_fail (compare_hash != NULL, FALSE);
+
+ db = edbh->priv->db;
+
+ /* Key dbt */
+ string_to_dbt (key, &dkey);
+
+ /* Lookup in database */
+ memset (&ddata, 0, sizeof (DBT));
+ db->get (db, NULL, &dkey, &ddata, 0);
+
+ /* Compare */
+ if (ddata.data) {
+ md5_get_digest (compare_data, strlen (compare_data), compare_hash);
+
+ if (memcmp (ddata.data, compare_hash, sizeof (guchar) * 16))
+ return E_DBHASH_STATUS_DIFFERENT;
+ } else {
+ return E_DBHASH_STATUS_NOT_FOUND;
+ }
+
+ return E_DBHASH_STATUS_SAME;
+}
+
+void
+e_dbhash_write (EDbHash *edbh)
+{
+ DB *db;
+
+ g_return_if_fail (edbh != NULL);
+ g_return_if_fail (edbh->priv != NULL);
+
+ db = edbh->priv->db;
+
+ /* Flush database to disk */
+ db->sync (db, 0);
+}
+
+void
+e_dbhash_destroy (EDbHash *edbh)
+{
+ DB *db;
+
+ g_return_if_fail (edbh != NULL);
+ g_return_if_fail (edbh->priv != NULL);
+
+ db = edbh->priv->db;
+
+ /* Close datbase */
+ db->close (db, 0);
+
+ g_free (edbh->priv);
+ g_free (edbh);
+}
diff --git a/libedataserver/e-dbhash.h b/libedataserver/e-dbhash.h
new file mode 100644
index 000000000..9772a60c4
--- /dev/null
+++ b/libedataserver/e-dbhash.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Writes hashes that go to/from disk in db form. Hash keys are strings
+ *
+ * Author:
+ * JP Rosevear (jpr@ximian.com)
+ *
+ * Copyright 2000, Ximian, Inc.
+ */
+
+#ifndef __E_DBHASH_H__
+#define __E_DBHASH_H__
+
+#include <glib.h>
+
+typedef enum {
+ E_DBHASH_STATUS_SAME,
+ E_DBHASH_STATUS_DIFFERENT,
+ E_DBHASH_STATUS_NOT_FOUND,
+} EDbHashStatus;
+
+typedef struct _EDbHash EDbHash;
+typedef struct _EDbHashPrivate EDbHashPrivate;
+
+struct _EDbHash
+{
+ EDbHashPrivate *priv;
+};
+
+typedef void (*EDbHashFunc) (const char *key, gpointer user_data);
+
+EDbHash *e_dbhash_new (const char *filename);
+
+void e_dbhash_add (EDbHash *edbh, const char *key, const char *data);
+void e_dbhash_remove (EDbHash *edbh, const char *key);
+
+EDbHashStatus e_dbhash_compare (EDbHash *edbh, const char *key, const char *compare_data);
+void e_dbhash_foreach_key (EDbHash *edbh, EDbHashFunc func, gpointer user_data);
+
+void e_dbhash_write (EDbHash *edbh);
+
+void e_dbhash_destroy (EDbHash *edbh);
+
+#endif /* ! __E_DBHASH_H__ */
+
diff --git a/libedataserver/e-iterator.c b/libedataserver/e-iterator.c
new file mode 100644
index 000000000..7f736719b
--- /dev/null
+++ b/libedataserver/e-iterator.c
@@ -0,0 +1,183 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors:
+ * Christopher James Lahey <clahey@umich.edu>
+ *
+ * Copyright (C) 2000 Ximian, Inc.
+ * Copyright (C) 1999 The Free Software Foundation
+ */
+
+#include <config.h>
+
+#include "e-iterator.h"
+#include "e-data-server-marshal.h"
+
+static void e_iterator_init (EIterator *card);
+static void e_iterator_class_init (EIteratorClass *klass);
+
+#define PARENT_TYPE G_TYPE_OBJECT
+
+static GObjectClass *parent_class;
+
+enum {
+ INVALIDATE,
+ LAST_SIGNAL
+};
+
+static guint e_iterator_signals [LAST_SIGNAL] = { 0, };
+
+/**
+ * e_iterator_get_type:
+ * @void:
+ *
+ * Registers the &EIterator class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &EIterator class.
+ **/
+GType
+e_iterator_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo info = {
+ sizeof (EIteratorClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) e_iterator_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (EIterator),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) e_iterator_init
+ };
+
+ type = g_type_register_static (PARENT_TYPE, "EIterator", &info, 0);
+ }
+
+ return type;
+}
+
+static void
+e_iterator_class_init (EIteratorClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_ref (PARENT_TYPE);
+
+ e_iterator_signals [INVALIDATE] =
+ g_signal_new ("invalidate",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EIteratorClass, invalidate),
+ NULL, NULL,
+ e_data_server_marshal_NONE__NONE, /* XXX need a new marshaller here */
+ G_TYPE_NONE, 0);
+
+ klass->invalidate = NULL;
+ klass->get = NULL;
+ klass->reset = NULL;
+ klass->last = NULL;
+ klass->next = NULL;
+ klass->prev = NULL;
+ klass->delete = NULL;
+ klass->insert = NULL;
+ klass->set = NULL;
+ klass->is_valid = NULL;
+}
+
+/**
+ * e_iterator_init:
+ */
+static void
+e_iterator_init (EIterator *card)
+{
+}
+
+/*
+ * Virtual functions:
+ */
+const void *
+e_iterator_get (EIterator *iterator)
+{
+ if (E_ITERATOR_GET_CLASS(iterator)->get)
+ return E_ITERATOR_GET_CLASS(iterator)->get(iterator);
+ else
+ return NULL;
+}
+
+void
+e_iterator_reset (EIterator *iterator)
+{
+ if (E_ITERATOR_GET_CLASS(iterator)->reset)
+ E_ITERATOR_GET_CLASS(iterator)->reset(iterator);
+}
+
+void
+e_iterator_last (EIterator *iterator)
+{
+ if (E_ITERATOR_GET_CLASS(iterator)->last)
+ E_ITERATOR_GET_CLASS(iterator)->last(iterator);
+}
+
+gboolean
+e_iterator_next (EIterator *iterator)
+{
+ if (E_ITERATOR_GET_CLASS(iterator)->next)
+ return E_ITERATOR_GET_CLASS(iterator)->next(iterator);
+ else
+ return FALSE;
+}
+
+gboolean
+e_iterator_prev (EIterator *iterator)
+{
+ if (E_ITERATOR_GET_CLASS(iterator)->prev)
+ return E_ITERATOR_GET_CLASS(iterator)->prev(iterator);
+ else
+ return FALSE;
+}
+
+void
+e_iterator_delete (EIterator *iterator)
+{
+ if (E_ITERATOR_GET_CLASS(iterator)->delete)
+ E_ITERATOR_GET_CLASS(iterator)->delete(iterator);
+}
+
+void e_iterator_insert (EIterator *iterator,
+ const void *object,
+ gboolean before)
+{
+ if (E_ITERATOR_GET_CLASS(iterator)->insert)
+ E_ITERATOR_GET_CLASS(iterator)->insert(iterator, object, before);
+}
+
+void
+e_iterator_set (EIterator *iterator,
+ const void *object)
+{
+ if (E_ITERATOR_GET_CLASS(iterator)->set)
+ E_ITERATOR_GET_CLASS(iterator)->set(iterator, object);
+}
+
+gboolean
+e_iterator_is_valid (EIterator *iterator)
+{
+ if (E_ITERATOR_GET_CLASS(iterator)->is_valid)
+ return E_ITERATOR_GET_CLASS(iterator)->is_valid(iterator);
+ else
+ return FALSE;
+}
+
+void
+e_iterator_invalidate (EIterator *iterator)
+{
+ g_return_if_fail (iterator != NULL);
+ g_return_if_fail (E_IS_ITERATOR (iterator));
+
+ g_signal_emit (iterator, e_iterator_signals [INVALIDATE], 0);
+}
diff --git a/libedataserver/e-iterator.h b/libedataserver/e-iterator.h
new file mode 100644
index 000000000..9dae97cc3
--- /dev/null
+++ b/libedataserver/e-iterator.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors:
+ * Chris Lahey <clahey@ximian.com>
+ *
+ * Copyright (C) 2000 Ximian, Inc.
+ * Copyright (C) 1999 The Free Software Foundation
+ */
+
+#ifndef __E_ITERATOR_H__
+#define __E_ITERATOR_H__
+
+#include <stdio.h>
+#include <time.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#define E_TYPE_ITERATOR (e_iterator_get_type ())
+#define E_ITERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_ITERATOR, EIterator))
+#define E_ITERATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_ITERATOR, EIteratorClass))
+#define E_IS_ITERATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_ITERATOR))
+#define E_IS_ITERATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_ITERATOR))
+#define E_ITERATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_ITERATOR, EIteratorClass))
+
+typedef struct _EIterator EIterator;
+typedef struct _EIteratorClass EIteratorClass;
+
+struct _EIterator {
+ GObject object;
+};
+
+struct _EIteratorClass {
+ GObjectClass parent_class;
+
+ /* Signals */
+ void (*invalidate) (EIterator *iterator);
+
+ /* Virtual functions */
+ const void * (*get) (EIterator *iterator);
+ void (*reset) (EIterator *iterator);
+ void (*last) (EIterator *iterator);
+ gboolean (*next) (EIterator *iterator);
+ gboolean (*prev) (EIterator *iterator);
+ void (*delete) (EIterator *iterator);
+ void (*insert) (EIterator *iterator,
+ const void *object,
+ gboolean before);
+ void (*set) (EIterator *iterator,
+ const void *object);
+ gboolean (*is_valid) (EIterator *iterator);
+};
+
+const void *e_iterator_get (EIterator *iterator);
+void e_iterator_reset (EIterator *iterator);
+void e_iterator_last (EIterator *iterator);
+gboolean e_iterator_next (EIterator *iterator);
+gboolean e_iterator_prev (EIterator *iterator);
+void e_iterator_delete (EIterator *iterator);
+void e_iterator_insert (EIterator *iterator,
+ const void *object,
+ gboolean before);
+void e_iterator_set (EIterator *iterator,
+ const void *object);
+gboolean e_iterator_is_valid (EIterator *iterator);
+
+void e_iterator_invalidate (EIterator *iterator);
+
+/* Standard Glib function */
+GType e_iterator_get_type (void);
+
+#endif /* ! __E_ITERATOR_H__ */
diff --git a/libedataserver/e-list-iterator.c b/libedataserver/e-list-iterator.c
new file mode 100644
index 000000000..e801c22ef
--- /dev/null
+++ b/libedataserver/e-list-iterator.c
@@ -0,0 +1,249 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors:
+ * Christopher James Lahey <clahey@umich.edu>
+ *
+ * Copyright (C) 2000 Ximian, Inc.
+ * Copyright (C) 1999 The Free Software Foundation
+ */
+
+#include <config.h>
+
+#include "e-list-iterator.h"
+#include "e-list.h"
+
+
+static void e_list_iterator_init (EListIterator *list);
+static void e_list_iterator_class_init (EListIteratorClass *klass);
+
+static void e_list_iterator_invalidate (EIterator *iterator);
+static gboolean e_list_iterator_is_valid (EIterator *iterator);
+static void e_list_iterator_set (EIterator *iterator,
+ const void *object);
+static void e_list_iterator_delete (EIterator *iterator);
+static void e_list_iterator_insert (EIterator *iterator,
+ const void *object,
+ gboolean before);
+static gboolean e_list_iterator_prev (EIterator *iterator);
+static gboolean e_list_iterator_next (EIterator *iterator);
+static void e_list_iterator_reset (EIterator *iterator);
+static void e_list_iterator_last (EIterator *iterator);
+static const void *e_list_iterator_get (EIterator *iterator);
+static void e_list_iterator_dispose (GObject *object);
+
+#define PARENT_TYPE E_TYPE_ITERATOR
+
+static EIteratorClass *parent_class;
+
+/**
+ * e_list_iterator_get_type:
+ * @void:
+ *
+ * Registers the &EListIterator class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &EListIterator class.
+ **/
+GType
+e_list_iterator_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo info = {
+ sizeof (EListIteratorClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) e_list_iterator_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (EListIterator),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) e_list_iterator_init
+ };
+
+ type = g_type_register_static (PARENT_TYPE, "EListIterator", &info, 0);
+ }
+
+ return type;
+}
+
+static void
+e_list_iterator_class_init (EListIteratorClass *klass)
+{
+ GObjectClass *object_class;
+ EIteratorClass *iterator_class;
+
+ object_class = G_OBJECT_CLASS(klass);
+ iterator_class = E_ITERATOR_CLASS(klass);
+
+ parent_class = g_type_class_ref (PARENT_TYPE);
+
+ object_class->dispose = e_list_iterator_dispose;
+
+ iterator_class->invalidate = e_list_iterator_invalidate;
+ iterator_class->get = e_list_iterator_get;
+ iterator_class->reset = e_list_iterator_reset;
+ iterator_class->last = e_list_iterator_last;
+ iterator_class->next = e_list_iterator_next;
+ iterator_class->prev = e_list_iterator_prev;
+ iterator_class->delete = e_list_iterator_delete;
+ iterator_class->insert = e_list_iterator_insert;
+ iterator_class->set = e_list_iterator_set;
+ iterator_class->is_valid = e_list_iterator_is_valid;
+}
+
+
+
+/**
+ * e_list_iterator_init:
+ */
+static void
+e_list_iterator_init (EListIterator *list)
+{
+}
+
+EIterator *
+e_list_iterator_new (EList *list)
+{
+ EListIterator *iterator = g_object_new (E_TYPE_LIST_ITERATOR, NULL);
+
+ iterator->list = list;
+ g_object_ref(list);
+ iterator->iterator = list->list;
+
+ return E_ITERATOR(iterator);
+}
+
+/*
+ * Virtual functions:
+ */
+static void
+e_list_iterator_dispose (GObject *object)
+{
+ EListIterator *iterator = E_LIST_ITERATOR(object);
+ e_list_remove_iterator(iterator->list, E_ITERATOR(iterator));
+ g_object_unref(iterator->list);
+
+ if (G_OBJECT_CLASS (parent_class)->dispose)
+ (* G_OBJECT_CLASS (parent_class)->dispose) (object);
+}
+
+static const void *
+e_list_iterator_get (EIterator *_iterator)
+{
+ EListIterator *iterator = E_LIST_ITERATOR(_iterator);
+ if (iterator->iterator)
+ return iterator->iterator->data;
+ else
+ return NULL;
+}
+
+static void
+e_list_iterator_reset (EIterator *_iterator)
+{
+ EListIterator *iterator = E_LIST_ITERATOR(_iterator);
+ iterator->iterator = iterator->list->list;
+}
+
+static void
+e_list_iterator_last (EIterator *_iterator)
+{
+ EListIterator *iterator = E_LIST_ITERATOR(_iterator);
+ iterator->iterator = g_list_last(iterator->list->list);
+}
+
+static gboolean
+e_list_iterator_next (EIterator *_iterator)
+{
+ EListIterator *iterator = E_LIST_ITERATOR(_iterator);
+ if (iterator->iterator)
+ iterator->iterator = g_list_next(iterator->iterator);
+ else
+ iterator->iterator = iterator->list->list;
+ return (iterator->iterator != NULL);
+}
+
+static gboolean
+e_list_iterator_prev (EIterator *_iterator)
+{
+ EListIterator *iterator = E_LIST_ITERATOR(_iterator);
+ if (iterator->iterator)
+ iterator->iterator = g_list_previous(iterator->iterator);
+ else
+ iterator->iterator = g_list_last(iterator->list->list);
+ return (iterator->iterator != NULL);
+}
+
+static void
+e_list_iterator_insert (EIterator *_iterator,
+ const void *object,
+ gboolean before)
+{
+ EListIterator *iterator = E_LIST_ITERATOR(_iterator);
+ void *data;
+ if (iterator->list->copy)
+ data = iterator->list->copy(object, iterator->list->closure);
+ else
+ data = (void *) object;
+ if (iterator->iterator) {
+ if (before) {
+ iterator->list->list = g_list_first(g_list_prepend(iterator->iterator, data));
+ iterator->iterator = iterator->iterator->prev;
+ } else {
+ if (iterator->iterator->next)
+ g_list_prepend(iterator->iterator->next, data);
+ else
+ g_list_append(iterator->iterator, data);
+ iterator->iterator = iterator->iterator->next;
+ }
+ e_list_invalidate_iterators(iterator->list, E_ITERATOR(iterator));
+ } else {
+ if (before) {
+ iterator->list->list = g_list_append(iterator->list->list, data);
+ iterator->iterator = g_list_last(iterator->list->list);
+ } else {
+ iterator->list->list = g_list_prepend(iterator->list->list, data);
+ iterator->iterator = iterator->list->list;
+ }
+ e_list_invalidate_iterators(iterator->list, E_ITERATOR(iterator));
+ }
+}
+
+static void
+e_list_iterator_delete (EIterator *_iterator)
+{
+ EListIterator *iterator = E_LIST_ITERATOR(_iterator);
+ if (iterator->iterator) {
+ e_list_remove_link (iterator->list, iterator->iterator);
+ }
+}
+
+static void
+e_list_iterator_set (EIterator *_iterator,
+ const void *object)
+{
+ EListIterator *iterator = E_LIST_ITERATOR(_iterator);
+ if (iterator->iterator) {
+ if (iterator->list->free)
+ iterator->list->free(iterator->iterator->data, iterator->list->closure);
+ if (iterator->list->copy)
+ iterator->iterator->data = iterator->list->copy(object, iterator->list->closure);
+ else
+ iterator->iterator->data = (void *) object;
+ }
+}
+
+static gboolean
+e_list_iterator_is_valid (EIterator *_iterator)
+{
+ EListIterator *iterator = E_LIST_ITERATOR(_iterator);
+ return iterator->iterator != NULL;
+}
+
+static void
+e_list_iterator_invalidate (EIterator *_iterator)
+{
+ EListIterator *iterator = E_LIST_ITERATOR(_iterator);
+ iterator->iterator = NULL;
+}
diff --git a/libedataserver/e-list-iterator.h b/libedataserver/e-list-iterator.h
new file mode 100644
index 000000000..e2b2e563b
--- /dev/null
+++ b/libedataserver/e-list-iterator.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors:
+ * Chris Lahey <clahey@ximian.com>
+ *
+ * Copyright (C) 2000 Ximian, Inc.
+ * Copyright (C) 1999 The Free Software Foundation
+ */
+
+#ifndef __E_LIST_ITERATOR_H__
+#define __E_LIST_ITERATOR_H__
+
+typedef struct _EListIterator EListIterator;
+typedef struct _EListIteratorClass EListIteratorClass;
+
+#include <stdio.h>
+#include <time.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libedataserver/e-iterator.h>
+#include <libedataserver/e-list.h>
+
+#define E_TYPE_LIST_ITERATOR (e_list_iterator_get_type ())
+#define E_LIST_ITERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_LIST_ITERATOR, EListIterator))
+#define E_LIST_ITERATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_LIST_ITERATOR, EListIteratorClass))
+#define E_IS_LIST_ITERATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_LIST_ITERATOR))
+#define E_IS_LIST_ITERATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_LIST_ITERATOR))
+
+struct _EListIterator {
+ EIterator parent;
+
+ EList *list;
+ GList *iterator;
+};
+
+struct _EListIteratorClass {
+ EIteratorClass parent_class;
+};
+
+EIterator *e_list_iterator_new (EList *list);
+
+/* Standard Glib function */
+GType e_list_iterator_get_type (void);
+
+#endif /* ! __E_LIST_ITERATOR_H__ */
diff --git a/libedataserver/e-list.c b/libedataserver/e-list.c
new file mode 100644
index 000000000..f1b190b16
--- /dev/null
+++ b/libedataserver/e-list.c
@@ -0,0 +1,191 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors:
+ * Christopher James Lahey <clahey@umich.edu>
+ *
+ * Copyright (C) 2000 Ximian, Inc.
+ * Copyright (C) 1999 The Free Software Foundation
+ */
+
+#include <config.h>
+
+#include "e-list.h"
+#include "e-list-iterator.h"
+
+static void e_list_init (EList *list);
+static void e_list_class_init (EListClass *klass);
+static void e_list_dispose (GObject *object);
+
+static GObjectClass *parent_class;
+
+/**
+ * e_list_get_type:
+ * @void:
+ *
+ * Registers the &EList class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &EList class.
+ **/
+GType
+e_list_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo info = {
+ sizeof (EListClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) e_list_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (EList),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) e_list_init
+ };
+
+ type = g_type_register_static (G_TYPE_OBJECT, "EList", &info, 0);
+ }
+
+ return type;
+}
+
+static void
+e_list_class_init (EListClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_ref (G_TYPE_OBJECT);
+
+ object_class->dispose = e_list_dispose;
+}
+
+/**
+ * e_list_init:
+ */
+static void
+e_list_init (EList *list)
+{
+ list->list = NULL;
+ list->iterators = NULL;
+}
+
+EList *
+e_list_new (EListCopyFunc copy, EListFreeFunc free, void *closure)
+{
+ EList *list = g_object_new (E_TYPE_LIST, NULL);
+ e_list_construct (list, copy, free, closure);
+ return list;
+}
+
+void
+e_list_construct (EList *list, EListCopyFunc copy, EListFreeFunc free, void *closure)
+{
+ list->copy = copy;
+ list->free = free;
+ list->closure = closure;
+}
+
+EList *
+e_list_duplicate (EList *old)
+{
+ EList *list = g_object_new (E_TYPE_LIST, NULL);
+
+ list->copy = old->copy;
+ list->free = old->free;
+ list->closure = old->closure;
+ list->list = g_list_copy(old->list);
+ if (list->copy) {
+ GList *listlist;
+ for (listlist = list->list; listlist; listlist = listlist->next) {
+ listlist->data = list->copy (listlist->data, list->closure);
+ }
+ }
+ return list;
+}
+
+EIterator *
+e_list_get_iterator (EList *list)
+{
+ EIterator *iterator = e_list_iterator_new(list);
+ list->iterators = g_list_append(list->iterators, iterator);
+ return iterator;
+}
+
+int
+e_list_length (EList *list)
+{
+ return g_list_length(list->list);
+}
+
+void
+e_list_append (EList *list, const void *data)
+{
+ e_list_invalidate_iterators(list, NULL);
+ if (list->copy)
+ list->list = g_list_append(list->list, list->copy(data, list->closure));
+ else
+ list->list = g_list_append(list->list, (void *) data);
+}
+
+void
+e_list_remove (EList *list, const void *data)
+{
+ GList *link;
+ link = g_list_find (list->list, data);
+ if (link)
+ e_list_remove_link(list, link);
+}
+
+void
+e_list_invalidate_iterators (EList *list, EIterator *skip)
+{
+ GList *iterators = list->iterators;
+ for (; iterators; iterators = iterators->next) {
+ if (iterators->data != skip) {
+ e_iterator_invalidate(E_ITERATOR(iterators->data));
+ }
+ }
+}
+
+/* FIXME: This doesn't work properly if the iterator is the first
+ iterator in the list. Well, the iterator doesn't continue on after
+ the next time next is called, at least. */
+void
+e_list_remove_link (EList *list, GList *link)
+{
+ GList *iterators = list->iterators;
+ for (; iterators; iterators = iterators->next) {
+ if (((EListIterator *)iterators->data)->iterator == link) {
+ e_iterator_prev(iterators->data);
+ }
+ }
+ if (list->free)
+ list->free(link->data, list->closure);
+ list->list = g_list_remove_link(list->list, link);
+ g_list_free_1(link);
+}
+
+void
+e_list_remove_iterator (EList *list, EIterator *iterator)
+{
+ list->iterators = g_list_remove(list->iterators, iterator);
+}
+
+/*
+ * Virtual functions
+ */
+static void
+e_list_dispose (GObject *object)
+{
+ EList *list = E_LIST(object);
+ if (list->free)
+ g_list_foreach(list->list, (GFunc) list->free, list->closure);
+ g_list_free(list->list);
+
+ (* G_OBJECT_CLASS (parent_class)->dispose) (object);
+}
+
diff --git a/libedataserver/e-list.h b/libedataserver/e-list.h
new file mode 100644
index 000000000..733bf49e9
--- /dev/null
+++ b/libedataserver/e-list.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors:
+ * Chris Lahey <clahey@ximian.com>
+ *
+ * Copyright (C) 2000 Ximian, Inc.
+ * Copyright (C) 1999 The Free Software Foundation
+ */
+
+#ifndef __E_LIST_H__
+#define __E_LIST_H__
+
+typedef struct _EList EList;
+typedef struct _EListClass EListClass;
+
+#include <stdio.h>
+#include <time.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <libedataserver/e-list-iterator.h>
+
+#define E_TYPE_LIST (e_list_get_type ())
+#define E_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_LIST, EList))
+#define E_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_LIST, EListClass))
+#define E_IS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_LIST))
+#define E_IS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_LIST))
+#define E_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_LIST, EListClass))
+
+typedef void *(*EListCopyFunc) (const void *data, void *closure);
+typedef void (*EListFreeFunc) (void *data, void *closure);
+
+struct _EList {
+ GObject object;
+ GList *list;
+ GList *iterators;
+ EListCopyFunc copy;
+ EListFreeFunc free;
+ void *closure;
+};
+
+struct _EListClass {
+ GObjectClass parent_class;
+};
+
+EList *e_list_new (EListCopyFunc copy,
+ EListFreeFunc free,
+ void *closure);
+void e_list_construct (EList *list,
+ EListCopyFunc copy,
+ EListFreeFunc free,
+ void *closure);
+EList *e_list_duplicate (EList *list);
+EIterator *e_list_get_iterator (EList *list);
+void e_list_append (EList *list,
+ const void *data);
+void e_list_remove (EList *list,
+ const void *data);
+int e_list_length (EList *list);
+
+/* For iterators to call. */
+void e_list_remove_link (EList *list,
+ GList *link);
+void e_list_remove_iterator (EList *list,
+ EIterator *iterator);
+void e_list_invalidate_iterators (EList *list,
+ EIterator *skip);
+
+/* Standard Glib function */
+GType e_list_get_type (void);
+
+#endif /* ! __E_LIST_H__ */
diff --git a/libedataserver/e-memory.c b/libedataserver/e-memory.c
new file mode 100644
index 000000000..cf32147d7
--- /dev/null
+++ b/libedataserver/e-memory.c
@@ -0,0 +1,1306 @@
+/*
+ * Copyright (c) 2000, 2001 Ximian Inc.
+ *
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ * Jacob Berkman <jacob@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
+
+*/
+
+#include "e-memory.h"
+
+#include <string.h> /* memset() */
+#include <stdlib.h> /* alloca() */
+#include <glib.h>
+
+#define s(x) /* strv debug */
+#define p(x) /* poolv debug */
+#define p2(x) /* poolv assertion checking */
+
+/*#define MALLOC_CHECK*/
+
+/*#define PROFILE_POOLV*/
+
+#ifdef PROFILE_POOLV
+#include <time.h>
+#define pp(x) x
+#else
+#define pp(x)
+#endif
+
+/*#define TIMEIT*/
+
+#ifdef TIMEIT
+#include <sys/time.h>
+#include <unistd.h>
+
+struct timeval timeit_start;
+
+static time_start(const char *desc)
+{
+ gettimeofday(&timeit_start, NULL);
+ printf("starting: %s\n", desc);
+}
+
+static time_end(const char *desc)
+{
+ unsigned long diff;
+ struct timeval end;
+
+ gettimeofday(&end, NULL);
+ diff = end.tv_sec * 1000 + end.tv_usec/1000;
+ diff -= timeit_start.tv_sec * 1000 + timeit_start.tv_usec/1000;
+ printf("%s took %ld.%03ld seconds\n",
+ desc, diff / 1000, diff % 1000);
+}
+#else
+#define time_start(x)
+#define time_end(x)
+#endif
+
+#ifdef MALLOC_CHECK
+#include <mcheck.h>
+#include <stdio.h>
+static void
+checkmem(void *p)
+{
+ if (p) {
+ int status = mprobe(p);
+
+ switch (status) {
+ case MCHECK_HEAD:
+ printf("Memory underrun at %p\n", p);
+ abort();
+ case MCHECK_TAIL:
+ printf("Memory overrun at %p\n", p);
+ abort();
+ case MCHECK_FREE:
+ printf("Double free %p\n", p);
+ abort();
+ }
+ }
+}
+#define MPROBE(x) checkmem((void *)(x))
+#else
+#define MPROBE(x)
+#endif
+
+/* mempool class */
+
+#define STRUCT_ALIGN (4)
+
+typedef struct _MemChunkFreeNode {
+ struct _MemChunkFreeNode *next;
+ unsigned int atoms;
+} MemChunkFreeNode;
+
+typedef struct _EMemChunk {
+ unsigned int blocksize; /* number of atoms in a block */
+ unsigned int atomsize; /* size of each atom */
+ GPtrArray *blocks; /* blocks of raw memory */
+ struct _MemChunkFreeNode *free;
+} MemChunk;
+
+/**
+ * e_memchunk_new:
+ * @atomcount: The number of atoms stored in a single malloc'd block of memory.
+ * @atomsize: The size of each allocation.
+ *
+ * Create a new memchunk header. Memchunks are an efficient way to allocate
+ * and deallocate identical sized blocks of memory quickly, and space efficiently.
+ *
+ * e_memchunks are effectively the same as gmemchunks, only faster (much), and
+ * they use less memory overhead for housekeeping.
+ *
+ * Return value: The new header.
+ **/
+MemChunk *e_memchunk_new(int atomcount, int atomsize)
+{
+ MemChunk *m = g_malloc(sizeof(*m));
+
+ m->blocksize = atomcount;
+ m->atomsize = MAX(atomsize, sizeof(MemChunkFreeNode));
+ m->blocks = g_ptr_array_new();
+ m->free = NULL;
+
+ return m;
+}
+
+/**
+ * memchunk_alloc:
+ * @m:
+ *
+ * Allocate a new atom size block of memory from a memchunk.
+ **/
+void *e_memchunk_alloc(MemChunk *m)
+{
+ char *b;
+ MemChunkFreeNode *f;
+ void *mem;
+
+ f = m->free;
+ if (f) {
+ f->atoms--;
+ if (f->atoms > 0) {
+ mem = ((char *)f) + (f->atoms*m->atomsize);
+ } else {
+ mem = f;
+ m->free = m->free->next;
+ }
+ return mem;
+ } else {
+ b = g_malloc(m->blocksize * m->atomsize);
+ g_ptr_array_add(m->blocks, b);
+ f = (MemChunkFreeNode *)&b[m->atomsize];
+ f->atoms = m->blocksize-1;
+ f->next = NULL;
+ m->free = f;
+ return b;
+ }
+}
+
+void *e_memchunk_alloc0(EMemChunk *m)
+{
+ void *mem;
+
+ mem = e_memchunk_alloc(m);
+ memset(mem, 0, m->atomsize);
+
+ return mem;
+}
+
+/**
+ * e_memchunk_free:
+ * @m:
+ * @mem: Address of atom to free.
+ *
+ * Free a single atom back to the free pool of atoms in the given
+ * memchunk.
+ **/
+void
+e_memchunk_free(MemChunk *m, void *mem)
+{
+ MemChunkFreeNode *f;
+
+ /* put the location back in the free list. If we knew if the preceeding or following
+ cells were free, we could merge the free nodes, but it doesn't really add much */
+ f = mem;
+ f->next = m->free;
+ m->free = f;
+ f->atoms = 1;
+
+ /* we could store the free list sorted - we could then do the above, and also
+ probably improve the locality of reference properties for the allocator */
+ /* and it would simplify some other algorithms at that, but slow this one down
+ significantly */
+}
+
+/**
+ * e_memchunk_empty:
+ * @m:
+ *
+ * Clean out the memchunk buffers. Marks all allocated memory as free blocks,
+ * but does not give it back to the system. Can be used if the memchunk
+ * is to be used repeatedly.
+ **/
+void
+e_memchunk_empty(MemChunk *m)
+{
+ int i;
+ MemChunkFreeNode *f, *h = NULL;
+
+ for (i=0;i<m->blocks->len;i++) {
+ f = (MemChunkFreeNode *)m->blocks->pdata[i];
+ f->atoms = m->blocksize;
+ f->next = h;
+ h = f;
+ }
+ m->free = h;
+}
+
+struct _cleaninfo {
+ struct _cleaninfo *next;
+ char *base;
+ int count;
+ int size; /* just so tree_search has it, sigh */
+};
+
+static int tree_compare(struct _cleaninfo *a, struct _cleaninfo *b)
+{
+ if (a->base < b->base)
+ return -1;
+ else if (a->base > b->base)
+ return 1;
+ return 0;
+}
+
+static int tree_search(struct _cleaninfo *a, char *mem)
+{
+ if (a->base <= mem) {
+ if (mem < &a->base[a->size])
+ return 0;
+ return 1;
+ }
+ return -1;
+}
+
+/**
+ * e_memchunk_clean:
+ * @m:
+ *
+ * Scan all empty blocks and check for blocks which can be free'd
+ * back to the system.
+ *
+ * This routine may take a while to run if there are many allocated
+ * memory blocks (if the total number of allocations is many times
+ * greater than atomcount).
+ **/
+void
+e_memchunk_clean(MemChunk *m)
+{
+ GTree *tree;
+ int i;
+ MemChunkFreeNode *f;
+ struct _cleaninfo *ci, *hi = NULL;
+
+ f = m->free;
+ if (m->blocks->len == 0 || f == NULL)
+ return;
+
+ /* first, setup the tree/list so we can map free block addresses to block addresses */
+ tree = g_tree_new((GCompareFunc)tree_compare);
+ for (i=0;i<m->blocks->len;i++) {
+ ci = alloca(sizeof(*ci));
+ ci->count = 0;
+ ci->base = m->blocks->pdata[i];
+ ci->size = m->blocksize * m->atomsize;
+ g_tree_insert(tree, ci, ci);
+ ci->next = hi;
+ hi = ci;
+ }
+
+ /* now, scan all free nodes, and count them in their tree node */
+ while (f) {
+ ci = g_tree_search(tree, (GCompareFunc) tree_search, f);
+ if (ci) {
+ ci->count += f->atoms;
+ } else {
+ g_warning("error, can't find free node in memory block\n");
+ }
+ f = f->next;
+ }
+
+ /* if any nodes are all free, free & unlink them */
+ ci = hi;
+ while (ci) {
+ if (ci->count == m->blocksize) {
+ MemChunkFreeNode *prev = NULL;
+
+ f = m->free;
+ while (f) {
+ if (tree_search (ci, (void *) f) == 0) {
+ /* prune this node from our free-node list */
+ if (prev)
+ prev->next = f->next;
+ else
+ m->free = f->next;
+ } else {
+ prev = f;
+ }
+
+ f = f->next;
+ }
+
+ g_ptr_array_remove_fast(m->blocks, ci->base);
+ g_free(ci->base);
+ }
+ ci = ci->next;
+ }
+
+ g_tree_destroy(tree);
+}
+
+/**
+ * e_memchunk_destroy:
+ * @m:
+ *
+ * Free the memchunk header, and all associated memory.
+ **/
+void
+e_memchunk_destroy(MemChunk *m)
+{
+ int i;
+
+ if (m == NULL)
+ return;
+
+ for (i=0;i<m->blocks->len;i++)
+ g_free(m->blocks->pdata[i]);
+ g_ptr_array_free(m->blocks, TRUE);
+ g_free(m);
+}
+
+typedef struct _MemPoolNode {
+ struct _MemPoolNode *next;
+
+ int free;
+ char data[1];
+} MemPoolNode;
+
+typedef struct _MemPoolThresholdNode {
+ struct _MemPoolThresholdNode *next;
+ char data[1];
+} MemPoolThresholdNode;
+
+typedef struct _EMemPool {
+ int blocksize;
+ int threshold;
+ unsigned int align;
+ struct _MemPoolNode *blocks;
+ struct _MemPoolThresholdNode *threshold_blocks;
+} MemPool;
+
+/* a pool of mempool header blocks */
+static MemChunk *mempool_memchunk;
+#ifdef G_THREADS_ENABLED
+static GStaticMutex mempool_mutex = G_STATIC_MUTEX_INIT;
+#endif
+
+/**
+ * e_mempool_new:
+ * @blocksize: The base blocksize to use for all system alocations.
+ * @threshold: If the allocation exceeds the threshold, then it is
+ * allocated separately and stored in a separate list.
+ * @flags: Alignment options: E_MEMPOOL_ALIGN_STRUCT uses native
+ * struct alignment, E_MEMPOOL_ALIGN_WORD aligns to 16 bits (2 bytes),
+ * and E_MEMPOOL_ALIGN_BYTE aligns to the nearest byte. The default
+ * is to align to native structures.
+ *
+ * Create a new mempool header. Mempools can be used to efficiently
+ * allocate data which can then be freed as a whole.
+ *
+ * Mempools can also be used to efficiently allocate arbitrarily
+ * aligned data (such as strings) without incurring the space overhead
+ * of aligning each allocation (which is not required for strings).
+ *
+ * However, each allocation cannot be freed individually, only all
+ * or nothing.
+ *
+ * Return value:
+ **/
+MemPool *e_mempool_new(int blocksize, int threshold, EMemPoolFlags flags)
+{
+ MemPool *pool;
+
+#ifdef G_THREADS_ENABLED
+ g_static_mutex_lock(&mempool_mutex);
+#endif
+ if (mempool_memchunk == NULL) {
+ mempool_memchunk = e_memchunk_new(8, sizeof(MemPool));
+ }
+ pool = e_memchunk_alloc(mempool_memchunk);
+#ifdef G_THREADS_ENABLED
+ g_static_mutex_unlock(&mempool_mutex);
+#endif
+ if (threshold >= blocksize)
+ threshold = blocksize * 2 / 3;
+ pool->blocksize = blocksize;
+ pool->threshold = threshold;
+ pool->blocks = NULL;
+ pool->threshold_blocks = NULL;
+
+ switch (flags & E_MEMPOOL_ALIGN_MASK) {
+ case E_MEMPOOL_ALIGN_STRUCT:
+ default:
+ pool->align = STRUCT_ALIGN-1;
+ break;
+ case E_MEMPOOL_ALIGN_WORD:
+ pool->align = 2-1;
+ break;
+ case E_MEMPOOL_ALIGN_BYTE:
+ pool->align = 1-1;
+ }
+ return pool;
+}
+
+/**
+ * e_mempool_alloc:
+ * @pool:
+ * @size:
+ *
+ * Allocate a new data block in the mempool. Size will
+ * be rounded up to the mempool's alignment restrictions
+ * before being used.
+ **/
+void *e_mempool_alloc(MemPool *pool, register int size)
+{
+ size = (size + pool->align) & (~(pool->align));
+ if (size>=pool->threshold) {
+ MemPoolThresholdNode *n;
+
+ n = g_malloc(sizeof(*n) - sizeof(char) + size);
+ n->next = pool->threshold_blocks;
+ pool->threshold_blocks = n;
+ return &n->data[0];
+ } else {
+ register MemPoolNode *n;
+
+ n = pool->blocks;
+ if (n && n->free >= size) {
+ n->free -= size;
+ return &n->data[n->free];
+ }
+
+ /* maybe we could do some sort of the free blocks based on size, but
+ it doubt its worth it at all */
+
+ n = g_malloc(sizeof(*n) - sizeof(char) + pool->blocksize);
+ n->next = pool->blocks;
+ pool->blocks = n;
+ n->free = pool->blocksize - size;
+ return &n->data[n->free];
+ }
+}
+
+char *e_mempool_strdup(EMemPool *pool, const char *str)
+{
+ char *out;
+
+ out = e_mempool_alloc(pool, strlen(str)+1);
+ strcpy(out, str);
+
+ return out;
+}
+
+/**
+ * e_mempool_flush:
+ * @pool:
+ * @freeall: Free all system allocated blocks as well.
+ *
+ * Flush used memory and mark allocated blocks as free.
+ *
+ * If @freeall is #TRUE, then all allocated blocks are free'd
+ * as well. Otherwise only blocks above the threshold are
+ * actually freed, and the others are simply marked as empty.
+ **/
+void e_mempool_flush(MemPool *pool, int freeall)
+{
+ MemPoolThresholdNode *tn, *tw;
+ MemPoolNode *pw, *pn;
+
+ tw = pool->threshold_blocks;
+ while (tw) {
+ tn = tw->next;
+ g_free(tw);
+ tw = tn;
+ }
+ pool->threshold_blocks = NULL;
+
+ if (freeall) {
+ pw = pool->blocks;
+ while (pw) {
+ pn = pw->next;
+ g_free(pw);
+ pw = pn;
+ }
+ pool->blocks = NULL;
+ } else {
+ pw = pool->blocks;
+ while (pw) {
+ pw->free = pool->blocksize;
+ pw = pw->next;
+ }
+ }
+}
+
+/**
+ * e_mempool_destroy:
+ * @pool:
+ *
+ * Free all memory associated with a mempool.
+ **/
+void e_mempool_destroy(MemPool *pool)
+{
+ if (pool) {
+ e_mempool_flush(pool, 1);
+ e_memchunk_free(mempool_memchunk, pool);
+ }
+}
+
+
+/*
+ string array classes
+*/
+
+#define STRV_UNPACKED ((unsigned char)(~0))
+
+struct _EStrv {
+ unsigned char length; /* how many entries we have (or the token STRV_UNPACKED) */
+ char data[1]; /* data follows */
+};
+
+struct _s_strv_string {
+ char *string; /* the string to output */
+ char *free; /* a string to free, if we referenced it */
+};
+
+struct _e_strvunpacked {
+ unsigned char type; /* we overload last to indicate this is unpacked */
+ MemPool *pool; /* pool of memory for strings */
+ struct _EStrv *source; /* if we were converted from a packed one, keep the source around for a while */
+ unsigned int length;
+ struct _s_strv_string strings[1]; /* the string array data follows */
+};
+
+/**
+ * e_strv_new:
+ * @size: The number of elements in the strv. Currently this is limited
+ * to 254 elements.
+ *
+ * Create a new strv (string array) header. strv's can be used to
+ * create and work with arrays of strings that can then be compressed
+ * into a space-efficient static structure. This is useful
+ * where a number of strings are to be stored for lookup, and not
+ * generally edited afterwards.
+ *
+ * The size limit is currently 254 elements. This will probably not
+ * change as arrays of this size suffer significant performance
+ * penalties when looking up strings with high indices.
+ *
+ * Return value:
+ **/
+struct _EStrv *
+e_strv_new(int size)
+{
+ struct _e_strvunpacked *s;
+
+ g_assert(size<255);
+
+ s = g_malloc(sizeof(*s) + (size-1)*sizeof(s->strings[0]));
+ s(printf("new strv=%p, size = %d bytes\n", s, sizeof(*s) + (size-1)*sizeof(char *)));
+ s->type = STRV_UNPACKED;
+ s->pool = NULL;
+ s->length = size;
+ s->source = NULL;
+ memset(s->strings, 0, size*sizeof(s->strings[0]));
+
+ return (struct _EStrv *)s;
+}
+
+static struct _e_strvunpacked *
+strv_unpack(struct _EStrv *strv)
+{
+ struct _e_strvunpacked *s;
+ register char *p;
+ int i;
+
+ s(printf("unpacking\n"));
+
+ s = (struct _e_strvunpacked *)e_strv_new(strv->length);
+ p = strv->data;
+ for (i=0;i<s->length;i++) {
+ if (i>0)
+ while (*p++)
+ ;
+ s->strings[i].string = p;
+ }
+ s->source = strv;
+ s->type = STRV_UNPACKED;
+
+ return s;
+}
+
+/**
+ * e_strv_set_ref:
+ * @strv:
+ * @index:
+ * @str:
+ *
+ * Set a string array element by reference. The string
+ * is not copied until the array is packed.
+ *
+ * If @strv has been packed, then it is unpacked ready
+ * for more inserts, and should be packed again once finished with.
+ * The memory used by the original @strv is not freed until
+ * the new strv is packed, or freed itself.
+ *
+ * Return value: A new EStrv if the strv has already
+ * been packed, otherwise @strv.
+ **/
+struct _EStrv *
+e_strv_set_ref(struct _EStrv *strv, int index, char *str)
+{
+ struct _e_strvunpacked *s;
+
+ s(printf("set ref %d '%s'\nawkmeharder: %s\n ", index, str, str));
+
+ if (strv->length != STRV_UNPACKED)
+ s = strv_unpack(strv);
+ else
+ s = (struct _e_strvunpacked *)strv;
+
+ g_assert(index>=0 && index < s->length);
+
+ s->strings[index].string = str;
+
+ return (struct _EStrv *)s;
+}
+
+/**
+ * e_strv_set_ref_free:
+ * @strv:
+ * @index:
+ * @str:
+ *
+ * Set a string by reference, similar to set_ref, but also
+ * free the string when finished with it. The string
+ * is not copied until the strv is packed, and not at
+ * all if the index is overwritten.
+ *
+ * Return value: @strv if already unpacked, otherwise an packed
+ * EStrv.
+ **/
+struct _EStrv *
+e_strv_set_ref_free(struct _EStrv *strv, int index, char *str)
+{
+ struct _e_strvunpacked *s;
+
+ s(printf("set ref %d '%s'\nawkmeevenharder: %s\n ", index, str, str));
+
+ if (strv->length != STRV_UNPACKED)
+ s = strv_unpack(strv);
+ else
+ s = (struct _e_strvunpacked *)strv;
+
+ g_assert(index>=0 && index < s->length);
+
+ s->strings[index].string = str;
+ if (s->strings[index].free)
+ g_free(s->strings[index].free);
+ s->strings[index].free = str;
+
+ return (struct _EStrv *)s;
+}
+
+/**
+ * e_strv_set:
+ * @strv:
+ * @index:
+ * @str:
+ *
+ * Set a string array reference. The string @str is copied
+ * into the string array at location @index.
+ *
+ * If @strv has been packed, then it is unpacked ready
+ * for more inserts, and should be packed again once finished with.
+ *
+ * Return value: A new EStrv if the strv has already
+ * been packed, otherwise @strv.
+ **/
+struct _EStrv *
+e_strv_set(struct _EStrv *strv, int index, const char *str)
+{
+ struct _e_strvunpacked *s;
+
+ s(printf("set %d '%s'\n", index, str));
+
+ if (strv->length != STRV_UNPACKED)
+ s = strv_unpack(strv);
+ else
+ s = (struct _e_strvunpacked *)strv;
+
+ g_assert(index>=0 && index < s->length);
+
+ if (s->pool == NULL)
+ s->pool = e_mempool_new(1024, 512, E_MEMPOOL_ALIGN_BYTE);
+
+ s->strings[index].string = e_mempool_alloc(s->pool, strlen(str)+1);
+ strcpy(s->strings[index].string, str);
+
+ return (struct _EStrv *)s;
+}
+
+/**
+ * e_strv_pack:
+ * @strv:
+ *
+ * Pack the @strv into a space efficient structure for later lookup.
+ *
+ * All strings are packed into a single allocated block, separated
+ * by single \0 characters, together with a count byte.
+ *
+ * Return value:
+ **/
+struct _EStrv *
+e_strv_pack(struct _EStrv *strv)
+{
+ struct _e_strvunpacked *s;
+ int len, i;
+ register char *src, *dst;
+
+ if (strv->length == STRV_UNPACKED) {
+ s = (struct _e_strvunpacked *)strv;
+
+ s(printf("packing string\n"));
+
+ len = 0;
+ for (i=0;i<s->length;i++)
+ len += s->strings[i].string?strlen(s->strings[i].string)+1:1;
+
+ strv = g_malloc(sizeof(*strv) + len);
+ s(printf("allocating strv=%p, size = %d\n", strv, sizeof(*strv)+len));
+ strv->length = s->length;
+ dst = strv->data;
+ for (i=0;i<s->length;i++) {
+ if ((src = s->strings[i].string)) {
+ while ((*dst++ = *src++))
+ ;
+ } else {
+ *dst++ = 0;
+ }
+ }
+ e_strv_destroy((struct _EStrv *)s);
+ }
+ return strv;
+}
+
+/**
+ * e_strv_get:
+ * @strv:
+ * @index:
+ *
+ * Retrieve a string by index. This function works
+ * identically on both packed and unpacked strv's, although
+ * may be much slower on a packed strv.
+ *
+ * Return value:
+ **/
+char *
+e_strv_get(struct _EStrv *strv, int index)
+{
+ struct _e_strvunpacked *s;
+ char *p;
+
+ if (strv->length != STRV_UNPACKED) {
+ g_assert(index>=0 && index < strv->length);
+ p = strv->data;
+ while (index > 0) {
+ while (*p++ != 0)
+ ;
+ index--;
+ }
+ return p;
+ } else {
+ s = (struct _e_strvunpacked *)strv;
+ g_assert(index>=0 && index < s->length);
+ return s->strings[index].string?s->strings[index].string:"";
+ }
+}
+
+/**
+ * e_strv_destroy:
+ * @strv:
+ *
+ * Free a strv and all associated memory. Works on packed
+ * or unpacked strv's.
+ **/
+void
+e_strv_destroy(struct _EStrv *strv)
+{
+ struct _e_strvunpacked *s;
+ int i;
+
+ s(printf("freeing strv\n"));
+
+ if (strv->length == STRV_UNPACKED) {
+ s = (struct _e_strvunpacked *)strv;
+ if (s->pool)
+ e_mempool_destroy(s->pool);
+ if (s->source)
+ e_strv_destroy(s->source);
+ for (i=0;i<s->length;i++) {
+ if (s->strings[i].free)
+ g_free(s->strings[i].free);
+ }
+ }
+
+ s(printf("freeing strv=%p\n", strv));
+
+ g_free(strv);
+}
+
+
+
+/* string pool stuff */
+
+/* TODO:
+ garbage collection, using the following technique:
+ Create a memchunk for each possible size of poolv, and allocate every poolv from those
+ To garbage collect, scan all memchunk internally, ignoring any free areas (or mark each
+ poolv when freeing it - set length 0?), and find out which strings are not anywhere,
+ then free them.
+
+ OR:
+ Just keep a refcount in the hashtable, instead of duplicating the key pointer.
+
+ either would also require a free for the mempool, so ignore it for now */
+
+/*#define POOLV_REFCNT*/ /* Define to enable refcounting code that does
+ automatic garbage collection of unused strings */
+
+static GHashTable *poolv_pool = NULL;
+static EMemPool *poolv_mempool = NULL;
+
+#ifdef MALLOC_CHECK
+static GPtrArray *poolv_table = NULL;
+#endif
+
+#ifdef PROFILE_POOLV
+static gulong poolv_hits = 0;
+static gulong poolv_misses = 0;
+static unsigned long poolv_mem, poolv_count;
+#endif
+
+#ifdef G_THREADS_ENABLED
+static GStaticMutex poolv_mutex = G_STATIC_MUTEX_INIT;
+#endif
+
+struct _EPoolv {
+ unsigned char length;
+ char *s[1];
+};
+
+/**
+ * e_poolv_new: @size: The number of elements in the poolv, maximum of 254 elements.
+ *
+ * create a new poolv (string vector which shares a global string
+ * pool). poolv's can be used to work with arrays of strings which
+ * save memory by eliminating duplicated allocations of the same
+ * string.
+ *
+ * this is useful when you have a log of read-only strings that do not
+ * go away and are duplicated a lot (such as email headers).
+ *
+ * we should probably in the future ref count the strings contained in
+ * the hash table, but for now let's not.
+ *
+ * Return value: new pooled string vector
+ **/
+EPoolv *
+e_poolv_new(unsigned int size)
+{
+ EPoolv *poolv;
+
+ g_assert(size < 255);
+
+ poolv = g_malloc0(sizeof (*poolv) + (size - 1) * sizeof (char *));
+ poolv->length = size;
+
+#ifdef G_THREADS_ENABLED
+ g_static_mutex_lock(&poolv_mutex);
+#endif
+ if (!poolv_pool)
+ poolv_pool = g_hash_table_new(g_str_hash, g_str_equal);
+
+ if (!poolv_mempool)
+ poolv_mempool = e_mempool_new(32 * 1024, 512, E_MEMPOOL_ALIGN_BYTE);
+
+#ifdef MALLOC_CHECK
+ {
+ int i;
+
+ if (poolv_table == NULL)
+ poolv_table = g_ptr_array_new();
+
+ for (i=0;i<poolv_table->len;i++)
+ MPROBE(poolv_table->pdata[i]);
+
+ g_ptr_array_add(poolv_table, poolv);
+ }
+#endif
+
+#ifdef G_THREADS_ENABLED
+ g_static_mutex_unlock(&poolv_mutex);
+#endif
+
+ p(printf("new poolv=%p\tsize=%d\n", poolv, sizeof(*poolv) + (size-1)*sizeof(char *)));
+
+#ifdef PROFILE_POOLV
+ poolv_count++;
+#endif
+ return poolv;
+}
+
+/**
+ * e_poolv_cpy:
+ * @dest: destination pooled string vector
+ * @src: source pooled string vector
+ *
+ * Copy the contents of a pooled string vector
+ *
+ * Return value: @dest, which may be re-allocated if the strings
+ * are different lengths.
+ **/
+EPoolv *
+e_poolv_cpy(EPoolv *dest, const EPoolv *src)
+{
+#ifdef POOLV_REFCNT
+ int i;
+ unsigned int ref;
+ char *key;
+#endif
+
+ p2(g_return_val_if_fail (dest != NULL, NULL));
+ p2(g_return_val_if_fail (src != NULL, NULL));
+
+ MPROBE(dest);
+ MPROBE(src);
+
+ if (dest->length != src->length) {
+ e_poolv_destroy(dest);
+ dest = e_poolv_new(src->length);
+ }
+
+#ifdef POOLV_REFCNT
+#ifdef G_THREADS_ENABLED
+ g_static_mutex_lock(&poolv_mutex);
+#endif
+ /* ref new copies */
+ for (i=0;i<src->length;i++) {
+ if (src->s[i]) {
+ if (g_hash_table_lookup_extended(poolv_pool, src->s[i], (void **)&key, (void **)&ref)) {
+ g_hash_table_insert(poolv_pool, key, (void *)(ref+1));
+ } else {
+ g_assert_not_reached();
+ }
+ }
+ }
+
+ /* unref the old ones */
+ for (i=0;i<dest->length;i++) {
+ if (dest->s[i]) {
+ if (g_hash_table_lookup_extended(poolv_pool, dest->s[i], (void **)&key, (void **)&ref)) {
+ /* if ref == 1 free it */
+ g_assert(ref > 0);
+ g_hash_table_insert(poolv_pool, key, (void *)(ref-1));
+ } else {
+ g_assert_not_reached();
+ }
+ }
+ }
+#ifdef G_THREADS_ENABLED
+ g_static_mutex_unlock(&poolv_mutex);
+#endif
+#endif
+
+ memcpy(dest->s, src->s, src->length * sizeof (char *));
+
+ return dest;
+}
+
+#ifdef PROFILE_POOLV
+static void
+poolv_profile_update (void)
+{
+ static time_t last_time = 0;
+ time_t new_time;
+
+ new_time = time (NULL);
+ if (new_time - last_time < 5)
+ return;
+
+ printf("poolv profile: %lu hits, %lu misses: %d%% hit rate, memory: %lu, instances: %lu\n",
+ poolv_hits, poolv_misses,
+ (int)(100.0 * ((double) poolv_hits / (double) (poolv_hits + poolv_misses))),
+ poolv_mem, poolv_count);
+
+ last_time = new_time;
+}
+#endif
+
+/**
+ * e_poolv_set:
+ * @poolv: pooled string vector
+ * @index: index in vector of string
+ * @str: string to set
+ * @freeit: whether the caller is releasing its reference to the
+ * string
+ *
+ * Set a string vector reference. If the caller will no longer be
+ * referencing the string, freeit should be TRUE. Otherwise, this
+ * will duplicate the string if it is not found in the pool.
+ *
+ * Return value: @poolv
+ **/
+EPoolv *
+e_poolv_set (EPoolv *poolv, int index, char *str, int freeit)
+{
+#ifdef POOLV_REFCNT
+ unsigned int ref;
+ char *key;
+#endif
+
+ p2(g_return_val_if_fail (poolv != NULL, NULL));
+
+ g_assert(index >=0 && index < poolv->length);
+
+ MPROBE(poolv);
+
+ p(printf("setting %d `%s'\n", index, str));
+
+ if (!str) {
+#ifdef POOLV_REFCNT
+ if (poolv->s[index]) {
+ if (g_hash_table_lookup_extended(poolv_pool, poolv->s[index], (void **)&key, (void **)&ref)) {
+ g_assert(ref > 0);
+ g_hash_table_insert(poolv_pool, key, (void *)(ref-1));
+ } else {
+ g_assert_not_reached();
+ }
+ }
+#endif
+ poolv->s[index] = NULL;
+ return poolv;
+ }
+
+#ifdef G_THREADS_ENABLED
+ g_static_mutex_lock(&poolv_mutex);
+#endif
+
+#ifdef POOLV_REFCNT
+ if (g_hash_table_lookup_extended(poolv_pool, str, (void **)&key, (void **)&ref)) {
+ g_hash_table_insert(poolv_pool, key, (void *)(ref+1));
+ poolv->s[index] = key;
+# ifdef PROFILE_POOLV
+ poolv_hits++;
+ poolv_profile_update ();
+# endif
+ } else {
+# ifdef PROFILE_POOLV
+ poolv_misses++;
+ poolv_mem += strlen(str);
+ poolv_profile_update ();
+# endif
+ poolv->s[index] = e_mempool_strdup(poolv_mempool, str);
+ g_hash_table_insert(poolv_pool, poolv->s[index], (void *)1);
+ }
+
+#else /* !POOLV_REFCNT */
+ if ((poolv->s[index] = g_hash_table_lookup(poolv_pool, str)) != NULL) {
+# ifdef PROFILE_POOLV
+ poolv_hits++;
+ poolv_profile_update ();
+# endif
+ } else {
+# ifdef PROFILE_POOLV
+ poolv_misses++;
+ poolv_mem += strlen(str);
+ poolv_profile_update ();
+# endif
+ poolv->s[index] = e_mempool_strdup(poolv_mempool, str);
+ g_hash_table_insert(poolv_pool, poolv->s[index], poolv->s[index]);
+ }
+#endif /* !POOLV_REFCNT */
+
+#ifdef G_THREADS_ENABLED
+ g_static_mutex_unlock(&poolv_mutex);
+#endif
+
+ if (freeit)
+ g_free(str);
+
+ return poolv;
+}
+
+/**
+ * e_poolv_get:
+ * @poolv: pooled string vector
+ * @index: index in vector of string
+ *
+ * Retrieve a string by index. This could possibly just be a macro.
+ *
+ * Since the pool is never freed, this string does not need to be
+ * duplicated, but should not be modified.
+ *
+ * Return value: string at that index.
+ **/
+const char *
+e_poolv_get(EPoolv *poolv, int index)
+{
+ g_assert(poolv != NULL);
+ g_assert(index>= 0 && index < poolv->length);
+
+ MPROBE(poolv);
+
+ p(printf("get %d = `%s'\n", index, poolv->s[index]));
+
+ return poolv->s[index]?poolv->s[index]:"";
+}
+
+/**
+ * e_poolv_destroy:
+ * @poolv: pooled string vector to free
+ *
+ * Free a pooled string vector. This doesn't free the strings from
+ * the vector, however.
+ **/
+void
+e_poolv_destroy(EPoolv *poolv)
+{
+#ifdef POOLV_REFCNT
+ int i;
+ unsigned int ref;
+ char *key;
+
+ MPROBE(poolv);
+
+#ifdef G_THREADS_ENABLED
+ g_static_mutex_lock(&poolv_mutex);
+#endif
+
+#ifdef MALLOC_CHECK
+ for (i=0;i<poolv_table->len;i++)
+ MPROBE(poolv_table->pdata[i]);
+
+ g_ptr_array_remove_fast(poolv_table, poolv);
+#endif
+
+ for (i=0;i<poolv->length;i++) {
+ if (poolv->s[i]) {
+ if (g_hash_table_lookup_extended(poolv_pool, poolv->s[i], (void **)&key, (void **)&ref)) {
+ /* if ref == 1 free it */
+ g_assert(ref > 0);
+ g_hash_table_insert(poolv_pool, key, (void *)(ref-1));
+ } else {
+ g_assert_not_reached();
+ }
+ }
+ }
+#ifdef G_THREADS_ENABLED
+ g_static_mutex_unlock(&poolv_mutex);
+#endif
+#endif
+
+#ifdef PROFILE_POOLV
+ poolv_count++;
+#endif
+ g_free(poolv);
+}
+
+#if 0
+
+#define CHUNK_SIZE (20)
+#define CHUNK_COUNT (32)
+
+#define s(x)
+
+main()
+{
+ int i;
+ MemChunk *mc;
+ void *mem, *last;
+ GMemChunk *gmc;
+ struct _EStrv *s;
+
+ s = strv_new(8);
+ s = strv_set(s, 1, "Testing 1");
+ s = strv_set(s, 2, "Testing 2");
+ s = strv_set(s, 3, "Testing 3");
+ s = strv_set(s, 4, "Testing 4");
+ s = strv_set(s, 5, "Testing 5");
+ s = strv_set(s, 6, "Testing 7");
+
+ for (i=0;i<8;i++) {
+ printf("s[%d] = %s\n", i, strv_get(s, i));
+ }
+
+ s(sleep(5));
+
+ printf("packing ...\n");
+ s = strv_pack(s);
+
+ for (i=0;i<8;i++) {
+ printf("s[%d] = %s\n", i, strv_get(s, i));
+ }
+
+ printf("setting ...\n");
+
+ s = strv_set_ref(s, 1, "Testing 1 x");
+
+ for (i=0;i<8;i++) {
+ printf("s[%d] = %s\n", i, strv_get(s, i));
+ }
+
+ printf("packing ...\n");
+ s = strv_pack(s);
+
+ for (i=0;i<8;i++) {
+ printf("s[%d] = %s\n", i, strv_get(s, i));
+ }
+
+ strv_free(s);
+
+#if 0
+ time_start("Using memchunks");
+ mc = memchunk_new(CHUNK_COUNT, CHUNK_SIZE);
+ for (i=0;i<1000000;i++) {
+ mem = memchunk_alloc(mc);
+ if ((i & 1) == 0)
+ memchunk_free(mc, mem);
+ }
+ s(sleep(10));
+ memchunk_destroy(mc);
+ time_end("allocating 1000000 memchunks, freeing 500k");
+
+ time_start("Using gmemchunks");
+ gmc = g_mem_chunk_new("memchunk", CHUNK_SIZE, CHUNK_SIZE*CHUNK_COUNT, G_ALLOC_AND_FREE);
+ for (i=0;i<1000000;i++) {
+ mem = g_mem_chunk_alloc(gmc);
+ if ((i & 1) == 0)
+ g_mem_chunk_free(gmc, mem);
+ }
+ s(sleep(10));
+ g_mem_chunk_destroy(gmc);
+ time_end("allocating 1000000 gmemchunks, freeing 500k");
+
+ time_start("Using memchunks");
+ mc = memchunk_new(CHUNK_COUNT, CHUNK_SIZE);
+ for (i=0;i<1000000;i++) {
+ mem = memchunk_alloc(mc);
+ }
+ s(sleep(10));
+ memchunk_destroy(mc);
+ time_end("allocating 1000000 memchunks");
+
+ time_start("Using gmemchunks");
+ gmc = g_mem_chunk_new("memchunk", CHUNK_SIZE, CHUNK_COUNT*CHUNK_SIZE, G_ALLOC_ONLY);
+ for (i=0;i<1000000;i++) {
+ mem = g_mem_chunk_alloc(gmc);
+ }
+ s(sleep(10));
+ g_mem_chunk_destroy(gmc);
+ time_end("allocating 1000000 gmemchunks");
+
+ time_start("Using malloc");
+ for (i=0;i<1000000;i++) {
+ malloc(CHUNK_SIZE);
+ }
+ time_end("allocating 1000000 malloc");
+#endif
+
+}
+
+#endif
diff --git a/libedataserver/e-memory.h b/libedataserver/e-memory.h
new file mode 100644
index 000000000..9cc89f2f2
--- /dev/null
+++ b/libedataserver/e-memory.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2001, Ximian Inc.
+ *
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ * Jacob Berkman <jacob@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_MEMORY_H
+#define _E_MEMORY_H
+
+/* memchunks - allocate/free fixed-size blocks of memory */
+/* this is like gmemchunk, only faster and less overhead (only 4 bytes for every atomcount allocations) */
+typedef struct _EMemChunk EMemChunk;
+
+EMemChunk *e_memchunk_new(int atomcount, int atomsize);
+void *e_memchunk_alloc(EMemChunk *m);
+void *e_memchunk_alloc0(EMemChunk *m);
+void e_memchunk_free(EMemChunk *m, void *mem);
+void e_memchunk_empty(EMemChunk *m);
+void e_memchunk_clean(EMemChunk *m);
+void e_memchunk_destroy(EMemChunk *m);
+
+/* mempools - allocate variable sized blocks of memory, and free as one */
+/* allocation is very fast, but cannot be freed individually */
+typedef struct _EMemPool EMemPool;
+typedef enum {
+ E_MEMPOOL_ALIGN_STRUCT = 0, /* allocate to native structure alignment */
+ E_MEMPOOL_ALIGN_WORD = 1, /* allocate to words - 16 bit alignment */
+ E_MEMPOOL_ALIGN_BYTE = 2, /* allocate to bytes - 8 bit alignment */
+ E_MEMPOOL_ALIGN_MASK = 3, /* which bits determine the alignment information */
+} EMemPoolFlags;
+
+EMemPool *e_mempool_new(int blocksize, int threshold, EMemPoolFlags flags);
+void *e_mempool_alloc(EMemPool *pool, int size);
+char *e_mempool_strdup(EMemPool *pool, const char *str);
+void e_mempool_flush(EMemPool *pool, int freeall);
+void e_mempool_destroy(EMemPool *pool);
+
+/* strv's string arrays that can be efficiently modified and then compressed mainly for retrival */
+/* building is relatively fast, once compressed it takes the minimum amount of memory possible to store */
+typedef struct _EStrv EStrv;
+
+EStrv *e_strv_new(int size);
+EStrv *e_strv_set_ref(EStrv *strv, int index, char *str);
+EStrv *e_strv_set_ref_free(EStrv *strv, int index, char *str);
+EStrv *e_strv_set(EStrv *strv, int index, const char *str);
+EStrv *e_strv_pack(EStrv *strv);
+char *e_strv_get(EStrv *strv, int index);
+void e_strv_destroy(EStrv *strv);
+
+/* poolv's are similar to strv's, but they store common strings */
+typedef struct _EPoolv EPoolv;
+
+EPoolv *e_poolv_new(unsigned int size);
+EPoolv *e_poolv_cpy(EPoolv *dest, const EPoolv *src);
+EPoolv *e_poolv_set(EPoolv *poolv, int index, char *str, int freeit);
+const char *e_poolv_get(EPoolv *poolv, int index);
+void e_poolv_destroy(EPoolv *poolv);
+
+#endif /* ! _E_MEMORY_H */
diff --git a/libedataserver/e-msgport.c b/libedataserver/e-msgport.c
new file mode 100644
index 000000000..36ea6cfe8
--- /dev/null
+++ b/libedataserver/e-msgport.c
@@ -0,0 +1,1041 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2002 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
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <pthread.h>
+
+#include <glib.h>
+
+#ifdef HAVE_NSS
+#include <nspr.h>
+#endif
+
+#include "e-msgport.h"
+
+#define m(x) /* msgport debug */
+#define t(x) /* thread debug */
+
+void e_dlist_init(EDList *v)
+{
+ v->head = (EDListNode *)&v->tail;
+ v->tail = 0;
+ v->tailpred = (EDListNode *)&v->head;
+}
+
+EDListNode *e_dlist_addhead(EDList *l, EDListNode *n)
+{
+ n->next = l->head;
+ n->prev = (EDListNode *)&l->head;
+ l->head->prev = n;
+ l->head = n;
+ return n;
+}
+
+EDListNode *e_dlist_addtail(EDList *l, EDListNode *n)
+{
+ n->next = (EDListNode *)&l->tail;
+ n->prev = l->tailpred;
+ l->tailpred->next = n;
+ l->tailpred = n;
+ return n;
+}
+
+EDListNode *e_dlist_remove(EDListNode *n)
+{
+ n->next->prev = n->prev;
+ n->prev->next = n->next;
+ return n;
+}
+
+EDListNode *e_dlist_remhead(EDList *l)
+{
+ EDListNode *n, *nn;
+
+ n = l->head;
+ nn = n->next;
+ if (nn) {
+ nn->prev = n->prev;
+ l->head = nn;
+ return n;
+ }
+ return NULL;
+}
+
+EDListNode *e_dlist_remtail(EDList *l)
+{
+ EDListNode *n, *np;
+
+ n = l->tailpred;
+ np = n->prev;
+ if (np) {
+ np->next = n->next;
+ l->tailpred = np;
+ return n;
+ }
+ return NULL;
+}
+
+int e_dlist_empty(EDList *l)
+{
+ return (l->head == (EDListNode *)&l->tail);
+}
+
+int e_dlist_length(EDList *l)
+{
+ EDListNode *n, *nn;
+ int count = 0;
+
+ n = l->head;
+ nn = n->next;
+ while (nn) {
+ count++;
+ n = nn;
+ nn = n->next;
+ }
+
+ return count;
+}
+
+struct _EMsgPort {
+ EDList queue;
+ int condwait; /* how many waiting in condwait */
+ union {
+ int pipe[2];
+ struct {
+ int read;
+ int write;
+ } fd;
+ } pipe;
+#ifdef HAVE_NSS
+ struct {
+ PRFileDesc *read;
+ PRFileDesc *write;
+ } prpipe;
+#endif
+ /* @#@$#$ glib stuff */
+ GCond *cond;
+ GMutex *lock;
+};
+
+EMsgPort *e_msgport_new(void)
+{
+ EMsgPort *mp;
+
+ mp = g_malloc(sizeof(*mp));
+ e_dlist_init(&mp->queue);
+ mp->lock = g_mutex_new();
+ mp->cond = g_cond_new();
+ mp->pipe.fd.read = -1;
+ mp->pipe.fd.write = -1;
+#ifdef HAVE_NSS
+ mp->prpipe.read = NULL;
+ mp->prpipe.write = NULL;
+#endif
+ mp->condwait = 0;
+
+ return mp;
+}
+
+void e_msgport_destroy(EMsgPort *mp)
+{
+ g_mutex_free(mp->lock);
+ g_cond_free(mp->cond);
+ if (mp->pipe.fd.read != -1) {
+ close(mp->pipe.fd.read);
+ close(mp->pipe.fd.write);
+ }
+#ifdef HAVE_NSS
+ if (mp->prpipe.read) {
+ PR_Close(mp->prpipe.read);
+ PR_Close(mp->prpipe.write);
+ }
+#endif
+ g_free(mp);
+}
+
+/* get a fd that can be used to wait on the port asynchronously */
+int e_msgport_fd(EMsgPort *mp)
+{
+ int fd;
+
+ g_mutex_lock(mp->lock);
+ fd = mp->pipe.fd.read;
+ if (fd == -1) {
+ pipe(mp->pipe.pipe);
+ fd = mp->pipe.fd.read;
+ }
+ g_mutex_unlock(mp->lock);
+
+ return fd;
+}
+
+#ifdef HAVE_NSS
+PRFileDesc *e_msgport_prfd(EMsgPort *mp)
+{
+ PRFileDesc *fd;
+
+ g_mutex_lock(mp->lock);
+ fd = mp->prpipe.read;
+ if (fd == NULL) {
+ PR_CreatePipe(&mp->prpipe.read, &mp->prpipe.write);
+ fd = mp->prpipe.read;
+ }
+ g_mutex_unlock(mp->lock);
+
+ return fd;
+}
+#endif
+
+void e_msgport_put(EMsgPort *mp, EMsg *msg)
+{
+ int fd;
+#ifdef HAVE_NSS
+ PRFileDesc *prfd;
+#endif
+
+ m(printf("put:\n"));
+ g_mutex_lock(mp->lock);
+ e_dlist_addtail(&mp->queue, &msg->ln);
+ if (mp->condwait > 0) {
+ m(printf("put: condwait > 0, waking up\n"));
+ g_cond_signal(mp->cond);
+ }
+ fd = mp->pipe.fd.write;
+#ifdef HAVE_NSS
+ prfd = mp->prpipe.write;
+#endif
+ g_mutex_unlock(mp->lock);
+
+ if (fd != -1) {
+ m(printf("put: have pipe, writing notification to it\n"));
+ write(fd, "", 1);
+ }
+
+#ifdef HAVE_NSS
+ if (prfd != NULL) {
+ m(printf("put: have pr pipe, writing notification to it\n"));
+ PR_Write(prfd, "", 1);
+ }
+#endif
+ m(printf("put: done\n"));
+}
+
+static void
+msgport_cleanlock(void *data)
+{
+ EMsgPort *mp = data;
+
+ g_mutex_unlock(mp->lock);
+}
+
+EMsg *e_msgport_wait(EMsgPort *mp)
+{
+ EMsg *msg;
+
+ m(printf("wait:\n"));
+ g_mutex_lock(mp->lock);
+ while (e_dlist_empty(&mp->queue)) {
+ if (mp->pipe.fd.read != -1) {
+ fd_set rfds;
+ int retry;
+
+ m(printf("wait: waitng on pipe\n"));
+ g_mutex_unlock(mp->lock);
+ do {
+ FD_ZERO(&rfds);
+ FD_SET(mp->pipe.fd.read, &rfds);
+ retry = select(mp->pipe.fd.read+1, &rfds, NULL, NULL, NULL) == -1 && errno == EINTR;
+ pthread_testcancel();
+ } while (retry);
+ g_mutex_lock(mp->lock);
+ m(printf("wait: got pipe\n"));
+#ifdef HAVE_NSS
+ } else if (mp->prpipe.read != NULL) {
+ PRPollDesc polltable[1];
+ int retry;
+
+ m(printf("wait: waitng on pr pipe\n"));
+ g_mutex_unlock(mp->lock);
+ do {
+ polltable[0].fd = mp->prpipe.read;
+ polltable[0].in_flags = PR_POLL_READ|PR_POLL_ERR;
+ retry = PR_Poll(polltable, 1, PR_INTERVAL_NO_TIMEOUT) == -1 && PR_GetError() == PR_PENDING_INTERRUPT_ERROR;
+ pthread_testcancel();
+ } while (retry);
+ g_mutex_lock(mp->lock);
+ m(printf("wait: got pr pipe\n"));
+#endif /* HAVE_NSS */
+ } else {
+ m(printf("wait: waiting on condition\n"));
+ mp->condwait++;
+ /* if we are cancelled in the cond-wait, then we need to unlock our lock when we cleanup */
+ pthread_cleanup_push(msgport_cleanlock, mp);
+ g_cond_wait(mp->cond, mp->lock);
+ pthread_cleanup_pop(0);
+ m(printf("wait: got condition\n"));
+ mp->condwait--;
+ }
+ }
+ msg = (EMsg *)mp->queue.head;
+ m(printf("wait: message = %p\n", msg));
+ g_mutex_unlock(mp->lock);
+ m(printf("wait: done\n"));
+ return msg;
+}
+
+EMsg *e_msgport_get(EMsgPort *mp)
+{
+ EMsg *msg;
+ char dummy[1];
+
+ g_mutex_lock(mp->lock);
+ msg = (EMsg *)e_dlist_remhead(&mp->queue);
+ if (msg) {
+ if (mp->pipe.fd.read != -1)
+ read(mp->pipe.fd.read, dummy, 1);
+#ifdef HAVE_NSS
+ if (mp->prpipe.read != NULL) {
+ int c;
+ c = PR_Read(mp->prpipe.read, dummy, 1);
+ g_assert(c == 1);
+ }
+#endif
+ }
+ m(printf("get: message = %p\n", msg));
+ g_mutex_unlock(mp->lock);
+
+ return msg;
+}
+
+void e_msgport_reply(EMsg *msg)
+{
+ if (msg->reply_port) {
+ e_msgport_put(msg->reply_port, msg);
+ }
+ /* else lost? */
+}
+
+struct _thread_info {
+ pthread_t id;
+ int busy;
+};
+
+struct _EThread {
+ struct _EThread *next;
+ struct _EThread *prev;
+
+ EMsgPort *server_port;
+ EMsgPort *reply_port;
+ pthread_mutex_t mutex;
+ e_thread_t type;
+ int queue_limit;
+
+ int waiting; /* if we are waiting for a new message, count of waiting processes */
+ pthread_t id; /* id of our running child thread */
+ GList *id_list; /* if THREAD_NEW, then a list of our child threads in thread_info structs */
+
+ EThreadFunc destroy;
+ void *destroy_data;
+
+ EThreadFunc received;
+ void *received_data;
+
+ EThreadFunc lost;
+ void *lost_data;
+};
+
+/* All active threads */
+static EDList ethread_list = E_DLIST_INITIALISER(ethread_list);
+static pthread_mutex_t ethread_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#define E_THREAD_NONE ((pthread_t)~0)
+#define E_THREAD_QUIT_REPLYPORT ((struct _EMsgPort *)~0)
+
+static void thread_destroy_msg(EThread *e, EMsg *m);
+
+static struct _thread_info *thread_find(EThread *e, pthread_t id)
+{
+ GList *node;
+ struct _thread_info *info;
+
+ node = e->id_list;
+ while (node) {
+ info = node->data;
+ if (info->id == id)
+ return info;
+ node = node->next;
+ }
+ return NULL;
+}
+
+#if 0
+static void thread_remove(EThread *e, pthread_t id)
+{
+ GList *node;
+ struct _thread_info *info;
+
+ node = e->id_list;
+ while (node) {
+ info = node->data;
+ if (info->id == id) {
+ e->id_list = g_list_remove(e->id_list, info);
+ g_free(info);
+ }
+ node = node->next;
+ }
+}
+#endif
+
+EThread *e_thread_new(e_thread_t type)
+{
+ EThread *e;
+
+ e = g_malloc0(sizeof(*e));
+ pthread_mutex_init(&e->mutex, 0);
+ e->type = type;
+ e->server_port = e_msgport_new();
+ e->id = E_THREAD_NONE;
+ e->queue_limit = INT_MAX;
+
+ pthread_mutex_lock(&ethread_lock);
+ e_dlist_addtail(&ethread_list, (EDListNode *)e);
+ pthread_mutex_unlock(&ethread_lock);
+
+ return e;
+}
+
+/* close down the threads & resources etc */
+void e_thread_destroy(EThread *e)
+{
+ int busy = FALSE;
+ EMsg *msg;
+ struct _thread_info *info;
+ GList *l;
+
+ /* make sure we soak up all the messages first */
+ while ( (msg = e_msgport_get(e->server_port)) ) {
+ thread_destroy_msg(e, msg);
+ }
+
+ pthread_mutex_lock(&e->mutex);
+
+ switch(e->type) {
+ case E_THREAD_QUEUE:
+ case E_THREAD_DROP:
+ /* if we have a thread, 'kill' it */
+ if (e->id != E_THREAD_NONE) {
+ pthread_t id = e->id;
+
+ t(printf("Sending thread '%d' quit message\n", id));
+
+ e->id = E_THREAD_NONE;
+
+ msg = g_malloc0(sizeof(*msg));
+ msg->reply_port = E_THREAD_QUIT_REPLYPORT;
+ e_msgport_put(e->server_port, msg);
+
+ pthread_mutex_unlock(&e->mutex);
+ t(printf("Joining thread '%d'\n", id));
+ pthread_join(id, 0);
+ t(printf("Joined thread '%d'!\n", id));
+ pthread_mutex_lock(&e->mutex);
+ }
+ busy = e->id != E_THREAD_NONE;
+ break;
+ case E_THREAD_NEW:
+ /* first, send everyone a quit message */
+ l = e->id_list;
+ while (l) {
+ info = l->data;
+ t(printf("Sending thread '%d' quit message\n", info->id));
+ msg = g_malloc0(sizeof(*msg));
+ msg->reply_port = E_THREAD_QUIT_REPLYPORT;
+ e_msgport_put(e->server_port, msg);
+ l = l->next;
+ }
+
+ /* then, wait for everyone to quit */
+ while (e->id_list) {
+ info = e->id_list->data;
+ e->id_list = g_list_remove(e->id_list, info);
+ pthread_mutex_unlock(&e->mutex);
+ t(printf("Joining thread '%d'\n", info->id));
+ pthread_join(info->id, 0);
+ t(printf("Joined thread '%d'!\n", info->id));
+ pthread_mutex_lock(&e->mutex);
+ g_free(info);
+ }
+ busy = g_list_length(e->id_list) != 0;
+ break;
+ }
+
+ pthread_mutex_unlock(&e->mutex);
+
+ /* and clean up, if we can */
+ if (busy) {
+ g_warning("threads were busy, leaked EThread");
+ return;
+ }
+
+ pthread_mutex_lock(&ethread_lock);
+ e_dlist_remove((EDListNode *)e);
+ pthread_mutex_unlock(&ethread_lock);
+
+ pthread_mutex_destroy(&e->mutex);
+ e_msgport_destroy(e->server_port);
+ g_free(e);
+}
+
+/* set the queue maximum depth, what happens when the queue
+ fills up depends on the queue type */
+void e_thread_set_queue_limit(EThread *e, int limit)
+{
+ e->queue_limit = limit;
+}
+
+/* set a msg destroy callback, this can not call any e_thread functions on @e */
+void e_thread_set_msg_destroy(EThread *e, EThreadFunc destroy, void *data)
+{
+ pthread_mutex_lock(&e->mutex);
+ e->destroy = destroy;
+ e->destroy_data = data;
+ pthread_mutex_unlock(&e->mutex);
+}
+
+/* set a message lost callback, called if any message is discarded */
+void e_thread_set_msg_lost(EThread *e, EThreadFunc lost, void *data)
+{
+ pthread_mutex_lock(&e->mutex);
+ e->lost = lost;
+ e->lost_data = lost;
+ pthread_mutex_unlock(&e->mutex);
+}
+
+/* set a reply port, if set, then send messages back once finished */
+void e_thread_set_reply_port(EThread *e, EMsgPort *reply_port)
+{
+ e->reply_port = reply_port;
+}
+
+/* set a received data callback */
+void e_thread_set_msg_received(EThread *e, EThreadFunc received, void *data)
+{
+ pthread_mutex_lock(&e->mutex);
+ e->received = received;
+ e->received_data = data;
+ pthread_mutex_unlock(&e->mutex);
+}
+
+/* find out if we're busy doing any work, e==NULL, check for all work */
+int e_thread_busy(EThread *e)
+{
+ int busy = FALSE;
+
+ if (e == NULL) {
+ pthread_mutex_lock(&ethread_lock);
+ e = (EThread *)ethread_list.head;
+ while (e->next && !busy) {
+ busy = e_thread_busy(e);
+ e = e->next;
+ }
+ pthread_mutex_unlock(&ethread_lock);
+ } else {
+ pthread_mutex_lock(&e->mutex);
+ switch (e->type) {
+ case E_THREAD_QUEUE:
+ case E_THREAD_DROP:
+ busy = e->waiting != 1 && e->id != E_THREAD_NONE;
+ break;
+ case E_THREAD_NEW:
+ busy = e->waiting != g_list_length(e->id_list);
+ break;
+ }
+ pthread_mutex_unlock(&e->mutex);
+ }
+
+ return busy;
+}
+
+static void
+thread_destroy_msg(EThread *e, EMsg *m)
+{
+ EThreadFunc func;
+ void *func_data;
+
+ /* we do this so we never get an incomplete/unmatched callback + data */
+ pthread_mutex_lock(&e->mutex);
+ func = e->destroy;
+ func_data = e->destroy_data;
+ pthread_mutex_unlock(&e->mutex);
+
+ if (func)
+ func(e, m, func_data);
+}
+
+static void
+thread_received_msg(EThread *e, EMsg *m)
+{
+ EThreadFunc func;
+ void *func_data;
+
+ /* we do this so we never get an incomplete/unmatched callback + data */
+ pthread_mutex_lock(&e->mutex);
+ func = e->received;
+ func_data = e->received_data;
+ pthread_mutex_unlock(&e->mutex);
+
+ if (func)
+ func(e, m, func_data);
+ else
+ g_warning("No processing callback for EThread, message unprocessed");
+}
+
+static void
+thread_lost_msg(EThread *e, EMsg *m)
+{
+ EThreadFunc func;
+ void *func_data;
+
+ /* we do this so we never get an incomplete/unmatched callback + data */
+ pthread_mutex_lock(&e->mutex);
+ func = e->lost;
+ func_data = e->lost_data;
+ pthread_mutex_unlock(&e->mutex);
+
+ if (func)
+ func(e, m, func_data);
+}
+
+/* the actual thread dispatcher */
+static void *
+thread_dispatch(void *din)
+{
+ EThread *e = din;
+ EMsg *m;
+ struct _thread_info *info;
+ pthread_t self = pthread_self();
+
+ t(printf("dispatch thread started: %ld\n", pthread_self()));
+
+ while (1) {
+ pthread_mutex_lock(&e->mutex);
+ m = e_msgport_get(e->server_port);
+ if (m == NULL) {
+ /* nothing to do? If we are a 'new' type thread, just quit.
+ Otherwise, go into waiting (can be cancelled here) */
+ info = NULL;
+ switch (e->type) {
+ case E_THREAD_NEW:
+ case E_THREAD_QUEUE:
+ case E_THREAD_DROP:
+ info = thread_find(e, self);
+ if (info)
+ info->busy = FALSE;
+ e->waiting++;
+ pthread_mutex_unlock(&e->mutex);
+ e_msgport_wait(e->server_port);
+ pthread_mutex_lock(&e->mutex);
+ e->waiting--;
+ pthread_mutex_unlock(&e->mutex);
+ break;
+#if 0
+ case E_THREAD_NEW:
+ e->id_list = g_list_remove(e->id_list, (void *)pthread_self());
+ pthread_mutex_unlock(&e->mutex);
+ return 0;
+#endif
+ }
+
+ continue;
+ } else if (m->reply_port == E_THREAD_QUIT_REPLYPORT) {
+ t(printf("Thread %d got quit message\n", self));
+ /* Handle a quit message, say we're quitting, free the message, and break out of the loop */
+ info = thread_find(e, self);
+ if (info)
+ info->busy = 2;
+ pthread_mutex_unlock(&e->mutex);
+ g_free(m);
+ break;
+ } else {
+ info = thread_find(e, self);
+ if (info)
+ info->busy = TRUE;
+ }
+ pthread_mutex_unlock(&e->mutex);
+
+ t(printf("got message in dispatch thread\n"));
+
+ /* process it */
+ thread_received_msg(e, m);
+
+ /* if we have a reply port, send it back, otherwise, lose it */
+ if (m->reply_port) {
+ e_msgport_reply(m);
+ } else {
+ thread_destroy_msg(e, m);
+ }
+ }
+
+ return NULL;
+}
+
+/* send a message to the thread, start thread if necessary */
+void e_thread_put(EThread *e, EMsg *msg)
+{
+ pthread_t id;
+ EMsg *dmsg = NULL;
+
+ pthread_mutex_lock(&e->mutex);
+
+ /* the caller forgot to tell us what to do, well, we can't do anything can we */
+ if (e->received == NULL) {
+ pthread_mutex_unlock(&e->mutex);
+ g_warning("EThread called with no receiver function, no work to do!");
+ thread_destroy_msg(e, msg);
+ return;
+ }
+
+ msg->reply_port = e->reply_port;
+
+ switch(e->type) {
+ case E_THREAD_QUEUE:
+ /* if the queue is full, lose this new addition */
+ if (e_dlist_length(&e->server_port->queue) < e->queue_limit) {
+ e_msgport_put(e->server_port, msg);
+ } else {
+ printf("queue limit reached, dropping new message\n");
+ dmsg = msg;
+ }
+ break;
+ case E_THREAD_DROP:
+ /* if the queue is full, lose the oldest (unprocessed) message */
+ if (e_dlist_length(&e->server_port->queue) < e->queue_limit) {
+ e_msgport_put(e->server_port, msg);
+ } else {
+ printf("queue limit reached, dropping old message\n");
+ e_msgport_put(e->server_port, msg);
+ dmsg = e_msgport_get(e->server_port);
+ }
+ break;
+ case E_THREAD_NEW:
+ /* it is possible that an existing thread can catch this message, so
+ we might create a thread with no work to do.
+ but that doesn't matter, the other alternative that it be lost is worse */
+ e_msgport_put(e->server_port, msg);
+ if (e->waiting == 0
+ && g_list_length(e->id_list) < e->queue_limit
+ && pthread_create(&id, NULL, thread_dispatch, e) == 0) {
+ struct _thread_info *info = g_malloc0(sizeof(*info));
+ t(printf("created NEW thread %ld\n", id));
+ info->id = id;
+ info->busy = TRUE;
+ e->id_list = g_list_append(e->id_list, info);
+ }
+ pthread_mutex_unlock(&e->mutex);
+ return;
+ }
+
+ /* create the thread, if there is none to receive it yet */
+ if (e->id == E_THREAD_NONE) {
+ int err;
+
+ if ((err = pthread_create(&e->id, NULL, thread_dispatch, e)) != 0) {
+ g_warning("Could not create dispatcher thread, message queued?: %s", strerror(err));
+ e->id = E_THREAD_NONE;
+ }
+ }
+
+ pthread_mutex_unlock(&e->mutex);
+
+ if (dmsg) {
+ thread_lost_msg(e, dmsg);
+ thread_destroy_msg(e, dmsg);
+ }
+}
+
+/* yet-another-mutex interface */
+struct _EMutex {
+ int type;
+ pthread_t owner;
+ short waiters;
+ short depth;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+};
+
+/* sigh, this is just painful to have to need, but recursive
+ read/write, etc mutexes just aren't very common in thread
+ implementations */
+/* TODO: Just make it use recursive mutexes if they are available */
+EMutex *e_mutex_new(e_mutex_t type)
+{
+ struct _EMutex *m;
+
+ m = g_malloc(sizeof(*m));
+ m->type = type;
+ m->waiters = 0;
+ m->depth = 0;
+ m->owner = E_THREAD_NONE;
+
+ switch (type) {
+ case E_MUTEX_SIMPLE:
+ pthread_mutex_init(&m->mutex, 0);
+ break;
+ case E_MUTEX_REC:
+ pthread_mutex_init(&m->mutex, 0);
+ pthread_cond_init(&m->cond, 0);
+ break;
+ /* read / write ? flags for same? */
+ }
+
+ return m;
+}
+
+int e_mutex_destroy(EMutex *m)
+{
+ int ret = 0;
+
+ switch (m->type) {
+ case E_MUTEX_SIMPLE:
+ ret = pthread_mutex_destroy(&m->mutex);
+ if (ret == -1)
+ g_warning("EMutex destroy failed: %s", strerror(errno));
+ g_free(m);
+ break;
+ case E_MUTEX_REC:
+ ret = pthread_mutex_destroy(&m->mutex);
+ if (ret == -1)
+ g_warning("EMutex destroy failed: %s", strerror(errno));
+ ret = pthread_cond_destroy(&m->cond);
+ if (ret == -1)
+ g_warning("EMutex destroy failed: %s", strerror(errno));
+ g_free(m);
+
+ }
+ return ret;
+}
+
+int e_mutex_lock(EMutex *m)
+{
+ pthread_t id;
+ int err;
+
+ switch (m->type) {
+ case E_MUTEX_SIMPLE:
+ return pthread_mutex_lock(&m->mutex);
+ case E_MUTEX_REC:
+ id = pthread_self();
+ if ((err = pthread_mutex_lock(&m->mutex)) != 0)
+ return err;
+ while (1) {
+ if (m->owner == E_THREAD_NONE) {
+ m->owner = id;
+ m->depth = 1;
+ break;
+ } else if (id == m->owner) {
+ m->depth++;
+ break;
+ } else {
+ m->waiters++;
+ if ((err = pthread_cond_wait(&m->cond, &m->mutex)) != 0)
+ return err;
+ m->waiters--;
+ }
+ }
+ return pthread_mutex_unlock(&m->mutex);
+ }
+
+ return EINVAL;
+}
+
+int e_mutex_unlock(EMutex *m)
+{
+ int err;
+
+ switch (m->type) {
+ case E_MUTEX_SIMPLE:
+ return pthread_mutex_unlock(&m->mutex);
+ case E_MUTEX_REC:
+ if ((err = pthread_mutex_lock(&m->mutex)) != 0)
+ return err;
+ g_assert(m->owner == pthread_self());
+
+ m->depth--;
+ if (m->depth == 0) {
+ m->owner = E_THREAD_NONE;
+ if (m->waiters > 0)
+ pthread_cond_signal(&m->cond);
+ }
+ return pthread_mutex_unlock(&m->mutex);
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+void e_mutex_assert_locked(EMutex *m)
+{
+ g_return_if_fail (m->type == E_MUTEX_REC);
+ pthread_mutex_lock(&m->mutex);
+ g_assert(m->owner == pthread_self());
+ pthread_mutex_unlock(&m->mutex);
+}
+
+int e_mutex_cond_wait(void *vcond, EMutex *m)
+{
+ int ret;
+ pthread_cond_t *cond = vcond;
+
+ switch(m->type) {
+ case E_MUTEX_SIMPLE:
+ return pthread_cond_wait(cond, &m->mutex);
+ case E_MUTEX_REC:
+ if ((ret = pthread_mutex_lock(&m->mutex)) != 0)
+ return ret;
+ g_assert(m->owner == pthread_self());
+ ret = pthread_cond_wait(cond, &m->mutex);
+ g_assert(m->owner == pthread_self());
+ pthread_mutex_unlock(&m->mutex);
+ return ret;
+ default:
+ g_return_val_if_reached(-1);
+ }
+}
+
+#ifdef STANDALONE
+EMsgPort *server_port;
+
+
+void *fdserver(void *data)
+{
+ int fd;
+ EMsg *msg;
+ int id = (int)data;
+ fd_set rfds;
+
+ fd = e_msgport_fd(server_port);
+
+ while (1) {
+ int count = 0;
+
+ printf("server %d: waiting on fd %d\n", id, fd);
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ select(fd+1, &rfds, NULL, NULL, NULL);
+ printf("server %d: Got async notification, checking for messages\n", id);
+ while ((msg = e_msgport_get(server_port))) {
+ printf("server %d: got message\n", id);
+ sleep(1);
+ printf("server %d: replying\n", id);
+ e_msgport_reply(msg);
+ count++;
+ }
+ printf("server %d: got %d messages\n", id, count);
+ }
+}
+
+void *server(void *data)
+{
+ EMsg *msg;
+ int id = (int)data;
+
+ while (1) {
+ printf("server %d: waiting\n", id);
+ msg = e_msgport_wait(server_port);
+ msg = e_msgport_get(server_port);
+ if (msg) {
+ printf("server %d: got message\n", id);
+ sleep(1);
+ printf("server %d: replying\n", id);
+ e_msgport_reply(msg);
+ } else {
+ printf("server %d: didn't get message\n", id);
+ }
+ }
+}
+
+void *client(void *data)
+{
+ EMsg *msg;
+ EMsgPort *replyport;
+ int i;
+
+ replyport = e_msgport_new();
+ msg = g_malloc0(sizeof(*msg));
+ msg->reply_port = replyport;
+ for (i=0;i<10;i++) {
+ /* synchronous operation */
+ printf("client: sending\n");
+ e_msgport_put(server_port, msg);
+ printf("client: waiting for reply\n");
+ e_msgport_wait(replyport);
+ e_msgport_get(replyport);
+ printf("client: got reply\n");
+ }
+
+ printf("client: sleeping ...\n");
+ sleep(2);
+ printf("client: sending multiple\n");
+
+ for (i=0;i<10;i++) {
+ msg = g_malloc0(sizeof(*msg));
+ msg->reply_port = replyport;
+ e_msgport_put(server_port, msg);
+ }
+
+ printf("client: receiving multiple\n");
+ for (i=0;i<10;i++) {
+ e_msgport_wait(replyport);
+ msg = e_msgport_get(replyport);
+ g_free(msg);
+ }
+
+ printf("client: done\n");
+}
+
+int main(int argc, char **argv)
+{
+ pthread_t serverid, clientid;
+
+ g_thread_init(NULL);
+
+ server_port = e_msgport_new();
+
+ /*pthread_create(&serverid, NULL, server, (void *)1);*/
+ pthread_create(&serverid, NULL, fdserver, (void *)1);
+ pthread_create(&clientid, NULL, client, NULL);
+
+ sleep(60);
+
+ return 0;
+}
+#endif
diff --git a/libedataserver/e-msgport.h b/libedataserver/e-msgport.h
new file mode 100644
index 000000000..8d4e0c20f
--- /dev/null
+++ b/libedataserver/e-msgport.h
@@ -0,0 +1,88 @@
+
+#ifndef _E_MSGPORT_H
+#define _E_MSGPORT_H
+
+/* double-linked list yeah another one, deal */
+typedef struct _EDListNode {
+ struct _EDListNode *next;
+ struct _EDListNode *prev;
+} EDListNode;
+
+typedef struct _EDList {
+ struct _EDListNode *head;
+ struct _EDListNode *tail;
+ struct _EDListNode *tailpred;
+} EDList;
+
+#define E_DLIST_INITIALISER(l) { (EDListNode *)&l.tail, 0, (EDListNode *)&l.head }
+
+void e_dlist_init(EDList *v);
+EDListNode *e_dlist_addhead(EDList *l, EDListNode *n);
+EDListNode *e_dlist_addtail(EDList *l, EDListNode *n);
+EDListNode *e_dlist_remove(EDListNode *n);
+EDListNode *e_dlist_remhead(EDList *l);
+EDListNode *e_dlist_remtail(EDList *l);
+int e_dlist_empty(EDList *l);
+int e_dlist_length(EDList *l);
+
+/* message ports - a simple inter-thread 'ipc' primitive */
+/* opaque handle */
+typedef struct _EMsgPort EMsgPort;
+
+/* header for any message */
+typedef struct _EMsg {
+ EDListNode ln;
+ EMsgPort *reply_port;
+} EMsg;
+
+EMsgPort *e_msgport_new(void);
+void e_msgport_destroy(EMsgPort *mp);
+/* get a fd that can be used to wait on the port asynchronously */
+int e_msgport_fd(EMsgPort *mp);
+void e_msgport_put(EMsgPort *mp, EMsg *msg);
+EMsg *e_msgport_wait(EMsgPort *mp);
+EMsg *e_msgport_get(EMsgPort *mp);
+void e_msgport_reply(EMsg *msg);
+#ifdef HAVE_NSS
+struct PRFileDesc *e_msgport_prfd(EMsgPort *mp);
+#endif
+
+/* e threads, a server thread with a message based request-response, and flexible queuing */
+typedef struct _EThread EThread;
+
+typedef enum {
+ E_THREAD_QUEUE = 0, /* run one by one, until done, if the queue_limit is reached, discard new request */
+ E_THREAD_DROP, /* run one by one, until done, if the queue_limit is reached, discard oldest requests */
+ E_THREAD_NEW, /* always run in a new thread, if the queue limit is reached, new requests are
+ stored in the queue until a thread becomes available for it, creating a thread pool */
+} e_thread_t;
+
+typedef void (*EThreadFunc)(EThread *, EMsg *, void *data);
+
+EThread *e_thread_new(e_thread_t type);
+void e_thread_destroy(EThread *e);
+void e_thread_set_queue_limit(EThread *e, int limit);
+void e_thread_set_msg_lost(EThread *e, EThreadFunc destroy, void *data);
+void e_thread_set_msg_destroy(EThread *e, EThreadFunc destroy, void *data);
+void e_thread_set_reply_port(EThread *e, EMsgPort *reply_port);
+void e_thread_set_msg_received(EThread *e, EThreadFunc received, void *data);
+void e_thread_put(EThread *e, EMsg *msg);
+int e_thread_busy(EThread *e);
+
+/* sigh, another mutex interface, this one allows different mutex types, portably */
+typedef struct _EMutex EMutex;
+
+typedef enum _e_mutex_t {
+ E_MUTEX_SIMPLE, /* == pthread_mutex */
+ E_MUTEX_REC, /* recursive mutex */
+} e_mutex_t;
+
+EMutex *e_mutex_new(e_mutex_t type);
+int e_mutex_destroy(EMutex *m);
+int e_mutex_lock(EMutex *m);
+int e_mutex_unlock(EMutex *m);
+void e_mutex_assert_locked(EMutex *m);
+/* this uses pthread cond's */
+int e_mutex_cond_wait(void *cond, EMutex *m);
+
+#endif
diff --git a/libedataserver/e-sexp.c b/libedataserver/e-sexp.c
new file mode 100644
index 000000000..a7619c59f
--- /dev/null
+++ b/libedataserver/e-sexp.c
@@ -0,0 +1,1379 @@
+/*
+ * Copyright 2000 Ximian (www.ximian.com).
+ *
+ * A simple, extensible s-exp evaluation engine.
+ *
+ * Author :
+ * Michael Zucchi <notzed@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
+ */
+
+/*
+ The following built-in s-exp's are supported:
+
+ list = (and list*)
+ perform an intersection of a number of lists, and return that.
+
+ bool = (and bool*)
+ perform a boolean AND of boolean values.
+
+ list = (or list*)
+ perform a union of a number of lists, returning the new list.
+
+ bool = (or bool*)
+ perform a boolean OR of boolean values.
+
+ int = (+ int*)
+ Add integers.
+
+ string = (+ string*)
+ Concat strings.
+
+ time_t = (+ time_t*)
+ Add time_t values.
+
+ int = (- int int*)
+ Subtract integers from the first.
+
+ time_t = (- time_t*)
+ Subtract time_t values from the first.
+
+ int = (cast-int string|int|bool)
+ Cast to an integer value.
+
+ string = (cast-string string|int|bool)
+ Cast to an string value.
+
+ Comparison operators:
+
+ bool = (< int int)
+ bool = (> int int)
+ bool = (= int int)
+
+ bool = (< string string)
+ bool = (> string string)
+ bool = (= string string)
+
+ bool = (< time_t time_t)
+ bool = (> time_t time_t)
+ bool = (= time_t time_t)
+ Perform a comparision of 2 integers, 2 string values, or 2 time values.
+
+ Function flow:
+
+ type = (if bool function)
+ type = (if bool function function)
+ Choose a flow path based on a boolean value
+
+ type = (begin func func func)
+ Execute a sequence. The last function return is the return type.
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+
+#include "e-sexp.h"
+#include "e-memory.h"
+
+#define p(x) /* parse debug */
+#define r(x) /* run debug */
+#define d(x) /* general debug */
+
+
+static struct _ESExpTerm * parse_list(ESExp *f, int gotbrace);
+static struct _ESExpTerm * parse_value(ESExp *f);
+
+static void parse_dump_term(struct _ESExpTerm *t, int depth);
+
+#ifdef E_SEXP_IS_G_OBJECT
+static GObjectClass *parent_class;
+#endif
+
+static GScannerConfig scanner_config =
+{
+ ( " \t\r\n") /* cset_skip_characters */,
+ ( G_CSET_a_2_z
+ "_+-<=>?"
+ G_CSET_A_2_Z) /* cset_identifier_first */,
+ ( G_CSET_a_2_z
+ "_0123456789-<>?"
+ G_CSET_A_2_Z
+ G_CSET_LATINS
+ G_CSET_LATINC ) /* cset_identifier_nth */,
+ ( ";\n" ) /* cpair_comment_single */,
+
+ FALSE /* case_sensitive */,
+
+ TRUE /* skip_comment_multi */,
+ TRUE /* skip_comment_single */,
+ TRUE /* scan_comment_multi */,
+ TRUE /* scan_identifier */,
+ TRUE /* scan_identifier_1char */,
+ FALSE /* scan_identifier_NULL */,
+ TRUE /* scan_symbols */,
+ FALSE /* scan_binary */,
+ TRUE /* scan_octal */,
+ TRUE /* scan_float */,
+ TRUE /* scan_hex */,
+ FALSE /* scan_hex_dollar */,
+ TRUE /* scan_string_sq */,
+ TRUE /* scan_string_dq */,
+ TRUE /* numbers_2_int */,
+ FALSE /* int_2_float */,
+ FALSE /* identifier_2_string */,
+ TRUE /* char_2_token */,
+ FALSE /* symbol_2_token */,
+ FALSE /* scope_0_fallback */,
+};
+
+/* jumps back to the caller of f->failenv, only to be called from inside a callback */
+void
+e_sexp_fatal_error(struct _ESExp *f, char *why, ...)
+{
+ va_list args;
+
+ if (f->error)
+ g_free(f->error);
+
+ va_start(args, why);
+ f->error = g_strdup_vprintf(why, args);
+ va_end(args);
+
+ longjmp(f->failenv, 1);
+}
+
+const char *
+e_sexp_error(struct _ESExp *f)
+{
+ return f->error;
+}
+
+struct _ESExpResult *
+e_sexp_result_new(struct _ESExp *f, int type)
+{
+ struct _ESExpResult *r = e_memchunk_alloc0(f->result_chunks);
+ r->type = type;
+ return r;
+}
+
+void
+e_sexp_result_free(struct _ESExp *f, struct _ESExpResult *t)
+{
+ if (t == NULL)
+ return;
+
+ switch(t->type) {
+ case ESEXP_RES_ARRAY_PTR:
+ g_ptr_array_free(t->value.ptrarray, TRUE);
+ break;
+ case ESEXP_RES_BOOL:
+ case ESEXP_RES_INT:
+ case ESEXP_RES_TIME:
+ break;
+ case ESEXP_RES_STRING:
+ g_free(t->value.string);
+ break;
+ case ESEXP_RES_UNDEFINED:
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ e_memchunk_free(f->result_chunks, t);
+}
+
+/* used in normal functions if they have to abort, and free their arguments */
+void
+e_sexp_resultv_free(struct _ESExp *f, int argc, struct _ESExpResult **argv)
+{
+ int i;
+
+ for (i=0;i<argc;i++) {
+ e_sexp_result_free(f, argv[i]);
+ }
+}
+
+/* implementations for the builtin functions */
+
+/* can you tell, i dont like glib? */
+/* we can only itereate a hashtable from a called function */
+struct _glib_sux_donkeys {
+ int count;
+ GPtrArray *uids;
+};
+
+/* ok, store any values that are in all sets */
+static void
+g_lib_sux_htand(char *key, int value, struct _glib_sux_donkeys *fuckup)
+{
+ if (value == fuckup->count) {
+ g_ptr_array_add(fuckup->uids, key);
+ }
+}
+
+/* or, store all unique values */
+static void
+g_lib_sux_htor(char *key, int value, struct _glib_sux_donkeys *fuckup)
+{
+ g_ptr_array_add(fuckup->uids, key);
+}
+
+static ESExpResult *
+term_eval_and(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data)
+{
+ struct _ESExpResult *r, *r1;
+ GHashTable *ht = g_hash_table_new(g_str_hash, g_str_equal);
+ struct _glib_sux_donkeys lambdafoo;
+ int type=-1;
+ int bool = TRUE;
+ int i;
+
+ r(printf("( and\n"));
+
+ r = e_sexp_result_new(f, ESEXP_RES_UNDEFINED);
+
+ for (i=0;bool && i<argc;i++) {
+ r1 = e_sexp_term_eval(f, argv[i]);
+ if (type == -1)
+ type = r1->type;
+ if (type != r1->type) {
+ e_sexp_result_free(f, r);
+ e_sexp_result_free(f, r1);
+ g_hash_table_destroy(ht);
+ e_sexp_fatal_error(f, "Invalid types in AND");
+ } else if (r1->type == ESEXP_RES_ARRAY_PTR) {
+ char **a1;
+ int l1, j;
+
+ a1 = (char **)r1->value.ptrarray->pdata;
+ l1 = r1->value.ptrarray->len;
+ for (j=0;j<l1;j++) {
+ gpointer ptr;
+ int n;
+ ptr = g_hash_table_lookup(ht, a1[j]);
+ n = GPOINTER_TO_INT(ptr);
+ g_hash_table_insert(ht, a1[j], GINT_TO_POINTER(n+1));
+ }
+ } else if (r1->type == ESEXP_RES_BOOL) {
+ bool = bool && r1->value.bool;
+ }
+ e_sexp_result_free(f, r1);
+ }
+
+ if (type == ESEXP_RES_ARRAY_PTR) {
+ lambdafoo.count = argc;
+ lambdafoo.uids = g_ptr_array_new();
+ g_hash_table_foreach(ht, (GHFunc)g_lib_sux_htand, &lambdafoo);
+ r->type = ESEXP_RES_ARRAY_PTR;
+ r->value.ptrarray = lambdafoo.uids;
+ } else if (type == ESEXP_RES_BOOL) {
+ r->type = ESEXP_RES_BOOL;
+ r->value.bool = bool;
+ }
+
+ g_hash_table_destroy(ht);
+
+ return r;
+}
+
+static ESExpResult *
+term_eval_or(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data)
+{
+ struct _ESExpResult *r, *r1;
+ GHashTable *ht = g_hash_table_new(g_str_hash, g_str_equal);
+ struct _glib_sux_donkeys lambdafoo;
+ int type = -1;
+ int bool = FALSE;
+ int i;
+
+ r(printf("(or \n"));
+
+ r = e_sexp_result_new(f, ESEXP_RES_UNDEFINED);
+
+ for (i=0;!bool && i<argc;i++) {
+ r1 = e_sexp_term_eval(f, argv[i]);
+ if (type == -1)
+ type = r1->type;
+ if (r1->type != type) {
+ e_sexp_result_free(f, r);
+ e_sexp_result_free(f, r1);
+ g_hash_table_destroy(ht);
+ e_sexp_fatal_error(f, "Invalid types in OR");
+ } else if (r1->type == ESEXP_RES_ARRAY_PTR) {
+ char **a1;
+ int l1, j;
+
+ a1 = (char **)r1->value.ptrarray->pdata;
+ l1 = r1->value.ptrarray->len;
+ for (j=0;j<l1;j++) {
+ g_hash_table_insert(ht, a1[j], (void *)1);
+ }
+ } else if (r1->type == ESEXP_RES_BOOL) {
+ bool |= r1->value.bool;
+ }
+ e_sexp_result_free(f, r1);
+ }
+
+ if (type == ESEXP_RES_ARRAY_PTR) {
+ lambdafoo.count = argc;
+ lambdafoo.uids = g_ptr_array_new();
+ g_hash_table_foreach(ht, (GHFunc)g_lib_sux_htor, &lambdafoo);
+ r->type = ESEXP_RES_ARRAY_PTR;
+ r->value.ptrarray = lambdafoo.uids;
+ } else if (type == ESEXP_RES_BOOL) {
+ r->type = ESEXP_RES_BOOL;
+ r->value.bool = bool;
+ }
+ g_hash_table_destroy(ht);
+
+ return r;
+}
+
+static ESExpResult *
+term_eval_not(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+ int res = TRUE;
+ ESExpResult *r;
+
+ if (argc>0) {
+ if (argv[0]->type == ESEXP_RES_BOOL
+ && argv[0]->value.bool)
+ res = FALSE;
+ }
+ r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+ r->value.bool = res;
+ return r;
+}
+
+/* this should support all arguments ...? */
+static ESExpResult *
+term_eval_lt(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data)
+{
+ struct _ESExpResult *r, *r1, *r2;
+
+ r = e_sexp_result_new(f, ESEXP_RES_UNDEFINED);
+
+ if (argc == 2) {
+ r1 = e_sexp_term_eval(f, argv[0]);
+ r2 = e_sexp_term_eval(f, argv[1]);
+ if (r1->type != r2->type) {
+ e_sexp_result_free(f, r1);
+ e_sexp_result_free(f, r2);
+ e_sexp_result_free(f, r);
+ e_sexp_fatal_error(f, "Incompatible types in compare <");
+ } else if (r1->type == ESEXP_RES_INT) {
+ r->type = ESEXP_RES_BOOL;
+ r->value.bool = r1->value.number < r2->value.number;
+ } else if (r1->type == ESEXP_RES_TIME) {
+ r->type = ESEXP_RES_BOOL;
+ r->value.bool = r1->value.time < r2->value.time;
+ } else if (r1->type == ESEXP_RES_STRING) {
+ r->type = ESEXP_RES_BOOL;
+ r->value.bool = strcmp(r1->value.string, r2->value.string) < 0;
+ }
+ e_sexp_result_free(f, r1);
+ e_sexp_result_free(f, r2);
+ }
+ return r;
+}
+
+/* this should support all arguments ...? */
+static ESExpResult *
+term_eval_gt(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data)
+{
+ struct _ESExpResult *r, *r1, *r2;
+
+ r = e_sexp_result_new(f, ESEXP_RES_UNDEFINED);
+
+ if (argc == 2) {
+ r1 = e_sexp_term_eval(f, argv[0]);
+ r2 = e_sexp_term_eval(f, argv[1]);
+ if (r1->type != r2->type) {
+ e_sexp_result_free(f, r1);
+ e_sexp_result_free(f, r2);
+ e_sexp_result_free(f, r);
+ e_sexp_fatal_error(f, "Incompatible types in compare >");
+ } else if (r1->type == ESEXP_RES_INT) {
+ r->type = ESEXP_RES_BOOL;
+ r->value.bool = r1->value.number > r2->value.number;
+ } else if (r1->type == ESEXP_RES_TIME) {
+ r->type = ESEXP_RES_BOOL;
+ r->value.bool = r1->value.time > r2->value.time;
+ } else if (r1->type == ESEXP_RES_STRING) {
+ r->type = ESEXP_RES_BOOL;
+ r->value.bool = strcmp(r1->value.string, r2->value.string) > 0;
+ }
+ e_sexp_result_free(f, r1);
+ e_sexp_result_free(f, r2);
+ }
+ return r;
+}
+
+/* this should support all arguments ...? */
+static ESExpResult *
+term_eval_eq(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data)
+{
+ struct _ESExpResult *r, *r1, *r2;
+
+ r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+
+ if (argc == 2) {
+ r1 = e_sexp_term_eval(f, argv[0]);
+ r2 = e_sexp_term_eval(f, argv[1]);
+ if (r1->type != r2->type) {
+ r->value.bool = FALSE;
+ } else if (r1->type == ESEXP_RES_INT) {
+ r->value.bool = r1->value.number == r2->value.number;
+ } else if (r1->type == ESEXP_RES_BOOL) {
+ r->value.bool = r1->value.bool == r2->value.bool;
+ } else if (r1->type == ESEXP_RES_TIME) {
+ r->value.bool = r1->value.time == r2->value.time;
+ } else if (r1->type == ESEXP_RES_STRING) {
+ r->value.bool = strcmp(r1->value.string, r2->value.string) == 0;
+ }
+ e_sexp_result_free(f, r1);
+ e_sexp_result_free(f, r2);
+ }
+ return r;
+}
+
+static ESExpResult *
+term_eval_plus(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+ struct _ESExpResult *r=NULL;
+ int type;
+ int i;
+
+ if (argc>0) {
+ type = argv[0]->type;
+ switch(type) {
+ case ESEXP_RES_INT: {
+ int total = argv[0]->value.number;
+ for (i=1;i<argc && argv[i]->type == ESEXP_RES_INT;i++) {
+ total += argv[i]->value.number;
+ }
+ if (i<argc) {
+ e_sexp_resultv_free(f, argc, argv);
+ e_sexp_fatal_error(f, "Invalid types in (+ ints)");
+ }
+ r = e_sexp_result_new(f, ESEXP_RES_INT);
+ r->value.number = total;
+ break; }
+ case ESEXP_RES_STRING: {
+ GString *s = g_string_new(argv[0]->value.string);
+ for (i=1;i<argc && argv[i]->type == ESEXP_RES_STRING;i++) {
+ g_string_append(s, argv[i]->value.string);
+ }
+ if (i<argc) {
+ e_sexp_resultv_free(f, argc, argv);
+ e_sexp_fatal_error(f, "Invalid types in (+ strings)");
+ }
+ r = e_sexp_result_new(f, ESEXP_RES_STRING);
+ r->value.string = s->str;
+ g_string_free(s, FALSE);
+ break; }
+ case ESEXP_RES_TIME: {
+ time_t total;
+
+ total = argv[0]->value.time;
+
+ for (i = 1; i < argc && argv[i]->type == ESEXP_RES_TIME; i++)
+ total += argv[i]->value.time;
+
+ if (i < argc) {
+ e_sexp_resultv_free (f, argc, argv);
+ e_sexp_fatal_error (f, "Invalid types in (+ time_t)");
+ }
+
+ r = e_sexp_result_new (f, ESEXP_RES_TIME);
+ r->value.time = total;
+ break; }
+ }
+ }
+
+ if (!r) {
+ r = e_sexp_result_new(f, ESEXP_RES_INT);
+ r->value.number = 0;
+ }
+ return r;
+}
+
+static ESExpResult *
+term_eval_sub(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+ struct _ESExpResult *r=NULL;
+ int type;
+ int i;
+
+ if (argc>0) {
+ type = argv[0]->type;
+ switch(type) {
+ case ESEXP_RES_INT: {
+ int total = argv[0]->value.number;
+ for (i=1;i<argc && argv[i]->type == ESEXP_RES_INT;i++) {
+ total -= argv[i]->value.number;
+ }
+ if (i<argc) {
+ e_sexp_resultv_free(f, argc, argv);
+ e_sexp_fatal_error(f, "Invalid types in -");
+ }
+ r = e_sexp_result_new(f, ESEXP_RES_INT);
+ r->value.number = total;
+ break; }
+ case ESEXP_RES_TIME: {
+ time_t total;
+
+ total = argv[0]->value.time;
+
+ for (i = 1; i < argc && argv[i]->type == ESEXP_RES_TIME; i++)
+ total -= argv[i]->value.time;
+
+ if (i < argc) {
+ e_sexp_resultv_free (f, argc, argv);
+ e_sexp_fatal_error (f, "Invalid types in (- time_t)");
+ }
+
+ r = e_sexp_result_new (f, ESEXP_RES_TIME);
+ r->value.time = total;
+ break; }
+ }
+ }
+
+ if (!r) {
+ r = e_sexp_result_new(f, ESEXP_RES_INT);
+ r->value.number = 0;
+ }
+ return r;
+}
+
+/* cast to int */
+static ESExpResult *
+term_eval_castint(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+ struct _ESExpResult *r;
+
+ if (argc != 1)
+ e_sexp_fatal_error(f, "Incorrect argument count to (int )");
+
+ r = e_sexp_result_new(f, ESEXP_RES_INT);
+ switch (argv[0]->type) {
+ case ESEXP_RES_INT:
+ r->value.number = argv[0]->value.number;
+ break;
+ case ESEXP_RES_BOOL:
+ r->value.number = argv[0]->value.bool != 0;
+ break;
+ case ESEXP_RES_STRING:
+ r->value.number = strtoul(argv[0]->value.string, 0, 10);
+ break;
+ default:
+ e_sexp_result_free(f, r);
+ e_sexp_fatal_error(f, "Invalid type in (cast-int )");
+ }
+
+ return r;
+}
+
+/* cast to string */
+static ESExpResult *
+term_eval_caststring(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
+{
+ struct _ESExpResult *r;
+
+ if (argc != 1)
+ e_sexp_fatal_error(f, "Incorrect argument count to (cast-string )");
+
+ r = e_sexp_result_new(f, ESEXP_RES_STRING);
+ switch (argv[0]->type) {
+ case ESEXP_RES_INT:
+ r->value.string = g_strdup_printf("%d", argv[0]->value.number);
+ break;
+ case ESEXP_RES_BOOL:
+ r->value.string = g_strdup_printf("%d", argv[0]->value.bool != 0);
+ break;
+ case ESEXP_RES_STRING:
+ r->value.string = g_strdup(argv[0]->value.string);
+ break;
+ default:
+ e_sexp_result_free(f, r);
+ e_sexp_fatal_error(f, "Invalid type in (int )");
+ }
+
+ return r;
+}
+
+/* implements 'if' function */
+static ESExpResult *
+term_eval_if(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data)
+{
+ struct _ESExpResult *r;
+ int doit;
+
+ if (argc >=2 && argc<=3) {
+ r = e_sexp_term_eval(f, argv[0]);
+ doit = (r->type == ESEXP_RES_BOOL && r->value.bool);
+ e_sexp_result_free(f, r);
+ if (doit) {
+ return e_sexp_term_eval(f, argv[1]);
+ } else if (argc>2) {
+ return e_sexp_term_eval(f, argv[2]);
+ }
+ }
+ return e_sexp_result_new(f, ESEXP_RES_UNDEFINED);
+}
+
+/* implements 'begin' statement */
+static ESExpResult *
+term_eval_begin(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data)
+{
+ struct _ESExpResult *r=NULL;
+ int i;
+
+ for (i=0;i<argc;i++) {
+ if (r)
+ e_sexp_result_free(f, r);
+ r = e_sexp_term_eval(f, argv[i]);
+ }
+ if (r)
+ return r;
+ else
+ return e_sexp_result_new(f, ESEXP_RES_UNDEFINED);
+}
+
+
+/* this must only be called from inside term evaluation callbacks! */
+struct _ESExpResult *
+e_sexp_term_eval(struct _ESExp *f, struct _ESExpTerm *t)
+{
+ struct _ESExpResult *r = NULL;
+ int i;
+ struct _ESExpResult **argv;
+
+ g_return_val_if_fail(t != NULL, NULL);
+
+ r(printf("eval term :\n"));
+ r(parse_dump_term(t, 0));
+
+ switch (t->type) {
+ case ESEXP_TERM_STRING:
+ r(printf(" (string \"%s\")\n", t->value.string));
+ r = e_sexp_result_new(f, ESEXP_RES_STRING);
+ /* erk, this shoul;dn't need to strdup this ... */
+ r->value.string = g_strdup(t->value.string);
+ break;
+ case ESEXP_TERM_INT:
+ r(printf(" (int %d)\n", t->value.number));
+ r = e_sexp_result_new(f, ESEXP_RES_INT);
+ r->value.number = t->value.number;
+ break;
+ case ESEXP_TERM_BOOL:
+ r(printf(" (int %d)\n", t->value.number));
+ r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+ r->value.bool = t->value.bool;
+ break;
+ case ESEXP_TERM_TIME:
+ r(printf(" (time_t %d)\n", t->value.time));
+ r = e_sexp_result_new (f, ESEXP_RES_TIME);
+ r->value.time = t->value.time;
+ break;
+ case ESEXP_TERM_IFUNC:
+ if (t->value.func.sym->f.ifunc)
+ r = t->value.func.sym->f.ifunc(f, t->value.func.termcount, t->value.func.terms, t->value.func.sym->data);
+ break;
+ case ESEXP_TERM_FUNC:
+ /* first evaluate all arguments to result types */
+ argv = alloca(sizeof(argv[0]) * t->value.func.termcount);
+ for (i=0;i<t->value.func.termcount;i++) {
+ argv[i] = e_sexp_term_eval(f, t->value.func.terms[i]);
+ }
+ /* call the function */
+ if (t->value.func.sym->f.func)
+ r = t->value.func.sym->f.func(f, t->value.func.termcount, argv, t->value.func.sym->data);
+
+ e_sexp_resultv_free(f, t->value.func.termcount, argv);
+ break;
+ default:
+ e_sexp_fatal_error(f, "Unknown type in parse tree: %d", t->type);
+ }
+
+ if (r==NULL)
+ r = e_sexp_result_new(f, ESEXP_RES_UNDEFINED);
+
+ return r;
+}
+
+#ifdef TESTER
+static void
+eval_dump_result(ESExpResult *r, int depth)
+{
+ int i;
+
+ if (r==NULL) {
+ printf("null result???\n");
+ return;
+ }
+
+ for (i=0;i<depth;i++)
+ printf(" ");
+
+ switch (r->type) {
+ case ESEXP_RES_ARRAY_PTR:
+ printf("array pointers\n");
+ break;
+ case ESEXP_RES_INT:
+ printf("int: %d\n", r->value.number);
+ break;
+ case ESEXP_RES_STRING:
+ printf("string: '%s'\n", r->value.string);
+ break;
+ case ESEXP_RES_BOOL:
+ printf("bool: %c\n", r->value.bool?'t':'f');
+ break;
+ case ESEXP_RES_TIME:
+ printf("time_t: %ld\n", (long) r->value.time);
+ break;
+ case ESEXP_RES_UNDEFINED:
+ printf(" <undefined>\n");
+ break;
+ }
+ printf("\n");
+}
+#endif
+
+static void
+parse_dump_term(struct _ESExpTerm *t, int depth)
+{
+ int i;
+
+ if (t==NULL) {
+ printf("null term??\n");
+ return;
+ }
+
+ for (i=0;i<depth;i++)
+ printf(" ");
+
+ switch (t->type) {
+ case ESEXP_TERM_STRING:
+ printf(" \"%s\"", t->value.string);
+ break;
+ case ESEXP_TERM_INT:
+ printf(" %d", t->value.number);
+ break;
+ case ESEXP_TERM_BOOL:
+ printf(" #%c", t->value.bool?'t':'f');
+ break;
+ case ESEXP_TERM_TIME:
+ printf(" %ld", (long) t->value.time);
+ break;
+ case ESEXP_TERM_IFUNC:
+ case ESEXP_TERM_FUNC:
+ printf(" (function %s\n", t->value.func.sym->name);
+ /*printf(" [%d] ", t->value.func.termcount);*/
+ for (i=0;i<t->value.func.termcount;i++) {
+ parse_dump_term(t->value.func.terms[i], depth+1);
+ }
+ for (i=0;i<depth;i++)
+ printf(" ");
+ printf(" )");
+ break;
+ case ESEXP_TERM_VAR:
+ printf(" (variable %s )\n", t->value.var->name);
+ break;
+ default:
+ printf("unknown type: %d\n", t->type);
+ }
+
+ printf("\n");
+}
+
+/*
+ PARSER
+*/
+
+static struct _ESExpTerm *
+parse_term_new(struct _ESExp *f, int type)
+{
+ struct _ESExpTerm *s = e_memchunk_alloc0(f->term_chunks);
+ s->type = type;
+ return s;
+}
+
+static void
+parse_term_free(struct _ESExp *f, struct _ESExpTerm *t)
+{
+ int i;
+
+ if (t==NULL) {
+ return;
+ }
+
+ switch (t->type) {
+ case ESEXP_TERM_INT:
+ case ESEXP_TERM_BOOL:
+ case ESEXP_TERM_TIME:
+ case ESEXP_TERM_VAR:
+ break;
+
+ case ESEXP_TERM_STRING:
+ g_free(t->value.string);
+ break;
+
+ case ESEXP_TERM_FUNC:
+ case ESEXP_TERM_IFUNC:
+ for (i=0;i<t->value.func.termcount;i++) {
+ parse_term_free(f, t->value.func.terms[i]);
+ }
+ g_free(t->value.func.terms);
+ break;
+
+ default:
+ printf("parse_term_free: unknown type: %d\n", t->type);
+ }
+ e_memchunk_free(f->term_chunks, t);
+}
+
+static struct _ESExpTerm **
+parse_values(ESExp *f, int *len)
+{
+ int token;
+ struct _ESExpTerm **terms;
+ int i, size = 0;
+ GScanner *gs = f->scanner;
+ GSList *list = NULL, *l;
+
+ p(printf("parsing values\n"));
+
+ while ( (token = g_scanner_peek_next_token(gs)) != G_TOKEN_EOF
+ && token != ')') {
+ list = g_slist_prepend(list, parse_value(f));
+ size++;
+ }
+
+ /* go over the list, and put them backwards into the term array */
+ terms = g_malloc(size * sizeof(*terms));
+ l = list;
+ for (i=size-1;i>=0;i--) {
+ g_assert(l);
+ g_assert(l->data);
+ terms[i] = l->data;
+ l = g_slist_next(l);
+ }
+ g_slist_free(list);
+
+ p(printf("found %d subterms\n", size));
+ *len = size;
+
+ p(printf("done parsing values\n"));
+ return terms;
+}
+
+static struct _ESExpTerm *
+parse_value(ESExp *f)
+{
+ int token, negative = FALSE;
+ struct _ESExpTerm *t = NULL;
+ GScanner *gs = f->scanner;
+ struct _ESExpSymbol *s;
+
+ p(printf("parsing value\n"));
+
+ token = g_scanner_get_next_token(gs);
+ switch(token) {
+ case G_TOKEN_LEFT_PAREN:
+ p(printf("got brace, its a list!\n"));
+ return parse_list(f, TRUE);
+ case G_TOKEN_STRING:
+ p(printf("got string\n"));
+ t = parse_term_new(f, ESEXP_TERM_STRING);
+ t->value.string = g_strdup(g_scanner_cur_value(gs).v_string);
+ break;
+ case '-':
+ p(printf ("got negative int?\n"));
+ token = g_scanner_get_next_token (gs);
+ if (token != G_TOKEN_INT) {
+ e_sexp_fatal_error (f, "Invalid format for a integer value");
+ return NULL;
+ }
+
+ negative = TRUE;
+ /* fall through... */
+ case G_TOKEN_INT:
+ t = parse_term_new(f, ESEXP_TERM_INT);
+ t->value.number = g_scanner_cur_value(gs).v_int;
+ if (negative)
+ t->value.number = -t->value.number;
+ p(printf("got int\n"));
+ break;
+ case '#': {
+ char *str;
+
+ p(printf("got bool?\n"));
+ token = g_scanner_get_next_token(gs);
+ if (token != G_TOKEN_IDENTIFIER) {
+ e_sexp_fatal_error (f, "Invalid format for a boolean value");
+ return NULL;
+ }
+
+ str = g_scanner_cur_value (gs).v_identifier;
+
+ g_assert (str != NULL);
+ if (!(strlen (str) == 1 && (str[0] == 't' || str[0] == 'f'))) {
+ e_sexp_fatal_error (f, "Invalid format for a boolean value");
+ return NULL;
+ }
+
+ t = parse_term_new(f, ESEXP_TERM_BOOL);
+ t->value.bool = (str[0] == 't');
+ break; }
+ case G_TOKEN_SYMBOL:
+ s = g_scanner_cur_value(gs).v_symbol;
+ switch (s->type) {
+ case ESEXP_TERM_FUNC:
+ case ESEXP_TERM_IFUNC:
+ /* this is basically invalid, since we can't use function
+ pointers, but let the runtime catch it ... */
+ t = parse_term_new(f, s->type);
+ t->value.func.sym = s;
+ t->value.func.terms = parse_values(f, &t->value.func.termcount);
+ break;
+ case ESEXP_TERM_VAR:
+ t = parse_term_new(f, s->type);
+ t->value.var = s;
+ break;
+ default:
+ e_sexp_fatal_error(f, "Invalid symbol type: %s: %d", s->name, s->type);
+ }
+ break;
+ case G_TOKEN_IDENTIFIER:
+ e_sexp_fatal_error(f, "Unknown identifier: %s", g_scanner_cur_value(gs).v_identifier);
+ break;
+ default:
+ e_sexp_fatal_error(f, "Unexpected token encountered: %d", token);
+ }
+ p(printf("done parsing value\n"));
+ return t;
+}
+
+/* FIXME: this needs some robustification */
+static struct _ESExpTerm *
+parse_list(ESExp *f, int gotbrace)
+{
+ int token;
+ struct _ESExpTerm *t = NULL;
+ GScanner *gs = f->scanner;
+
+ p(printf("parsing list\n"));
+ if (gotbrace)
+ token = '(';
+ else
+ token = g_scanner_get_next_token(gs);
+ if (token =='(') {
+ token = g_scanner_get_next_token(gs);
+ switch(token) {
+ case G_TOKEN_SYMBOL: {
+ struct _ESExpSymbol *s;
+
+ s = g_scanner_cur_value(gs).v_symbol;
+ p(printf("got funciton: %s\n", s->name));
+ t = parse_term_new(f, s->type);
+ p(printf("created new list %p\n", t));
+ /* if we have a variable, find out its base type */
+ while (s->type == ESEXP_TERM_VAR) {
+ s = ((ESExpTerm *)(s->data))->value.var;
+ }
+ if (s->type == ESEXP_TERM_FUNC
+ || s->type == ESEXP_TERM_IFUNC) {
+ t->value.func.sym = s;
+ t->value.func.terms = parse_values(f, &t->value.func.termcount);
+ } else {
+ parse_term_free(f, t);
+ e_sexp_fatal_error(f, "Trying to call variable as function: %s", s->name);
+ }
+ break; }
+ case G_TOKEN_IDENTIFIER:
+ e_sexp_fatal_error(f, "Unknown identifier: %s", g_scanner_cur_value(gs).v_identifier);
+ break;
+ default:
+ e_sexp_fatal_error(f, "Unexpected token encountered: %d", token);
+ }
+ token = g_scanner_get_next_token(gs);
+ if (token != ')') {
+ e_sexp_fatal_error(f, "Missing ')'");
+ }
+ } else {
+ e_sexp_fatal_error(f, "Missing '('");
+ }
+
+ p(printf("returning list %p\n", t));
+ return t;
+}
+
+static void e_sexp_finalise(void *);
+
+#ifdef E_SEXP_IS_G_OBJECT
+static void
+e_sexp_class_init (ESExpClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = e_sexp_finalise;
+
+ parent_class = g_type_class_ref (g_object_get_type ());
+}
+#endif
+
+/* 'builtin' functions */
+static struct {
+ char *name;
+ ESExpFunc *func;
+ int type; /* set to 1 if a function can perform shortcut evaluation, or
+ doesn't execute everything, 0 otherwise */
+} symbols[] = {
+ { "and", (ESExpFunc *)term_eval_and, 1 },
+ { "or", (ESExpFunc *)term_eval_or, 1 },
+ { "not", (ESExpFunc *)term_eval_not, 0 },
+ { "<", (ESExpFunc *)term_eval_lt, 1 },
+ { ">", (ESExpFunc *)term_eval_gt, 1 },
+ { "=", (ESExpFunc *)term_eval_eq, 1 },
+ { "+", (ESExpFunc *)term_eval_plus, 0 },
+ { "-", (ESExpFunc *)term_eval_sub, 0 },
+ { "cast-int", (ESExpFunc *)term_eval_castint, 0 },
+ { "cast-string", (ESExpFunc *)term_eval_caststring, 0 },
+ { "if", (ESExpFunc *)term_eval_if, 1 },
+ { "begin", (ESExpFunc *)term_eval_begin, 1 },
+};
+
+static void
+free_symbol(void *key, void *value, void *data)
+{
+ struct _ESExpSymbol *s = value;
+
+ g_free(s->name);
+ g_free(s);
+}
+
+static void
+e_sexp_finalise(void *o)
+{
+ ESExp *s = (ESExp *)o;
+
+ if (s->tree) {
+ parse_term_free(s, s->tree);
+ s->tree = NULL;
+ }
+
+ e_memchunk_destroy(s->term_chunks);
+ e_memchunk_destroy(s->result_chunks);
+
+ g_scanner_scope_foreach_symbol(s->scanner, 0, free_symbol, 0);
+ g_scanner_destroy(s->scanner);
+
+#ifdef E_SEXP_IS_G_OBJECT
+ G_OBJECT_CLASS (parent_class)->finalize (o);
+#endif
+}
+
+static void
+e_sexp_init (ESExp *s)
+{
+ int i;
+
+ s->scanner = g_scanner_new(&scanner_config);
+ s->term_chunks = e_memchunk_new(16, sizeof(struct _ESExpTerm));
+ s->result_chunks = e_memchunk_new(16, sizeof(struct _ESExpResult));
+
+ /* load in builtin symbols? */
+ for(i=0;i<sizeof(symbols)/sizeof(symbols[0]);i++) {
+ if (symbols[i].type == 1) {
+ e_sexp_add_ifunction(s, 0, symbols[i].name, (ESExpIFunc *)symbols[i].func, &symbols[i]);
+ } else {
+ e_sexp_add_function(s, 0, symbols[i].name, symbols[i].func, &symbols[i]);
+ }
+ }
+
+#ifndef E_SEXP_IS_G_OBJECT
+ s->refcount = 1;
+#endif
+}
+
+#ifdef E_SEXP_IS_G_OBJECT
+GType
+e_sexp_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof (ESExpClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) e_sexp_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (ESExp),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) e_sexp_init,
+ };
+
+ type = g_type_register_static (G_TYPE_OBJECT, "ESExp", &info, 0);
+ }
+
+ return type;
+}
+#endif
+
+ESExp *
+e_sexp_new (void)
+{
+#ifdef E_SEXP_IS_G_OBJECT
+ ESExp *f = (ESexp *) g_object_new (E_TYPE_SEXP, NULL);
+#else
+ ESExp *f = g_malloc0 (sizeof (ESExp));
+ e_sexp_init (f);
+#endif
+
+ return f;
+}
+
+#ifndef E_SEXP_IS_G_OBJECT
+void
+e_sexp_ref (ESExp *f)
+{
+ f->refcount++;
+}
+
+void
+e_sexp_unref (ESExp *f)
+{
+ f->refcount--;
+ if (f->refcount == 0) {
+ e_sexp_finalise(f);
+ g_free(f);
+ }
+}
+#endif
+
+void
+e_sexp_add_function(ESExp *f, int scope, char *name, ESExpFunc *func, void *data)
+{
+ struct _ESExpSymbol *s;
+
+ g_return_if_fail (IS_E_SEXP (f));
+ g_return_if_fail (name != NULL);
+
+ e_sexp_remove_symbol (f, scope, name);
+
+ s = g_malloc0(sizeof(*s));
+ s->name = g_strdup(name);
+ s->f.func = func;
+ s->type = ESEXP_TERM_FUNC;
+ s->data = data;
+ g_scanner_scope_add_symbol(f->scanner, scope, s->name, s);
+}
+
+void
+e_sexp_add_ifunction(ESExp *f, int scope, char *name, ESExpIFunc *ifunc, void *data)
+{
+ struct _ESExpSymbol *s;
+
+ g_return_if_fail (IS_E_SEXP (f));
+ g_return_if_fail (name != NULL);
+
+ e_sexp_remove_symbol (f, scope, name);
+
+ s = g_malloc0(sizeof(*s));
+ s->name = g_strdup(name);
+ s->f.ifunc = ifunc;
+ s->type = ESEXP_TERM_IFUNC;
+ s->data = data;
+ g_scanner_scope_add_symbol(f->scanner, scope, s->name, s);
+}
+
+void
+e_sexp_add_variable(ESExp *f, int scope, char *name, ESExpTerm *value)
+{
+ struct _ESExpSymbol *s;
+
+ g_return_if_fail (IS_E_SEXP (f));
+ g_return_if_fail (name != NULL);
+
+ s = g_malloc0(sizeof(*s));
+ s->name = g_strdup(name);
+ s->type = ESEXP_TERM_VAR;
+ s->data = value;
+ g_scanner_scope_add_symbol(f->scanner, scope, s->name, s);
+}
+
+void
+e_sexp_remove_symbol(ESExp *f, int scope, char *name)
+{
+ int oldscope;
+ struct _ESExpSymbol *s;
+
+ g_return_if_fail (IS_E_SEXP (f));
+ g_return_if_fail (name != NULL);
+
+ oldscope = g_scanner_set_scope(f->scanner, scope);
+ s = g_scanner_lookup_symbol(f->scanner, name);
+ g_scanner_scope_remove_symbol(f->scanner, scope, name);
+ g_scanner_set_scope(f->scanner, oldscope);
+ if (s) {
+ g_free(s->name);
+ g_free(s);
+ }
+}
+
+int
+e_sexp_set_scope(ESExp *f, int scope)
+{
+ g_return_val_if_fail (IS_E_SEXP (f), 0);
+
+ return g_scanner_set_scope(f->scanner, scope);
+}
+
+void
+e_sexp_input_text(ESExp *f, const char *text, int len)
+{
+ g_return_if_fail (IS_E_SEXP (f));
+ g_return_if_fail (text != NULL);
+
+ g_scanner_input_text(f->scanner, text, len);
+}
+
+void
+e_sexp_input_file (ESExp *f, int fd)
+{
+ g_return_if_fail (IS_E_SEXP (f));
+
+ g_scanner_input_file(f->scanner, fd);
+}
+
+/* returns -1 on error */
+int
+e_sexp_parse(ESExp *f)
+{
+ g_return_val_if_fail (IS_E_SEXP (f), -1);
+
+ if (setjmp(f->failenv)) {
+ g_warning("Error in parsing: %s", f->error);
+ return -1;
+ }
+
+ if (f->tree)
+ parse_term_free(f, f->tree);
+
+ f->tree = parse_value (f);
+
+ return 0;
+}
+
+/* returns NULL on error */
+struct _ESExpResult *
+e_sexp_eval(ESExp *f)
+{
+ g_return_val_if_fail (IS_E_SEXP (f), NULL);
+ g_return_val_if_fail (f->tree != NULL, NULL);
+
+ if (setjmp(f->failenv)) {
+ g_warning("Error in execution: %s", f->error);
+ return NULL;
+ }
+
+ return e_sexp_term_eval(f, f->tree);
+}
+
+/**
+ * e_sexp_encode_bool:
+ * @s:
+ * @state:
+ *
+ * Encode a bool into an s-expression @s. Bools are
+ * encoded using #t #f syntax.
+ **/
+void
+e_sexp_encode_bool(GString *s, gboolean state)
+{
+ if (state)
+ g_string_append(s, " #t");
+ else
+ g_string_append(s, " #f");
+}
+
+/**
+ * e_sexp_encode_string:
+ * @s: Destination string.
+ * @string: String expression.
+ *
+ * Add a c string @string to the s-expression stored in
+ * the gstring @s. Quotes are added, and special characters
+ * are escaped appropriately.
+ **/
+void
+e_sexp_encode_string(GString *s, const char *string)
+{
+ char c;
+ const char *p;
+
+ if (string == NULL)
+ p = "";
+ else
+ p = string;
+ g_string_append(s, " \"");
+ while ( (c = *p++) ) {
+ if (c=='\\' || c=='\"' || c=='\'')
+ g_string_append_c(s, '\\');
+ g_string_append_c(s, c);
+ }
+ g_string_append(s, "\"");
+}
+
+#ifdef TESTER
+int main(int argc, char **argv)
+{
+ ESExp *f;
+ char *t = "(+ \"foo\" \"\\\"\" \"bar\" \"\\\\ blah \\x \")";
+ ESExpResult *r;
+
+ gtk_init(&argc, &argv);
+
+ f = e_sexp_new();
+
+ e_sexp_add_variable(f, 0, "test", NULL);
+
+ e_sexp_input_text(f, t, strlen(t));
+ e_sexp_parse(f);
+
+ if (f->tree) {
+ parse_dump_term(f->tree, 0);
+ }
+
+ r = e_sexp_eval(f);
+ if (r) {
+ eval_dump_result(r, 0);
+ } else {
+ printf("no result?|\n");
+ }
+
+ return 0;
+}
+#endif
diff --git a/libedataserver/e-sexp.h b/libedataserver/e-sexp.h
new file mode 100644
index 000000000..5f41c97dc
--- /dev/null
+++ b/libedataserver/e-sexp.h
@@ -0,0 +1,172 @@
+/*
+ generic s-exp evaluator class
+*/
+#ifndef _E_SEXP_H
+#define _E_SEXP_H
+
+#include <setjmp.h>
+#include <time.h>
+#include <glib.h>
+
+#ifdef E_SEXP_IS_G_OBJECT
+#include <glib-object.h>
+#endif
+
+#ifdef E_SEXP_IS_G_OBJECT
+#define E_TYPE_SEXP (e_sexp_get_type ())
+#define E_SEXP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_SEXP, ESExp))
+#define E_SEXP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_SEXP, ESExpClass))
+#define IS_E_SEXP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_SEXP))
+#define IS_E_SEXP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_SEXP))
+#define E_SEXP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_SEXP, ESExpClass))
+#else
+#define E_TYPE_SEXP (0)
+#define E_SEXP(obj) ((struct _ESExp *) (obj))
+#define E_SEXP_CLASS(klass) ((struct _ESExpClass *) (klass))
+#define IS_E_SEXP(obj) (1)
+#define IS_E_SEXP_CLASS(obj) (1)
+#define E_SEXP_GET_CLASS(obj) (NULL)
+#endif
+
+typedef struct _ESExp ESExp;
+typedef struct _ESExpClass ESExpClass;
+
+typedef struct _ESExpSymbol ESExpSymbol;
+typedef struct _ESExpResult ESExpResult;
+typedef struct _ESExpTerm ESExpTerm;
+
+typedef struct _ESExpResult *(ESExpFunc)(struct _ESExp *sexp, int argc,
+ struct _ESExpResult **argv,
+ void *data);
+
+typedef struct _ESExpResult *(ESExpIFunc)(struct _ESExp *sexp, int argc,
+ struct _ESExpTerm **argv,
+ void *data);
+
+enum _ESExpResultType {
+ ESEXP_RES_ARRAY_PTR=0, /* type is a ptrarray, what it points to is implementation dependant */
+ ESEXP_RES_INT, /* type is a number */
+ ESEXP_RES_STRING, /* type is a pointer to a single string */
+ ESEXP_RES_BOOL, /* boolean type */
+ ESEXP_RES_TIME, /* time_t type */
+ ESEXP_RES_UNDEFINED /* unknown type */
+};
+
+struct _ESExpResult {
+ enum _ESExpResultType type;
+ union {
+ GPtrArray *ptrarray;
+ int number;
+ char *string;
+ int bool;
+ time_t time;
+ } value;
+};
+
+enum _ESExpTermType {
+ ESEXP_TERM_INT = 0, /* integer literal */
+ ESEXP_TERM_BOOL, /* boolean literal */
+ ESEXP_TERM_STRING, /* string literal */
+ ESEXP_TERM_TIME, /* time_t literal (number of seconds past the epoch) */
+ ESEXP_TERM_FUNC, /* normal function, arguments are evaluated before calling */
+ ESEXP_TERM_IFUNC, /* immediate function, raw terms are arguments */
+ ESEXP_TERM_VAR, /* variable reference */
+};
+
+struct _ESExpSymbol {
+ int type; /* ESEXP_TERM_FUNC or ESEXP_TERM_VAR */
+ char *name;
+ void *data;
+ union {
+ ESExpFunc *func;
+ ESExpIFunc *ifunc;
+ } f;
+};
+
+struct _ESExpTerm {
+ enum _ESExpTermType type;
+ union {
+ char *string;
+ int number;
+ int bool;
+ time_t time;
+ struct {
+ struct _ESExpSymbol *sym;
+ struct _ESExpTerm **terms;
+ int termcount;
+ } func;
+ struct _ESExpSymbol *var;
+ } value;
+};
+
+
+
+struct _ESExp {
+#ifdef E_SEXP_IS_G_OBJECT
+ GObject parent_object;
+#else
+ int refcount;
+#endif
+ GScanner *scanner; /* for parsing text version */
+ ESExpTerm *tree; /* root of expression tree */
+
+ /* private stuff */
+ jmp_buf failenv;
+ char *error;
+
+ /* TODO: may also need a pool allocator for term strings, so we dont lose them
+ in error conditions? */
+ struct _EMemChunk *term_chunks;
+ struct _EMemChunk *result_chunks;
+};
+
+struct _ESExpClass {
+#ifdef E_SEXP_IS_G_OBJECT
+ GObjectClass parent_class;
+#else
+ int dummy;
+#endif
+};
+
+#ifdef E_SEXP_IS_G_OBJECT
+GType e_sexp_get_type (void);
+#endif
+ESExp *e_sexp_new (void);
+#ifdef E_SEXP_IS_G_OBJECT
+#define e_sexp_ref(f) g_object_ref (f)
+#define e_sexp_unref(f) g_object_unref (f)
+#else
+void e_sexp_ref (ESExp *f);
+void e_sexp_unref (ESExp *f);
+#endif
+void e_sexp_add_function (ESExp *f, int scope, char *name, ESExpFunc *func, void *data);
+void e_sexp_add_ifunction (ESExp *f, int scope, char *name, ESExpIFunc *func, void *data);
+void e_sexp_add_variable (ESExp *f, int scope, char *name, ESExpTerm *value);
+void e_sexp_remove_symbol (ESExp *f, int scope, char *name);
+int e_sexp_set_scope (ESExp *f, int scope);
+
+void e_sexp_input_text (ESExp *f, const char *text, int len);
+void e_sexp_input_file (ESExp *f, int fd);
+
+
+int e_sexp_parse (ESExp *f);
+ESExpResult *e_sexp_eval (ESExp *f);
+
+ESExpResult *e_sexp_term_eval (struct _ESExp *f, struct _ESExpTerm *t);
+ESExpResult *e_sexp_result_new (struct _ESExp *f, int type);
+void e_sexp_result_free (struct _ESExp *f, struct _ESExpResult *t);
+
+/* used in normal functions if they have to abort, to free their arguments */
+void e_sexp_resultv_free (struct _ESExp *f, int argc, struct _ESExpResult **argv);
+
+/* utility functions for creating s-exp strings. */
+void e_sexp_encode_bool (GString *s, gboolean state);
+void e_sexp_encode_string (GString *s, const char *string);
+
+/* only to be called from inside a callback to signal a fatal execution error */
+void e_sexp_fatal_error (struct _ESExp *f, char *why, ...);
+
+/* return the error string */
+const char *e_sexp_error (struct _ESExp *f);
+
+#endif /* _E_SEXP_H */
diff --git a/libedataserver/e-uid.c b/libedataserver/e-uid.c
new file mode 100644
index 000000000..90c036e0c
--- /dev/null
+++ b/libedataserver/e-uid.c
@@ -0,0 +1,61 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-uid.c - Unique ID generator.
+ *
+ * Copyright (C) 2002 Ximian, Inc.
+ *
+ * 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.
+ *
+ * Author: Dan Winship <danw@ximian.com>
+ */
+
+#include "e-uid.h"
+
+#include <glib/gstrfuncs.h>
+
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+
+/**
+ * e_uid_new:
+ *
+ * Generate a new unique string for use e.g. in account lists.
+ *
+ * Return value: the newly generated UID. The caller should free the string
+ * when it's done with it.
+ **/
+char *
+e_uid_new (void)
+{
+ static char *hostname;
+ static int serial;
+
+ if (!hostname) {
+ static char buffer [512];
+
+ if ((gethostname (buffer, sizeof (buffer) - 1) == 0) &&
+ (buffer [0] != 0))
+ hostname = buffer;
+ else
+ hostname = "localhost";
+ }
+
+ return g_strdup_printf ("%lu.%lu.%d@%s",
+ (unsigned long) time (NULL),
+ (unsigned long) getpid (),
+ serial++,
+ hostname);
+}
diff --git a/libedataserver/e-uid.h b/libedataserver/e-uid.h
new file mode 100644
index 000000000..44ec8c0dd
--- /dev/null
+++ b/libedataserver/e-uid.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-uid.h - Unique ID generator.
+ *
+ * Copyright (C) 2002 Ximian, Inc.
+ *
+ * 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.
+ *
+ * Author: Dan Winship <danw@ximian.com>
+ */
+
+#ifndef E_UID_H
+#define E_UID_H
+
+char *e_uid_new (void);
+
+#endif /* E_UID_H */
diff --git a/libedataserver/e-url.c b/libedataserver/e-url.c
new file mode 100644
index 000000000..8dba46b62
--- /dev/null
+++ b/libedataserver/e-url.c
@@ -0,0 +1,341 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * e-url.c
+ *
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Developed by Jon Trowbridge <trow@ximian.com>
+ * Rodrigo Moya <rodrigo@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.
+ */
+
+#include <config.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "e-url.h"
+
+char *
+e_url_shroud (const char *url)
+{
+ const char *first_colon = NULL;
+ const char *last_at = NULL;
+ const char *p;
+ char *shrouded;
+
+ if (url == NULL)
+ return NULL;
+
+ /* Skip past the moniker */
+ for (p = url; *p && *p != ':'; ++p);
+ if (*p)
+ ++p;
+
+ while (*p) {
+ if (first_colon == NULL && *p == ':')
+ first_colon = p;
+ if (*p == '@')
+ last_at = p;
+ ++p;
+ }
+
+ if (first_colon && last_at && first_colon < last_at) {
+ shrouded = g_strdup_printf ("%.*s%s", first_colon - url, url, last_at);
+ } else {
+ shrouded = g_strdup (url);
+ }
+
+ return shrouded;
+}
+
+gboolean
+e_url_equal (const char *url1, const char *url2)
+{
+ char *shroud1 = e_url_shroud (url1);
+ char *shroud2 = e_url_shroud (url2);
+ gint len1, len2;
+ gboolean rv;
+
+ if (shroud1 == NULL || shroud2 == NULL) {
+ rv = (shroud1 == shroud2);
+ } else {
+ len1 = strlen (shroud1);
+ len2 = strlen (shroud2);
+
+ rv = !strncmp (shroud1, shroud2, MIN (len1, len2));
+ }
+
+ g_free (shroud1);
+ g_free (shroud2);
+
+ return rv;
+}
+
+#define HEXVAL(c) (isdigit (c) ? (c) - '0' : tolower (c) - 'a' + 10)
+
+static void
+uri_decode (char *part)
+{
+ guchar *s, *d;
+
+ s = d = (guchar *)part;
+ while (*s) {
+ if (*s == '%') {
+ if (isxdigit (s[1]) && isxdigit (s[2])) {
+ *d++ = HEXVAL (s[1]) * 16 + HEXVAL (s[2]);
+ s += 3;
+ } else
+ *d++ = *s++;
+ } else
+ *d++ = *s++;
+ }
+ *d = '\0';
+}
+
+EUri *
+e_uri_new (const char *uri_string)
+{
+ EUri *uri;
+ const char *end, *hash, *colon, *semi, *at, *slash, *question;
+ const char *p;
+
+ if (!uri_string)
+ return NULL;
+
+ uri = g_new0 (EUri, 1);
+
+ /* find fragment */
+ end = hash = strchr (uri_string, '#');
+ if (hash && hash[1]) {
+ uri->fragment = g_strdup (hash + 1);
+ uri_decode (uri->fragment);
+ }
+ else
+ end = uri_string + strlen (uri_string);
+
+ /* find protocol: initial [a-z+.-]* substring until ":" */
+ p = uri_string;
+ while (p < end && (isalnum ((unsigned char) *p) ||
+ *p == '.' || *p == '+' || *p == '-'))
+ p++;
+
+ if (p > uri_string && *p == ':') {
+ uri->protocol = g_ascii_strdown (uri_string, p - uri_string);
+ uri_string = p + 1;
+ }
+ else
+ uri->protocol = g_strdup ("file");
+
+ if (!*uri_string)
+ return uri;
+
+ /* check for authority */
+ if (strncmp (uri_string, "//", 2) == 0) {
+ uri_string += 2;
+
+ slash = uri_string + strcspn (uri_string, "/#");
+ at = strchr (uri_string, '@');
+ if (at && at < slash) {
+ colon = strchr (uri_string, ':');
+ if (colon && colon < at) {
+ uri->passwd = g_strndup (colon + 1, at - colon - 1);
+ uri_decode (uri->passwd);
+ }
+ else {
+ uri->passwd = NULL;
+ colon = at;
+ }
+
+ semi = strchr (uri_string, ';');
+ if (semi && semi < colon &&
+ !strncasecmp (semi, ";auth=", 6)) {
+ uri->authmech = g_strndup (semi + 6, colon - semi - 6);
+ uri_decode (uri->authmech);
+ }
+ else {
+ uri->authmech = NULL;
+ semi = colon;
+ }
+
+ uri->user = g_strndup (uri_string, semi - uri_string);
+ uri_decode (uri->user);
+ uri_string = at + 1;
+ }
+ else
+ uri->user = uri->passwd = uri->authmech = NULL;
+
+ /* find host and port */
+ colon = strchr (uri_string, ':');
+ if (colon && colon < slash) {
+ uri->host = g_strndup (uri_string, colon - uri_string);
+ uri->port = strtoul (colon + 1, NULL, 10);
+ }
+ else {
+ uri->host = g_strndup (uri_string, slash - uri_string);
+ uri_decode (uri->host);
+ uri->port = 0;
+ }
+
+ uri_string = slash;
+ }
+
+ /* find query */
+ question = memchr (uri_string, '?', end - uri_string);
+ if (question) {
+ if (question[1]) {
+ uri->query = g_strndup (question + 1, end - (question + 1));
+ uri_decode (uri->query);
+ }
+ end = question;
+ }
+
+ /* find parameters */
+ semi = memchr (uri_string, ';', end - uri_string);
+ if (semi) {
+ if (semi[1]) {
+ const char *cur, *p, *eq;
+ char *name, *value;
+
+ for (cur = semi + 1; cur < end; cur = p + 1) {
+ p = memchr (cur, ';', end - cur);
+ if (!p)
+ p = end;
+ eq = memchr (cur, '=', p - cur);
+ if (eq) {
+ name = g_strndup (cur, eq - cur);
+ value = g_strndup (eq + 1, p - (eq + 1));
+ uri_decode (value);
+ } else {
+ name = g_strndup (cur, p - cur);
+ value = g_strdup ("");
+ }
+ uri_decode (name);
+ g_datalist_set_data_full (&uri->params, name,
+ value, g_free);
+ g_free (name);
+ }
+ }
+ end = semi;
+ }
+
+ if (end != uri_string) {
+ uri->path = g_strndup (uri_string, end - uri_string);
+ uri_decode (uri->path);
+ }
+
+ return uri;
+}
+
+void
+e_uri_free (EUri *uri)
+{
+ if (uri) {
+ g_free (uri->protocol);
+ g_free (uri->user);
+ g_free (uri->authmech);
+ g_free (uri->passwd);
+ g_free (uri->host);
+ g_free (uri->path);
+ g_datalist_clear (&uri->params);
+ g_free (uri->query);
+ g_free (uri->fragment);
+
+ g_free (uri);
+ }
+}
+
+const char *
+e_uri_get_param (EUri *uri, const char *name)
+{
+ return g_datalist_get_data (&uri->params, name);
+}
+
+static void
+copy_param_cb (GQuark key_id, gpointer data, gpointer user_data)
+{
+ GData *params = (GData *) user_data;
+
+ g_datalist_id_set_data_full (&params, key_id, g_strdup (data), g_free);
+}
+
+EUri *
+e_uri_copy (EUri *uri)
+{
+ EUri *uri_copy;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ uri_copy = g_new0 (EUri, 1);
+ uri_copy->protocol = g_strdup (uri->protocol);
+ uri_copy->user = g_strdup (uri->user);
+ uri_copy->authmech = g_strdup (uri->authmech);
+ uri_copy->passwd = g_strdup (uri->passwd);
+ uri_copy->host = g_strdup (uri->host);
+ uri_copy->port = uri->port;
+ uri_copy->path = g_strdup (uri->path);
+ uri_copy->query = g_strdup (uri->query);
+ uri_copy->fragment = g_strdup (uri->fragment);
+
+ /* copy uri->params */
+ g_datalist_foreach (&uri->params,
+ (GDataForeachFunc) copy_param_cb,
+ &uri_copy->params);
+
+ return uri_copy;
+}
+
+char *
+e_uri_to_string (EUri *uri, gboolean show_password)
+{
+ char *str_uri = NULL;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ if (uri->port != 0)
+ str_uri = g_strdup_printf (
+ "%s://%s%s%s%s%s%s%s:%d%s%s%s",
+ uri->protocol,
+ uri->user ? uri->user : "",
+ uri->authmech ? ";auth=" : "",
+ uri->authmech ? uri->authmech : "",
+ uri->passwd && show_password ? ":" : "",
+ uri->passwd && show_password ? uri->passwd : "",
+ uri->user ? "@" : "",
+ uri->host ? uri->host : "",
+ uri->port,
+ uri->path ? uri->path : "",
+ uri->query ? "?" : "",
+ uri->query ? uri->query : "");
+ else
+ str_uri = g_strdup_printf(
+ "%s://%s%s%s%s%s%s%s%s%s%s",
+ uri->protocol,
+ uri->user ? uri->user : "",
+ uri->authmech ? ";auth=" : "",
+ uri->authmech ? uri->authmech : "",
+ uri->passwd && show_password ? ":" : "",
+ uri->passwd && show_password ? uri->passwd : "",
+ uri->user ? "@" : "",
+ uri->host ? uri->host : "",
+ uri->path ? uri->path : "",
+ uri->query ? "?" : "",
+ uri->query ? uri->query : "");
+
+ return str_uri;
+}
diff --git a/libedataserver/e-url.h b/libedataserver/e-url.h
new file mode 100644
index 000000000..205ad3cfc
--- /dev/null
+++ b/libedataserver/e-url.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * e-url.h
+ *
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Developed by Jon Trowbridge <trow@ximian.com>
+ * Rodrigo Moya <rodrigo@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_URL_H__
+#define __E_URL_H__
+
+#include <glib.h>
+
+char *e_url_shroud (const char *url);
+gboolean e_url_equal (const char *url1, const char *url2);
+
+typedef struct {
+ char *protocol;
+ char *user;
+ char *authmech;
+ char *passwd;
+ char *host;
+ int port;
+ char *path;
+ GData *params;
+ char *query;
+ char *fragment;
+} EUri;
+
+EUri *e_uri_new (const char *uri_string);
+void e_uri_free (EUri *uri);
+const char *e_uri_get_param (EUri *uri, const char *name);
+EUri *e_uri_copy (EUri *uri);
+char *e_uri_to_string (EUri *uri, gboolean show_password);
+
+#endif /* __E_URL_H__ */
+
diff --git a/libedataserver/e-xml-hash-utils.c b/libedataserver/e-xml-hash-utils.c
new file mode 100644
index 000000000..ba72321af
--- /dev/null
+++ b/libedataserver/e-xml-hash-utils.c
@@ -0,0 +1,252 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2003 Ximian, Inc.
+ *
+ * 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
+
+#include "e-xml-hash-utils.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/entities.h>
+
+GHashTable *
+e_xml_to_hash (xmlDoc *doc, EXmlHashType type)
+{
+ xmlNode *root, *node;
+ const char *key;
+ xmlChar *value;
+ GHashTable *hash;
+
+ hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ root = xmlDocGetRootElement (doc);
+ for (node = root->xmlChildrenNode; node; node = node->next) {
+ if (node->name == NULL || node->type != XML_ELEMENT_NODE)
+ continue;
+
+ if (type == E_XML_HASH_TYPE_OBJECT_UID &&
+ !strcmp (node->name, "object"))
+ key = xmlGetProp (node, "uid");
+ else
+ key = node->name;
+
+ value = xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
+ if (!key || !value) {
+ g_warning ("Found an entry with missing properties!!");
+ continue;
+ }
+
+ g_hash_table_insert (hash, g_strdup (key), g_strdup (value));
+ xmlFree (value);
+ }
+
+ return hash;
+}
+
+
+struct save_data {
+ EXmlHashType type;
+ xmlDoc *doc;
+ xmlNode *root;
+};
+
+static void
+foreach_save_func (gpointer key, gpointer value, gpointer user_data)
+{
+ struct save_data *sd = user_data;
+ xmlNodePtr new_node;
+ xmlChar *enc;
+
+ if (sd->type == E_XML_HASH_TYPE_OBJECT_UID) {
+ new_node = xmlNewNode (NULL, "object");
+ xmlNewProp (new_node, "uid", (const char *) key);
+ } else
+ new_node = xmlNewNode (NULL, (const char *) key);
+
+ enc = xmlEncodeSpecialChars (sd->doc, value);
+ xmlNodeSetContent (new_node, enc);
+ xmlFree (enc);
+
+ xmlAddChild (sd->root, new_node);
+}
+
+xmlDoc *
+e_xml_from_hash (GHashTable *hash, EXmlHashType type, const char *root_name)
+{
+ xmlDoc *doc;
+ struct save_data sd;
+
+ doc = xmlNewDoc ("1.0");
+ sd.type = type;
+ sd.doc = doc;
+ sd.root = xmlNewDocNode (doc, NULL, root_name, NULL);
+ xmlDocSetRootElement (doc, sd.root);
+
+ g_hash_table_foreach (hash, foreach_save_func, &sd);
+ return doc;
+}
+
+static void
+free_values (gpointer key, gpointer value, gpointer data)
+{
+ g_free (key);
+ g_free (value);
+}
+
+void
+e_xml_destroy_hash (GHashTable *hash)
+{
+ g_hash_table_foreach (hash, free_values, NULL);
+ g_hash_table_destroy (hash);
+}
+
+
+
+struct EXmlHash {
+ char *filename;
+ GHashTable *objects;
+};
+
+EXmlHash *
+e_xmlhash_new (const char *filename)
+{
+ EXmlHash *hash;
+ xmlDoc *doc = NULL;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ hash = g_new0 (EXmlHash, 1);
+ hash->filename = g_strdup (filename);
+
+ if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
+ doc = xmlParseFile (filename);
+ if (!doc) {
+ e_xmlhash_destroy (hash);
+
+ return NULL;
+ }
+ hash->objects = e_xml_to_hash (doc, E_XML_HASH_TYPE_OBJECT_UID);
+ xmlFreeDoc (doc);
+ } else {
+ hash->objects = g_hash_table_new (g_str_hash, g_str_equal);
+ }
+
+ return hash;
+}
+
+void
+e_xmlhash_add (EXmlHash *hash, const char *key, const char *data)
+{
+ g_return_if_fail (hash != NULL);
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (data != NULL);
+
+ e_xmlhash_remove (hash, key);
+ g_hash_table_insert (hash->objects, g_strdup (key), g_strdup (data));
+}
+
+void
+e_xmlhash_remove (EXmlHash *hash, const char *key)
+{
+ gpointer orig_key;
+ gpointer orig_value;
+
+ g_return_if_fail (hash != NULL);
+ g_return_if_fail (key != NULL);
+
+ if (g_hash_table_lookup_extended (hash->objects, key, &orig_key, &orig_value)) {
+ g_hash_table_remove (hash->objects, key);
+ g_free (orig_key);
+ g_free (orig_value);
+ }
+}
+
+EXmlHashStatus
+e_xmlhash_compare (EXmlHash *hash, const char *key, const char *compare_data)
+{
+ char *data;
+ int rc;
+
+ g_return_val_if_fail (hash != NULL, E_XMLHASH_STATUS_NOT_FOUND);
+ g_return_val_if_fail (key != NULL, E_XMLHASH_STATUS_NOT_FOUND);
+ g_return_val_if_fail (compare_data != NULL, E_XMLHASH_STATUS_NOT_FOUND);
+
+ data = g_hash_table_lookup (hash->objects, key);
+ if (!data)
+ return E_XMLHASH_STATUS_NOT_FOUND;
+
+ rc = strcmp (data, compare_data);
+ if (rc == 0)
+ return E_XMLHASH_STATUS_SAME;
+
+ return E_XMLHASH_STATUS_DIFFERENT;
+}
+
+typedef struct {
+ EXmlHashFunc func;
+ gpointer user_data;
+} foreach_data_t;
+
+static void
+foreach_hash_func (gpointer key, gpointer value, gpointer user_data)
+{
+ foreach_data_t *data = (foreach_data_t *) user_data;
+
+ data->func (key, data->user_data);
+}
+
+void
+e_xmlhash_foreach_key (EXmlHash *hash, EXmlHashFunc func, gpointer user_data)
+{
+ foreach_data_t data;
+
+ g_return_if_fail (hash != NULL);
+ g_return_if_fail (func != NULL);
+
+ data.func = func;
+ data.user_data = user_data;
+ g_hash_table_foreach (hash->objects, foreach_hash_func, &data);
+}
+
+void
+e_xmlhash_write (EXmlHash *hash)
+{
+ xmlDoc *doc;
+
+ g_return_if_fail (hash != NULL);
+
+ doc = e_xml_from_hash (hash->objects, E_XML_HASH_TYPE_OBJECT_UID, "xmlhash");
+ xmlSaveFile (hash->filename, doc);
+ xmlFreeDoc (doc);
+}
+
+void
+e_xmlhash_destroy (EXmlHash *hash)
+{
+ g_return_if_fail (hash != NULL);
+
+ g_free (hash->filename);
+ if (hash->objects)
+ e_xml_destroy_hash (hash->objects);
+
+ g_free (hash);
+}
diff --git a/libedataserver/e-xml-hash-utils.h b/libedataserver/e-xml-hash-utils.h
new file mode 100644
index 000000000..b972aa4b1
--- /dev/null
+++ b/libedataserver/e-xml-hash-utils.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2003 Ximian, Inc.
+ *
+ * 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_XML_HASH_UTILS_H__
+#define __E_XML_HASH_UTILS_H__
+
+#include <glib.h>
+#include <libxml/parser.h>
+
+typedef enum {
+ E_XML_HASH_TYPE_OBJECT_UID,
+ E_XML_HASH_TYPE_PROPERTY
+} EXmlHashType;
+
+GHashTable *e_xml_to_hash (xmlDoc *doc,
+ EXmlHashType type);
+xmlDoc *e_xml_from_hash (GHashTable *hash,
+ EXmlHashType type,
+ const char *root_node);
+
+void e_xml_destroy_hash (GHashTable *hash);
+
+
+
+typedef enum {
+ E_XMLHASH_STATUS_SAME,
+ E_XMLHASH_STATUS_DIFFERENT,
+ E_XMLHASH_STATUS_NOT_FOUND
+} EXmlHashStatus;
+
+typedef void (* EXmlHashFunc) (const char *key, gpointer user_data);
+
+typedef struct EXmlHash EXmlHash;
+
+EXmlHash *e_xmlhash_new (const char *filename);
+
+void e_xmlhash_add (EXmlHash *hash,
+ const char *key,
+ const char *data);
+void e_xmlhash_remove (EXmlHash *hash,
+ const char *key);
+
+EXmlHashStatus e_xmlhash_compare (EXmlHash *hash,
+ const char *key,
+ const char *compare_data);
+void e_xmlhash_foreach_key (EXmlHash *hash,
+ EXmlHashFunc func,
+ gpointer user_data);
+
+void e_xmlhash_write (EXmlHash *hash);
+void e_xmlhash_destroy (EXmlHash *hash);
+
+#endif
diff --git a/libedataserver/ename/Makefile.am b/libedataserver/ename/Makefile.am
new file mode 100644
index 000000000..384693160
--- /dev/null
+++ b/libedataserver/ename/Makefile.am
@@ -0,0 +1,45 @@
+INCLUDES = \
+ -DG_LOG_DOMAIN=\"EName\" \
+ -I$(srcdir) \
+ -I$(srcdir)/.. \
+ -I$(top_srcdir) \
+ -I. \
+ -I.. \
+ -I$(top_builddir) \
+ $(E_NAME_CFLAGS)
+
+lib_LTLIBRARIES = libename.la
+
+libename_la_SOURCES = \
+ e-address-western.c \
+ e-name-western.c
+
+libename_la_LDFLAGS = \
+ -version-info $(LIBEDATASERVER_CURRENT):$(LIBEDATASERVER_REVISION):$(LIBEDATASERVER_AGE) \
+ -no-undefined
+
+libenameincludedir = $(includedir)/libedataserver-1.0/libename
+
+libenameinclude_HEADERS = \
+ e-address-western.h \
+ e-name-western-tables.h \
+ e-name-western.h
+
+
+#noinst_PROGRAMS = \
+# test-ename-western \
+# test-ename-western-gtk
+
+#test_ename_western_SOURCES = \
+# test-ename-western.c
+
+#test_ename_western_LDADD = \
+# $(ename_libs)
+
+#test_ename_western_gtk_SOURCES = \
+# test-ename-western-gtk.c
+
+#test_ename_western_gtk_LDADD = \
+# $(ename_libs) \
+# $(E_UTIL_LIBS) \
+# $(top_builddir)/e-util/libeutil.la
diff --git a/libedataserver/ename/TODO b/libedataserver/ename/TODO
new file mode 100644
index 000000000..669661eea
--- /dev/null
+++ b/libedataserver/ename/TODO
@@ -0,0 +1,2 @@
+* Support other naming systems.
+* Handle misspelled suffixes better.
diff --git a/libedataserver/ename/e-address-western.c b/libedataserver/ename/e-address-western.c
new file mode 100644
index 000000000..9d325f138
--- /dev/null
+++ b/libedataserver/ename/e-address-western.c
@@ -0,0 +1,444 @@
+/* --------------------------------------------------
+
+ An address parser, yielding fields as per RFC 2426.
+
+ Author:
+ Jesse Pavel (jpavel@ximian.com)
+
+ Copyright 2000, Ximian, Inc.
+ --------------------------------------------------
+*/
+
+#include <ctype.h>
+#include <string.h>
+#include <glib.h>
+
+#ifdef E_ADDRESS_WESTERN_TEST
+
+#include "e-address-western.h"
+
+#else
+
+#include <ename/e-address-western.h>
+#include <gal/util/e-util.h>
+
+#endif
+
+/* These are the keywords that will distinguish the start of an extended
+ address. */
+
+static char *extended_keywords[] = {
+ "apt", "apartment", "suite", NULL
+};
+
+
+
+static gboolean
+e_address_western_is_line_blank (gchar *line)
+{
+ gboolean blank = TRUE;
+ gint cntr;
+
+ /* A blank line consists of whitespace only, or a NULL line. */
+ for (cntr = 0; line[cntr] != '\0'; cntr++ ) {
+ if (!isspace(line[cntr])) {
+ blank = FALSE;
+ break;
+ }
+ }
+
+ return blank;
+}
+
+
+
+/* In the array of lines, `lines', we will erase the line at line_num, and
+ shift the remaining lines, up to line number num_lines, up one position. */
+
+static void
+e_address_western_shift_line (gchar *lines[], gint line_num, gint num_lines)
+{
+ gint cntr;
+
+ if (line_num >= (num_lines - 1)) {
+ /* It is the last line, so simply shift in a NULL. */
+ lines[line_num] = NULL;
+ }
+ else {
+ for (cntr = line_num; cntr < num_lines; cntr++)
+ lines[cntr] = lines[cntr + 1];
+ }
+}
+
+
+static void
+e_address_western_remove_blank_lines (gchar *lines[], gint *linecntr)
+{
+ gint cntr;
+
+ for (cntr = 0; cntr < *linecntr; cntr++) {
+ if (e_address_western_is_line_blank (lines[cntr])) {
+ /* Delete the blank line, and shift all subsequent lines up
+ one spot to fill its old spot. */
+ e_address_western_shift_line (lines, cntr, *linecntr);
+
+ /* Since we must check the newly shifted line, let's
+ not advance the counter on this next pass. */
+ cntr--;
+
+ /* There is now one less line, total. */
+ *linecntr -= 1;
+ }
+ }
+}
+
+
+static gboolean
+e_address_western_is_po_box (gchar *line)
+{
+ gboolean retval = FALSE;
+
+ /* In which phase of processing are we? */
+ enum State { FIRSTCHAR, SECONDCHAR, WHITESPACE } state;
+
+
+ /* If the first two letters of the line are `p' and `o', and these
+ are in turn followed by whitespace before another letter, then I
+ will deem the line a representation of a PO Box address. */
+
+ gint cntr;
+
+ state = FIRSTCHAR;
+ for (cntr = 0; line[cntr] != '\0'; cntr++) {
+ if (state == FIRSTCHAR) {
+ if (isalnum(line[cntr])) {
+ if (tolower(line[cntr]) == 'p')
+ state = SECONDCHAR;
+ else {
+ retval = FALSE;
+ break;
+ }
+ }
+ }
+ else if (state == SECONDCHAR) {
+ if (isalnum (line[cntr])) {
+ if (tolower(line[cntr]) == 'o')
+ state = WHITESPACE;
+ else {
+ retval = FALSE;
+ break;
+ }
+ }
+ }
+ else if (state == WHITESPACE) {
+ if (isspace (line[cntr])) {
+ retval = TRUE;
+ break;
+ }
+ else if (isalnum (line[cntr])) {
+ retval = FALSE;
+ break;
+ }
+ }
+ }
+
+ return retval;
+}
+
+/* A line that contains a comma followed eventually by a number is
+ deemed to be the line in the form of <town, region postal-code>. */
+
+static gboolean
+e_address_western_is_postal (guchar *line)
+{
+ gboolean retval;
+ int cntr;
+
+ if (strchr (line, ',') == NULL)
+ retval = FALSE; /* No comma. */
+ else {
+ int index;
+
+ /* Ensure that the first character after the comma is
+ a letter. */
+ index = strcspn (line, ",");
+ index++;
+ while (isspace(line[index]))
+ index++;
+
+ if (!isalpha (line[index]))
+ return FALSE; /* FIXME: ugly control flow. */
+
+ cntr = strlen(line) - 1;
+
+ /* Go to the character immediately following the last
+ whitespace character. */
+ while (cntr >= 0 && isspace(line[cntr]))
+ cntr--;
+
+ while (cntr >= 0 && !isspace(line[cntr]))
+ cntr--;
+
+ if (cntr == 0)
+ retval = FALSE;
+ else {
+ if (isdigit (line[cntr+1]))
+ retval = TRUE;
+ else
+ retval = FALSE;
+ }
+ }
+
+ return retval;
+}
+
+static gchar *
+e_address_western_extract_po_box (gchar *line)
+{
+ /* Return everything from the beginning of the line to
+ the end of the first word that contains a number. */
+
+ int index;
+
+ index = 0;
+ while (!isdigit(line[index]))
+ index++;
+
+ while (isgraph(line[index]))
+ index++;
+
+ return g_strndup (line, index);
+}
+
+static gchar *
+e_address_western_extract_locality (gchar *line)
+{
+ gint index;
+
+ /* Everything before the comma is the locality. */
+ index = strcspn(line, ",");
+
+ if (index == 0)
+ return NULL;
+ else
+ return g_strndup (line, index);
+}
+
+
+/* Whatever resides between the comma and the start of the
+ postal code is deemed to be the region. */
+
+static gchar *
+e_address_western_extract_region (gchar *line)
+{
+ gint start, end;
+
+ start = strcspn (line, ",");
+ start++;
+ while (isspace(line[start]))
+ start++;
+
+ end = strlen(line) - 1;
+ while (isspace (line[end]))
+ end--;
+
+ while (!isspace (line[end]))
+ end--;
+
+ while (isspace (line[end]))
+ end--;
+ end++;
+
+ /* Between start and end lie the string. */
+ return g_strndup ( (line+start), end-start);
+}
+
+static gchar *
+e_address_western_extract_postal_code (gchar *line)
+{
+ int start, end;
+
+ end = strlen (line) - 1;
+ while (isspace(line[end]))
+ end--;
+
+ start = end;
+ end++;
+
+ while (!isspace(line[start]))
+ start--;
+ start++;
+
+ /* Between start and end lie the string. */
+ return g_strndup ( (line+start), end-start);
+}
+
+
+
+static void
+e_address_western_extract_street (gchar *line, gchar **street, gchar **extended)
+{
+ const gchar *split = NULL;
+ gint cntr;
+
+ for (cntr = 0; extended_keywords[cntr] != NULL; cntr++) {
+ split = e_strstrcase (line, extended_keywords[cntr]);
+ if (split != NULL)
+ break;
+ }
+
+ if (split != NULL) {
+ *street = g_strndup (line, (split - line));
+ *extended = g_strdup (split);
+ }
+ else {
+ *street = g_strdup (line);
+ *extended = NULL;
+ }
+
+}
+
+
+
+EAddressWestern *
+e_address_western_parse (const gchar *in_address)
+{
+ gchar **lines;
+ gint linecntr, lineindex;
+ gchar *address;
+ gint cntr;
+ gboolean found_po_box, found_postal;
+
+ EAddressWestern *eaw;
+#if 0
+ gint start, end; /* To be used to classify address lines. */
+#endif
+
+ if (in_address == NULL)
+ return NULL;
+
+ eaw = (EAddressWestern *)g_malloc (sizeof(EAddressWestern));
+ eaw->po_box = NULL;
+ eaw->extended = NULL;
+ eaw->street = NULL;
+ eaw->locality = NULL;
+ eaw->region = NULL;
+ eaw->postal_code = NULL;
+ eaw->country = NULL;
+
+ address = g_strndup (in_address, 2047);
+
+ /* The first thing I'll do is divide the multiline input string
+ into lines. */
+
+ /* ... count the lines. */
+ linecntr = 1;
+ lineindex = 0;
+ while (address[lineindex] != '\0') {
+ if (address[lineindex] == '\n')
+ linecntr++;
+
+ lineindex++;
+ }
+
+ /* ... tally them. */
+ lines = (gchar **)g_malloc (sizeof(gchar *) * (linecntr+3));
+ lineindex = 0;
+ lines[0] = &address[0];
+ linecntr = 1;
+ while (address[lineindex] != '\0') {
+ if (address[lineindex] == '\n') {
+ lines[linecntr] = &address[lineindex + 1];
+ linecntr++;
+ }
+
+ lineindex++;
+ }
+
+ /* Convert the newlines at the end of each line (except the last,
+ because it is already NULL terminated) to NULLs. */
+ for (cntr = 0; cntr < (linecntr - 1); cntr++) {
+ *(strchr (lines[cntr], '\n')) = '\0';
+ }
+
+ e_address_western_remove_blank_lines (lines, &linecntr);
+
+ /* Let's just test these functions. */
+ found_po_box = FALSE;
+ found_postal = FALSE;
+
+ for (cntr = 0; cntr < linecntr; cntr++) {
+ if (e_address_western_is_po_box (lines[cntr])) {
+ if (eaw->po_box == NULL)
+ eaw->po_box = e_address_western_extract_po_box (lines[cntr]);
+ found_po_box = TRUE;
+ }
+ else if (e_address_western_is_postal (lines[cntr])) {
+ if (eaw->locality == NULL)
+ eaw->locality = e_address_western_extract_locality (lines[cntr]);
+ if (eaw->region == NULL)
+ eaw->region = e_address_western_extract_region (lines[cntr]);
+ if (eaw->postal_code == NULL)
+ eaw->postal_code = e_address_western_extract_postal_code (lines[cntr]);
+ found_postal = TRUE;
+ }
+ else {
+ if (found_postal) {
+ if (eaw->country == NULL)
+ eaw->country = g_strdup (lines[cntr]);
+ else {
+ gchar *temp;
+ temp = g_strconcat (eaw->country, "\n", lines[cntr], NULL);
+ g_free (eaw->country);
+ eaw->country = temp;
+ }
+ }
+ else {
+ if (eaw->street == NULL) {
+ e_address_western_extract_street (lines[cntr], &eaw->street,
+ &eaw->extended );
+ }
+ else {
+ gchar *temp;
+ temp = g_strdup_printf (
+ "%s\n%s",
+ eaw->extended ? eaw->extended: "",
+ lines[cntr]);
+ g_free (eaw->extended);
+ eaw->extended = temp;
+ }
+ }
+ }
+ }
+
+ g_free (lines);
+ g_free (address);
+
+ return eaw;
+}
+
+
+void
+e_address_western_free (EAddressWestern *eaw)
+{
+ if (eaw == NULL)
+ return;
+
+ if (eaw->po_box != NULL)
+ g_free(eaw->po_box);
+ if (eaw->extended != NULL)
+ g_free(eaw->extended);
+ if (eaw->street != NULL)
+ g_free(eaw->street);
+ if (eaw->locality != NULL)
+ g_free(eaw->locality);
+ if (eaw->region != NULL)
+ g_free(eaw->region);
+ if (eaw->postal_code != NULL)
+ g_free(eaw->postal_code);
+ if (eaw->country != NULL)
+ g_free(eaw->country);
+
+ g_free (eaw);
+}
+
diff --git a/libedataserver/ename/e-address-western.h b/libedataserver/ename/e-address-western.h
new file mode 100644
index 000000000..e6417f88c
--- /dev/null
+++ b/libedataserver/ename/e-address-western.h
@@ -0,0 +1,21 @@
+#ifndef __E_ADDRESS_WESTERN_H__
+#define __E_ADDRESS_WESTERN_H__
+
+typedef struct {
+
+ /* Public */
+ char *po_box;
+ char *extended; /* I'm not sure what this is. */
+ char *street;
+ char *locality; /* For example, the city or town. */
+ char *region; /* The state or province. */
+ char *postal_code;
+ char *country;
+} EAddressWestern;
+
+EAddressWestern *e_address_western_parse (const char *address);
+void e_address_western_free (EAddressWestern *eaw);
+
+#endif /* ! __E_ADDRESS_WESTERN_H__ */
+
+
diff --git a/libedataserver/ename/e-name-western-tables.h b/libedataserver/ename/e-name-western-tables.h
new file mode 100644
index 000000000..b5459049f
--- /dev/null
+++ b/libedataserver/ename/e-name-western-tables.h
@@ -0,0 +1,74 @@
+#ifndef __E_NAME_WESTERN_TABLES_H__
+#define __E_NAME_WESTERN_TABLES_H__
+
+char *e_name_western_pfx_table[] = {
+
+ /*
+ * English.
+ */
+ "mister", "miss.", "mr.", "mrs.", "ms.",
+ "miss", "mr", "mrs", "ms", "sir",
+ "professor", "prof.", "dr", "dr.", "doctor",
+ "judge", "justice", "chief justice",
+ "congressman", "congresswoman", "commander",
+ "lieutenant", "lt.", "colonel", "col.", "major", "maj.",
+ "general", "gen.", "admiral", "admr.", "sergeant", "sgt.",
+ "lord", "lady", "baron", "baroness", "duke", "duchess",
+ "king", "queen", "prince", "princess",
+
+ "the most honorable", "the honorable",
+ "the reverend", "his holiness",
+ "his eminence", "his majesty", "her majesty",
+ "his grace", "her grace",
+
+ "president", "vice president", "secretary", "undersecretary",
+ "consul", "ambassador",
+
+ "senator", "saint", "st.", "pastor", "deacon",
+ "father", "bishop", "archbishop", "cardinal", "pope",
+ "reverend", "rev.", "rabbi",
+
+ /*
+ * French.
+ */
+ "monsieur", "m.", "mademoiselle", "melle",
+ "madame", "mme", "professeur", "dauphin", "dauphine",
+
+ /*
+ * German
+ */
+ "herr", "frau", "fraulein", "herr doktor", "doktor frau", "doktor frau doktor",
+ "frau doktor",
+
+
+ /*
+ * Spanish.
+ */
+ "senor", "senora", "sra.", "senorita", "srita.",
+
+ NULL};
+
+char *e_name_western_sfx_table[] = {
+
+ /*
+ * English.
+ */
+ "junior", "senior", "jr", "sr", "I", "II", "III", "IV", "V",
+ "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV",
+ "XV", "XVI", "XVII", "XVIII", "XIX", "XX", "XXI", "XXII",
+ "phd", "ms", "md", "esq", "esq.", "esquire",
+
+ NULL};
+
+char *e_name_western_twopart_sfx_table[] = {
+
+ /*
+ * English.
+ */
+ "the first", "the second", "the third",
+
+ NULL};
+
+char *e_name_western_complex_last_table[] = {"van", "von", "de", "di", NULL};
+
+#endif /* ! __E_NAME_WESTERN_TABLES_H__ */
diff --git a/libedataserver/ename/e-name-western.c b/libedataserver/ename/e-name-western.c
new file mode 100644
index 000000000..b6802c433
--- /dev/null
+++ b/libedataserver/ename/e-name-western.c
@@ -0,0 +1,982 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * A simple Western name parser.
+ *
+ * <Nat> Jamie, do you know anything about name parsing?
+ * <jwz> Are you going down that rat hole? Bring a flashlight.
+ *
+ * Authors:
+ * Nat Friedman <nat@ximian.com>
+ *
+ * Copyright 1999 - 2001, Ximian, Inc.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <glib.h>
+
+#include <ename/e-name-western.h>
+#include <ename/e-name-western-tables.h>
+
+typedef struct {
+ int prefix_idx;
+ int first_idx;
+ int middle_idx;
+ int nick_idx;
+ int last_idx;
+ int suffix_idx;
+} ENameWesternIdxs;
+
+static int
+e_name_western_str_count_words (char *str)
+{
+ int word_count;
+ char *p;
+
+ word_count = 0;
+
+ for (p = str; p != NULL; p = g_utf8_strchr (p, -1, ' ')) {
+ word_count ++;
+ p = g_utf8_next_char (p);
+ }
+
+ return word_count;
+}
+
+static void
+e_name_western_cleanup_string (char **str)
+{
+ char *newstr;
+ char *p;
+
+ if (*str == NULL)
+ return;
+
+ /* skip any spaces and commas at the start of the string */
+ p = *str;
+ while (g_unichar_isspace (g_utf8_get_char(p)) || *p == ',')
+ p = g_utf8_next_char (p);
+
+ /* make the copy we're going to return */
+ newstr = g_strdup (p);
+
+ if ( strlen(newstr) > 0) {
+ /* now search from the back, skipping over any spaces and commas */
+ p = newstr + strlen (newstr);
+ p = g_utf8_prev_char (p);
+ while (g_unichar_isspace (g_utf8_get_char(p)) || *p == ',')
+ p = g_utf8_prev_char (p);
+ /* advance p to after the character that caused us to exit the
+ previous loop, and end the string. */
+ if ((! g_unichar_isspace (g_utf8_get_char (p))) && *p != ',')
+ p = g_utf8_next_char (p);
+ *p = '\0';
+ }
+
+ g_free (*str);
+ *str = newstr;
+}
+
+static char *
+e_name_western_get_words_at_idx (char *str, int idx, int num_words)
+{
+ GString *words;
+ char *p;
+ int word_count;
+
+ /*
+ * Walk to the end of the words.
+ */
+ words = g_string_new ("");
+ word_count = 0;
+ p = str + idx;
+ while (word_count < num_words && *p != '\0') {
+ while (! g_unichar_isspace (g_utf8_get_char (p)) && *p != '\0') {
+ words = g_string_append_unichar (words, g_utf8_get_char (p));
+ p = g_utf8_next_char (p);
+ }
+
+ while (g_unichar_isspace (g_utf8_get_char (p)) && *p != '\0')
+ p = g_utf8_next_char (p);
+
+ word_count ++;
+ }
+
+ return g_string_free (words, FALSE);
+}
+
+/*
+ * What the fuck is wrong with glib's MAX macro.
+ */
+static int
+e_name_western_max (const int a, const int b)
+{
+ if (a > b)
+ return a;
+
+ return b;
+}
+
+static gboolean
+e_name_western_word_is_suffix (char *word)
+{
+ int i;
+
+ for (i = 0; e_name_western_sfx_table [i] != NULL; i ++) {
+ int length = strlen (e_name_western_sfx_table [i]);
+ if (!g_strcasecmp (word, e_name_western_sfx_table [i]) ||
+ ( !g_strncasecmp (word, e_name_western_sfx_table [i], length) &&
+ strlen(word) == length + 1 &&
+ word[length] == '.' ))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static char *
+e_name_western_get_one_prefix_at_str (char *str)
+{
+ char *word;
+ int i;
+
+ /*
+ * Check for prefixes from our table.
+ */
+ for (i = 0; e_name_western_pfx_table [i] != NULL; i ++) {
+ int pfx_words;
+ char *words;
+
+ pfx_words = e_name_western_str_count_words (e_name_western_pfx_table [i]);
+ words = e_name_western_get_words_at_idx (str, 0, pfx_words);
+
+ if (! g_strcasecmp (words, e_name_western_pfx_table [i]))
+ return words;
+
+ g_free (words);
+ }
+
+ /*
+ * Check for prefixes we don't know about. These are always a
+ * sequence of more than one letters followed by a period.
+ */
+ word = e_name_western_get_words_at_idx (str, 0, 1);
+
+ if (g_utf8_strlen (word, -1) > 2 &&
+ g_unichar_isalpha (g_utf8_get_char (word)) &&
+ g_unichar_isalpha (g_utf8_get_char (g_utf8_next_char (word))) &&
+ word [strlen (word) - 1] == '.')
+ return word;
+
+ g_free (word);
+
+ return NULL;
+}
+
+static char *
+e_name_western_get_prefix_at_str (char *str)
+{
+ char *pfx;
+ char *pfx1;
+ char *pfx2;
+ char *p;
+
+ /* Get the first prefix. */
+ pfx1 = e_name_western_get_one_prefix_at_str (str);
+
+ if (pfx1 == NULL)
+ return NULL;
+
+ /* Check for a second prefix. */
+ p = str + strlen (pfx1);
+ while (g_unichar_isspace (g_utf8_get_char (p)) && *p != '\0')
+ p = g_utf8_next_char (p);
+
+ pfx2 = e_name_western_get_one_prefix_at_str (p);
+
+ if (pfx2 != NULL) {
+ int pfx_len;
+
+ pfx_len = (p + strlen (pfx2)) - str;
+ pfx = g_malloc0 (pfx_len + 1);
+ strncpy (pfx, str, pfx_len);
+ } else {
+ pfx = g_strdup (pfx1);
+ }
+
+ g_free (pfx1);
+ g_free (pfx2);
+
+ return pfx;
+}
+
+static void
+e_name_western_extract_prefix (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+ char *pfx;
+
+ pfx = e_name_western_get_prefix_at_str (name->full);
+
+ if (pfx == NULL)
+ return;
+
+ idxs->prefix_idx = 0;
+ name->prefix = pfx;
+}
+
+static gboolean
+e_name_western_is_complex_last_beginning (char *word)
+{
+ int i;
+
+ for (i = 0; e_name_western_complex_last_table [i] != NULL; i ++) {
+
+ if (! g_strcasecmp (
+ word, e_name_western_complex_last_table [i]))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+e_name_western_extract_first (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+ /*
+ * If there's a prefix, then the first name is right after it.
+ */
+ if (idxs->prefix_idx != -1) {
+ int first_idx;
+ char *p;
+
+ first_idx = idxs->prefix_idx + strlen (name->prefix);
+
+ /* Skip past white space. */
+ p = name->full + first_idx;
+ while (g_unichar_isspace (g_utf8_get_char (p)) && *p != '\0')
+ p = g_utf8_next_char (p);
+
+ if (*p == '\0')
+ return;
+
+ idxs->first_idx = p - name->full;
+ name->first = e_name_western_get_words_at_idx (
+ name->full, idxs->first_idx, 1);
+
+ } else {
+
+ /*
+ * Otherwise, the first name is probably the first string.
+ */
+ idxs->first_idx = 0;
+ name->first = e_name_western_get_words_at_idx (
+ name->full, idxs->first_idx, 1);
+ }
+
+ /*
+ * Check that we didn't just assign the beginning of a
+ * compound last name to the first name.
+ */
+ if (name->first != NULL) {
+ if (e_name_western_is_complex_last_beginning (name->first)) {
+ g_free (name->first);
+ name->first = NULL;
+ idxs->first_idx = -1;
+ }
+ }
+}
+
+static void
+e_name_western_extract_middle (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+ char *word;
+ char *middle;
+
+ /*
+ * Middle names can only exist if you have a first name.
+ */
+ if (idxs->first_idx == -1)
+ return;
+
+ middle = name->full + idxs->first_idx + strlen (name->first);
+ if (*middle == '\0')
+ return;
+
+ middle = g_utf8_next_char (middle);
+ if (*middle == '\0')
+ return;
+
+ /*
+ * Search for the first space (or the terminating \0)
+ */
+ while (g_unichar_isspace (g_utf8_get_char (middle)) &&
+ *middle != '\0')
+ middle = g_utf8_next_char (middle);
+
+ if (*middle == '\0')
+ return;
+
+ /*
+ * Skip past the nickname, if it's there.
+ */
+ if (*middle == '\"') {
+ if (idxs->nick_idx == -1)
+ return;
+
+ middle = name->full + idxs->nick_idx + strlen (name->nick);
+ middle = g_utf8_next_char (middle);
+
+ while (g_unichar_isspace (g_utf8_get_char (middle)) &&
+ *middle != '\0')
+ middle = g_utf8_next_char (middle);
+
+ if (*middle == '\0')
+ return;
+ }
+
+ /*
+ * Make sure this isn't the beginning of a complex last name.
+ */
+ word = e_name_western_get_words_at_idx (name->full, middle - name->full, 1);
+ if (e_name_western_is_complex_last_beginning (word)) {
+ g_free (word);
+ return;
+ }
+
+ /*
+ * Make sure this isn't a suffix.
+ */
+ e_name_western_cleanup_string (& word);
+ if (e_name_western_word_is_suffix (word)) {
+ g_free (word);
+ return;
+ }
+
+ /*
+ * Make sure we didn't just grab a cute nickname.
+ */
+ if (word [0] == '\"') {
+ g_free (word);
+ return;
+ }
+
+ idxs->middle_idx = middle - name->full;
+ name->middle = word;
+}
+
+static void
+e_name_western_extract_nickname (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+ char *nick;
+ int start_idx;
+ GString *str;
+
+ if (idxs->first_idx == -1)
+ return;
+
+ if (idxs->middle_idx > idxs->first_idx)
+ nick = name->full + idxs->middle_idx + strlen (name->middle);
+ else
+ nick = name->full + idxs->first_idx + strlen (name->first);
+
+ while (*nick != '\"' && *nick != '\0')
+ nick = g_utf8_next_char (nick);
+
+ if (*nick != '\"')
+ return;
+
+ start_idx = nick - name->full;
+
+ /*
+ * Advance to the next double quote.
+ */
+ str = g_string_new ("\"");
+ nick = g_utf8_next_char (nick);
+
+ while (*nick != '\"' && *nick != '\0') {
+ str = g_string_append_unichar (str, g_utf8_get_char (nick));
+ nick = g_utf8_next_char (nick);
+ }
+
+ if (*nick == '\0') {
+ g_string_free (str, TRUE);
+ return;
+ }
+ str = g_string_append (str, "\"");
+
+ name->nick = g_string_free (str, FALSE);
+
+ idxs->nick_idx = start_idx;
+}
+
+static int
+e_name_western_last_get_max_idx (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+ int max_idx = -1;
+
+ if (name->prefix != NULL)
+ max_idx = e_name_western_max (
+ max_idx, idxs->prefix_idx + strlen (name->prefix));
+
+ if (name->first != NULL)
+ max_idx = e_name_western_max (
+ max_idx, idxs->first_idx + strlen (name->first));
+
+ if (name->middle != NULL)
+ max_idx = e_name_western_max (
+ max_idx, idxs->middle_idx + strlen (name->middle));
+
+ if (name->nick != NULL)
+ max_idx = e_name_western_max (
+ max_idx, idxs->nick_idx + strlen (name->nick));
+
+ return max_idx;
+}
+
+static void
+e_name_western_extract_last (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+ char *word;
+ int idx = -1;
+ char *last;
+
+ idx = e_name_western_last_get_max_idx (name, idxs);
+
+ /*
+ * In the case where there is no preceding name element, the
+ * name is either just a first name ("Nat", "John"), is a
+ * single-element name ("Cher", which we treat as a first
+ * name), or is just a last name. The only time we can
+ * differentiate a last name alone from a single-element name
+ * or a first name alone is if it's a complex last name ("de
+ * Icaza", "van Josephsen"). So if there is no preceding name
+ * element, we check to see whether or not the first part of
+ * the name is the beginning of a complex name. If it is,
+ * we subsume the entire string. If we accidentally subsume
+ * the suffix, this will get fixed in the fixup routine.
+ */
+ if (idx == -1) {
+ word = e_name_western_get_words_at_idx (name->full, 0, 1);
+ if (! e_name_western_is_complex_last_beginning (word)) {
+ g_free (word);
+ return;
+ }
+
+ name->last = g_strdup (name->full);
+ idxs->last_idx = 0;
+ return;
+ }
+
+ last = name->full + idx;
+
+ /* Skip past the white space. */
+ while (g_unichar_isspace (g_utf8_get_char (last)) && *last != '\0')
+ last = g_utf8_next_char (last);
+
+ if (*last == '\0')
+ return;
+
+ word = e_name_western_get_words_at_idx (name->full, last - name->full, 1);
+ e_name_western_cleanup_string (& word);
+ if (e_name_western_word_is_suffix (word)) {
+ g_free (word);
+ return;
+ }
+ g_free (word);
+
+ /*
+ * Subsume the rest of the string into the last name. If we
+ * accidentally include the prefix, it will get fixed later.
+ * This is the only way to handle things like "Miguel de Icaza
+ * Amozorrutia" without dropping data and forcing the user
+ * to retype it.
+ */
+ name->last = g_strdup (last);
+ idxs->last_idx = last - name->full;
+}
+
+static char *
+e_name_western_get_preceding_word (char *str, int idx)
+{
+ int word_len;
+ char *word;
+ char *p;
+
+ p = str + idx;
+
+ while (g_unichar_isspace (g_utf8_get_char (p)) && p > str)
+ p = g_utf8_prev_char (p);
+
+ while (! g_unichar_isspace (g_utf8_get_char (p)) && p > str)
+ p = g_utf8_prev_char (p);
+
+ if (g_unichar_isspace (g_utf8_get_char (p)))
+ p = g_utf8_next_char (p);
+
+ word_len = (str + idx) - p;
+ word = g_malloc0 (word_len + 1);
+ if (word_len > 0)
+ strncpy (word, p, word_len);
+
+ return word;
+}
+
+static char *
+e_name_western_get_suffix_at_str_end (char *str)
+{
+ char *suffix;
+ char *p;
+
+ /*
+ * Walk backwards till we reach the beginning of the
+ * (potentially-comma-separated) list of suffixes.
+ */
+ p = str + strlen (str);
+ while (1) {
+ char *nextp;
+ char *word;
+
+ word = e_name_western_get_preceding_word (str, p - str);
+ nextp = p - strlen (word);
+ if (nextp == str) {
+ g_free (word);
+ break;
+ }
+ nextp = g_utf8_prev_char (nextp);
+
+ e_name_western_cleanup_string (& word);
+
+ if (e_name_western_word_is_suffix (word)) {
+ p = nextp;
+ g_free (word);
+ } else {
+ g_free (word);
+ break;
+ }
+ }
+
+ if (p == (str + strlen (str)))
+ return NULL;
+
+ suffix = g_strdup (p);
+ e_name_western_cleanup_string (& suffix);
+
+ if (strlen (suffix) == 0) {
+ g_free (suffix);
+ return NULL;
+ }
+
+ return suffix;
+}
+
+static void
+e_name_western_extract_suffix (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+ name->suffix = e_name_western_get_suffix_at_str_end (name->full);
+
+ if (name->suffix == NULL)
+ return;
+
+ idxs->suffix_idx = strlen (name->full) - strlen (name->suffix);
+}
+
+static gboolean
+e_name_western_detect_backwards (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+ char *comma;
+ char *word;
+
+ comma = g_utf8_strchr (name->full, -1, ',');
+
+ if (comma == NULL)
+ return FALSE;
+
+ /*
+ * If there's a comma, we need to detect whether it's
+ * separating the last name from the first or just separating
+ * suffixes. So we grab the word which comes before the
+ * comma and check if it's a suffix.
+ */
+ word = e_name_western_get_preceding_word (name->full, comma - name->full);
+
+ if (e_name_western_word_is_suffix (word)) {
+ g_free (word);
+ return FALSE;
+ }
+
+ g_free (word);
+ return TRUE;
+}
+
+static void
+e_name_western_reorder_asshole (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+ char *prefix;
+ char *last;
+ char *suffix;
+ char *firstmidnick;
+ char *newfull;
+
+ char *comma;
+ char *p;
+
+ if (! e_name_western_detect_backwards (name, idxs))
+ return;
+
+ /*
+ * Convert
+ * <Prefix> <Last name>, <First name> <Middle[+nick] name> <Suffix>
+ * to
+ * <Prefix> <First name> <Middle[+nick] name> <Last name> <Suffix>
+ */
+
+ /*
+ * Grab the prefix from the beginning.
+ */
+ prefix = e_name_western_get_prefix_at_str (name->full);
+
+ /*
+ * Everything from the end of the prefix to the comma is the
+ * last name.
+ */
+ comma = g_utf8_strchr (name->full, -1, ',');
+ if (comma == NULL)
+ return;
+
+ p = name->full + (prefix == NULL ? 0 : strlen (prefix));
+
+ while (g_unichar_isspace (g_utf8_get_char (p)) && *p != '\0')
+ p = g_utf8_next_char (p);
+
+ last = g_malloc0 (comma - p + 1);
+ strncpy (last, p, comma - p);
+
+ /*
+ * Get the suffix off the end.
+ */
+ suffix = e_name_western_get_suffix_at_str_end (name->full);
+
+ /*
+ * Firstmidnick is everything from the comma to the beginning
+ * of the suffix.
+ */
+ p = g_utf8_next_char (comma);
+
+ while (g_unichar_isspace (g_utf8_get_char (p)) && *p != '\0')
+ p = g_utf8_next_char (p);
+
+ if (suffix != NULL) {
+ char *q;
+
+ /*
+ * Point q at the beginning of the suffix.
+ */
+ q = name->full + strlen (name->full) - strlen (suffix);
+ q = g_utf8_prev_char (q);
+
+ /*
+ * Walk backwards until we hit the space which
+ * separates the suffix from firstmidnick.
+ */
+ while (! g_unichar_isspace (g_utf8_get_char (q)) && q > comma)
+ q = g_utf8_prev_char (q);
+
+ if ((q - p + 1) > 0) {
+ firstmidnick = g_malloc0 (q - p + 1);
+ strncpy (firstmidnick, p, q - p);
+ } else
+ firstmidnick = NULL;
+ } else {
+ firstmidnick = g_strdup (p);
+ }
+
+ /*
+ * Create our new reordered version of the name.
+ */
+#define NULLSTR(a) ((a) == NULL ? "" : (a))
+ newfull = g_strdup_printf ("%s %s %s %s", NULLSTR (prefix), NULLSTR (firstmidnick),
+ NULLSTR (last), NULLSTR (suffix));
+ g_strstrip (newfull);
+ g_free (name->full);
+ name->full = newfull;
+
+
+ g_free (prefix);
+ g_free (firstmidnick);
+ g_free (last);
+ g_free (suffix);
+}
+
+static void
+e_name_western_zap_nil (char **str, int *idx)
+{
+ if (*str == NULL)
+ return;
+
+ if (strlen (*str) != 0)
+ return;
+
+ *idx = -1;
+ g_free (*str);
+ *str = NULL;
+}
+
+#define FINISH_CHECK_MIDDLE_NAME_FOR_CONJUNCTION \
+ char *last_start = NULL; \
+ if (name->last) \
+ last_start = g_utf8_strchr (name->last, -1, ' '); \
+ if (last_start) { \
+ char *new_last, *new_first; \
+ \
+ new_last = g_strdup (g_utf8_next_char (last_start)); \
+ *last_start = '\0'; \
+ \
+ idxs->last_idx += (last_start - name->last) + 1; \
+ \
+ new_first = g_strdup_printf ("%s %s %s", \
+ name->first, \
+ name->middle, \
+ name->last); \
+ \
+ g_free (name->first); \
+ g_free (name->middle); \
+ g_free (name->last); \
+ \
+ name->first = new_first; \
+ name->middle = NULL; \
+ name->last = new_last; \
+ \
+ idxs->middle_idx = -1; \
+ } else { \
+ char *new_first; \
+ \
+ new_first = g_strdup_printf ("%s %s %s", \
+ name->first, \
+ name->middle, \
+ name->last); \
+ \
+ g_free (name->first); \
+ g_free (name->middle); \
+ g_free (name->last); \
+ \
+ name->first = new_first; \
+ name->middle = NULL; \
+ name->last = NULL; \
+ idxs->middle_idx = -1; \
+ idxs->last_idx = -1; \
+ }
+
+#define CHECK_MIDDLE_NAME_FOR_CONJUNCTION(conj) \
+ if (idxs->middle_idx != -1 && !strcmp (name->middle, conj)) { \
+ FINISH_CHECK_MIDDLE_NAME_FOR_CONJUNCTION \
+ }
+
+#define CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE(conj) \
+ if (idxs->middle_idx != -1 && !strcasecmp (name->middle, conj)) { \
+ FINISH_CHECK_MIDDLE_NAME_FOR_CONJUNCTION \
+ }
+
+static void
+e_name_western_fixup (ENameWestern *name, ENameWesternIdxs *idxs)
+{
+ /*
+ * The middle and last names cannot be the same.
+ */
+ if (idxs->middle_idx != -1 && idxs->middle_idx == idxs->last_idx) {
+ idxs->middle_idx = -1;
+ g_free (name->middle);
+ name->middle = NULL;
+ }
+
+ /*
+ * If we have a middle name and no last name, then we mistook
+ * the last name for the middle name.
+ */
+ if (idxs->last_idx == -1 && idxs->middle_idx != -1) {
+ idxs->last_idx = idxs->middle_idx;
+ name->last = name->middle;
+ name->middle = NULL;
+ idxs->middle_idx = -1;
+ }
+
+ /*
+ * Check to see if we accidentally included the suffix in the
+ * last name.
+ */
+ if (idxs->suffix_idx != -1 && idxs->last_idx != -1 &&
+ idxs->suffix_idx < (idxs->last_idx + strlen (name->last))) {
+ char *sfx;
+
+ sfx = name->last + (idxs->suffix_idx - idxs->last_idx);
+ if (sfx != NULL) {
+ char *newlast;
+ char *p;
+
+ p = sfx;
+ p = g_utf8_prev_char (p);
+ while (g_unichar_isspace (g_utf8_get_char (p)) && p > name->last)
+ p = g_utf8_prev_char (p);
+ p = g_utf8_next_char (p);
+
+ newlast = g_malloc0 (p - name->last + 1);
+ strncpy (newlast, name->last, p - name->last);
+ g_free (name->last);
+ name->last = newlast;
+ }
+ }
+
+ /*
+ * If we have a prefix and a first name, but no last name,
+ * then we need to assign the first name to the last name.
+ * This way we get things like "Mr Friedman" correctly.
+ */
+ if (idxs->first_idx != -1 && idxs->prefix_idx != -1 &&
+ idxs->last_idx == -1) {
+ name->last = name->first;
+ idxs->last_idx = idxs->first_idx;
+ idxs->first_idx = -1;
+ name->first = NULL;
+ }
+
+ if (idxs->middle_idx != -1) {
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("&");
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("*");
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("|");
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("^");
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("&&");
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("||");
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("+");
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("-");
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("and");
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("or");
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("plus");
+
+ /* Spanish */
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("y");
+
+ /* German */
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("und");
+
+ /* Italian */
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("e");
+
+ /* Czech */
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("a");
+
+ /* Finnish */
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("ja");
+
+ /* French */
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION_CASE ("et");
+
+ /* Russian */
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("\xd0\x98"); /* u+0418 */
+ CHECK_MIDDLE_NAME_FOR_CONJUNCTION ("\xd0\xb8"); /* u+0438 */
+ }
+
+ /*
+ * Remove stray spaces and commas (although there don't seem
+ * to be any in the test cases, they might show up later).
+ */
+ e_name_western_cleanup_string (& name->prefix);
+ e_name_western_cleanup_string (& name->first);
+ e_name_western_cleanup_string (& name->middle);
+ e_name_western_cleanup_string (& name->nick);
+ e_name_western_cleanup_string (& name->last);
+ e_name_western_cleanup_string (& name->suffix);
+
+ /*
+ * Make zero-length strings just NULL.
+ */
+ e_name_western_zap_nil (& name->prefix, & idxs->prefix_idx);
+ e_name_western_zap_nil (& name->first, & idxs->first_idx);
+ e_name_western_zap_nil (& name->middle, & idxs->middle_idx);
+ e_name_western_zap_nil (& name->nick, & idxs->nick_idx);
+ e_name_western_zap_nil (& name->last, & idxs->last_idx);
+ e_name_western_zap_nil (& name->suffix, & idxs->suffix_idx);
+}
+
+/**
+ * e_name_western_western_parse_fullname:
+ * @full_name: A string containing a Western name.
+ *
+ * Parses @full_name and returns an #ENameWestern object filled with
+ * the component parts of the name.
+ */
+ENameWestern *
+e_name_western_parse (const char *full_name)
+{
+ ENameWesternIdxs *idxs;
+ ENameWestern *wname;
+ char *end;
+
+ if (!g_utf8_validate (full_name, -1, (const char **)&end)) {
+ g_warning ("e_name_western_parse passed invalid UTF-8 sequence");
+ *end = '\0';
+ }
+
+ wname = g_new0 (ENameWestern, 1);
+
+ wname->full = g_strdup (full_name);
+
+ idxs = g_new0 (ENameWesternIdxs, 1);
+
+ idxs->prefix_idx = -1;
+ idxs->first_idx = -1;
+ idxs->middle_idx = -1;
+ idxs->nick_idx = -1;
+ idxs->last_idx = -1;
+ idxs->suffix_idx = -1;
+
+ /*
+ * An extremely simple algorithm.
+ *
+ * The goal here is to get it right 95% of the time for
+ * Western names.
+ *
+ * First we check to see if this is an ass-backwards name
+ * ("Prefix Last, First Middle Suffix"). These names really
+ * suck (imagine "Dr von Johnson, Albert Roderick Jr"), so
+ * we reorder them first and then parse them.
+ *
+ * Next, we grab the most obvious assignments for the various
+ * parts of the name. Once this is done, we check for stupid
+ * errors and fix them up.
+ */
+ e_name_western_reorder_asshole (wname, idxs);
+
+ e_name_western_extract_prefix (wname, idxs);
+ e_name_western_extract_first (wname, idxs);
+ e_name_western_extract_nickname (wname, idxs);
+ e_name_western_extract_middle (wname, idxs);
+ e_name_western_extract_last (wname, idxs);
+ e_name_western_extract_suffix (wname, idxs);
+
+ e_name_western_fixup (wname, idxs);
+
+ g_free (idxs);
+
+ return wname;
+}
+
+/**
+ * e_name_western_free:
+ * @name: An ENameWestern object which needs to be freed.
+ *
+ * Deep-frees @name
+ */
+void
+e_name_western_free (ENameWestern *w)
+{
+
+ g_free (w->prefix);
+ g_free (w->first);
+ g_free (w->middle);
+ g_free (w->nick);
+ g_free (w->last);
+ g_free (w->suffix);
+
+ g_free (w->full);
+
+ g_free (w);
+}
diff --git a/libedataserver/ename/e-name-western.h b/libedataserver/ename/e-name-western.h
new file mode 100644
index 000000000..fa5bac494
--- /dev/null
+++ b/libedataserver/ename/e-name-western.h
@@ -0,0 +1,21 @@
+#ifndef __E_NAME_WESTERN_H__
+#define __E_NAME_WESTERN_H__
+
+typedef struct {
+
+ /* Public */
+ char *prefix;
+ char *first;
+ char *middle;
+ char *nick;
+ char *last;
+ char *suffix;
+
+ /* Private */
+ char *full;
+} ENameWestern;
+
+ENameWestern *e_name_western_parse (const char *full_name);
+void e_name_western_free (ENameWestern *w);
+
+#endif /* ! __E_NAME_WESTERN_H__ */
diff --git a/libedataserver/libedataserver-1.0.pc.in b/libedataserver/libedataserver-1.0.pc.in
new file mode 100644
index 000000000..bcf4f87f9
--- /dev/null
+++ b/libedataserver/libedataserver-1.0.pc.in
@@ -0,0 +1,14 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+idldir=@idldir@
+IDL_INCLUDES=-I ${idldir} @IDL_INCLUDES@
+
+Name: libedataserver
+Description: Utily library for evolution data servers
+Version: @VERSION@
+Requires: libbonobo-2.0 libgnome-2.0 gal-2.2 >= @GAL_REQUIRED@
+Libs: -L${libdir} -ledataserver
+Cflags: -I${includedir}/evolution-data-server-1.0
diff --git a/libedataserver/md5-utils.c b/libedataserver/md5-utils.c
new file mode 100644
index 000000000..6f7e06f48
--- /dev/null
+++ b/libedataserver/md5-utils.c
@@ -0,0 +1,363 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to md5_init, call md5_update as
+ * needed on buffers full of bytes, and then call md5_Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* parts of this file are :
+ * Written March 1993 by Branko Lankester
+ * Modified June 1993 by Colin Plumb for altered md5.c.
+ * Modified October 1995 by Erik Troan for RPM
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include "md5-utils.h"
+
+
+static void md5_transform (guint32 buf[4], const guint32 in[16]);
+
+static gint _ie = 0x44332211;
+static union _endian { gint i; gchar b[4]; } *_endian = (union _endian *)&_ie;
+#define IS_BIG_ENDIAN() (_endian->b[0] == '\x44')
+#define IS_LITTLE_ENDIAN() (_endian->b[0] == '\x11')
+
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void
+_byte_reverse (guchar *buf, guint32 longs)
+{
+ guint32 t;
+ do {
+ t = (guint32) ((guint32) buf[3] << 8 | buf[2]) << 16 |
+ ((guint32) buf[1] << 8 | buf[0]);
+ *(guint32 *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+
+/**
+ * md5_init: Initialise an md5 context object
+ * @ctx: md5 context
+ *
+ * Initialise an md5 buffer.
+ *
+ **/
+void
+md5_init (MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+
+ if (IS_BIG_ENDIAN())
+ ctx->doByteReverse = 1;
+ else
+ ctx->doByteReverse = 0;
+}
+
+
+
+/**
+ * md5_update: add a buffer to md5 hash computation
+ * @ctx: conetxt object used for md5 computaion
+ * @buf: buffer to add
+ * @len: buffer length
+ *
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes. Use this to progressively construct an md5 hash.
+ **/
+void
+md5_update (MD5Context *ctx, const guchar *buf, guint32 len)
+{
+ guint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((guint32) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ guchar *p = (guchar *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy (p, buf, len);
+ return;
+ }
+ memcpy (p, buf, t);
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 16);
+ md5_transform (ctx->buf, (guint32 *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy (ctx->in, buf, 64);
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 16);
+ md5_transform (ctx->buf, (guint32 *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy (ctx->in, buf, len);
+}
+
+
+
+
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+/**
+ * md5_final: copy the final md5 hash to a bufer
+ * @digest: 16 bytes buffer
+ * @ctx: context containing the calculated md5
+ *
+ * copy the final md5 hash to a bufer
+ **/
+void
+md5_final (MD5Context *ctx, guchar digest[16])
+{
+ guint32 count;
+ guchar *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset (p, 0, count);
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 16);
+ md5_transform (ctx->buf, (guint32 *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset (ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset (p, 0, count - 8);
+ }
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((guint32 *) ctx->in)[14] = ctx->bits[0];
+ ((guint32 *) ctx->in)[15] = ctx->bits[1];
+
+ md5_transform (ctx->buf, (guint32 *) ctx->in);
+ if (ctx->doByteReverse)
+ _byte_reverse ((guchar *) ctx->buf, 4);
+ memcpy (digest, ctx->buf, 16);
+}
+
+
+
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. md5_Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+md5_transform (guint32 buf[4], const guint32 in[16])
+{
+ register guint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP (F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP (F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP (F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP (F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP (F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP (F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP (F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP (F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP (F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP (F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP (F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP (F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP (F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP (F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP (F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP (F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP (F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP (F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP (F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP (F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP (F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP (F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP (F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP (F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP (F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP (F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP (F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP (F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP (F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP (F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP (F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP (F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP (F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP (F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP (F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP (F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP (F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP (F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP (F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP (F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP (F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP (F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP (F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP (F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP (F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP (F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP (F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP (F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP (F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP (F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP (F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP (F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP (F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP (F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP (F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP (F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP (F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP (F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP (F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP (F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP (F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP (F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP (F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP (F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+
+
+
+/**
+ * md5_get_digest: get the md5 hash of a buffer
+ * @buffer: byte buffer
+ * @buffer_size: buffer size (in bytes)
+ * @digest: 16 bytes buffer receiving the hash code.
+ *
+ * Get the md5 hash of a buffer. The result is put in
+ * the 16 bytes buffer @digest .
+ **/
+void
+md5_get_digest (const gchar *buffer, gint buffer_size, guchar digest[16])
+{
+ MD5Context ctx;
+
+ md5_init (&ctx);
+ md5_update (&ctx, buffer, buffer_size);
+ md5_final (&ctx, digest);
+
+}
+
+
+/**
+ * md5_get_digest_from_file: get the md5 hash of a file
+ * @filename: file name
+ * @digest: 16 bytes buffer receiving the hash code.
+ *
+ * Get the md5 hash of a file. The result is put in
+ * the 16 bytes buffer @digest .
+ **/
+void
+md5_get_digest_from_file (const gchar *filename, guchar digest[16])
+{
+ MD5Context ctx;
+ guchar tmp_buf[1024];
+ gint nb_bytes_read;
+ FILE *fp;
+
+ printf("generating checksum\n");
+
+ md5_init (&ctx);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ return;
+ }
+
+ while ((nb_bytes_read = fread (tmp_buf, sizeof (guchar), 1024, fp)) > 0)
+ md5_update (&ctx, tmp_buf, nb_bytes_read);
+
+ if (ferror(fp)) {
+ fclose(fp);
+ return;
+ }
+
+
+ md5_final (&ctx, digest);
+
+ printf("checksum done\n");
+}
+
+
+
+
diff --git a/libedataserver/md5-utils.h b/libedataserver/md5-utils.h
new file mode 100644
index 000000000..607471a75
--- /dev/null
+++ b/libedataserver/md5-utils.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to rpmMD5Init, call rpmMD5Update as
+ * needed on buffers full of bytes, and then call rpmMD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* parts of this file are :
+ * Written March 1993 by Branko Lankester
+ * Modified June 1993 by Colin Plumb for altered md5.c.
+ * Modified October 1995 by Erik Troan for RPM
+ */
+
+
+#ifndef MD5_UTILS_H
+#define MD5_UTILS_H
+
+#include <glib.h>
+
+
+typedef struct _MD5Context {
+ guint32 buf[4];
+ guint32 bits[2];
+ guchar in[64];
+ gint doByteReverse;
+} MD5Context;
+
+
+void md5_get_digest (const gchar *buffer, gint buffer_size, guchar digest[16]);
+
+/* use this one when speed is needed */
+/* for use in provider code only */
+void md5_get_digest_from_file (const gchar *filename, guchar digest[16]);
+
+/* raw routines */
+void md5_init (MD5Context *ctx);
+void md5_update (MD5Context *ctx, const guchar *buf, guint32 len);
+void md5_final (MD5Context *ctx, guchar digest[16]);
+
+
+#endif /* MD5_UTILS_H */