]> granicus.if.org Git - neomutt/commitdiff
Fuzzy date matching. From Eike Rathke <er@erack.de>.
authorThomas Roessler <roessler@does-not-exist.org>
Tue, 6 Feb 2001 22:00:01 +0000 (22:00 +0000)
committerThomas Roessler <roessler@does-not-exist.org>
Tue, 6 Feb 2001 22:00:01 +0000 (22:00 +0000)
date.c
pattern.c

diff --git a/date.c b/date.c
index 8d3eda75755123b95c53d15e318b2f2c7ad87b32..91aa0df6b902c75454596fb975bdb169afa66993 100644 (file)
--- a/date.c
+++ b/date.c
@@ -101,33 +101,66 @@ time_t mutt_mktime (struct tm *t, int local)
   return (g);
 }
 
+/* Return 1 if month is February of leap year, else 0 */
+static int isLeapYearFeb (struct tm *tm)
+{
+  if (tm->tm_mon == 1)
+  {
+    int y = tm->tm_year + 1900;
+    return (((y & 3) == 0) && (((y % 100) != 0) || ((y % 400) == 0)));
+  }
+  return (0);
+}
+
 void mutt_normalize_time (struct tm *tm)
 {
   static char DaysPerMonth[12] = {
     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
   };
+  int nLeap;
 
   while (tm->tm_sec < 0)
   {
     tm->tm_sec += 60;
     tm->tm_min--;
   }
+  while (tm->tm_sec >= 60)
+  {
+    tm->tm_sec -= 60;
+    tm->tm_min++;
+  }
   while (tm->tm_min < 0)
   {
     tm->tm_min += 60;
     tm->tm_hour--;
   }
+  while (tm->tm_min >= 60)
+  {
+    tm->tm_min -= 60;
+    tm->tm_hour++;
+  }
   while (tm->tm_hour < 0)
   {
     tm->tm_hour += 24;
     tm->tm_mday--;
   }
+  while (tm->tm_hour >= 24)
+  {
+    tm->tm_hour -= 24;
+    tm->tm_mday++;
+  }
+  /* use loops on NNNdwmy user input values? */
   while (tm->tm_mon < 0)
   {
     tm->tm_mon += 12;
     tm->tm_year--;
   }
-  while (tm->tm_mday < 0)
+  while (tm->tm_mon >= 12)
+  {
+    tm->tm_mon -= 12;
+    tm->tm_year++;
+  }
+  while (tm->tm_mday <= 0)
   {
     if (tm->tm_mon)
       tm->tm_mon--;
@@ -136,6 +169,18 @@ void mutt_normalize_time (struct tm *tm)
       tm->tm_mon = 11;
       tm->tm_year--;
     }
-    tm->tm_mday += DaysPerMonth[tm->tm_mon];
+    tm->tm_mday += DaysPerMonth[tm->tm_mon] + isLeapYearFeb (tm);
+  }
+  while (tm->tm_mday > (DaysPerMonth[tm->tm_mon] + 
+       (nLeap = isLeapYearFeb (tm))))
+  {
+    tm->tm_mday -= DaysPerMonth[tm->tm_mon] + nLeap;
+    if (tm->tm_mon < 11)
+      tm->tm_mon++;
+    else
+    {
+      tm->tm_mon = 0;
+      tm->tm_year++;
+    }
   }
 }
index 18c1fe0dcfcadf35a1b11271305fa4e16a77b9d9..acb03772920b9bc4437173cc8725a6555a592251 100644 (file)
--- a/pattern.c
+++ b/pattern.c
@@ -100,6 +100,17 @@ static char LastSearchExpn[LONG_STRING] = { 0 }; /* expanded version of
 
 #define M_MAXRANGE -1
 
+/* constants for parse_date_range() */
+#define M_PDR_NONE     0x0000
+#define M_PDR_MINUS    0x0001
+#define M_PDR_PLUS     0x0002
+#define M_PDR_WINDOW   0x0004
+#define M_PDR_ABSOLUTE 0x0008
+#define M_PDR_DONE     0x0010
+#define M_PDR_ERROR    0x0100
+#define M_PDR_ERRORDONE        (M_PDR_ERROR | M_PDR_DONE)
+
+
 int mutt_getvaluebychar (char ch, struct mapping_t *table)
 {
   int i;
@@ -382,30 +393,142 @@ static const char *getDate (const char *s, struct tm *t, BUFFER *err)
    Nm  months
    Nw  weeks
    Nd  days */
-static const char *get_offset (struct tm *tm, const char *s)
+static const char *get_offset (struct tm *tm, const char *s, int sign)
 {
   char *ps;
   int offset = strtol (s, &ps, 0);
+  if ((sign < 0 && offset > 0) || (sign > 0 && offset < 0))
+    offset = -offset;
 
   switch (*ps)
   {
     case 'y':
-      tm->tm_year -= offset;
+      tm->tm_year += offset;
       break;
     case 'm':
-      tm->tm_mon -= offset;
+      tm->tm_mon += offset;
       break;
     case 'w':
-      tm->tm_mday -= 7 * offset;
+      tm->tm_mday += 7 * offset;
       break;
     case 'd':
-      tm->tm_mday -= offset;
+      tm->tm_mday += offset;
       break;
+    default:
+      return s;
   }
   mutt_normalize_time (tm);
   return (ps + 1);
 }
 
+static void adjust_date_range (struct tm *min, struct tm *max)
+{
+  if (min->tm_year > max->tm_year
+      || (min->tm_year == max->tm_year && min->tm_mon > max->tm_mon)
+      || (min->tm_year == max->tm_year && min->tm_mon == max->tm_mon
+       && min->tm_mday > max->tm_mday))
+  {
+    int tmp;
+    
+    tmp = min->tm_year;
+    min->tm_year = max->tm_year;
+    max->tm_year = tmp;
+      
+    tmp = min->tm_mon;
+    min->tm_mon = max->tm_mon;
+    max->tm_mon = tmp;
+      
+    tmp = min->tm_mday;
+    min->tm_mday = max->tm_mday;
+    max->tm_mday = tmp;
+    
+    min->tm_hour = min->tm_min = min->tm_sec = 0;
+    max->tm_hour = 23;
+    max->tm_min = max->tm_sec = 59;
+  }
+}
+
+static const char * parse_date_range (const char* pc, struct tm *min,
+    struct tm *max, int haveMin, struct tm *baseMin, BUFFER *err)
+{
+  int flag = M_PDR_NONE;       
+  while (*pc && ((flag & M_PDR_DONE) == 0))
+  {
+    const char *pt;
+    char ch = *pc++;
+    SKIPWS (pc);
+    switch (ch)
+    {
+      case '-':
+      {
+       /* try a range of absolute date minus offset of Ndwmy */
+       pt = get_offset (min, pc, -1);
+       if (pc == pt)
+       {
+         if (flag == M_PDR_NONE)
+         { /* nothing yet and no offset parsed => absolute date? */
+           if (!getDate (pc, max, err))
+             flag |= (M_PDR_ABSOLUTE | M_PDR_ERRORDONE);  /* done bad */
+           else
+           {
+             /* reestablish initial base minimum if not specified */
+             if (!haveMin)
+               memcpy (min, baseMin, sizeof(struct tm));
+             flag |= (M_PDR_ABSOLUTE | M_PDR_DONE);  /* done good */
+           }
+         }
+         else
+           flag |= M_PDR_ERRORDONE;
+       }
+       else
+       {
+         pc = pt;
+         if (flag == M_PDR_NONE && !haveMin)
+         { /* the very first "-3d" without a previous absolute date */
+           max->tm_year = min->tm_year;
+           max->tm_mon = min->tm_mon;
+           max->tm_mday = min->tm_mday;
+         }
+         flag |= M_PDR_MINUS;
+       }
+      }
+      break;
+      case '+':
+      { /* enlarge plusRange */
+       pt = get_offset (max, pc, 1);
+       if (pc == pt)
+         flag |= M_PDR_ERRORDONE;
+       else
+       {
+         pc = pt;
+         flag |= M_PDR_PLUS;
+       }
+      }
+      break;
+      case '*':
+      { /* enlarge window in both directions */
+       pt = get_offset (min, pc, -1);
+       if (pc == pt)
+         flag |= M_PDR_ERRORDONE;
+       else
+       {
+         pc = get_offset (max, pc, 1);
+         flag |= M_PDR_WINDOW;
+       }
+      }
+      break;
+      default:
+       flag |= M_PDR_ERRORDONE;
+    }
+    SKIPWS (pc);
+  }
+  if ((flag & M_PDR_ERROR) && !(flag & M_PDR_ABSOLUTE))
+  { /* getDate has its own error message, don't overwrite it here */
+    snprintf (err->data, err->dsize, _("Invalid relative date: %s"), pc-1);
+  }
+  return ((flag & M_PDR_ERROR) ? NULL : pc);
+}
+
 static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
 {
   BUFFER buffer;
@@ -462,9 +585,10 @@ static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
        exact++;
     }
     tm->tm_hour = 23;
-    tm->tm_min = max.tm_sec = 59;
+    tm->tm_min = tm->tm_sec = 59;
 
-    get_offset (tm, buffer.data + 1);
+    /* force negative offset */
+    get_offset (tm, buffer.data + 1, -1);
 
     if (exact)
     {
@@ -477,7 +601,9 @@ static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
   {
     const char *pc = buffer.data;
 
-    if (*pc != '-')
+    int haveMin = FALSE;
+    int untilNow = FALSE;
+    if (isdigit ((unsigned char)*pc))
     {
       /* mininum date specified */
       if ((pc = getDate (pc, &min, err)) == NULL)
@@ -485,29 +611,47 @@ static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
        FREE (&buffer.data);
        return (-1);
       }
-    }
-
-    if (*pc && *pc == '-')
-    {
-      /* max date */
-      pc++; /* skip the `-' */
+      haveMin = TRUE;
       SKIPWS (pc);
-      if (*pc)
-       if (!getDate (pc, &max, err))
-       {
-         FREE (&buffer.data);
-         return (-1);
-       }
+      if (*pc == '-')
+      {
+        const char *pt = pc + 1;
+       SKIPWS (pt);
+       untilNow = (*pt == '\0');
+      }
     }
-    else
-    {
-      /* search for messages on a specific day */
+
+    if (!untilNow)
+    { /* max date or relative range/window */
+
+      struct tm baseMin;
+
+      if (!haveMin)
+      { /* save base minimum and set current date, e.g. for "-3d+1d" */
+       time_t now = time (NULL);
+       struct tm *tm = localtime (&now);
+       memcpy (&baseMin, &min, sizeof(baseMin));
+       memcpy (&min, tm, sizeof (min));
+       min.tm_hour = min.tm_sec = min.tm_min = 0;
+      }
+      
+      /* preset max date for relative offsets,
+        if nothing follows we search for messages on a specific day */
       max.tm_year = min.tm_year;
       max.tm_mon = min.tm_mon;
       max.tm_mday = min.tm_mday;
+
+      if (!parse_date_range (pc, &min, &max, haveMin, &baseMin, err))
+      { /* bail out on any parsing error */
+       FREE (&buffer.data);
+       return (-1);
+      }
     }
   }
 
+  /* Since we allow two dates to be specified we'll have to adjust that. */
+  adjust_date_range (&min, &max);
+
   pat->min = mutt_mktime (&min, 1);
   pat->max = mutt_mktime (&max, 1);