 +};
 +
 +/* structures used for maintaining the indexes */
-+typedef struct _EBookBackendFileIndexField EBookBackendFileIndexField;
-+
-+typedef int (* EBookBackendFileBDBIndexCallback) (DB *sdb, const DBT *pkey, const DBT *pdata, DBT *skey);
-+typedef int (* EBookBackendFileIndexCallback) (EBookBackendFileIndex *index, EBookBackendFileIndexField *field, 
-+    DB *sdb, const DBT *pkey, const DBT *pdata, DBT *skey);
-+
-+struct _EBookBackendFileIndexField
-+{
-+  gchar *query_term;                /* what the query uses to get this */
-+  EContactField vfield;            /* the (vcard) field this an index of */
-+  gchar *index_name;                /* name for index */
-+  EBookBackendFileIndexCallback cb; /* callback for index generation */
++typedef enum
++{
++  INDEX_FIELD_TYPE_STRING = 0,     /* single string */
++  INDEX_FIELD_TYPE_STRING_LIST /* list of strings */
++} EBookBackendFileIndexFieldType;
++
++typedef struct 
++{
++  gchar *query_term;                    /* what the query uses to get this */
++  gchar *index_name;                    /* name for index */
++  gchar *vfield;                        /* the (vcard) field this an index of */
++} EBookBackendFileIndexData;
++
++static const EBookBackendFileIndexData indexes[] = {
++  {"full-name", "full_name", EVC_FN},
++  {"im-jabber", "im_jabber", EVC_X_JABBER},
++  {"tel", "tel", EVC_TEL},
 +};
 +
-+static int generic_field_cb (EBookBackendFileIndex *index,
-+    EBookBackendFileIndexField *field, DB *sdb, const DBT *pkey, const DBT *pdata, DBT *skey);
-+
-+static const EBookBackendFileIndexField indexes[] = {
-+  {"full-name", E_CONTACT_FULL_NAME, "full_name", generic_field_cb}
++typedef struct _EBookBackendFileIndexPrivate EBookBackendFileIndexPrivate;
++
++struct _EBookBackendFileIndexPrivate {
++  DB *db;  /* primary database */
++  GHashTable *sdbs; /* mapping for query->term to database used for it */
++  GList *ids_to_free; /* a list of ids to free in the _finish call */
 +};
 +
-+typedef struct _EBookBackendFileIndexPrivate EBookBackendFileIndexPrivate;
-+
-+struct _EBookBackendFileIndexPrivate {
-+  DB **sdbs;
-+};
-+
++static gboolean index_populate (EBookBackendFileIndex *index, GList *fields);
++static void index_add_contact (EBookBackendFileIndex *index, EContact *contact, 
++    EBookBackendFileIndexData *data);
++static void index_remove_contact (EBookBackendFileIndex *index, const gchar *uid, 
++    EBookBackendFileIndexData *data);
++static gboolean index_close_db_func (gpointer key, gpointer value, gpointer userdata);
++
++#define E_BOOK_BACKEND_FILE_VERSION_NAME "PAS-DB-VERSION"
 +
 +static void
 +e_book_backend_file_index_dispose (GObject *object)
 +{
++  EBookBackendFileIndex *index = (EBookBackendFileIndex *)object;
++  EBookBackendFileIndexPrivate *priv = GET_PRIVATE (index);
++
++  if (priv->sdbs)
++  {
++    /* close all the open index databases */
++    g_hash_table_foreach_remove (priv->sdbs, index_close_db_func, 
++        E_BOOK_BACKEND_FILE_INDEX (object));
++
++    g_hash_table_unref (priv->sdbs);
++    priv->sdbs = NULL;
++  }
++
++  /* free any ids we may have left from a query */
++  e_book_backend_file_index_finish_query (index);
++
 +  if (G_OBJECT_CLASS (e_book_backend_file_index_parent_class)->dispose)
 +    G_OBJECT_CLASS (e_book_backend_file_index_parent_class)->dispose (object);
 +}

 
+
+	bf->priv->index = e_book_backend_file_index_new ();
+
 	e_book_backend_set_is_loaded (backend, TRUE);
 	e_book_backend_set_is_writable (backend, writable);
 	return GNOME_Evolution_Addressbook_Success;
@@ -1486,6 +1497,11 @@
 		bf->priv->summary = NULL;
 	}
 
+	if (bf->priv->index) {
+		g_object_unref (bf->priv->index);
+		bf->priv->index = NULL;
+	}
+
 	G_OBJECT_CLASS (e_book_backend_file_parent_class)->dispose (object);	
 }
 
Index: git/addressbook/backends/file/e-book-backend-file-index.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ git/addressbook/backends/file/e-book-backend-file-index.c	2008-04-30 21:50:18.000000000 +0100
@@ -0,0 +1,212 @@
+#include "e-book-backend-file-index.h"
+#include "libedataserver/e-sexp.h"
+#include "libebook/e-contact.h"

+  /* evaluate ... */
+  sexp_result = e_sexp_eval(sexp);
+
+  /* and then ignore the result */
+  e_sexp_result_free (sexp, sexp_result);
+  e_sexp_unref (sexp);
+
+  return res;
+}
+
+GPtrArray *
+e_book_backend_file_index_query (EBookBackendFileIndex *index, const gchar *query)
+{
+  ESExp *sexp = NULL;
+  ESExpResult *sexp_result = NULL;
+  gint i = 0;
+  gint sexp_error = 0;
+  GPtrArray *res = NULL;
+
+  sexp = e_sexp_new ();
+
+  g_debug (G_STRLOC ": Using query %s", query);
+
+  /* Now add the 'test' functions, we just use these to find out whether we
+   * can do a query. They return booleans and then ESexp logic should figure
+   * out the logic...
+   */
+  for (i = 0; i < G_N_ELEMENTS (sexp_symbols); i++)
+  {
+    e_sexp_add_function (sexp, 0, sexp_symbols[i].name, 
+        sexp_symbols[i].result_function, index);
+  }
+
+  /* Pull in the query as the text for the s-exp */
+  e_sexp_input_text (sexp, query, strlen (query));
+  sexp_error = e_sexp_parse (sexp);
+
+  if (sexp_error == -1)
+  {
+    g_warning (G_STRLOC ": Failed to parse query %s, %s", query, e_sexp_error (sexp));
+    e_sexp_unref (sexp);
+    return NULL;
+  }
+
+  /* evaluate ... */
+  sexp_result = e_sexp_eval(sexp);
+
+  if (sexp_result)
+  {
+    if (sexp_result->type == ESEXP_RES_ARRAY_PTR)
+    {
+      /* we have to copy everything into a new GPtrArray because the old one
+       * gets freed with e_sexp_result_free. :-(
+       */
+      res = g_ptr_array_sized_new (sexp_result->value.ptrarray->len);
+
+      for (i = 0; i < sexp_result->value.ptrarray->len; i ++)
+      {
+        g_ptr_array_add (res, g_ptr_array_index (sexp_result->value.ptrarray, i));
+      }
+
+      e_sexp_result_free (sexp, sexp_result);
+    } else {
+      g_warning (G_STRLOC ": Unexpected result type");
+    }
+  }
+
+  e_sexp_unref (sexp);
+  return res;
+}
+
+void
+e_book_backend_file_index_finish_query (EBookBackendFileIndex *index)
+{
+  EBookBackendFileIndexPrivate *priv = GET_PRIVATE (index);
+  GList *l = NULL;
+
+  for (l = priv->ids_to_free; l != NULL; l = l->next)
+  {
+    g_free (l->data);
+  }
+
+  g_list_free (priv->ids_to_free);
+  priv->ids_to_free = NULL;
+}
+
+/* setup indicies in index_filename for the primary database db */
+gboolean
+e_book_backend_file_index_setup_indicies (EBookBackendFileIndex *index, DB *db, 
+    const gchar *index_filename)
+{
+  EBookBackendFileIndexPrivate *priv = GET_PRIVATE (index);
+
+  GList *dbs_to_populate = NULL; /* list of EBookBackendFileIndexData to populate */
+  int db_error = 0;
+  int i = 0;
+  DB *sdb = NULL;
+  DB_ENV *env = NULL;
+
+  /* for each database we go through and try and create it exclusively, if
+   * that fails then we know that we don't need to populate it so we go and
+   * create it anyway and add it for the list of databases to populate */
+
+  g_return_val_if_fail (db != NULL, FALSE);
+  priv->db = db;
+
+  db_error = db->get_env (db, &env);
+
+  for (i = 0; i < G_N_ELEMENTS (indexes); i++)
+  {
+    db_error = db_create (&sdb, env, 0);
+
+    if (db_error != 0) {
+      g_warning (G_STRLOC ": db_create failed: %s", db_strerror (db_error));
+      return FALSE;
+    }
+
+    db_error = sdb->set_flags(sdb, DB_DUP | DB_DUPSORT);
+    if (db_error != 0) {
+      g_warning (G_STRLOC ":set_flags failed: %s", db_strerror (db_error));
+    }
+
+    db_error = sdb->open (sdb, NULL, index_filename, 
+        indexes[i].index_name, DB_BTREE, DB_CREATE | DB_EXCL | DB_THREAD, 0666);
+
+    if (db_error == EEXIST) {
+
+      /* try and open again, this time without exclusivity */
+      db_error = sdb->open (sdb, NULL, index_filename,
+          indexes[i].index_name, DB_BTREE, DB_CREATE | DB_THREAD, 0666);
+
+      if (db_error != 0)
+      {
+        g_warning (G_STRLOC ": open failed: %s", db_strerror (db_error));
+        sdb->close (sdb, 0);
+        return FALSE;
+      }
+    } else if (db_error != 0) {
+      if (db_error != 0)
+      {
+        g_warning (G_STRLOC ": open failed: %s", db_strerror (db_error));
+        sdb->close (sdb, 0);
+        return FALSE;
+      }
+    } else {
+      /* we know need to populate this one */
+      dbs_to_populate = g_list_prepend (dbs_to_populate, (gpointer)&(indexes[i]));
+    }
+
+    /* add it to the hash table of mappings of query term to database */
+    g_hash_table_insert (priv->sdbs, indexes[i].query_term, sdb);
+  }
+
+  /* now go through and populate these */
+  if (dbs_to_populate)
+  {
+    if (!index_populate (index, dbs_to_populate))
+    {
+      g_warning (G_STRLOC ": Error whilst trying to populate the index");
+      g_list_free (dbs_to_populate);
+      return FALSE;
+    }
+    g_list_free (dbs_to_populate);
+  }
+
+  return TRUE;
+}
+
+void
+e_book_backend_file_index_add_contact (EBookBackendFileIndex *index, EContact *contact)
+{
+  gint i = 0;
+
+  /* for each index database try add this contact */
+  for (i = 0; i < G_N_ELEMENTS (indexes); i++)
+  {
+    index_add_contact (index, contact, (EBookBackendFileIndexData *)&indexes[i]);
+  }
+}
+
+void
+e_book_backend_file_index_remove_contact (EBookBackendFileIndex *index, const gchar *uid)
+{
+  gint i = 0;
+
+  /* for each index database try add this contact */
+  for (i = 0; i < G_N_ELEMENTS (indexes); i++)
+  {
+    index_remove_contact (index, uid, (EBookBackendFileIndexData *)&indexes[i]);
+  }
+}
+
+void
+e_book_backend_file_index_modify_contact (EBookBackendFileIndex *index, EContact *contact)
+{
+  gchar *uid = NULL;
+  gint i = 0;
+
+  uid = (gchar *)e_contact_get (contact, E_CONTACT_UID);
+
+  /* for each index database try add this contact */
+  for (i = 0; i < G_N_ELEMENTS (indexes); i++)
+  {
+    index_remove_contact (index, uid, (EBookBackendFileIndexData *)&indexes[i]);
+    index_add_contact (index, contact, (EBookBackendFileIndexData *)&indexes[i]);
+  }
+
+  g_free (uid);
+}
+
+/* functions used for testing whether we can use the index */
