 +      argv[0]->type == ESEXP_RES_STRING &&
 +      argv[1]->type == ESEXP_RES_STRING)
 +  {
++    query_term = argv[0]->value.string;
++
 +    for (i = 0; i < G_N_ELEMENTS (indexes); i++)
 +    {
-+      gchar *query_term = argv[0]->value.string;
-+
 +      if (query_term && g_str_equal (query_term, indexes[i].query_term))
 +      {
-+        /* we found that we support this query term as an index. woohoo */
-+        result->value.bool = TRUE;
++        /* if we can we do nothing since the value defaults to true */
++      } else {
++        *(gboolean *)userdata = FALSE;
 +      }
 +    }
-+
 +  } else {
-+    g_warning ("Unexpected query structure");
++    g_warning (G_STRLOC ": Unexpected query structure");
++    *(gboolean *)userdata = FALSE;
 +  }
 +
 +  return result;
 +}
 +
 +/* index operations themselves */
-+static int 
-+generic_field_cb (EBookBackendFileIndex *index,
-+    EBookBackendFileIndexField *field, DB *sdb, const DBT *pkey, const DBT *pdata, DBT *skey)
-+{
-+  return 0;
-+}
-+
-Index: git/addressbook/backends/file/e-book-backend-file-index.h
++
++/* boring helper function */
++static void
++dbt_fill_with_string (DBT *dbt, gchar *str)
++{
++  memset (dbt, 0, sizeof (DBT));
++  dbt->data = (void *)str;
++  dbt->size = strlen (str) + 1;
++  dbt->flags = DB_DBT_USERMEM;
++}
++
++/* when given a list of fields, walk through the primary database, walk
++ * through all contacts, parse them, then call the function to add to each
++ * index that needs updating
++ */
++static gboolean 
++index_populate (EBookBackendFileIndex *index, GList *fields)
++{
++  EBookBackendFileIndexPrivate *priv = GET_PRIVATE (index);
++
++  GList *l = NULL;
++  DBC *dbc = NULL; /* primary cursor */
++  DBT id_dbt, vcard_dbt;
++  EContact *contact = NULL;
++  gint db_error = 0;
++  DB *db = NULL;
++
++  db = priv->db;
++
++  db_error = db->cursor (db, NULL, &dbc, 0);
++
++  if (db_error != 0)
++  {
++    g_warning (G_STRLOC ": db->cursor failed: %s", db_strerror (db_error)); 
++    return FALSE;
++  }
++
++  memset (&id_dbt, 0, sizeof (id_dbt));
++  memset (&vcard_dbt, 0, sizeof (vcard_dbt));
++
++  db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST);
++
++  while (db_error == 0)
++  {
++    if (!g_str_equal (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME))
++    {
++      /* parse the vcard */
++      contact = e_contact_new_from_vcard (vcard_dbt.data);
++
++      if (contact)
++      {
++        /* now we interate through all the indexes that need doing */
++        for (l = fields; l != NULL; l = l->next)
++        {
++          index_add_contact (index, contact, (EBookBackendFileIndexData *)l->data);
++        }
++      }
++    }
++
++    db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT);
++  }
++
++  if (db_error != DB_NOTFOUND)
++  {
++    g_warning (G_STRLOC ":dbc->c_get failed: %s", db_strerror (db_error));
++    return FALSE;
++  }
++
++  db_error = dbc->c_close (dbc);
++
++  if (db_error != 0)
++  {
++    g_warning (G_STRLOC ":dbc->c_close failed: %s", db_strerror (db_error));
++    return FALSE;
++  }
++
++  return TRUE;
++}
++
++/* 
++ * for a given index add this contact, for the single string field case we
++ * just add a single entry to the index database. for the multiple case we add
++ * all the values for the field
++ */
++static void
++index_add_contact (EBookBackendFileIndex *index, EContact *contact, 
++    EBookBackendFileIndexData *data)
++{
++  EBookBackendFileIndexPrivate *priv = GET_PRIVATE (index);
++  gchar *uid = NULL;
++  DBT index_dbt, id_dbt;
++  DB *db = NULL;
++  GList *attrs; 
++  GList *values;
++  gint db_error = 0;
++  gchar *tmp = NULL;
++
++  /* we need the uid, this becomes the data for our index databases */
++  uid = (gchar *)e_contact_get (contact, E_CONTACT_UID);
++  dbt_fill_with_string (&id_dbt, uid);
++
++  /* we need the index database too */
++  db = g_hash_table_lookup (priv->sdbs, data->query_term);
++
++  for (attrs = e_vcard_get_attributes (E_VCARD (contact)); 
++      attrs != NULL; 
++      attrs = attrs->next)
++  {
++    EVCardAttribute *attr = (EVCardAttribute *)attrs->data;
++
++    if (g_str_equal (e_vcard_attribute_get_name (attr), data->vfield))
++    {
++      for (values = e_vcard_attribute_get_values (attr);
++          values != NULL; 
++          values = values->next)
++      {
++        tmp = g_utf8_casefold (values->data, -1);
++        dbt_fill_with_string (&index_dbt, tmp);
++
++        g_debug (G_STRLOC ": adding to index with key %s and data %s", 
++            index_dbt.data, id_dbt.data);
++
++        db_error = db->put (db, NULL, &index_dbt, &id_dbt, 0);
++
++        if (db_error != 0)
++        {
++          g_warning (G_STRLOC ": db->put failed: %s", db_strerror (db_error));
++        }
++      }
++    }
++  }
++
++  db_error = db->sync (db, 0);
++
++  if (db_error != 0)
++  {
++    g_warning ("db->sync failed: %s", db_strerror (db_error));
++  }
++}
++
++/* for a given index datbase remove all entries that have a data that matches
++ * the given uid. to do this we have to iterate through all the key/data pairs
++ * in the index and remove them if the data matches
++ *
++ * it is expected that this function will be called by for each index
++ */
++static void
++index_remove_contact (EBookBackendFileIndex *index, const gchar *uid, 
++    EBookBackendFileIndexData *data)
++{
++  EBookBackendFileIndexPrivate *priv = GET_PRIVATE (index);
++  
++  DBT index_dbt, id_dbt;
++  DB *db = NULL;
++  gint db_error = 0;
++  DBC *dbc = NULL; /* cursor */
++
++  /* we need the index database too */
++  db = g_hash_table_lookup (priv->sdbs, data->query_term);
++
++  /* create the cursor for the interation */
++  db_error = db->cursor (db, NULL, &dbc, 0);
++
++  if (db_error != 0)
++  {
++    g_warning (G_STRLOC ": db->cursor failed: %s", db_strerror (db_error)); 
++    return;
++  }
++
++  memset (&index_dbt, 0, sizeof (index_dbt));
++  memset (&id_dbt, 0, sizeof (id_dbt));
++
++  db_error = dbc->c_get (dbc, &index_dbt, &id_dbt, DB_FIRST);
++
++  while (db_error == 0)
++  {
++    g_debug (G_STRLOC ": Considering %s against %s", id_dbt.data, uid);
++    /* if the id is the one we're lookin for then delete! */
++    if (g_str_equal ((gchar *)id_dbt.data, uid))
++    {
++      g_debug (G_STRLOC ": removing from index with id: %s", (gchar *)id_dbt.data);
++      db_error = dbc->c_del (dbc, 0);
++
++      if (db_error != 0)
++      {
++        g_warning (G_STRLOC ": dbc->del failed: %s", db_strerror (db_error));
++        db_error = dbc->c_close (dbc);
++        return;
++      }
++    }
++
++    db_error = dbc->c_get (dbc, &index_dbt, &id_dbt, DB_NEXT);
++  }
++
++  if (db_error != DB_NOTFOUND)
++  {
++    g_warning (G_STRLOC ": dbc->c_get failed: %s", db_strerror (db_error));
++    db_error = dbc->c_close (dbc);
++    return;
++  }
++
++  db_error = dbc->c_close (dbc);
++
++  if (db_error != 0)
++  {
++    g_warning (G_STRLOC ": dbc->c_close failed: %s", db_strerror (db_error));
++    return;
++  }
++
++  db_error = db->sync (db, 0);
++
++  if (db_error != 0)
++  {
++    g_warning (G_STRLOC ": db->sync failed: %s", db_strerror (db_error));
++  }
++}
++
++static ESExpResult *
++is_query (ESExp *sexp, gint argc, ESExpResult **argv, gpointer userdata)
++{
++  EBookBackendFileIndex *index = (EBookBackendFileIndex *)userdata;
++  EBookBackendFileIndexPrivate *priv = GET_PRIVATE (index);
++  
++  ESExpResult *result = NULL;
++  DBT index_dbt, id_dbt;
++  DB *db = NULL;
++  gint db_error = 0;
++  gchar *query_term = NULL, *query_key = NULL;
++  GPtrArray *ids = NULL;
++  DBC *dbc = NULL;
++
++  ids = g_ptr_array_new ();
++
++  /* we must have two arguments and they must both be strings */
++  if (argc == 2 &&
++      argv[0]->type == ESEXP_RES_STRING &&
++      argv[1]->type == ESEXP_RES_STRING)
++  {
++    /* we need the index database too */
++    query_term = argv[0]->value.string;
++    db = g_hash_table_lookup (priv->sdbs, query_term);
++
++    /* populate a dbt for looking up in the index */
++    query_key = g_utf8_casefold (argv[1]->value.string, -1);
++    dbt_fill_with_string (&index_dbt, query_key);
++
++    /* we want bdb to use g_malloc this memory for us */
++    memset (&id_dbt, 0, sizeof (id_dbt));
++    id_dbt.flags = DB_DBT_MALLOC;
++
++    /* cursor */
++    db_error = db->cursor (db, NULL, &dbc, 0);
++
++    if (db_error != 0)
++    {
++      g_warning (G_STRLOC ": db->cursor failed: %s", db_strerror (db_error)); 
++      goto out;
++    } else {
++      db_error = dbc->c_get (dbc, &index_dbt, &id_dbt, DB_SET);
++
++      while (db_error == 0)
++      {
++        g_debug (G_STRLOC ": index query found: %s", id_dbt.data);
++        g_ptr_array_add (ids, id_dbt.data);
++
++        /* we add it to the list of strings to be freed */
++        priv->ids_to_free = g_list_append (priv->ids_to_free, id_dbt.data);
++
++        /* clear the index dbt since this time bdb will push memory into it */
++        memset (&index_dbt, 0, sizeof (index_dbt));
++        db_error = dbc->c_get (dbc, &index_dbt, &id_dbt, DB_NEXT_DUP);
++      }
++
++      if (db_error != DB_NOTFOUND)
++      {
++        g_warning (G_STRLOC ": dbc->c_get failed: %s", db_strerror (db_error));
++        goto out;
++      }
++
++      db_error = dbc->c_close (dbc);
++
++      if (db_error != 0)
++      {
++        g_warning (G_STRLOC ": dbc->c_close failed: %s", db_strerror (db_error));
++        goto out;
++      }
++    }
++  } else {
++    g_warning (G_STRLOC ": Unexpected query structure");
++  }
++
++out:
++
++  g_free (query_key);
++  result = e_sexp_result_new (sexp, ESEXP_RES_ARRAY_PTR);
++  result->value.ptrarray = ids;
++
++  return result;
++}
++
++static ESExpResult *
++beginswith_query (ESExp *sexp, gint argc, ESExpResult **argv, gpointer userdata)
++{
++  EBookBackendFileIndex *index = (EBookBackendFileIndex *)userdata;
++  EBookBackendFileIndexPrivate *priv = GET_PRIVATE (index);
++  
++  ESExpResult *result = NULL;
++  DBT index_dbt, id_dbt;
++  DB *db = NULL;
++  gint db_error = 0;
++  gchar *query_term = NULL, *query_key = NULL;
++  GPtrArray *ids = NULL;
++  DBC *dbc = NULL;
++
++  ids = g_ptr_array_new ();
++
++  /* we must have two arguments and they must both be strings */
++  if (argc == 2 &&
++      argv[0]->type == ESEXP_RES_STRING &&
++      argv[1]->type == ESEXP_RES_STRING)
++  {
++    /* we need the index database too */
++    query_term = argv[0]->value.string;
++    db = g_hash_table_lookup (priv->sdbs, query_term);
++
++    /* populate a dbt for looking up in the index */
++    query_key = g_utf8_casefold (argv[1]->value.string, -1);
++    dbt_fill_with_string (&index_dbt, query_key);
++
++    /* we want bdb to use g_malloc this memory for us */
++    memset (&id_dbt, 0, sizeof (id_dbt));
++    id_dbt.flags = DB_DBT_MALLOC;
++
++    /* we want bdb to actually manage chunk of memory for returning the
++     * actual key to us
++     */
++    index_dbt.flags = 0;
++
++    /* cursor */
++    db_error = db->cursor (db, NULL, &dbc, 0);
++
++    if (db_error != 0)
++    {
++      g_warning (G_STRLOC ": db->cursor failed: %s", db_strerror (db_error)); 
++      goto out;
++    } else {
++      db_error = dbc->c_get (dbc, &index_dbt, &id_dbt, DB_SET_RANGE);
++
++      while (db_error == 0)
++      {
++        if (g_str_has_prefix (index_dbt.data, query_key))
++        {
++          g_debug (G_STRLOC ": index query found: %s for %s for %s", id_dbt.data, query_key, index_dbt.data);
++          g_ptr_array_add (ids, id_dbt.data);
++
++          /* we add it to the list of strings to be freed */
++          priv->ids_to_free = g_list_append (priv->ids_to_free, id_dbt.data);
++          memset (&index_dbt, 0, sizeof (index_dbt));
++          db_error = dbc->c_get (dbc, &index_dbt, &id_dbt, DB_NEXT);
++        } else {
++          break;
++        }
++      }
++
++      if (db_error != 0 && db_error != DB_NOTFOUND)
++      {
++        g_warning (G_STRLOC ": dbc->c_get failed: %s", db_strerror (db_error));
++        goto out;
++      }
++
++      db_error = dbc->c_close (dbc);
++
++      if (db_error != 0)
++      {
++        g_warning (G_STRLOC ": dbc->c_close failed: %s", db_strerror (db_error));
++        goto out;
++      }
++    }
++  } else {
++    g_warning (G_STRLOC ": Unexpected query structure");
++  }
++
++out:
++  g_free (query_key);
++
++  result = e_sexp_result_new (sexp, ESEXP_RES_ARRAY_PTR);
++  result->value.ptrarray = ids;
++
++  return result;
++}
++
++static gboolean
++index_close_db_func (gpointer key, gpointer value, gpointer userdata)
++{
++  DB *db = (DB *)value;
++  gint db_error = 0;
++
++  db_error = db->close (db, 0);
++
++  if (db_error != 0)
++  {
++    g_warning (G_STRLOC ": close failed: %s", db_strerror (db_error));
++  }
++
++  return TRUE;
++}
+Index: git-foo/addressbook/backends/file/e-book-backend-file-index.h
 ===================================================================
 --- /dev/null	1970-01-01 00:00:00.000000000 +0000
-+++ git/addressbook/backends/file/e-book-backend-file-index.h	2008-04-30 21:50:18.000000000 +0100
-@@ -0,0 +1,40 @@
++++ git-foo/addressbook/backends/file/e-book-backend-file-index.h	2008-05-08 12:37:30.000000000 +0100
+@@ -0,0 +1,68 @@
++/*
++ * Copyright (C) 2008 OpenedHand Ltd
++ *
++ * This program is free software; you can redistribute it and/or modify it under
++ * the terms of version 2 of the GNU Lesser 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 Lesser 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: Rob Bradford <rob@openedhand.com>
++ */
++
 +#ifndef _E_BOOK_BACKEND_FILE_INDEX
 +#define _E_BOOK_BACKEND_FILE_INDEX
 +
 +#include <glib-object.h>
++#include "libebook/e-contact.h"
++#include "db.h"
 +
 +G_BEGIN_DECLS
 +

+};
+
+/* 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 */
+};
+
+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 **sdbs;
+};
+
+
+static void
+e_book_backend_file_index_dispose (GObject *object)
+{
+  if (G_OBJECT_CLASS (e_book_backend_file_index_parent_class)->dispose)
+    G_OBJECT_CLASS (e_book_backend_file_index_parent_class)->dispose (object);
+}

 	e-book-backend-file-factory.c			\
 	$(top_srcdir)/libedataserver/e-db3-utils.c	\
 	$(top_srcdir)/libedataserver/e-dbhash.c
Index: git-foo/addressbook/backends/file/e-book-backend-file.c
===================================================================
--- git-foo.orig/addressbook/backends/file/e-book-backend-file.c	2008-05-08 12:37:29.000000000 +0100
+++ git-foo/addressbook/backends/file/e-book-backend-file.c	2008-05-08 16:26:32.000000000 +0100
@@ -54,6 +54,8 @@
 
 #include "e-book-backend-file.h"
