1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * cal-backend-card-sexp.c
4 * Copyright 1999, 2000, 2001, Ximian, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License, version 2, as published by the Free Software Foundation.
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 #include <glib/gi18n-lib.h>
27 #include "libedataserver/e-data-server-util.h"
28 #include <libecal/e-cal-time-util.h>
30 #include "e-cal-backend-sexp.h"
32 static GObjectClass *parent_class;
34 typedef struct _SearchContext SearchContext;
36 struct _ECalBackendSExpPrivate {
39 SearchContext *search_context;
42 struct _SearchContext {
48 static ESExpResult *func_is_completed (ESExp *esexp, int argc, ESExpResult **argv, void *data);
51 * e_cal_backend_sexp_func_time_now:
52 * @esexp: An #ESExp object.
53 * @argc: Number of arguments.
54 * @argv: The arguments.
55 * @data: Closure data.
57 * Processes the (time-now) sexp expression.
59 * Return value: The result of the function.
62 e_cal_backend_sexp_func_time_now (ESExp *esexp, int argc, ESExpResult **argv, void *data)
67 e_sexp_fatal_error (esexp, _("\"%s\" expects no arguments"),
72 result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
73 result->value.time = time (NULL);
79 * e_cal_backend_sexp_func_make_time:
80 * @esexp: An #ESExp object.
81 * @argc: Number of arguments.
82 * @argv: The arguments.
83 * @data: Closure data.
86 * ISODATE - string, ISO 8601 date/time representation
88 * Constructs a time_t value for the specified date.
90 * Return value: The result of the function.
93 e_cal_backend_sexp_func_make_time (ESExp *esexp, int argc, ESExpResult **argv, void *data)
100 e_sexp_fatal_error (esexp, _("\"%s\" expects one argument"),
105 if (argv[0]->type != ESEXP_RES_STRING) {
106 e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
107 "argument to be a string"),
111 str = argv[0]->value.string;
113 e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
114 "argument to be a string"),
119 t = time_from_isodate (str);
121 e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
122 "argument to be an ISO 8601 "
128 result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
129 result->value.time = t;
135 * e_cal_backend_sexp_func_time_add_day:
136 * @esexp: An #ESExp object.
137 * @argc: Number of arguments.
138 * @argv: The arguments.
139 * @data: Closure data.
141 * (time-add-day TIME N)
142 * TIME - time_t, base time
143 * N - int, number of days to add
145 * Adds the specified number of days to a time value.
147 * FIXME: TIMEZONES - need to use a timezone or daylight saving changes will
148 * make the result incorrect.
150 * Return value: The result of the function.
153 e_cal_backend_sexp_func_time_add_day (ESExp *esexp, int argc, ESExpResult **argv, void *data)
160 e_sexp_fatal_error (esexp, _("\"%s\" expects two arguments"),
165 if (argv[0]->type != ESEXP_RES_TIME) {
166 e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
167 "argument to be a time_t"),
171 t = argv[0]->value.time;
173 if (argv[1]->type != ESEXP_RES_INT) {
174 e_sexp_fatal_error (esexp, _("\"%s\" expects the second "
175 "argument to be an integer"),
179 n = argv[1]->value.number;
181 result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
182 result->value.time = time_add_day (t, n);
188 * e_cal_backend_sexp_func_time_day_begin:
189 * @esexp: An #ESExp object.
190 * @argc: Number of arguments.
191 * @argv: The arguments.
192 * @data: Closure data.
194 * (time-day-begin TIME)
195 * TIME - time_t, base time
197 * Returns the start of the day, according to the local time.
199 * FIXME: TIMEZONES - this uses the current Unix timezone.
201 * Return value: The result of the function.
204 e_cal_backend_sexp_func_time_day_begin (ESExp *esexp, int argc, ESExpResult **argv, void *data)
210 e_sexp_fatal_error (esexp, _("\"%s\" expects one argument"),
215 if (argv[0]->type != ESEXP_RES_TIME) {
216 e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
217 "argument to be a time_t"),
221 t = argv[0]->value.time;
223 result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
224 result->value.time = time_day_begin (t);
230 * e_cal_backend_sexp_func_time_day_end:
231 * @esexp: An #ESExp object.
232 * @argc: Number of arguments.
233 * @argv: The arguments.
234 * @data: Closure data.
236 * (time-day-end TIME)
237 * TIME - time_t, base time
239 * Returns the end of the day, according to the local time.
241 * FIXME: TIMEZONES - this uses the current Unix timezone.
243 * Return value: The result of the function.
246 e_cal_backend_sexp_func_time_day_end (ESExp *esexp, int argc, ESExpResult **argv, void *data)
252 e_sexp_fatal_error (esexp, _("\"%s\" expects one argument"),
257 if (argv[0]->type != ESEXP_RES_TIME) {
258 e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
259 "argument to be a time_t"),
263 t = argv[0]->value.time;
265 result = e_sexp_result_new (esexp, ESEXP_RES_TIME);
266 result->value.time = time_day_end (t);
273 * UID - the uid of the component
275 * Returns a boolean indicating whether the component has the given UID
278 func_uid (ESExp *esexp, int argc, ESExpResult **argv, void *data)
280 SearchContext *ctx = data;
281 const char *uid = NULL, *arg_uid;
285 /* Check argument types */
288 e_sexp_fatal_error (esexp, _("\"%s\" expects one argument"),
293 if (argv[0]->type != ESEXP_RES_STRING) {
294 e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
295 "argument to be a string"),
300 arg_uid = argv[0]->value.string;
301 e_cal_component_get_uid (ctx->comp, &uid);
303 if (!arg_uid && !uid)
305 else if ((!arg_uid || !uid) && arg_uid != uid)
307 else if (e_util_utf8_strstrcase (arg_uid, uid) != NULL && strlen (arg_uid) == strlen (uid))
312 result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
313 result->value.bool = equal;
319 check_instance_time_range_cb (ECalComponent *comp, time_t instance_start, time_t instance_end, gpointer data)
321 SearchContext *ctx = data;
323 /* if we get called, the event has an occurrence in the given time range */
329 static icaltimezone *
330 resolve_tzid (const char *tzid, gpointer user_data)
332 SearchContext *ctx = user_data;
334 if (!tzid || !tzid[0])
336 else if (!strcmp (tzid, "UTC"))
337 return icaltimezone_get_utc_timezone ();
339 return e_cal_backend_internal_get_timezone (ctx->backend, tzid);
342 /* (occur-in-time-range? START END)
344 * START - time_t, start of the time range
345 * END - time_t, end of the time range
347 * Returns a boolean indicating whether the component has any occurrences in the
348 * specified time range.
351 func_occur_in_time_range (ESExp *esexp, int argc, ESExpResult **argv, void *data)
353 SearchContext *ctx = data;
356 icaltimezone *default_zone;
358 /* Check argument types */
361 e_sexp_fatal_error (esexp, _("\"%s\" expects two arguments"),
362 "occur-in-time-range");
366 if (argv[0]->type != ESEXP_RES_TIME) {
367 e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
368 "argument to be a time_t"),
369 "occur-in-time-range");
372 start = argv[0]->value.time;
374 if (argv[1]->type != ESEXP_RES_TIME) {
375 e_sexp_fatal_error (esexp, _("\"%s\" expects the second "
376 "argument to be a time_t"),
377 "occur-in-time-range");
380 end = argv[1]->value.time;
382 /* See if the object occurs in the specified time range */
383 default_zone = e_cal_backend_internal_get_default_timezone (ctx->backend);
385 default_zone = icaltimezone_get_utc_timezone ();
388 e_cal_recur_generate_instances (ctx->comp, start, end,
389 (ECalRecurInstanceFn) check_instance_time_range_cb,
390 ctx, resolve_tzid, ctx,
393 result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
394 result->value.bool = ctx->occurs;
400 func_due_in_time_range (ESExp *esexp, int argc, ESExpResult **argv, void *data)
402 SearchContext *ctx = data;
406 ECalComponentDateTime dt;
410 /* Check argument types */
413 e_sexp_fatal_error (esexp, _("\"%s\" expects two arguments"),
414 "due-in-time-range");
418 if (argv[0]->type != ESEXP_RES_TIME) {
419 e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
420 "argument to be a time_t"),
421 "due-in-time-range");
425 start = argv[0]->value.time;
427 if (argv[1]->type != ESEXP_RES_TIME) {
428 e_sexp_fatal_error (esexp, _("\"%s\" expects the second "
429 "argument to be a time_t"),
430 "due-in-time-range");
434 end = argv[1]->value.time;
435 e_cal_component_get_due (ctx->comp, &dt);
437 if(dt.value != NULL) {
438 zone = resolve_tzid (dt.tzid, ctx);
439 result = e_sexp_result_new (esexp, ESEXP_RES_INT);
441 due_t = icaltime_as_timet_with_zone(*dt.value,zone);
443 due_t = icaltime_as_timet(*dt.value);
446 if(dt.value != NULL && (due_t <= end && due_t >= start))
451 result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
452 result->value.bool = retval;
454 e_cal_component_free_datetime (&dt);
459 /* Returns whether a list of ECalComponentText items matches the specified string */
461 matches_text_list (GSList *text_list, const char *str)
468 for (l = text_list; l; l = l->next) {
469 ECalComponentText *text;
472 g_assert (text->value != NULL);
474 if (e_util_utf8_strstrcasedecomp (text->value, str) != NULL) {
483 /* Returns whether the comments in a component matches the specified string */
485 matches_comment (ECalComponent *comp, const char *str)
490 e_cal_component_get_comment_list (comp, &list);
491 matches = matches_text_list (list, str);
492 e_cal_component_free_text_list (list);
497 /* Returns whether the description in a component matches the specified string */
499 matches_description (ECalComponent *comp, const char *str)
504 e_cal_component_get_description_list (comp, &list);
505 matches = matches_text_list (list, str);
506 e_cal_component_free_text_list (list);
512 matches_attendee (ECalComponent *comp, const char *str)
514 GSList *a_list = NULL, *l;
515 gboolean matches = FALSE;
517 e_cal_component_get_attendee_list (comp, &a_list);
519 for (l = a_list; l; l = l->next) {
520 ECalComponentAttendee *att = l->data;
522 if ((att->value && e_util_strstrcase (att->value, str)) || (att->cn != NULL &&
523 e_util_strstrcase (att->cn, str))) {
529 e_cal_component_free_attendee_list (a_list);
536 matches_organizer (ECalComponent *comp, const char *str)
539 ECalComponentOrganizer org;
541 e_cal_component_get_organizer (comp, &org);
545 if ((org.value && e_util_strstrcase (org.value, str)) ||
546 (org.cn && e_util_strstrcase (org.cn, str)))
553 matches_classification (ECalComponent *comp, const char *str)
555 ECalComponentClassification classification;
556 ECalComponentClassification classification1;
561 if(g_str_equal (str, "Public"))
562 classification1 = E_CAL_COMPONENT_CLASS_PUBLIC;
563 else if(g_str_equal (str, "Private"))
564 classification1 = E_CAL_COMPONENT_CLASS_PRIVATE;
565 else if(g_str_equal (str, "Confidential"))
566 classification1 = E_CAL_COMPONENT_CLASS_CONFIDENTIAL;
568 classification1 = E_CAL_COMPONENT_CLASS_UNKNOWN;
570 e_cal_component_get_classification(comp, &classification);
572 return (classification == classification1 ? TRUE : FALSE);
575 /* Returns whether the summary in a component matches the specified string */
577 matches_summary (ECalComponent *comp, const char *str)
579 ECalComponentText text;
581 e_cal_component_get_summary (comp, &text);
589 return e_util_utf8_strstrcasedecomp (text.value, str) != NULL;
592 /* Returns whether the location in a component matches the specified string */
594 matches_location (ECalComponent *comp, const char *str)
596 const char *location = NULL;
598 e_cal_component_get_location (comp, &location);
603 return e_util_utf8_strstrcasedecomp (location, str) != NULL;
606 /* Returns whether any text field in a component matches the specified string */
608 matches_any (ECalComponent *comp, const char *str)
610 /* As an optimization, and to make life easier for the individual
611 * predicate functions, see if we are looking for the empty string right
614 if (strlen (str) == 0)
617 return (matches_comment (comp, str)
618 || matches_description (comp, str)
619 || matches_summary (comp, str)
620 || matches_location (comp, str));
624 matches_priority (ECalComponent *comp ,const char *pr)
626 int *priority = NULL;
628 e_cal_component_get_priority (comp, &priority);
630 if (!priority || !*priority)
633 if (g_str_equal (pr, "HIGH") && *priority <= 4)
635 else if (g_str_equal (pr, "NORMAL") && *priority == 5)
637 else if (g_str_equal (pr, "LOW") && *priority > 5)
639 else if (g_str_equal (pr, "UNDEFINED") && (!priority || !*priority))
646 matches_status (ECalComponent *comp ,const char *str)
648 icalproperty_status status ;
653 e_cal_component_get_status (comp, &status);
655 if (g_str_equal (str, "NOT STARTED") && status == ICAL_STATUS_NONE)
657 else if (g_str_equal (str, "COMPLETED") && status == ICAL_STATUS_COMPLETED)
659 else if(g_str_equal (str, "CANCELLED") && status == ICAL_STATUS_CANCELLED)
661 else if(g_str_equal (str, "IN PROGRESS") && status == ICAL_STATUS_INPROCESS)
668 func_has_attachment (ESExp *esexp, int argc, ESExpResult **argv, void *data)
670 SearchContext *ctx = data;
674 e_sexp_fatal_error (esexp, _("\"%s\" expects no arguments"),
679 result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
680 result->value.bool = e_cal_component_has_attachments (ctx->comp);
686 func_percent_complete (ESExp *esexp, int argc, ESExpResult **argv, void *data)
688 SearchContext *ctx = data;
689 ESExpResult *result = NULL;
693 e_sexp_fatal_error (esexp, _("\"%s\" expects no arguments"),
694 "percent-completed");
698 e_cal_component_get_percent (ctx->comp, &percent);
700 if (percent && *percent) {
701 result = e_sexp_result_new (esexp, ESEXP_RES_INT);
702 result->value.number = *percent;
709 /* (contains? FIELD STR)
711 * FIELD - string, name of field to match (any, comment, description, summary, location)
712 * STR - string, match string
714 * Returns a boolean indicating whether the specified field contains the
718 func_contains (ESExp *esexp, int argc, ESExpResult **argv, void *data)
720 SearchContext *ctx = data;
726 /* Check argument types */
729 e_sexp_fatal_error (esexp, _("\"%s\" expects two arguments"),
734 if (argv[0]->type != ESEXP_RES_STRING) {
735 e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
736 "argument to be a string"),
740 field = argv[0]->value.string;
742 if (argv[1]->type != ESEXP_RES_STRING) {
743 e_sexp_fatal_error (esexp, _("\"%s\" expects the second "
744 "argument to be a string"),
748 str = argv[1]->value.string;
750 /* See if it matches */
752 if (strcmp (field, "any") == 0)
753 matches = matches_any (ctx->comp, str);
754 else if (strcmp (field, "comment") == 0)
755 matches = matches_comment (ctx->comp, str);
756 else if (strcmp (field, "description") == 0)
757 matches = matches_description (ctx->comp, str);
758 else if (strcmp (field, "summary") == 0)
759 matches = matches_summary (ctx->comp, str);
760 else if (strcmp (field, "location") == 0)
761 matches = matches_location (ctx->comp, str);
762 else if (strcmp (field, "attendee") == 0)
763 matches = matches_attendee (ctx->comp, str);
764 else if (strcmp (field, "organizer") == 0)
765 matches = matches_organizer (ctx->comp, str);
766 else if(strcmp (field, "classification") == 0)
767 matches = matches_classification (ctx->comp, str);
768 else if(strcmp (field, "status") == 0)
769 matches = matches_status (ctx->comp, str);
770 else if(strcmp (field, "priority") == 0)
771 matches = matches_priority (ctx->comp, str);
773 e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
774 "argument to be either \"any\", "
775 "\"summary\", or \"description\", or \"location\", or \"attendee\", or \"organizer\", or \"classification\""),
780 result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
781 result->value.bool = matches;
788 * A boolean value for components that have/dont have alarms.
790 * Returns: a boolean indicating whether the component has alarms or not.
793 func_has_alarms (ESExp *esexp, int argc, ESExpResult **argv, void *data)
795 SearchContext *ctx = data;
798 /* Check argument types */
801 e_sexp_fatal_error (esexp, _("\"%s\" expects no arguments"),
806 result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
807 result->value.bool = e_cal_component_has_alarms (ctx->comp);
812 /* (has-alarms-in-range? START END)
814 * START - time_t, start of the time range
815 * END - time_t, end of the time range
817 * Returns: a boolean indicating whether the component has alarms in the given
821 func_has_alarms_in_range (ESExp *esexp, int argc, ESExpResult **argv, void *data)
825 icaltimezone *default_zone;
826 ECalComponentAlarms *alarms;
827 ECalComponentAlarmAction omit[] = {-1};
828 SearchContext *ctx = data;
830 /* Check argument types */
833 e_sexp_fatal_error (esexp, _("\"%s\" expects two arguments"),
834 "has-alarms-in-range");
838 if (argv[0]->type != ESEXP_RES_TIME) {
839 e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
840 "argument to be a time_t"),
841 "has-alarms-in-range");
844 start = argv[0]->value.time;
846 if (argv[1]->type != ESEXP_RES_TIME) {
847 e_sexp_fatal_error (esexp, _("\"%s\" expects the second "
848 "argument to be a time_t"),
849 "has-alarms-in-range");
852 end = argv[1]->value.time;
854 /* See if the object has alarms in the given time range */
855 default_zone = e_cal_backend_internal_get_default_timezone (ctx->backend);
857 default_zone = icaltimezone_get_utc_timezone ();
859 alarms = e_cal_util_generate_alarms_for_comp (ctx->comp, start, end,
863 result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
865 result->value.bool = TRUE;
866 e_cal_component_alarms_free (alarms);
868 result->value.bool = FALSE;
873 /* (has-categories? STR+)
874 * (has-categories? #f)
876 * STR - At least one string specifying a category
877 * Or you can specify a single #f (boolean false) value for components
878 * that have no categories assigned to them ("unfiled").
880 * Returns a boolean indicating whether the component has all the specified
884 func_has_categories (ESExp *esexp, int argc, ESExpResult **argv, void *data)
886 SearchContext *ctx = data;
893 /* Check argument types */
896 e_sexp_fatal_error (esexp, _("\"%s\" expects at least one "
902 if (argc == 1 && argv[0]->type == ESEXP_RES_BOOL)
908 for (i = 0; i < argc; i++)
909 if (argv[i]->type != ESEXP_RES_STRING) {
910 e_sexp_fatal_error (esexp, _("\"%s\" expects "
921 /* Search categories. First, if there are no categories we return
922 * whether unfiled components are supposed to match.
925 e_cal_component_get_categories_list (ctx->comp, &categories);
927 result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
928 result->value.bool = unfiled;
933 /* Otherwise, we *do* have categories but unfiled components were
934 * requested, so this component does not match.
937 result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
938 result->value.bool = FALSE;
945 for (i = 0; i < argc; i++) {
948 gboolean has_category;
950 sought = argv[i]->value.string;
952 has_category = FALSE;
954 for (l = categories; l; l = l->next) {
955 const char *category;
959 if (strcmp (category, sought) == 0) {
971 e_cal_component_free_categories_list (categories);
973 result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
974 result->value.bool = matches;
979 /* (has-recurrences?)
981 * A boolean value for components that have/dont have recurrences.
983 * Returns: a boolean indicating whether the component has recurrences or not.
986 func_has_recurrences (ESExp *esexp, int argc, ESExpResult **argv, void *data)
988 SearchContext *ctx = data;
991 /* Check argument types */
994 e_sexp_fatal_error (esexp, _("\"%s\" expects no arguments"),
999 result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
1000 result->value.bool = (e_cal_component_has_recurrences (ctx->comp) || e_cal_component_is_instance (ctx->comp));
1007 * Returns a boolean indicating whether the component is completed (i.e. has
1008 * a COMPLETED property. This is really only useful for TODO components.
1010 static ESExpResult *
1011 func_is_completed (ESExp *esexp, int argc, ESExpResult **argv, void *data)
1013 SearchContext *ctx = data;
1014 ESExpResult *result;
1015 struct icaltimetype *t;
1016 gboolean complete = FALSE;
1018 /* Check argument types */
1021 e_sexp_fatal_error (esexp, _("\"%s\" expects no arguments"),
1026 e_cal_component_get_completed (ctx->comp, &t);
1029 e_cal_component_free_icaltimetype (t);
1032 result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
1033 result->value.bool = complete;
1038 /* (completed-before? TIME)
1042 * Returns a boolean indicating whether the component was completed on or
1043 * before the given time (i.e. it checks the COMPLETED property).
1044 * This is really only useful for TODO components.
1046 static ESExpResult *
1047 func_completed_before (ESExp *esexp, int argc, ESExpResult **argv, void *data)
1049 SearchContext *ctx = data;
1050 ESExpResult *result;
1051 struct icaltimetype *tt;
1053 gboolean retval = FALSE;
1054 time_t before_time, completed_time;
1056 /* Check argument types */
1059 e_sexp_fatal_error (esexp, _("\"%s\" expects one argument"),
1060 "completed-before");
1064 if (argv[0]->type != ESEXP_RES_TIME) {
1065 e_sexp_fatal_error (esexp, _("\"%s\" expects the first "
1066 "argument to be a time_t"),
1067 "completed-before");
1070 before_time = argv[0]->value.time;
1072 e_cal_component_get_completed (ctx->comp, &tt);
1074 /* COMPLETED must be in UTC. */
1075 zone = icaltimezone_get_utc_timezone ();
1076 completed_time = icaltime_as_timet_with_zone (*tt, zone);
1079 g_print ("Query Time : %s", ctime (&before_time));
1080 g_print ("Completed Time: %s", ctime (&completed_time));
1083 /* We want to return TRUE if before_time is after
1085 if (difftime (before_time, completed_time) > 0) {
1087 g_print (" Returning TRUE\n");
1092 e_cal_component_free_icaltimetype (tt);
1095 result = e_sexp_result_new (esexp, ESEXP_RES_BOOL);
1096 result->value.bool = retval;
1102 static struct prop_info {
1103 ECardSimpleField field_id;
1104 const char *query_prop;
1105 const char *ecard_prop;
1106 #define PROP_TYPE_NORMAL 0x01
1107 #define PROP_TYPE_LIST 0x02
1108 #define PROP_TYPE_LISTITEM 0x03
1109 #define PROP_TYPE_ID 0x04
1111 gboolean (*list_compare)(ECardSimple *ecard, const char *str,
1112 char *(*compare)(const char*, const char*));
1114 } prop_info_table[] = {
1115 #define NORMAL_PROP(f,q,e) {f, q, e, PROP_TYPE_NORMAL, NULL}
1116 #define ID_PROP {0, "id", NULL, PROP_TYPE_ID, NULL}
1117 #define LIST_PROP(q,e,c) {0, q, e, PROP_TYPE_LIST, c}
1119 /* query prop, ecard prop, type, list compare function */
1120 NORMAL_PROP ( E_CARD_SIMPLE_FIELD_FILE_AS, "file_as", "file_as" ),
1121 LIST_PROP ( "full_name", "full_name", compare_name), /* not really a list, but we need to compare both full and surname */
1122 NORMAL_PROP ( E_CARD_SIMPLE_FIELD_URL, "url", "url" ),
1123 NORMAL_PROP ( E_CARD_SIMPLE_FIELD_MAILER, "mailer", "mailer"),
1124 NORMAL_PROP ( E_CARD_SIMPLE_FIELD_ORG, "org", "org"),
1125 NORMAL_PROP ( E_CARD_SIMPLE_FIELD_ORG_UNIT, "org_unit", "org_unit"),
1126 NORMAL_PROP ( E_CARD_SIMPLE_FIELD_OFFICE, "office", "office"),
1127 NORMAL_PROP ( E_CARD_SIMPLE_FIELD_TITLE, "title", "title"),
1128 NORMAL_PROP ( E_CARD_SIMPLE_FIELD_ROLE, "role", "role"),
1129 NORMAL_PROP ( E_CARD_SIMPLE_FIELD_MANAGER, "manager", "manager"),
1130 NORMAL_PROP ( E_CARD_SIMPLE_FIELD_ASSISTANT, "assistant", "assistant"),
1131 NORMAL_PROP ( E_CARD_SIMPLE_FIELD_NICKNAME, "nickname", "nickname"),
1132 NORMAL_PROP ( E_CARD_SIMPLE_FIELD_SPOUSE, "spouse", "spouse" ),
1133 NORMAL_PROP ( E_CARD_SIMPLE_FIELD_NOTE, "note", "note"),
1135 LIST_PROP ( "email", "email", compare_email ),
1136 LIST_PROP ( "phone", "phone", compare_phone ),
1137 LIST_PROP ( "address", "address", compare_address ),
1138 LIST_PROP ( "category", "category", compare_category ),
1139 LIST_PROP ( "arbitrary", "arbitrary", compare_arbitrary )
1141 static int num_prop_infos = sizeof(prop_info_table) / sizeof(prop_info_table[0]);
1143 static ESExpResult *
1144 entry_compare(SearchContext *ctx, struct _ESExp *f,
1145 int argc, struct _ESExpResult **argv,
1146 char *(*compare)(const char*, const char*))
1152 && argv[0]->type == ESEXP_RES_STRING
1153 && argv[1]->type == ESEXP_RES_STRING) {
1155 struct prop_info *info = NULL;
1159 propname = argv[0]->value.string;
1161 any_field = !strcmp(propname, "x-evolution-any-field");
1162 for (i = 0; i < num_prop_infos; i ++) {
1164 || !strcmp (prop_info_table[i].query_prop, propname)) {
1165 info = &prop_info_table[i];
1167 if (info->prop_type == PROP_TYPE_NORMAL) {
1169 /* searches where the query's property
1170 maps directly to an ecard property */
1172 prop = e_card_simple_get (ctx->card, info->field_id);
1174 if (prop && compare(prop, argv[1]->value.string)) {
1177 if ((!prop) && compare("", argv[1]->value.string)) {
1181 } else if (info->prop_type == PROP_TYPE_LIST) {
1182 /* the special searches that match any of the list elements */
1183 truth = info->list_compare (ctx->card, argv[1]->value.string, compare);
1184 } else if (info->prop_type == PROP_TYPE_ID) {
1185 const char *prop = NULL;
1186 /* searches where the query's property
1187 maps directly to an ecard property */
1189 prop = e_card_get_id (ctx->card->card);
1191 if (prop && compare(prop, argv[1]->value.string)) {
1194 if ((!prop) && compare("", argv[1]->value.string)) {
1199 /* if we're looking at all fields and find a match,
1200 or if we're just looking at this one field,
1202 if ((any_field && truth)
1209 r = e_sexp_result_new(f, ESEXP_RES_BOOL);
1210 r->value.bool = truth;
1216 /* 'builtin' functions */
1220 int type; /* set to 1 if a function can perform shortcut evaluation, or
1221 doesn't execute everything, 0 otherwise */
1223 /* Time-related functions */
1224 { "time-now", e_cal_backend_sexp_func_time_now, 0 },
1225 { "make-time", e_cal_backend_sexp_func_make_time, 0 },
1226 { "time-add-day", e_cal_backend_sexp_func_time_add_day, 0 },
1227 { "time-day-begin", e_cal_backend_sexp_func_time_day_begin, 0 },
1228 { "time-day-end", e_cal_backend_sexp_func_time_day_end, 0 },
1229 /* Component-related functions */
1230 { "uid?", func_uid, 0 },
1231 { "occur-in-time-range?", func_occur_in_time_range, 0 },
1232 { "due-in-time-range?", func_due_in_time_range, 0 },
1233 { "contains?", func_contains, 0 },
1234 { "has-alarms?", func_has_alarms, 0 },
1235 { "has-alarms-in-range?", func_has_alarms_in_range, 0 },
1236 { "has-recurrences?", func_has_recurrences, 0 },
1237 { "has-categories?", func_has_categories, 0 },
1238 { "is-completed?", func_is_completed, 0 },
1239 { "completed-before?", func_completed_before, 0 },
1240 { "has-attachments?", func_has_attachment, 0 },
1241 { "percent-complete?", func_percent_complete, 0 }
1245 * e_cal_backend_sexp_match_comp:
1246 * @sexp: An #ESExp object.
1247 * @comp: Component to match against the expression.
1248 * @backend: Backend.
1250 * Matches the given ECalComponent against the expression.
1252 * Return value: TRUE if the component matched the expression, FALSE if not.
1255 e_cal_backend_sexp_match_comp (ECalBackendSExp *sexp, ECalComponent *comp, ECalBackend *backend)
1260 g_return_val_if_fail (E_IS_CAL_BACKEND_SEXP (sexp), FALSE);
1261 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
1262 g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
1264 sexp->priv->search_context->comp = g_object_ref (comp);
1265 sexp->priv->search_context->backend = g_object_ref (backend);
1267 /* if it's not a valid vcard why is it in our db? :) */
1268 if (!sexp->priv->search_context->comp) {
1269 g_object_unref (sexp->priv->search_context->backend);
1272 r = e_sexp_eval(sexp->priv->search_sexp);
1274 retval = (r && r->type == ESEXP_RES_BOOL && r->value.bool);
1276 g_object_unref (sexp->priv->search_context->comp);
1277 g_object_unref (sexp->priv->search_context->backend);
1279 e_sexp_result_free(sexp->priv->search_sexp, r);
1285 * e_cal_backend_sexp_match_object:
1286 * @sexp: An #ESExp object.
1287 * @object: An iCalendar string.
1288 * @backend: A backend.
1290 * Match an iCalendar expression against the expression.
1292 * Return value: TRUE if the object matches the expression, FALSE if not.
1295 e_cal_backend_sexp_match_object (ECalBackendSExp *sexp, const char *object, ECalBackend *backend)
1297 ECalComponent *comp;
1298 icalcomponent *icalcomp;
1301 icalcomp = icalcomponent_new_from_string ((char *) object);
1305 comp = e_cal_component_new ();
1306 e_cal_component_set_icalcomponent (comp, icalcomp);
1308 retval = e_cal_backend_sexp_match_comp (sexp, comp, backend);
1310 g_object_unref (comp);
1318 * e_cal_backend_card_sexp_new:
1319 * @text: The expression to use.
1321 * Creates a new #EXCalBackendSExp object.
1323 * Return value: The newly created ECalBackendSExp object.
1326 e_cal_backend_sexp_new (const char *text)
1328 ECalBackendSExp *sexp = g_object_new (E_TYPE_CAL_BACKEND_SEXP, NULL);
1332 sexp->priv->search_sexp = e_sexp_new();
1333 sexp->priv->text = g_strdup (text);
1335 for (i = 0; i < G_N_ELEMENTS (symbols); i++) {
1336 if (symbols[i].type == 1) {
1337 e_sexp_add_ifunction(sexp->priv->search_sexp, 0, symbols[i].name,
1338 (ESExpIFunc *)symbols[i].func, sexp->priv->search_context);
1340 e_sexp_add_function(sexp->priv->search_sexp, 0, symbols[i].name,
1341 symbols[i].func, sexp->priv->search_context);
1345 e_sexp_input_text(sexp->priv->search_sexp, text, strlen(text));
1346 esexp_error = e_sexp_parse(sexp->priv->search_sexp);
1348 if (esexp_error == -1) {
1349 g_object_unref (sexp);
1357 * e_cal_backend_sexp_text:
1358 * @sexp: An #ECalBackendSExp object.
1360 * Retrieve the text expression for the given ECalBackendSExp object.
1362 * Return value: The text expression.
1365 e_cal_backend_sexp_text (ECalBackendSExp *sexp)
1367 ECalBackendSExpPrivate *priv;
1369 g_return_val_if_fail (sexp != NULL, NULL);
1370 g_return_val_if_fail (E_IS_CAL_BACKEND_SEXP (sexp), NULL);
1378 e_cal_backend_sexp_dispose (GObject *object)
1380 ECalBackendSExp *sexp = E_CAL_BACKEND_SEXP (object);
1383 e_sexp_unref(sexp->priv->search_sexp);
1385 g_free (sexp->priv->text);
1387 g_free (sexp->priv->search_context);
1388 g_free (sexp->priv);
1392 if (G_OBJECT_CLASS (parent_class)->dispose)
1393 G_OBJECT_CLASS (parent_class)->dispose (object);
1397 e_cal_backend_sexp_class_init (ECalBackendSExpClass *klass)
1399 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1401 parent_class = g_type_class_peek_parent (klass);
1403 /* Set the virtual methods. */
1405 object_class->dispose = e_cal_backend_sexp_dispose;
1409 e_cal_backend_sexp_init (ECalBackendSExp *sexp)
1411 ECalBackendSExpPrivate *priv;
1413 priv = g_new0 (ECalBackendSExpPrivate, 1);
1416 priv->search_context = g_new (SearchContext, 1);
1420 * e_cal_backend_sexp_get_type:
1422 * Registers the #ECalBackendSExp class if needed.
1424 * Return value: The unique identifier of the class.
1427 e_cal_backend_sexp_get_type (void)
1429 static GType type = 0;
1433 sizeof (ECalBackendSExpClass),
1434 NULL, /* base_class_init */
1435 NULL, /* base_class_finalize */
1436 (GClassInitFunc) e_cal_backend_sexp_class_init,
1437 NULL, /* class_finalize */
1438 NULL, /* class_data */
1439 sizeof (ECalBackendSExp),
1440 0, /* n_preallocs */
1441 (GInstanceInitFunc) e_cal_backend_sexp_init
1444 type = g_type_register_static (G_TYPE_OBJECT, "ECalBackendSExp", &info, 0);