From: Thomas Roessler Date: Tue, 6 Feb 2001 22:00:01 +0000 (+0000) Subject: Fuzzy date matching. From Eike Rathke . X-Git-Tag: mutt-1-3-15-rel~13 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0e18337c53468a839c5846b9ba62c129ea010d40;p=mutt Fuzzy date matching. From Eike Rathke . --- diff --git a/date.c b/date.c index 8d3eda75..91aa0df6 100644 --- 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++; + } } } diff --git a/pattern.c b/pattern.c index 18c1fe0d..acb03772 100644 --- 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);