[PATCH]: Improved dates filters



	Hi all,
this patch gives the possibility to specify relative dates in the dates filters: for instance if you want to filter the previous month you enter (in US locale mm-dd-yyyy):
-1-1-* to -1-31-*
Meaning: take the current date then decrease month by one and replace the day by 1 for the low date and same thing for the high date but you replace the day by 31 which means take everything of the previous month. As you guess the star means take the field of the current date as it is, a number (with no sign) means replace the corresponding field by this number and an offset (signed number like -3 or +5) means add this offset to the current field. I normally took great care to test it but it could still have some rough edges, so interested people are invited to test it first. The patch applies cleanly on 2.3.7 but I have tested it until now only on 2.3.5 (well yes I am a bit behind), but nothing connected in the filter arena has been modified in between so it should work.
Waiting for you feddback,
Bye
Manu
diff -u ../../balsa-2.3.5/libbalsa/filter.c balsa-2.3.5/libbalsa/filter.c
--- ../../balsa-2.3.5/libbalsa/filter.c	2005-09-20 14:08:25.000000000 -0400
+++ balsa-2.3.5/libbalsa/filter.c	2005-12-04 20:54:25.000000000 -0400
@@ -201,9 +201,9 @@
     case CONDITION_REGEX:
         break;
     case CONDITION_DATE:
-        match = message->headers->date>=cond->match.date.date_low 
-	       && (cond->match.date.date_high==0 || 
-                   message->headers->date<=cond->match.date.date_high);
+        match = message->headers->date>=cond->match.date.date_low.computed
+	       && (cond->match.date.date_high.computed==0 || 
+                   message->headers->date<=cond->match.date.date_high.computed);
         break;
     case CONDITION_FLAG:
         match = LIBBALSA_MESSAGE_HAS_FLAG(message, cond->match.flags);
@@ -231,19 +231,27 @@
 filters_prepare_to_run(GSList * filters)
 {
     LibBalsaFilter* fil;
-    gboolean ok=TRUE;
-
+    gboolean ok = TRUE;
+    g_print("prepare_to_run begins...");
     for(;filters;filters=g_slist_next(filters)) {
 	fil=(LibBalsaFilter*) filters->data;
 	if (!FILTER_CHKFLAG(fil,FILTER_VALID)) {
 		libbalsa_information(LIBBALSA_INFORMATION_ERROR,
                                      _("Invalid filter: %s"),fil->name);
-	    ok=FALSE;
+	    ok = FALSE;
+	}
+	else {
+	    struct tm today;
+	    time_t tmp = time(&tmp);
+	    
+	    (void) localtime_r(&tmp, &today);
+	    if (!FILTER_CHKFLAG(fil,FILTER_COMPILED))
+		ok = libbalsa_filter_compile_regexs(fil);
+	    ok = ok &&
+		libbalsa_condition_prepare_dates(fil->condition, &today);
 	}
-	else if (!FILTER_CHKFLAG(fil,FILTER_COMPILED))
-	    ok=libbalsa_filter_compile_regexs(fil);
     }
-
+    g_print("prepare_to_run ends\n");
     return ok;
 }
 
Seulement dans balsa-2.3.5/libbalsa/: filter.c~
Seulement dans balsa-2.3.5/libbalsa/: filter-error.o
diff -u ../../balsa-2.3.5/libbalsa/filter-file.c balsa-2.3.5/libbalsa/filter-file.c
--- ../../balsa-2.3.5/libbalsa/filter-file.c	2005-03-19 23:05:41.000000000 -0400
+++ balsa-2.3.5/libbalsa/filter-file.c	2005-11-26 21:26:55.000000000 -0400
@@ -220,7 +220,6 @@
     gint nbregexs, i;
     LibBalsaConditionRegex *newreg;
 #endif
-    struct tm date;
     gchar *str, *p;
     unsigned fields;
 
@@ -257,29 +256,19 @@
 	break;
     case CONDITION_DATE:
 	str = libbalsa_conf_get_string("Low-date");
-	if (str[0] == '\0')
-	    newc->match.date.date_low = 0;
-	else {
-	    (void) strptime("00:00:00", "%T", &date);
-	    p = (gchar *) strptime(str, "%Y-%m-%d", &date);
-	    if (!p || *p != '\0')
-		filter_errno = FILTER_EFILESYN;
-	    else
-		newc->match.date.date_low = mktime(&date);
-	}
-	g_free(str);
+	if (strlen(str)>15) {
+	    newc->match.date.date_low.symb_date =
+		g_strndup(str, 15);
+	    g_free(str);
+	} else
+	    newc->match.date.date_low.symb_date = str;
 	str = libbalsa_conf_get_string("High-date");
-	if (str[0] == '\0')
-	    newc->match.date.date_high = 0;
-	else {
-	    (void) strptime("23:59:59", "%T", &date);
-	    p = (gchar *) strptime(str, "%Y-%m-%d", &date);
-	    if (!p || *p != '\0')
-		filter_errno = FILTER_EFILESYN;
-	    else
-		newc->match.date.date_high = mktime(&date);
-	}
-	g_free(str);
+	if (strlen(str)>15) {
+	    newc->match.date.date_high.symb_date =
+		g_strndup(str, 15);
+	    g_free(str);
+	} else
+	    newc->match.date.date_high.symb_date = str;
 	break;
     case CONDITION_FLAG:
 	newc->match.flags = libbalsa_conf_get_int("Flags");
diff -u ../../balsa-2.3.5/libbalsa/filter-funcs.c balsa-2.3.5/libbalsa/filter-funcs.c
--- ../../balsa-2.3.5/libbalsa/filter-funcs.c	2005-11-22 21:42:34.000000000 -0400
+++ balsa-2.3.5/libbalsa/filter-funcs.c	2005-12-11 13:46:01.000000000 -0400
@@ -33,24 +33,32 @@
 
 #include "filter-funcs.h"
 #include "filter-private.h"
+#include "i18n.h"
 
 /* Conditions */
 
+gchar * libbalsa_condition_rel_dates_units[] =
+    { "DAYS",
+      "WEEKS",
+      "MONTHS",
+      "YEARS" };
+
 static gchar*
 get_quoted_string(gchar **pstr)
 {
     gchar *str = *pstr;
     GString *res = g_string_new("");
     if(*str == '"') {
-        while(*++str && *str != '"') {
+	++str;
+        while(*str && *str != '"') {
             if(*str == '\\') str++;
-            g_string_append_c(res, *str);
+            g_string_append_c(res, *str++);
         }
-	if(*str == '"')
-	  ++str;
+	if(*str=='"')
+	    str++;
     } else {
-        while(*++str && !isspace((int)*str))
-            g_string_append_c(res, *str);
+        while(*str && !isspace((int)*str))
+            g_string_append_c(res, *str++);
     }
     *pstr = str;
     return g_string_free(res, FALSE);
@@ -122,15 +130,14 @@
     return cond;
 }
 LibBalsaCondition*
-libbalsa_condition_new_date(gboolean negated, time_t *from, time_t *to)
+libbalsa_condition_new_date(gboolean negated, gchar *from, gchar *to)
 {
     LibBalsaCondition *cond;
     cond = g_new(LibBalsaCondition,1);
     cond->negate = negated;
-    cond->type = CONDITION_STRING;
     cond->type = CONDITION_DATE;
-    cond->match.date.date_low  = from ? *from : 0;
-    cond->match.date.date_high = to   ? *to   : 0;
+    cond->match.date.date_low.symb_date  = from;
+    cond->match.date.date_high.symb_date = to;
     return cond;
 }
 
@@ -139,9 +146,7 @@
 {
     LibBalsaCondition *cond;
     gchar *hi, *lo = get_quoted_string(string);
-    time_t tlo, thi;
-    struct tm date;
-
+ 
     if(lo == NULL)
         return NULL;
     if(*(*string)++ != ' ') {
@@ -154,32 +159,7 @@
         return NULL;
     }
 
-    /* strptime with our format will not set time, only date */
-    date.tm_sec = 0;
-    date.tm_min = 0;
-    date.tm_hour = 0;
-
-    if(*lo == '\0')
-       tlo = 0;
-    else {
-        strptime(lo, "%Y-%m-%d", &date);
-        tlo = mktime(&date);
-    }
-    if(*hi == '\0')
-       thi =  0;
-    else {
-        strptime(hi, "%Y-%m-%d", &date);
-        thi = mktime(&date) + 24*3600 - 1 /* 24*3600 - 1 = 23:59:59 */;
-    }
-        
-    cond = g_new(LibBalsaCondition,1);
-    cond->negate = negated;
-    cond->type = CONDITION_DATE;
-    cond->match.date.date_low  = tlo;
-    cond->match.date.date_high = thi;
-    g_free(lo);
-    g_free(hi);
-    return cond;
+    return libbalsa_condition_new_date(negated, lo, hi);
 }
 
 static LibBalsaCondition*
@@ -314,18 +294,11 @@
 #endif        
 	break;
     case CONDITION_DATE:
-        g_string_append(res, "DATE ");
-	if (cond->match.date.date_low) {
-	    localtime_r(&cond->match.date.date_low, &date);
-	    strftime(str,sizeof(str),"%Y-%m-%d", &date);
-	} else str[0]='\0';
-        append_quoted_string(res, str);
-        g_string_append_c(res, ' ');
-	if (cond->match.date.date_high) {
-	    localtime_r(&cond->match.date.date_high, &date);
-	    strftime(str,sizeof(str),"%Y-%m-%d", &date);
-	} else str[0]='\0';
-        append_quoted_string(res, str);
+        g_string_append_printf(res, "DATE \"%s\" \"%s\"",
+			       cond->match.date.date_low.symb_date ?
+			       cond->match.date.date_low.symb_date : "",
+			       cond->match.date.date_high.symb_date ?
+			       cond->match.date.date_high.symb_date : "");
 	break;
     case CONDITION_FLAG:
         g_string_append_printf(res, "FLAG %u", cond->match.flags);
@@ -395,6 +368,9 @@
     case CONDITION_REGEX:
 	/* FIXME: regexs_free(cond->match.regexs); */
     case CONDITION_DATE:
+	g_free(cond->match.date.date_low.symb_date);
+	g_free(cond->match.date.date_high.symb_date);
+	break;
     case CONDITION_FLAG:
 	/* nothing to do */
         break;
@@ -422,6 +398,8 @@
     filter_errno = FILTER_NOERR;
     new_cnd = g_new(LibBalsaCondition,1);
 
+    g_print("Clone ");
+
     new_cnd->negate = cnd->negate;
     new_cnd->type    = cnd->type;
     switch (new_cnd->type) {
@@ -447,8 +425,18 @@
 #endif
         break;
     case CONDITION_DATE:
-        new_cnd->match.date.date_low  = cnd->match.date.date_low;
-        new_cnd->match.date.date_high = cnd->match.date.date_high;
+        if (cnd->match.date.date_low.symb_date)
+	    new_cnd->match.date.date_low.symb_date
+		= g_strdup(cnd->match.date.date_low.symb_date);
+	else new_cnd->match.date.date_low.symb_date = NULL;
+        if (cnd->match.date.date_high.symb_date)
+	    new_cnd->match.date.date_high.symb_date
+		= g_strdup(cnd->match.date.date_high.symb_date);
+	else new_cnd->match.date.date_high.symb_date = NULL;
+	new_cnd->match.date.date_low.computed
+	    = cnd->match.date.date_low.computed;
+	new_cnd->match.date.date_high.computed
+	    = cnd->match.date.date_high.computed;
         break;
     case CONDITION_FLAG:
         new_cnd->match.flags=cnd->match.flags;
@@ -508,7 +496,7 @@
     gboolean res = FALSE;
     switch (c1->type) {
     case CONDITION_STRING:
-        res = (c2->type == CONDITION_STRING ||
+        res = (c2->type == CONDITION_STRING &&
                g_ascii_strcasecmp(c1->match.string.string,
                                   c2->match.string.string) == 0);
         break;
@@ -520,10 +508,13 @@
 #endif
         break;
     case CONDITION_DATE:
-        res = (c2->type == CONDITION_DATE &&
-               c1->match.date.date_low == c2->match.date.date_low &&
-               c1->match.date.date_high == c2->match.date.date_high);
-        break;
+	res = (c2->type == CONDITION_DATE &&
+               g_ascii_strcasecmp(c1->match.date.date_low.symb_date,
+                                  c2->match.date.date_low.symb_date) == 0 &&
+               g_ascii_strcasecmp(c1->match.date.date_high.symb_date,
+                                  c2->match.date.date_high.symb_date) == 0
+	       );
+	break;
     case CONDITION_FLAG:
         res = (c2->type == CONDITION_FLAG &&
                c1->match.flags == c2->match.flags);
@@ -542,6 +533,204 @@
     return res;
 }
 
+/*static time_t
+lbc_calc_rel_date(RelDatesUnitsType u, gint offset,
+		  struct tm * today)
+{
+    time_t today2 = 0;
+
+    switch (u) {
+    case UNIT_DAYS:
+	today2 = mktime(today);
+	today2 += offset*24*3600;
+	break;
+    case UNIT_WEEKS:
+	today2 = mktime(today);
+	today2 += offset*7*24*3600;
+	break;
+    case UNIT_MONTHS:
+	today->tm_year += (offset / 12);
+	today->tm_mon += (offset % 12);
+	Adjust month/year if overflowed 
+	if (today->tm_mon<0) {
+	    today->tm_mon += 12;
+	    today->tm_year--;
+	}
+	if (today->tm_mon>11) {
+	    today->tm_mon -= 12;
+	    today->tm_year++;
+	}
+	 Back to time_t format 
+	today2 = mktime(today);
+	break;
+    case UNIT_YEARS:
+	today->tm_year += offset;
+	today2 = mktime(today);
+	break;
+    case UNIT_NONE:
+	break;
+    }
+    return today2;
+}*/
+
+/* Checks the order of the day/month/year fields
+ * in the current locale using strftime("%x",...)
+ * pattern must be a 3 guints array to store the pattern
+ * returns the locale's date delim
+ */
+static gchar
+ld_locale_date_order(gint * pattern)
+{
+    static gchar xformat[] = "%x"; /* to suppress error in strftime */
+    struct tm test;
+    gchar str[25];
+    gchar * p = str;
+    gchar delim_locale='\0';
+
+    gint i;
+    
+    test.tm_mday = 2;   /* Second day of the month modulo 3 == 2*/
+    test.tm_mon = 0;    /* First month modulo 3 == 1*/
+    test.tm_year = 101; /* => year 2001, divisible by 3, modulo 3 == 0 */
+    /* Date is (in us locale) : 01/02/2001 */
+    test.tm_yday = 0;
+    test.tm_sec = 0;
+    test.tm_min = 0;
+    test.tm_hour = 0;
+    (void) strftime (str, 24, xformat, &test);
+
+    /* Now check where the days/months/years are! */
+    for (i=0;i<3;i++) {
+	g_assert(g_ascii_isdigit(*p));
+	/* Hack to shorten this mess! */
+	pattern[i]=atoi(p)%3;
+	while (*p && g_ascii_isdigit(*p)) p++;
+	/* Should not happen !!! */
+	g_assert(i==2 || *p);
+	/* Store locale's delim */
+	if (i==0) delim_locale = *p;
+	p++;
+    }
+    return delim_locale;
+}
+
+/* Convert the date in canonical form YYYY-mm-dd
+ * to the current locale: this just means shuffling
+ * the year/month/day fields to fit the current locale
+ * returns NULL if there is a format error else
+ * returns a newly allocated string
+ */
+
+gchar * libbalsa_date_to_locale(gchar * date)
+{
+    gchar delim_locale;
+    guint pattern[3]; /* 0=year, 1=month, 2=day
+			 because we store dates as YYYY-mm-dd */
+    GString * res = g_string_new("");
+    gchar * p1 = date,*p2;
+    gchar ** fields = g_new(gchar *, 3);
+    gint i;
+    
+    if (!date || !*date) return g_strdup("");
+    g_print("Date canon=%s <>",date);
+    for (i=0;i<2;i++) {
+	p2 = strchr(p1,'-');
+	/* p2=p1 means the field is a negative offset, jump
+	   to the next '-' which is the delimiter */
+	if (p2 == p1) p2 = strchr(p1+1, '-');
+	g_assert(p2);
+	p2++;
+	g_assert(*p2);
+	fields[i] = g_strndup(p1, p2-p1-1);
+	p1 = p2;
+    }
+    while (*p2) p2++;
+    fields[2] = g_strndup(p1, p2-p1);
+    for (i=0;i<3;i++) g_print("f[%d]=%s",i,fields[i]);
+    delim_locale = ld_locale_date_order(pattern);
+    g_assert(delim_locale!='\0');
+    /* And now we build the date in the locale according to pattern */
+    for (i=0;i < 3;i++) {
+	res = g_string_append(res, fields[pattern[i]]);
+	if (i<2) res = g_string_append_c(res, delim_locale);
+    }
+    for (i=0;i<3;i++) g_free(fields[i]);
+    g_free(fields);
+    g_print(" <> RES=%s\n",res->str);
+    return g_string_free(res, FALSE);
+}
+
+/* Convert the date in locale form to the canonical
+ * form YYYY-mm-dd: this just means shuffling
+ * the year/month/day fields to fit the current locale
+ * returns NULL if there is a format error else
+ * returns a newly allocated string
+ */
+
+gchar * libbalsa_date_from_locale(const gchar * date)
+{
+    gchar delim;
+    gboolean skipped = FALSE, cont = TRUE;
+    gint pattern[3];
+    GString * res = g_string_new("");
+    gchar ** fields = g_new(gchar *, 3);
+    gint i=0, j, len;
+    const gchar * p1 = date,* p2 = NULL;
+
+    if (!date || !*date) return NULL;
+    delim = ld_locale_date_order(pattern);
+    g_print("locale date=%s\n", date);
+    g_assert(delim!='\0');
+    for (i=0;i<2 && cont;i++) {
+	p2 = strchr(p1, delim);
+	/* No delimit means one field is missing, we will
+	   fill it with stars later */
+	if (!p2) {
+	    p2 = p1;
+	    while (*p2) p2++;
+	    cont = FALSE;
+	} else {
+	    /* p2==p1 means the field is a negative offset, jump
+	       to the next delim (case where delim=='-' also)
+	       or the user skipped a field (meaning just replace
+	       it by a *)
+	    */
+	    if (p2 == p1)
+		if (delim=='-') {
+		    p2 = strchr(p1+1, delim);
+		    g_assert(p2);
+		    g_assert(*(p2+1));
+		} else skipped = TRUE;
+	}
+	if (!skipped) fields[i] = g_strndup(p1, p2-p1);
+	else fields[i]= g_strdup("*"); 
+	skipped = FALSE;
+	if (cont) p1 = ++p2;
+    }
+    len = i;
+    if (p2 && *p2) {
+	while (*p2) p2++;
+	fields[2] = g_strndup(p1, p2-p1);
+    } else /* Fills the missing fields with stars */
+	for (;i<3;i++) fields[i] = g_strdup("*");
+    for (i=0;i<3;i++) g_print("f[%d]=%s",i,fields[i]);
+    i = 0;
+    while (i<3) {
+	for (j = 0;j<3 && i!=pattern[j]; j++);
+	g_assert(j<3);
+	if (j<len)
+	    res = g_string_append(res, fields[j]);
+	/* Unspecified fields are replaced by a star */
+	else res = g_string_append_c(res, '*');
+	if (i<2)
+	    res = g_string_append_c(res, '-');
+	i++;
+    }
+    for (i=0;i<3;i++) g_free(fields[i]);
+    g_free(fields);
+    return g_string_free(res, FALSE);
+}
+
 /* BIG FIXME : result of certain function of regex compilation are useless
  * and we should have a way to tell which regexs we were unable to compile
  * that's for later
@@ -688,6 +877,155 @@
     return TRUE;
 }                       /* end of filter_compile_regexs */
 
+/* "Compute" the date from the date string: take today's field
+ * for any field (day/month/year) unchanged if the corresponding
+ * field is a star "*" in the string. Else if the field is a
+ * relative value (+/-number) take today's field plus the offset
+ * (this in general requires some overflow control)
+ * Else this is an absolute value that replaces the field of today's
+ * date.
+ * Return -1 on error, else the computed date.
+ */
+time_t libbalsa_date_compute(const gchar * date,
+			     struct tm * today)
+{
+    const gchar * p = date;
+    struct tm test;
+
+    /* NULL string=> no bound */
+    if (!date) return 0;
+
+    today->tm_sec = 0;
+    today->tm_min = 0;
+    today->tm_hour = 0;
+
+    /* Empty string => today's date */
+    if (!*p) return mktime(today);
+    if (g_ascii_isdigit(*p)) {
+	today->tm_year = atoi(p)-1900;
+	while (*p && g_ascii_isdigit(*p)) p++;
+    } else if (*p=='-' || *p=='+') {
+	today->tm_year += atoi(p++);
+	while (*p && g_ascii_isdigit(*p)) p++;
+    }
+    else while (*p=='*') p++;
+    g_print("Year=%d <>", today->tm_year);
+    if (!*p) return mktime(today);
+    if (*p!='-') {
+	libbalsa_information(LIBBALSA_INFORMATION_ERROR,
+			     _("Bad date filter: %s, bad separator (should be \'-\') or bad year (should be either *, a number or an offset like -2)"),
+			     date);
+	return -1;
+    }
+    if (!*++p) return mktime(today);
+
+    if (g_ascii_isdigit(*p)) {
+	today->tm_mon = atoi(p)-1;
+	while (*p && g_ascii_isdigit(*p)) p++;
+    } else if (*p=='-' || *p=='+') {
+	today->tm_mon += atoi(p++);
+	while (*p && g_ascii_isdigit(*p)) p++;
+    }
+    else while (*p=='*') p++;
+    g_print("Month=%d <>", today->tm_mon);
+    if (!*p) return mktime(today);
+    if (*p!='-') {
+	libbalsa_information(LIBBALSA_INFORMATION_ERROR,
+			     _("Bad date filter: %s, bad separator (should be \'-\') or bad month (should be either ** or a number)"),
+			     date);
+	return -1;
+    }
+    if (!*++p) return mktime(today);
+
+    if (g_ascii_isdigit(*p)) {
+	today->tm_mday = atoi(p);
+	while (*p && g_ascii_isdigit(*p)) p++;
+    } else if (*p=='-' || *p=='+') {
+	today->tm_mday += atoi(p++);
+	while (*p && g_ascii_isdigit(*p)) p++;
+    }
+    else while (*p=='*') p++;
+    g_print("Day=%d <>", today->tm_mday);
+    test.tm_sec = 0;
+    test.tm_min = 0;
+    test.tm_hour = 0;
+    test.tm_year = 105;
+    test.tm_mon = 11;
+    test.tm_mday = 30;
+    g_print("%d == %d\n", mktime(today),mktime(&test));
+    if (!*p) return mktime(today); 
+    libbalsa_information(LIBBALSA_INFORMATION_ERROR,
+			 _("Bad date filter: %s, bad separator (should be \'-\') or bad day (should be either ** or a number)"),
+			 date);
+    return -1;
+}
+
+/*
+ * libbalsa_filter_prepare_dates
+ *
+ * Compute the partial dates
+ *
+ * Arguments:
+ *    LibBalsaCondition * cond - the condition to prepare
+ *    struct tm * today - today's date
+ *
+ * Returns:
+ *    TRUE on success.
+ * 
+ */
+
+gboolean
+libbalsa_condition_prepare_dates(LibBalsaCondition * cond, struct tm * today)
+{
+    struct tm date;
+
+    switch (cond->type) {
+    case CONDITION_STRING:
+    case CONDITION_FLAG:
+    case CONDITION_REGEX:
+	/* Nothing to do */
+	break;
+    case CONDITION_AND:
+    case CONDITION_OR:
+	return
+	    libbalsa_condition_prepare_dates(cond->match.andor.left,
+					     today) &&
+	    libbalsa_condition_prepare_dates(cond->match.andor.right,
+					     today);
+    case CONDITION_DATE:
+	memcpy(&date, today, sizeof(struct tm));
+	/* libbalsa_date_compute set the time to 00:00:00
+	 */
+	g_print("Prepare date=%s to %s\n",cond->match.date.date_low.symb_date,
+		cond->match.date.date_high.symb_date);
+	cond->match.date.date_low.computed =
+	    libbalsa_date_compute(cond->match.date.date_low.symb_date,
+				  &date);
+	if (cond->match.date.date_low.computed == (time_t)-1) {
+	    libbalsa_information(LIBBALSA_INFORMATION_ERROR,
+				 "Incorrect date \"%s\" in filter",
+				 cond->match.date.date_low.symb_date);
+	    cond->match.date.date_low.computed = (time_t)0;
+	}
+
+	memcpy(&date, today, sizeof(struct tm));
+	cond->match.date.date_high.computed =
+	    libbalsa_date_compute(cond->match.date.date_high.symb_date,
+				  &date);
+	if (cond->match.date.date_high.computed == (time_t)-1) {
+	    libbalsa_information(LIBBALSA_INFORMATION_ERROR,
+				 "Incorrect date \"%s\" in filter",
+				 cond->match.date.date_high.symb_date);
+	    cond->match.date.date_high.computed = 0;
+	} else if (cond->match.date.date_high.computed)
+	    /* libbalsa_date_compute set the time to 00:00:00
+	     * and this is the higher bound so hour must be 23:59:59
+	     */
+	    cond->match.date.date_high.computed += 59+59*60+23*3600;
+    }
+    return TRUE;
+}                       /* end of filter_prepare_dates */
+
 /*
  * libbalsa_filter_free()
  *
@@ -845,7 +1183,6 @@
 	/* FIXME */
 	buffer=g_string_append(buffer,"TRUE");
     case CONDITION_DATE:
-	/*FIXME */
     case CONDITION_NONE:
     case CONDITION_AND:
     case CONDITION_OR:
diff -u ../../balsa-2.3.5/libbalsa/filter-funcs.h balsa-2.3.5/libbalsa/filter-funcs.h
--- ../../balsa-2.3.5/libbalsa/filter-funcs.h	2004-01-11 10:23:25.000000000 -0400
+++ balsa-2.3.5/libbalsa/filter-funcs.h	2005-11-22 14:48:30.000000000 -0400
@@ -77,6 +77,11 @@
 void libbalsa_condition_compile_regexs(LibBalsaCondition* cond);
 gboolean libbalsa_condition_compare(LibBalsaCondition *c1,
                                     LibBalsaCondition *c2);
+gboolean libbalsa_condition_rel_dates_match(LibBalsaCondition * c,
+					    time_t date);
+
+gboolean libbalsa_condition_prepare_dates(LibBalsaCondition * cond,
+					  struct tm * today);
 
 /* Filters */
 /* Free a filter
Seulement dans balsa-2.3.5/libbalsa/: filter-funcs.h~
Seulement dans balsa-2.3.5/libbalsa/: filter-funcs.o
diff -u ../../balsa-2.3.5/libbalsa/filter.h balsa-2.3.5/libbalsa/filter.h
--- ../../balsa-2.3.5/libbalsa/filter.h	2005-03-19 23:05:41.000000000 -0400
+++ balsa-2.3.5/libbalsa/filter.h	2005-11-30 21:37:04.000000000 -0400
@@ -58,14 +58,81 @@
     CONDITION_NONE,
     CONDITION_STRING, /*  */
     CONDITION_REGEX,
-    CONDITION_DATE,
+    CONDITION_DATE, /* Absolute dates */
     CONDITION_FLAG,
     CONDITION_AND, /* Condition has a list of subconditions and
                     * matches if all the subconditions match. */
-    CONDITION_OR   /* Condition has a list of subconditions and
+    CONDITION_OR  /* Condition has a list of subconditions and
                     * matches if any subcondition matches. */
 } ConditionMatchType;
 
+struct _LibBalsaDate {
+    /* This is the computed date. Only meaningful when
+     * the filter which owns this date is run
+     */
+    time_t computed;
+    /* symb_date is the string containing the symbolic
+     * date, which means either:
+     * - a true date in YYYY-mm-dd form (because
+     *   of locale problems see below)
+     * - or a "partial" one in the following sense:
+     *   + any * is replaced by the corresponding field
+     *   of today's date (where field means day/month or year);
+     *   + an "offset" which means this field must evaluate to
+     *   the one of today's date + this offset
+     * Examples: 2005-11-05: normal date no problem;
+     *           *-11-05: will evaluate to the 5th of November
+     *           of the current year;
+     *           *-*--15: means current year and month, but 15
+     *           days sooner; basically fifteen days earlier
+     *           -1-*-5: means the fifth of the same month but
+     *           one year earlier and so on...
+     * Note: using this canonical form enables us to avoid the
+     * problem encountered by an user trying to transfer its
+     * filters file to another PC (with another locale) or 
+     * to another locale or worse for someone using several
+     * locales on its computer! But we need each time to convert
+     * to the current locale when displaying the date and from
+     * it when the user enters a new dates filter; basically what
+     * this boils down to is just to find in which order the
+     * days, months, years are and do the switching...
+     */
+    gchar * symb_date;
+};
+
+typedef struct _LibBalsaDate LibBalsaDate;
+
+void libbalsa_date_clone(const LibBalsaDate src, LibBalsaDate * date);
+/* Convert the date in canonical form YYYY-mm-dd
+ * to the current locale: this just means shuffling
+ * the year/month/day fields to fit the current locale
+ * returns NULL if there is a format error else
+ * returns a newly allocated string
+ */
+
+gchar * libbalsa_date_to_locale(gchar * date);
+
+/* Convert the date in locale form to the canonical
+ * form YYYY-mm-dd: this just means shuffling
+ * the year/month/day fields to fit the current locale
+ * returns NULL if there is a format error else
+ * returns a newly allocated string
+ */
+
+gchar * libbalsa_date_from_locale(const gchar * date);
+
+/* "Compute" the date from the date string: take today's field
+ * for any field (day/month/year) unchanged if the corresponding
+ * field is a star "*" in the string. Else if the field is a
+ * relative value (+/-number) take today's field plus the offset
+ * (this in general requires some overflow control)
+ * Else this is an absolute value that replaces the field of today's
+ * date.
+ * Return -1 on error, else the computed date.
+ */
+time_t libbalsa_date_compute(const gchar * date,
+			     struct tm * today);
+
 struct _LibBalsaCondition {
     gboolean negate; /* negate the result of the condition. */
     ConditionMatchType type;
@@ -90,9 +157,8 @@
             /* GSList * regexs; */
         } regex;
         /* CONDITION_DATE */
-	struct {
-	    time_t date_low,date_high; /* for CONDITION_DATE            */
-                                       /* (date_high==0=>no high limit) */
+	struct _date {
+	    LibBalsaDate date_low,date_high;
 	} date;
         /* CONDITION_FLAG */
 	LibBalsaMessageFlag flags;
@@ -115,7 +181,7 @@
                                                  gchar *str,
                                                  gchar *user_header);
 LibBalsaCondition* libbalsa_condition_new_date(gboolean negated,
-                                               time_t *from, time_t *to);
+                                               gchar *from, gchar *to);
 LibBalsaCondition* libbalsa_condition_new_bool_ptr(gboolean negated,
                                                    ConditionMatchType cmt,
                                                    LibBalsaCondition *left,
Seulement dans balsa-2.3.5/libbalsa/: filter.h~
Seulement dans balsa-2.3.5/libbalsa/: filter.o
Seulement dans balsa-2.3.5/libbalsa/: folder-scanners.o
Seulement dans balsa-2.3.5/libbalsa/: gmime-gpgme-context.o
Seulement dans balsa-2.3.5/libbalsa/: gmime-gpgme-signature.o
Seulement dans balsa-2.3.5/libbalsa/: gmime-part-rfc2440.o
Seulement dans balsa-2.3.5/libbalsa/: html.o
Seulement dans balsa-2.3.5/libbalsa/: identity.o
Les sous-répertoires ../../balsa-2.3.5/libbalsa/imap et balsa-2.3.5/libbalsa/imap sont identiques.
Seulement dans balsa-2.3.5/libbalsa/: imap-server.o
Seulement dans balsa-2.3.5/libbalsa/: information.o
Seulement dans balsa-2.3.5/libbalsa/: libbalsa.a
Seulement dans balsa-2.3.5/libbalsa/: libbalsa-conf.o
Seulement dans balsa-2.3.5/libbalsa/: libbalsa-marshal.o
Seulement dans balsa-2.3.5/libbalsa/: libbalsa.o
Seulement dans balsa-2.3.5/libbalsa/: mailbox-filter.o
diff -u ../../balsa-2.3.5/libbalsa/mailbox_imap.c balsa-2.3.5/libbalsa/mailbox_imap.c
--- ../../balsa-2.3.5/libbalsa/mailbox_imap.c	2005-09-13 12:26:38.000000000 -0400
+++ balsa-2.3.5/libbalsa/mailbox_imap.c	2005-11-30 20:58:12.000000000 -0400
@@ -1329,12 +1329,12 @@
         break;
     case CONDITION_DATE: {
         ImapSearchKey *slo = NULL;
-        if (cond->match.date.date_low)
+        if (cond->match.date.date_low.computed)
             query  = slo = imap_search_key_new_date
-                (IMSE_D_SINCE, FALSE, cond->match.date.date_low);
-        if (cond->match.date.date_high) {
+                (IMSE_D_SINCE, FALSE, cond->match.date.date_low.computed);
+        if (cond->match.date.date_high.computed) {
             query = imap_search_key_new_date
-                (IMSE_D_BEFORE, FALSE, cond->match.date.date_high);
+                (IMSE_D_BEFORE, FALSE, cond->match.date.date_high.computed);
             if(slo)
                 imap_search_key_set_next(query, slo);
         }
Seulement dans balsa-2.3.5/libbalsa/: mailbox_imap.c~
Seulement dans balsa-2.3.5/libbalsa/: mailbox_imap.o
diff -u ../../balsa-2.3.5/libbalsa/mailbox_local.c balsa-2.3.5/libbalsa/mailbox_local.c
--- ../../balsa-2.3.5/libbalsa/mailbox_local.c	2005-09-20 14:08:25.000000000 -0400
+++ balsa-2.3.5/libbalsa/mailbox_local.c	2005-12-03 20:44:57.000000000 -0400
@@ -502,10 +502,12 @@
     case CONDITION_REGEX:
         break;
     case CONDITION_DATE:
+	g_print("%d <= %d <= %d\n",cond->match.date.date_low.computed,
+		entry->msg_date,cond->match.date.date_high.computed);
         match = 
-            entry->msg_date >= cond->match.date.date_low &&
-            (cond->match.date.date_high==0 || 
-             entry->msg_date<=cond->match.date.date_high);
+            entry->msg_date >= cond->match.date.date_low.computed &&
+            (cond->match.date.date_high.computed==0 || 
+             entry->msg_date<=cond->match.date.date_high.computed);
         break;
     case CONDITION_FLAG:
         match = libbalsa_mailbox_msgno_has_flags(mailbox, msgno,
diff -u ../../balsa-2.3.5/src/filter-edit-callbacks.c balsa-2.3.5/src/filter-edit-callbacks.c
--- ../../balsa-2.3.5/src/filter-edit-callbacks.c	2005-08-21 16:10:28.000000000 -0400
+++ balsa-2.3.5/src/filter-edit-callbacks.c	2005-12-03 15:21:04.000000000 -0400
@@ -47,6 +47,7 @@
 /* Defined in filter-edit-dialog.c*/
 extern option_list fe_search_type[4];
 extern GList * fe_user_headers_list;
+extern gchar * libbalsa_condition_rel_dates_units[];
 
 static void fe_add_pressed(GtkWidget * widget, gpointer throwaway);
 static void fe_remove_pressed(GtkWidget * widget, gpointer throwaway);
@@ -76,6 +77,7 @@
 extern GtkWidget *fe_name_entry;
     
 /* widgets for the matching fields */
+GtkWidget *fe_field_frame;
 GtkWidget *fe_matching_fields_body;
 GtkWidget *fe_matching_fields_to;
 GtkWidget *fe_matching_fields_from;
@@ -289,7 +291,7 @@
 
     condition_has_changed = TRUE;
     gtk_notebook_set_current_page(GTK_NOTEBOOK(fe_type_notebook),
-                                  type - 1);
+				  type - 1);
 
     switch (type) {
     case CONDITION_STRING:
@@ -484,7 +486,6 @@
         gtk_tree_view_get_model(fe_type_regex_list);
     GtkTreeIter iter;
 
-
     /* Sanity checks, prevent "empty" condition */
 
     new_cnd->type=get_condition_type();
@@ -542,48 +543,54 @@
             return FALSE;
         }
         break;
-    case CONDITION_DATE:
-        c_str = gtk_entry_get_text(GTK_ENTRY(fe_type_date_low_entry));
+    case CONDITION_DATE: {
+	gint err;
+	struct tm today;
+	time_t tmp = time(&tmp);
+	    
+	new_cnd->type = CONDITION_DATE;
+	c_str = gtk_entry_get_text(GTK_ENTRY(fe_type_date_low_entry));
         if (c_str && c_str[0]!='\0') {
-            (void) strptime("00:00:00","%T",&date);
-            p=(gchar *)strptime(c_str,"%x",&date);
-            if (!p || *p!='\0') {
-                balsa_information(LIBBALSA_INFORMATION_ERROR,
-                                  _("Low date is incorrect"));
-                return FALSE;
-            }
-            new_cnd->match.date.date_low=mktime(&date);
-        }
+	    (void) localtime_r(&tmp, &today);
+	    p = libbalsa_date_from_locale(c_str);
+	    if (p) {
+		if (libbalsa_date_compute(p, &today)==-1) {
+		    g_free(p);
+		    return FALSE;
+		}
+		new_cnd->match.date.date_low.symb_date = p;
+		g_print("Date1=%s <> ",p);
+	    }
+	} else new_cnd->match.date.date_low.symb_date = NULL;
+
         c_str = gtk_entry_get_text(GTK_ENTRY(fe_type_date_high_entry));
         if (c_str && c_str[0]!='\0') {
-            (void) strptime("23:59:59","%T",&date);
-            p=(gchar *)strptime(c_str,"%x",&date);
-            if (!p || *p!='\0') {
-                balsa_information(LIBBALSA_INFORMATION_ERROR,
-                                  _("High date is incorrect"));
-                return FALSE;
-            }
-            new_cnd->match.date.date_high=mktime(&date);
-        }
-        if (new_cnd->match.date.date_low >
-            new_cnd->match.date.date_high) {
-            balsa_information(LIBBALSA_INFORMATION_ERROR,
-                              _("Low date is greater than high date"));
-            return FALSE;
-        }
-        break;
+	    (void) localtime_r(&tmp, &today);
+	    p = libbalsa_date_from_locale(c_str);
+	    if (p) {
+		if (libbalsa_date_compute(p, &today)==-1) {
+		    g_free(p);
+		    return FALSE;
+		}
+		new_cnd->match.date.date_high.symb_date = p;
+		g_print("Date2 = %s\n",p);
+	    }
+	} else new_cnd->match.date.date_high.symb_date = NULL;
+	break;
+    }
     case CONDITION_FLAG:
     case CONDITION_NONE:
     case CONDITION_AND:
     case CONDITION_OR:
-        /* to avoid warnings */
+	/* to avoid warnings */
 	break;
     }
-
+	
     /* Sanity checks OK, retrieve datas from widgets */
-
+	
     new_cnd->negate = condition_not;
-    if (CONDITION_CHKMATCH(new_cnd,CONDITION_MATCH_US_HEAD))
+    if (new_cnd->type!=CONDITION_DATE && new_cnd->type!=CONDITION_FLAG
+	&& CONDITION_CHKMATCH(new_cnd,CONDITION_MATCH_US_HEAD))
         new_cnd->match.string.user_header =
             gtk_editable_get_chars(GTK_EDITABLE
                                    (GTK_BIN(fe_user_header)->child), 0,
@@ -670,10 +677,10 @@
     GtkTreeModel *model =
         gtk_tree_view_get_model(fe_type_regex_list);
     gchar str[20];
+    gchar * tmp;
     struct tm date;
     gint row,col;
     gboolean andmask;
-    static gchar xformat[] = "%x"; /* to suppress error in strftime */
     
     condition_not=cnd->negate;
     /* Clear all widgets */
@@ -686,10 +693,10 @@
     gtk_list_store_clear(GTK_LIST_STORE(model));
 
     gtk_notebook_set_current_page(GTK_NOTEBOOK(fe_type_notebook),
-                                  cnd->type - 1);
+				  cnd->type-1);
 
     gtk_combo_box_set_active(GTK_COMBO_BOX(fe_search_option_menu),
-                             cnd->type - 1);
+			     cnd->type-1);
 
     /* First update matching fields
      * but if type is date or flag, these are meaning less so we disable them */
@@ -713,7 +720,9 @@
     else {
 	gtk_widget_set_sensitive(fe_user_header,FALSE);
 	gtk_entry_set_text(GTK_ENTRY(GTK_BIN(fe_user_header)->child),"");
-    }	
+    }
+    if (andmask) gtk_widget_show(fe_field_frame);
+    else gtk_widget_hide(fe_field_frame);
     /* Next update type specific fields */
     switch (cnd->type) {
     case CONDITION_STRING:
@@ -741,18 +750,10 @@
 #endif
         break;
     case CONDITION_DATE:
-        if (cnd->match.date.date_low==0) str[0]='\0';
-        else {
-            localtime_r(&cnd->match.date.date_low, &date);
-            strftime(str, sizeof(str), xformat, &date);
-        }
-        gtk_entry_set_text(GTK_ENTRY(fe_type_date_low_entry),str);
-        if (cnd->match.date.date_high==0) str[0]='\0';
-        else {
-            localtime_r(&cnd->match.date.date_high, &date);
-            strftime(str,sizeof(str), xformat, &date);
-        }
-        gtk_entry_set_text(GTK_ENTRY(fe_type_date_high_entry),str);
+        gtk_entry_set_text(GTK_ENTRY(fe_type_date_low_entry),
+			   libbalsa_date_to_locale(cnd->match.date.date_low.symb_date));
+        gtk_entry_set_text(GTK_ENTRY(fe_type_date_high_entry),
+			   libbalsa_date_to_locale(cnd->match.date.date_high.symb_date));
         fe_update_label(fe_type_date_label, &date_label);
         break;
     case CONDITION_FLAG:
@@ -769,7 +770,7 @@
 	break;
     }
     gtk_combo_box_set_active(GTK_COMBO_BOX(fe_search_option_menu),
-                             cnd->type - 1);
+			     cnd->type-1);
 }            /* end fill_condition_widget */
 
 static void
@@ -878,10 +879,10 @@
 static GtkWidget*
 get_field_frame(void)
 {
-    GtkWidget *table;
-    GtkWidget *frame = gtk_frame_new(_("Match Fields"));
+    GtkWidget *table, *frame;
     GList *list;
 
+    frame = gtk_frame_new(_("Match Fields"));
     gtk_frame_set_label_align(GTK_FRAME(frame), 
                               GTK_POS_LEFT, GTK_POS_TOP);
     gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
@@ -1109,7 +1110,8 @@
 void build_condition_dialog(GtkWidget * condition_dialog)
 {
     GtkWidget *label,* box;
-    GtkWidget *field_frame = get_field_frame();
+    
+    fe_field_frame = get_field_frame();
 
     /* builds the toggle buttons to specify fields concerned by the
      * conditions of the filter */
@@ -1118,13 +1120,13 @@
     gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 5);
     fe_search_option_menu = 
         fe_build_option_menu(fe_search_type, ELEMENTS(fe_search_type),
-                             G_CALLBACK(fe_typesmenu_cb), field_frame);
+                             G_CALLBACK(fe_typesmenu_cb), fe_field_frame);
     gtk_box_pack_start(GTK_BOX(box), fe_search_option_menu, FALSE, FALSE, 5);
     gtk_label_set_mnemonic_widget(GTK_LABEL(label), fe_search_option_menu);
     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(condition_dialog)->vbox),
                        box, FALSE, FALSE, 2);
     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(condition_dialog)->vbox),
-                       field_frame, FALSE, FALSE, 2);
+                       fe_field_frame, FALSE, FALSE, 2);
 
     build_type_notebook();
     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(condition_dialog)->vbox),
Seulement dans balsa-2.3.5/src/: toolbar-prefs.o



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]