aboutsummaryrefslogtreecommitdiffstats
path: root/calendar/pcs/cal-backend-object-sexp.c
diff options
context:
space:
mode:
authorEttore Perazzoli <ettore@src.gnome.org>2003-10-22 02:51:30 +0800
committerEttore Perazzoli <ettore@src.gnome.org>2003-10-22 02:51:30 +0800
commit21743ac2cfa2d2ddcd539e9b7695cc8dd720ef36 (patch)
tree0111dbe4d8a6d5617f5e5406001b0024e4293169 /calendar/pcs/cal-backend-object-sexp.c
parent653cfffc0e00dfb59b36813c1b45c53d3f773c65 (diff)
downloadgsoc2013-evolution-21743ac2cfa2d2ddcd539e9b7695cc8dd720ef36.tar
gsoc2013-evolution-21743ac2cfa2d2ddcd539e9b7695cc8dd720ef36.tar.gz
gsoc2013-evolution-21743ac2cfa2d2ddcd539e9b7695cc8dd720ef36.tar.bz2
gsoc2013-evolution-21743ac2cfa2d2ddcd539e9b7695cc8dd720ef36.tar.lz
gsoc2013-evolution-21743ac2cfa2d2ddcd539e9b7695cc8dd720ef36.tar.xz
gsoc2013-evolution-21743ac2cfa2d2ddcd539e9b7695cc8dd720ef36.tar.zst
gsoc2013-evolution-21743ac2cfa2d2ddcd539e9b7695cc8dd720ef36.zip
Merge new-ui-branch into the trunk.
svn path=/trunk/; revision=22966
Diffstat (limited to 'calendar/pcs/cal-backend-object-sexp.c')
-rw-r--r--calendar/pcs/cal-backend-object-sexp.c1007
1 files changed, 1007 insertions, 0 deletions
diff --git a/calendar/pcs/cal-backend-object-sexp.c b/calendar/pcs/cal-backend-object-sexp.c
new file mode 100644
index 0000000000..4bc6b78ebe
--- /dev/null
+++ b/calendar/pcs/cal-backend-object-sexp.c
@@ -0,0 +1,1007 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * cal-backend-card-sexp.c
+ * Copyright 1999, 2000, 2001, Ximian, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ */
+
+#include <string.h>
+#include <libgnome/gnome-i18n.h>
+#include <e-util/e-sexp.h>
+#include <gal/widgets/e-unicode.h>
+#include <cal-util/timeutil.h>
+
+#include "cal-backend-object-sexp.h"
+
+static GObjectClass *parent_class;
+
+typedef struct _SearchContext SearchContext;
+
+struct _CalBackendObjectSExpPrivate {
+ ESExp *search_sexp;
+ char *text;
+ SearchContext *search_context;
+};
+
+struct _SearchContext {
+ CalComponent *comp;
+ CalBackend *backend;
+};
+
+static ESExpResult *
+func_time_now (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+ ESExpResult *result;
+
+ if (argc != 0) {
+ e_sexp_fatal_error (esexp, _("time-now expects 0 arguments"));
+ return NULL;
+ }
+
+ result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
+ result->value.time = time (NULL);
+
+ return result;
+}
+
+/* (make-time ISODATE)
+ *
+ * ISODATE - string, ISO 8601 date/time representation
+ *
+ * Constructs a time_t value for the specified date.
+ */
+static ESExpResult *
+func_make_time (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+ const char *str;
+ time_t t;
+ ESExpResult *result;
+
+ if (argc != 1) {
+ e_sexp_fatal_error (esexp, _("make-time expects 1 argument"));
+ return NULL;
+ }
+
+ if (argv[0]->type != ESEXP_RES_STRING) {
+ e_sexp_fatal_error (esexp, _("make-time expects argument 1 "
+ "to be a string"));
+ return NULL;
+ }
+ str = argv[0]->value.string;
+
+ t = time_from_isodate (str);
+ if (t == -1) {
+ e_sexp_fatal_error (esexp, _("make-time argument 1 must be an "
+ "ISO 8601 date/time string"));
+ return NULL;
+ }
+
+ result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
+ result->value.time = t;
+
+ return result;
+}
+
+/* (time-add-day TIME N)
+ *
+ * TIME - time_t, base time
+ * N - int, number of days to add
+ *
+ * Adds the specified number of days to a time value.
+ *
+ * FIXME: TIMEZONES - need to use a timezone or daylight saving changes will
+ * make the result incorrect.
+ */
+static ESExpResult *
+func_time_add_day (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+ ESExpResult *result;
+ time_t t;
+ int n;
+
+ if (argc != 2) {
+ e_sexp_fatal_error (esexp, _("time-add-day expects 2 arguments"));
+ return NULL;
+ }
+
+ if (argv[0]->type != ESEXP_RES_TIME) {
+ e_sexp_fatal_error (esexp, _("time-add-day expects argument 1 "
+ "to be a time_t"));
+ return NULL;
+ }
+ t = argv[0]->value.time;
+
+ if (argv[1]->type != ESEXP_RES_INT) {
+ e_sexp_fatal_error (esexp, _("time-add-day expects argument 2 "
+ "to be an integer"));
+ return NULL;
+ }
+ n = argv[1]->value.number;
+
+ result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
+ result->value.time = time_add_day (t, n);
+
+ return result;
+}
+
+/* (time-day-begin TIME)
+ *
+ * TIME - time_t, base time
+ *
+ * Returns the start of the day, according to the local time.
+ *
+ * FIXME: TIMEZONES - this uses the current Unix timezone.
+ */
+static ESExpResult *
+func_time_day_begin (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+ time_t t;
+ ESExpResult *result;
+
+ if (argc != 1) {
+ e_sexp_fatal_error (esexp, _("time-day-begin expects 1 argument"));
+ return NULL;
+ }
+
+ if (argv[0]->type != ESEXP_RES_TIME) {
+ e_sexp_fatal_error (esexp, _("time-day-begin expects argument 1 "
+ "to be a time_t"));
+ return NULL;
+ }
+ t = argv[0]->value.time;
+
+ result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
+ result->value.time = time_day_begin (t);
+
+ return result;
+}
+
+/* (time-day-end TIME)
+ *
+ * TIME - time_t, base time
+ *
+ * Returns the end of the day, according to the local time.
+ *
+ * FIXME: TIMEZONES - this uses the current Unix timezone.
+ */
+static ESExpResult *
+func_time_day_end (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+ time_t t;
+ ESExpResult *result;
+
+ if (argc != 1) {
+ e_sexp_fatal_error (esexp, _("time-day-end expects 1 argument"));
+ return NULL;
+ }
+
+ if (argv[0]->type != ESEXP_RES_TIME) {
+ e_sexp_fatal_error (esexp, _("time-day-end expects argument 1 "
+ "to be a time_t"));
+ return NULL;
+ }
+ t = argv[0]->value.time;
+
+ result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
+ result->value.time = time_day_end (t);
+
+ return result;
+}
+
+/* (get-vtype)
+ *
+ * Returns a string indicating the type of component (VEVENT, VTODO, VJOURNAL,
+ * VFREEBUSY, VTIMEZONE, UNKNOWN).
+ */
+static ESExpResult *
+func_get_vtype (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+ SearchContext *ctx = data;
+ CalComponentVType vtype;
+ char *str;
+ ESExpResult *result;
+
+ /* Check argument types */
+
+ if (argc != 0) {
+ e_sexp_fatal_error (esexp, _("get-vtype expects 0 arguments"));
+ return NULL;
+ }
+
+ /* Get the type */
+
+ vtype = cal_component_get_vtype (ctx->comp);
+
+ switch (vtype) {
+ case CAL_COMPONENT_EVENT:
+ str = g_strdup ("VEVENT");
+ break;
+
+ case CAL_COMPONENT_TODO:
+ str = g_strdup ("VTODO");
+ break;
+
+ case CAL_COMPONENT_JOURNAL:
+ str = g_strdup ("VJOURNAL");
+ break;
+
+ case CAL_COMPONENT_FREEBUSY:
+ str = g_strdup ("VFREEBUSY");
+ break;
+
+ case CAL_COMPONENT_TIMEZONE:
+ str = g_strdup ("VTIMEZONE");
+ break;
+
+ default:
+ str = g_strdup ("UNKNOWN");
+ break;
+ }
+
+ result = e_sexp_result_new (esexp, ESEXP_RES_STRING);
+ result->value.string = str;
+
+ return result;
+}
+
+/* (occur-in-time-range? START END)
+ *
+ * START - time_t, start of the time range
+ * END - time_t, end of the time range
+ *
+ * Returns a boolean indicating whether the component has any occurrences in the
+ * specified time range.
+ */
+static ESExpResult *
+func_occur_in_time_range (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+ SearchContext *ctx = data;
+ time_t start, end, tt;
+ gboolean occurs;
+ ESExpResult *result;
+ CalComponentDateTime dt;
+
+ /* Check argument types */
+
+ if (argc != 2) {
+ e_sexp_fatal_error (esexp, _("occur-in-time-range? expects 2 arguments"));
+ return NULL;
+ }
+
+ if (argv[0]->type != ESEXP_RES_TIME) {
+ e_sexp_fatal_error (esexp, _("occur-in-time-range? expects argument 1 "
+ "to be a time_t"));
+ return NULL;
+ }
+ start = argv[0]->value.time;
+
+ if (argv[1]->type != ESEXP_RES_TIME) {
+ e_sexp_fatal_error (esexp, _("occur-in-time-range? expects argument 2 "
+ "to be a time_t"));
+ return NULL;
+ }
+ end = argv[1]->value.time;
+
+ /* See if the object occurs in the specified time range */
+ occurs = FALSE;
+
+ cal_component_get_dtstart (ctx->comp, &dt);
+ if (dt.value) {
+ icaltimezone *zone;
+
+ if (dt.tzid)
+ zone = cal_backend_internal_get_timezone (ctx->backend, dt.tzid);
+ else
+ zone = cal_backend_internal_get_default_timezone (ctx->backend);
+
+ tt = icaltime_as_timet_with_zone (*dt.value, zone);
+ if (tt >= start && tt <= end)
+ occurs = TRUE;
+ else {
+ cal_component_get_dtend (ctx->comp, &dt);
+ if (dt.value) {
+ if (dt.tzid)
+ zone = cal_backend_internal_get_timezone (ctx->backend, dt.tzid);
+ else
+ zone = cal_backend_internal_get_default_timezone (ctx->backend);
+
+ tt = icaltime_as_timet_with_zone (*dt.value, zone);
+ if (tt >= start && tt <= end)
+ occurs = TRUE;
+ }
+ }
+ }
+
+ result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+ result->value.bool = occurs;
+
+ return result;
+}
+
+/* Returns whether a list of CalComponentText items matches the specified string */
+static gboolean
+matches_text_list (GSList *text_list, const char *str)
+{
+ GSList *l;
+ gboolean matches;
+
+ matches = FALSE;
+
+ for (l = text_list; l; l = l->next) {
+ CalComponentText *text;
+
+ text = l->data;
+ g_assert (text->value != NULL);
+
+ if (e_utf8_strstrcasedecomp (text->value, str) != NULL) {
+ matches = TRUE;
+ break;
+ }
+ }
+
+ return matches;
+}
+
+/* Returns whether the comments in a component matches the specified string */
+static gboolean
+matches_comment (CalComponent *comp, const char *str)
+{
+ GSList *list;
+ gboolean matches;
+
+ cal_component_get_comment_list (comp, &list);
+ matches = matches_text_list (list, str);
+ cal_component_free_text_list (list);
+
+ return matches;
+}
+
+/* Returns whether the description in a component matches the specified string */
+static gboolean
+matches_description (CalComponent *comp, const char *str)
+{
+ GSList *list;
+ gboolean matches;
+
+ cal_component_get_description_list (comp, &list);
+ matches = matches_text_list (list, str);
+ cal_component_free_text_list (list);
+
+ return matches;
+}
+
+/* Returns whether the summary in a component matches the specified string */
+static gboolean
+matches_summary (CalComponent *comp, const char *str)
+{
+ CalComponentText text;
+
+ cal_component_get_summary (comp, &text);
+
+ if (!text.value)
+ return FALSE;
+
+ return e_utf8_strstrcasedecomp (text.value, str) != NULL;
+}
+
+/* Returns whether any text field in a component matches the specified string */
+static gboolean
+matches_any (CalComponent *comp, const char *str)
+{
+ /* As an optimization, and to make life easier for the individual
+ * predicate functions, see if we are looking for the empty string right
+ * away.
+ */
+ if (strlen (str) == 0)
+ return TRUE;
+
+ return (matches_comment (comp, str)
+ || matches_description (comp, str)
+ || matches_summary (comp, str));
+}
+
+/* (contains? FIELD STR)
+ *
+ * FIELD - string, name of field to match (any, comment, description, summary)
+ * STR - string, match string
+ *
+ * Returns a boolean indicating whether the specified field contains the
+ * specified string.
+ */
+static ESExpResult *
+func_contains (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+ SearchContext *ctx = data;
+ const char *field;
+ const char *str;
+ gboolean matches;
+ ESExpResult *result;
+
+ /* Check argument types */
+
+ if (argc != 2) {
+ e_sexp_fatal_error (esexp, _("contains? expects 2 arguments"));
+ return NULL;
+ }
+
+ if (argv[0]->type != ESEXP_RES_STRING) {
+ e_sexp_fatal_error (esexp, _("contains? expects argument 1 "
+ "to be a string"));
+ return NULL;
+ }
+ field = argv[0]->value.string;
+
+ if (argv[1]->type != ESEXP_RES_STRING) {
+ e_sexp_fatal_error (esexp, _("contains? expects argument 2 "
+ "to be a string"));
+ return NULL;
+ }
+ str = argv[1]->value.string;
+
+ /* See if it matches */
+
+ if (strcmp (field, "any") == 0)
+ matches = matches_any (ctx->comp, str);
+ else if (strcmp (field, "comment") == 0)
+ matches = matches_comment (ctx->comp, str);
+ else if (strcmp (field, "description") == 0)
+ matches = matches_description (ctx->comp, str);
+ else if (strcmp (field, "summary") == 0)
+ matches = matches_summary (ctx->comp, str);
+ else {
+ e_sexp_fatal_error (esexp, _("contains? expects argument 1 to "
+ "be one of \"any\", \"summary\", \"description\""));
+ return NULL;
+ }
+
+ result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+ result->value.bool = matches;
+
+ return result;
+}
+
+/* (has-alarms? #f|#t)
+ *
+ * A boolean value for components that have/dont have alarms.
+ *
+ * Returns: a boolean indicating whether the component has alarms or not.
+ */
+static ESExpResult *
+func_has_alarms (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+ SearchContext *ctx = data;
+ ESExpResult *result;
+ gboolean has_to_have_alarms;
+
+ /* Check argument types */
+
+ if (argc != 1) {
+ e_sexp_fatal_error (esexp, _("has-alarms? expects at least 1 argument"));
+ return NULL;
+ }
+
+ if (argv[0]->type != ESEXP_RES_BOOL) {
+ e_sexp_fatal_error (esexp, _("has-alarms? excepts argument to be a boolean"));
+ return NULL;
+ }
+
+ has_to_have_alarms = argv[0]->value.bool;
+ result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+
+ if (has_to_have_alarms && cal_component_has_alarms (ctx->comp))
+ result->value.bool = TRUE;
+ else if (!has_to_have_alarms && !cal_component_has_alarms (ctx->comp))
+ result->value.bool = TRUE;
+ else
+ result->value.bool = FALSE;
+
+ return result;
+}
+
+/* (has-categories? STR+)
+ * (has-categories? #f)
+ *
+ * STR - At least one string specifying a category
+ * Or you can specify a single #f (boolean false) value for components
+ * that have no categories assigned to them ("unfiled").
+ *
+ * Returns a boolean indicating whether the component has all the specified
+ * categories.
+ */
+static ESExpResult *
+func_has_categories (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+ SearchContext *ctx = data;
+ gboolean unfiled;
+ int i;
+ GSList *categories;
+ gboolean matches;
+ ESExpResult *result;
+
+ /* Check argument types */
+
+ if (argc < 1) {
+ e_sexp_fatal_error (esexp, _("has-categories? expects at least 1 argument"));
+ return NULL;
+ }
+
+ if (argc == 1 && argv[0]->type == ESEXP_RES_BOOL)
+ unfiled = TRUE;
+ else
+ unfiled = FALSE;
+
+ if (!unfiled)
+ for (i = 0; i < argc; i++)
+ if (argv[i]->type != ESEXP_RES_STRING) {
+ e_sexp_fatal_error (esexp, _("has-categories? expects all arguments "
+ "to be strings or one and only one "
+ "argument to be a boolean false (#f)"));
+ return NULL;
+ }
+
+ /* Search categories. First, if there are no categories we return
+ * whether unfiled components are supposed to match.
+ */
+
+ cal_component_get_categories_list (ctx->comp, &categories);
+ if (!categories) {
+ result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+ result->value.bool = unfiled;
+
+ return result;
+ }
+
+ /* Otherwise, we *do* have categories but unfiled components were
+ * requested, so this component does not match.
+ */
+ if (unfiled) {
+ result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+ result->value.bool = FALSE;
+
+ return result;
+ }
+
+ matches = TRUE;
+
+ for (i = 0; i < argc; i++) {
+ const char *sought;
+ GSList *l;
+ gboolean has_category;
+
+ sought = argv[i]->value.string;
+
+ has_category = FALSE;
+
+ for (l = categories; l; l = l->next) {
+ const char *category;
+
+ category = l->data;
+
+ if (strcmp (category, sought) == 0) {
+ has_category = TRUE;
+ break;
+ }
+ }
+
+ if (!has_category) {
+ matches = FALSE;
+ break;
+ }
+ }
+
+ cal_component_free_categories_list (categories);
+
+ result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+ result->value.bool = matches;
+
+ return result;
+}
+
+/* (is-completed?)
+ *
+ * Returns a boolean indicating whether the component is completed (i.e. has
+ * a COMPLETED property. This is really only useful for TODO components.
+ */
+static ESExpResult *
+func_is_completed (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+ SearchContext *ctx = data;
+ ESExpResult *result;
+ struct icaltimetype *t;
+ gboolean complete = FALSE;
+
+ /* Check argument types */
+
+ if (argc != 0) {
+ e_sexp_fatal_error (esexp, _("is-completed? expects 0 arguments"));
+ return NULL;
+ }
+
+ cal_component_get_completed (ctx->comp, &t);
+ if (t) {
+ complete = TRUE;
+ cal_component_free_icaltimetype (t);
+ }
+
+ result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+ result->value.bool = complete;
+
+ return result;
+}
+
+/* (completed-before? TIME)
+ *
+ * TIME - time_t
+ *
+ * Returns a boolean indicating whether the component was completed on or
+ * before the given time (i.e. it checks the COMPLETED property).
+ * This is really only useful for TODO components.
+ */
+static ESExpResult *
+func_completed_before (ESExp *esexp, int argc, ESExpResult **argv, void *data)
+{
+ SearchContext *ctx = data;
+ ESExpResult *result;
+ struct icaltimetype *tt;
+ icaltimezone *zone;
+ gboolean retval = FALSE;
+ time_t before_time, completed_time;
+
+ /* Check argument types */
+
+ if (argc != 1) {
+ e_sexp_fatal_error (esexp, _("completed-before? expects 1 argument"));
+ return NULL;
+ }
+
+ if (argv[0]->type != ESEXP_RES_TIME) {
+ e_sexp_fatal_error (esexp, _("completed-before? expects argument 1 "
+ "to be a time_t"));
+ return NULL;
+ }
+ before_time = argv[0]->value.time;
+
+ cal_component_get_completed (ctx->comp, &tt);
+ if (tt) {
+ /* COMPLETED must be in UTC. */
+ zone = icaltimezone_get_utc_timezone ();
+ completed_time = icaltime_as_timet_with_zone (*tt, zone);
+
+#if 0
+ g_print ("Query Time : %s", ctime (&before_time));
+ g_print ("Completed Time: %s", ctime (&completed_time));
+#endif
+
+ /* We want to return TRUE if before_time is after
+ completed_time. */
+ if (difftime (before_time, completed_time) > 0) {
+#if 0
+ g_print (" Returning TRUE\n");
+#endif
+ retval = TRUE;
+ }
+
+ cal_component_free_icaltimetype (tt);
+ }
+
+ result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
+ result->value.bool = retval;
+
+ return result;
+}
+
+#if 0
+static struct prop_info {
+ ECardSimpleField field_id;
+ const char *query_prop;
+ const char *ecard_prop;
+#define PROP_TYPE_NORMAL 0x01
+#define PROP_TYPE_LIST 0x02
+#define PROP_TYPE_LISTITEM 0x03
+#define PROP_TYPE_ID 0x04
+ int prop_type;
+ gboolean (*list_compare)(ECardSimple *ecard, const char *str,
+ char *(*compare)(const char*, const char*));
+
+} prop_info_table[] = {
+#define NORMAL_PROP(f,q,e) {f, q, e, PROP_TYPE_NORMAL, NULL}
+#define ID_PROP {0, "id", NULL, PROP_TYPE_ID, NULL}
+#define LIST_PROP(q,e,c) {0, q, e, PROP_TYPE_LIST, c}
+
+ /* query prop, ecard prop, type, list compare function */
+ NORMAL_PROP ( E_CARD_SIMPLE_FIELD_FILE_AS, "file_as", "file_as" ),
+ LIST_PROP ( "full_name", "full_name", compare_name), /* not really a list, but we need to compare both full and surname */
+ NORMAL_PROP ( E_CARD_SIMPLE_FIELD_URL, "url", "url" ),
+ NORMAL_PROP ( E_CARD_SIMPLE_FIELD_MAILER, "mailer", "mailer"),
+ NORMAL_PROP ( E_CARD_SIMPLE_FIELD_ORG, "org", "org"),
+ NORMAL_PROP ( E_CARD_SIMPLE_FIELD_ORG_UNIT, "org_unit", "org_unit"),
+ NORMAL_PROP ( E_CARD_SIMPLE_FIELD_OFFICE, "office", "office"),
+ NORMAL_PROP ( E_CARD_SIMPLE_FIELD_TITLE, "title", "title"),
+ NORMAL_PROP ( E_CARD_SIMPLE_FIELD_ROLE, "role", "role"),
+ NORMAL_PROP ( E_CARD_SIMPLE_FIELD_MANAGER, "manager", "manager"),
+ NORMAL_PROP ( E_CARD_SIMPLE_FIELD_ASSISTANT, "assistant", "assistant"),
+ NORMAL_PROP ( E_CARD_SIMPLE_FIELD_NICKNAME, "nickname", "nickname"),
+ NORMAL_PROP ( E_CARD_SIMPLE_FIELD_SPOUSE, "spouse", "spouse" ),
+ NORMAL_PROP ( E_CARD_SIMPLE_FIELD_NOTE, "note", "note"),
+ ID_PROP,
+ LIST_PROP ( "email", "email", compare_email ),
+ LIST_PROP ( "phone", "phone", compare_phone ),
+ LIST_PROP ( "address", "address", compare_address ),
+ LIST_PROP ( "category", "category", compare_category ),
+ LIST_PROP ( "arbitrary", "arbitrary", compare_arbitrary )
+};
+static int num_prop_infos = sizeof(prop_info_table) / sizeof(prop_info_table[0]);
+
+static ESExpResult *
+entry_compare(SearchContext *ctx, struct _ESExp *f,
+ int argc, struct _ESExpResult **argv,
+ char *(*compare)(const char*, const char*))
+{
+ ESExpResult *r;
+ int truth = FALSE;
+
+ if (argc == 2
+ && argv[0]->type == ESEXP_RES_STRING
+ && argv[1]->type == ESEXP_RES_STRING) {
+ char *propname;
+ struct prop_info *info = NULL;
+ int i;
+ gboolean any_field;
+
+ propname = argv[0]->value.string;
+
+ any_field = !strcmp(propname, "x-evolution-any-field");
+ for (i = 0; i < num_prop_infos; i ++) {
+ if (any_field
+ || !strcmp (prop_info_table[i].query_prop, propname)) {
+ info = &prop_info_table[i];
+
+ if (info->prop_type == PROP_TYPE_NORMAL) {
+ char *prop = NULL;
+ /* searches where the query's property
+ maps directly to an ecard property */
+
+ prop = e_card_simple_get (ctx->card, info->field_id);
+
+ if (prop && compare(prop, argv[1]->value.string)) {
+ truth = TRUE;
+ }
+ if ((!prop) && compare("", argv[1]->value.string)) {
+ truth = TRUE;
+ }
+ g_free (prop);
+ } else if (info->prop_type == PROP_TYPE_LIST) {
+ /* the special searches that match any of the list elements */
+ truth = info->list_compare (ctx->card, argv[1]->value.string, compare);
+ } else if (info->prop_type == PROP_TYPE_ID) {
+ const char *prop = NULL;
+ /* searches where the query's property
+ maps directly to an ecard property */
+
+ prop = e_card_get_id (ctx->card->card);
+
+ if (prop && compare(prop, argv[1]->value.string)) {
+ truth = TRUE;
+ }
+ if ((!prop) && compare("", argv[1]->value.string)) {
+ truth = TRUE;
+ }
+ }
+
+ /* if we're looking at all fields and find a match,
+ or if we're just looking at this one field,
+ break. */
+ if ((any_field && truth)
+ || !any_field)
+ break;
+ }
+ }
+
+ }
+ r = e_sexp_result_new(f, ESEXP_RES_BOOL);
+ r->value.bool = truth;
+
+ return r;
+}
+#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[] = {
+ /* Time-related functions */
+ { "time-now", func_time_now, 0 },
+ { "make-time", func_make_time, 0 },
+ { "time-add-day", func_time_add_day, 0 },
+ { "time-day-begin", func_time_day_begin, 0 },
+ { "time-day-end", func_time_day_end, 0 },
+
+ /* Component-related functions */
+ { "get-vtype", func_get_vtype, 0 },
+ { "occur-in-time-range?", func_occur_in_time_range, 0 },
+ { "contains?", func_contains, 0 },
+ { "has-alarms?", func_has_alarms, 0 },
+ { "has-categories?", func_has_categories, 0 },
+ { "is-completed?", func_is_completed, 0 },
+ { "completed-before?", func_completed_before, 0 }
+};
+
+gboolean
+cal_backend_object_sexp_match_comp (CalBackendObjectSExp *sexp, CalComponent *comp, CalBackend *backend)
+{
+ ESExpResult *r;
+ gboolean retval;
+
+ sexp->priv->search_context->comp = g_object_ref (comp);
+ sexp->priv->search_context->backend = g_object_ref (backend);
+
+ /* if it's not a valid vcard why is it in our db? :) */
+ if (!sexp->priv->search_context->comp)
+ return FALSE;
+
+ r = e_sexp_eval(sexp->priv->search_sexp);
+
+ retval = (r && r->type == ESEXP_RES_BOOL && r->value.bool);
+
+ g_object_unref (sexp->priv->search_context->comp);
+ g_object_unref (sexp->priv->search_context->backend);
+
+ e_sexp_result_free(sexp->priv->search_sexp, r);
+
+ return retval;
+}
+
+gboolean
+cal_backend_object_sexp_match_object (CalBackendObjectSExp *sexp, const char *object, CalBackend *backend)
+{
+ CalComponent *comp;
+ icalcomponent *icalcomp;
+ gboolean retval;
+
+ icalcomp = icalcomponent_new_from_string ((char *) object);
+ if (!icalcomp)
+ return FALSE;
+
+ comp = cal_component_new ();
+ cal_component_set_icalcomponent (comp, icalcomp);
+
+ retval = cal_backend_object_sexp_match_comp (sexp, comp, backend);
+
+ g_object_unref (comp);
+
+ return retval;
+}
+
+
+
+/**
+ * cal_backend_card_sexp_new:
+ */
+CalBackendObjectSExp *
+cal_backend_object_sexp_new (const char *text)
+{
+ CalBackendObjectSExp *sexp = g_object_new (CAL_TYPE_BACKEND_OBJECT_SEXP, NULL);
+ int esexp_error;
+ int i;
+
+ sexp->priv->search_sexp = e_sexp_new();
+ sexp->priv->text = g_strdup (text);
+
+ for(i=0;i<sizeof(symbols)/sizeof(symbols[0]);i++) {
+ if (symbols[i].type == 1) {
+ e_sexp_add_ifunction(sexp->priv->search_sexp, 0, symbols[i].name,
+ (ESExpIFunc *)symbols[i].func, sexp->priv->search_context);
+ } else {
+ e_sexp_add_function(sexp->priv->search_sexp, 0, symbols[i].name,
+ symbols[i].func, sexp->priv->search_context);
+ }
+ }
+
+ e_sexp_input_text(sexp->priv->search_sexp, text, strlen(text));
+ esexp_error = e_sexp_parse(sexp->priv->search_sexp);
+
+ if (esexp_error == -1) {
+ g_object_unref (sexp);
+ sexp = NULL;
+ }
+
+ return sexp;
+}
+
+const char *
+cal_backend_object_sexp_text (CalBackendObjectSExp *sexp)
+{
+ CalBackendObjectSExpPrivate *priv;
+
+ g_return_val_if_fail (sexp != NULL, NULL);
+ g_return_val_if_fail (CAL_IS_BACKEND_OBJECT_SEXP (sexp), NULL);
+
+ priv = sexp->priv;
+
+ return priv->text;
+}
+
+static void
+cal_backend_object_sexp_dispose (GObject *object)
+{
+ CalBackendObjectSExp *sexp = CAL_BACKEND_OBJECT_SEXP (object);
+
+ if (sexp->priv) {
+ e_sexp_unref(sexp->priv->search_sexp);
+
+ g_free (sexp->priv->text);
+
+ g_free (sexp->priv->search_context);
+ g_free (sexp->priv);
+ sexp->priv = NULL;
+ }
+
+ if (G_OBJECT_CLASS (parent_class)->dispose)
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+cal_backend_object_sexp_class_init (CalBackendObjectSExpClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ /* Set the virtual methods. */
+
+ object_class->dispose = cal_backend_object_sexp_dispose;
+}
+
+static void
+cal_backend_object_sexp_init (CalBackendObjectSExp *sexp)
+{
+ CalBackendObjectSExpPrivate *priv;
+
+ priv = g_new0 (CalBackendObjectSExpPrivate, 1);
+
+ sexp->priv = priv;
+ priv->search_context = g_new (SearchContext, 1);
+}
+
+/**
+ * cal_backend_object_sexp_get_type:
+ */
+GType
+cal_backend_object_sexp_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo info = {
+ sizeof (CalBackendObjectSExpClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) cal_backend_object_sexp_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (CalBackendObjectSExp),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) cal_backend_object_sexp_init
+ };
+
+ type = g_type_register_static (G_TYPE_OBJECT, "CalBackendObjectSExp", &info, 0);
+ }
+
+ return type;
+}