/*
mairix - message index builder and finder for maildir folders.
**********************************************************************
* Copyright (C) Richard P. Curnow 2002-2004,2006
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
**********************************************************************
*/
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <assert.h>
#include "mairix.h"
#include "dates.h"
#include "datescan.h"
static enum DATESCAN_TYPE discover_type(char *first, char *last)/*{{{*/
{
int current_state = 0;
int token;
char *p;
p = first;
while (p < last) {
token = datescan_char2tok[(int)*(unsigned char*)p];
current_state = datescan_next_state(current_state, token);
if (current_state < 0) break;
p++;
}
if (current_state < 0) {
return DS_FAILURE;
} else {
return datescan_attr[current_state];
}
}
/*}}}*/
static int match_month(char *p)/*{{{*/
{
if (!strncasecmp(p, "jan", 3)) return 1;
if (!strncasecmp(p, "feb", 3)) return 2;
if (!strncasecmp(p, "mar", 3)) return 3;
if (!strncasecmp(p, "apr", 3)) return 4;
if (!strncasecmp(p, "may", 3)) return 5;
if (!strncasecmp(p, "jun", 3)) return 6;
if (!strncasecmp(p, "jul", 3)) return 7;
if (!strncasecmp(p, "aug", 3)) return 8;
if (!strncasecmp(p, "sep", 3)) return 9;
if (!strncasecmp(p, "oct", 3)) return 10;
if (!strncasecmp(p, "nov", 3)) return 11;
if (!strncasecmp(p, "dec", 3)) return 12;
return 0;
}
/*}}}*/
static int year_fix(int y)/*{{{*/
{
if (y>100) {
return y-1900;
} else if (y < 70) {
/* 2000-2069 */
return y+100;
} else {
/* 1970-1999 */
return y;
}
}
/*}}}*/
static int last_day(int mon, int y) {/*{{{*/
/* mon in [0,11], y=year-1900 */
static unsigned char days[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
if (mon != 1) {
return days[mon];
} else {
/* Because 2000 was a leap year, we don't have to bother about the %100
* rule, at least not in this range of dates. */
if ((y % 4) == 0) {
return 29;
} else {
return 28;
}
}
}
/*}}}*/
static void set_day(struct tm *x, int y)/*{{{*/
{
if (y > x->tm_mday) {
/* Shorthand for that day in previous month */
if (x->tm_mon == 0) {
x->tm_mon = 11;
--x->tm_year;
} else {
--x->tm_mon;
}
}
x->tm_mday = y; /* Always */
}
/*}}}*/
static int is_later_dm(struct tm *x, int m, int d)/*{{{*/
{
int m1 = m-1;
return ((x->tm_mon < m1) || ((x->tm_mon == m1) && (x->tm_mday < d)));
}
/*}}}*/
static int scan_date_expr(char *first, char *last, struct tm *start, struct tm *end)/*{{{*/
{
enum DATESCAN_TYPE type;
time_t now;
time(&now);
type = discover_type(first, last);
if (type == DS_SCALED) {/*{{{*/
int v;
char *p;
time_t then;
p = first;
v = 0;
while (isdigit(*p)) {
v = (v*10) + (*p - '0');
p++;
}
switch(*p) {
case 'd': v *= 86400; break;
case 'w': v *= 7*86400; break;
case 'm': v *= 30*86400; break;
case 'y': v *= 365*86400; break;
default:
fprintf(stderr, "Unrecognized relative date scaling '%c'\n", *p);
return -1;
}
then = now - v;
if (start) {
*start = *localtime(&then);
}
if (end) {
*end = *localtime(&then);
}/*}}}*/
} else if (type == DS_FAILURE) {
fputs("Cannot parse date expression [", stderr);
fwrite(first, sizeof(char), last-first, stderr);
fputs("]\n", stderr);
return -1;
} else {
/* something else */
int v1, v3;
int m2; /* decoded month */
char *p;
v1 = v3 = m2 = 0;
p = first;
while (p < last && isdigit(*p)) {
v1 = (v1*10) + (*p - '0');
p++;
}
if (p < last) {
m2 = match_month(p);
p += 3;
if (m2 == 0) {
return -1; /* failure */
}
}
while (p < last && isdigit(*p)) {
v3 = (v3*10) + (*p - '0');
p++;
}
assert(p==last); /* should be true in all cases. */
switch (type) {
case DS_D:/*{{{*/
if (start) set_day(start, v1);
if (end) set_day(end, v1);
break;
/*}}}*/
case DS_Y:/*{{{*/
if (start) {
start->tm_mday = 1;
start->tm_mon = 0; /* january */
start->tm_year = year_fix(v1);
}
if (end) {
end->tm_mday = 31;
end->tm_mon = 11;
end->tm_year = year_fix(v1);
}
break;
/*}}}*/
case DS_YYMMDD:/*{{{*/
if (start) {
start->tm_mday = v1 % 100;
start->tm_mon = ((v1 / 100) % 100) - 1;
start->tm_year = year_fix(v1/10000);
}
if (end) {
end->tm_mday = v1 % 100;
end->tm_mon = ((v1 / 100) % 100) - 1;
end->tm_year = year_fix(v1/10000);
}
break;
/*}}}*/
case DS_M:/*{{{*/
if (start) {
if (m2-1 > start->tm_mon) --start->tm_year; /* shorthand for previous year */
start->tm_mon = m2-1;
start->tm_mday = 1;
}
if (end) {
if (m2-1 > end->tm_mon) --end->tm_year; /* shorthand for previous year */
end->tm_mon = m2-1;
end->tm_mday = last_day(m2-1, end->tm_year);
}
break;
/*}}}*/
case DS_DM:/*{{{*/
if (start) {
if (is_later_dm(start, m2, v1)) --start->tm_year; /* shorthand for previous year. */
start->tm_mon = m2-1;
start->tm_mday = v1;
}
if (end) {
if (is_later_dm(end, m2, v1)) --end->tm_year; /* shorthand for previous year. */
end->tm_mon = m2-1;
end->tm_mday = v1;
}
break;
/*}}}*/
case DS_MD:/*{{{*/
if (start) {
if (is_later_dm(start, m2, v3)) --start->tm_year; /* shorthand for previous year. */
start->tm_mon = m2-1;
start->tm_mday = v3;
}
if (end) {
if (is_later_dm(end, m2, v3)) --end->tm_year; /* shorthand for previous year. */
end->tm_mon = m2-1;
end->tm_mday = v3;
}
break;
/*}}}*/
case DS_DMY:/*{{{*/
if (start) {
start->tm_mon = m2-1;
start->tm_mday = v1;
start->tm_year = year_fix(v3);
}
if (end) {
end->tm_mon = m2-1;
end->tm_mday = v1;
end->tm_year = year_fix(v3);
}
break;
/*}}}*/
case DS_YMD:/*{{{*/
if (start) {
start->tm_mon = m2-1;
start->tm_mday = v3;
start->tm_year = year_fix(v1);
}
if (end) {
end->tm_mon = m2-1;
end->tm_mday = v3;
end->tm_year = year_fix(v1);
}
break;
/*}}}*/
case DS_MY:/*{{{*/
if (start) {
start->tm_year = year_fix(v3);
start->tm_mon = m2 - 1;
start->tm_mday = 1;
}
if (end) {
end->tm_year = year_fix(v3);
end->tm_mon = m2 - 1;
end->tm_mday = last_day(end->tm_mon, end->tm_year);
}
break;
/*}}}*/
case DS_YM:/*{{{*/
if (start) {
start->tm_year = year_fix(v1);
start->tm_mon = m2 - 1;
start->tm_mday = 1;
}
if (end) {
end->tm_year = year_fix(v1);
end->tm_mon = m2 - 1;
end->tm_mday = last_day(end->tm_mon, end->tm_year);
}
break;/*}}}*/
case DS_FAILURE:
return -1;
break;
case DS_SCALED:
assert(0);
break;
}
}
return 0;
}
/*}}}*/
int scan_date_string(char *in, time_t *start, int *has_start, time_t *end, int *has_end)/*{{{*/
{
char *hyphen;
time_t now;
struct tm start_tm, end_tm;
char *nullchar;
int status;
*has_start = *has_end = 0;
nullchar = in;
while (*nullchar) nullchar++;
time(&now);
start_tm = end_tm = *localtime(&now);
start_tm.tm_hour = 0;
start_tm.tm_min = 0;
start_tm.tm_sec = 0;
end_tm.tm_hour = 23;
end_tm.tm_min = 59;
end_tm.tm_sec = 59;
hyphen = strchr(in, '-');
if (!hyphen) {
/* Start and end are the same. */
*has_start = *has_end = 1;
status = scan_date_expr(in, nullchar, &start_tm, &end_tm);
if (status) return status;
*start = mktime(&start_tm);
*end = mktime(&end_tm);
return 0;
} else {
if (hyphen+1 < nullchar) {
*has_end = 1;
status = scan_date_expr(hyphen+1, nullchar, NULL, &end_tm);
if (status) return status;
*end = mktime(&end_tm);
start_tm = end_tm;
}
if (hyphen > in) {
*has_start = 1;
status = scan_date_expr(in, hyphen, &start_tm, NULL);
if (status) return status;
*start = mktime(&start_tm);
}
}
return 0;
}
/*}}}*/
#ifdef TEST
static void check(char *in)/*{{{*/
{
struct tm start, end;
int result;
result = scan_date_string(in, &start, &end);
if (result) printf("Conversion for <%s> failed\n", in);
else {
char buf1[128], buf2[128];
strftime(buf1, 128, "%d-%b-%Y", &start);
strftime(buf2, 128, "%d-%b-%Y", &end);
printf("Computed range for <%s> : %s - %s\n", in, buf1, buf2);
}
}
/*}}}*/
int main (int argc, char **argv)/*{{{*/
{
check("2w-1w");
check("4m-1w");
check("2002-2003");
check("may2002-2003");
check("2002may-2003");
check("feb98-15may99");
check("feb98-15may1999");
check("2feb98-1y");
check("02feb98-1y");
check("970617-20010618");
return 0;
}
/*}}}*/
#endif
syntax highlighted by Code2HTML, v. 0.9.1