diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-12-05 20:09:36 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-12-05 20:19:49 +0200 |
commit | 937173596bd77d773c3fc8330a1ebbe2c8c13df6 (patch) | |
tree | 559222a39bffe42f3b0ac9df12cc0a7a1d65f24f /libmailutils/base/date.c | |
parent | 025c888ab07458c7b2a1e73d6b607cc9ce26cca6 (diff) | |
download | mailutils-937173596bd77d773c3fc8330a1ebbe2c8c13df6.tar.gz mailutils-937173596bd77d773c3fc8330a1ebbe2c8c13df6.tar.bz2 |
Implement mu_scan_datetime.
* include/mailutils/stream.h (mu_fixed_memory_stream_create): New proto.
* libmailutils/stream/memory_stream.c
(mu_fixed_memory_stream_create): New function.
* include/mailutils/util.h (mu_parse_imap_date_time)
(mu_parse_ctime_date_time): Remove.
(mu_scan_datetime): New proto.
(mu_strftime): Remove const from the last arg.
(MU_DATETIME_FROM,MU_DATETIME_IMAP)
(MU_DATETIME_IMAP_SEARCH,MU_DATETIME_INTERNALDATE): New defines.
* libmailutils/base/strftime.c: New file.
* libmailutils/base/Makefile.am (libbase_la_SOURCES): Add strftime.c.
* libmailutils/base/date.c (mu_scan_datetime): New function.
* libmailutils/base/mutil.c (mu_strftime): Remove.
* libmailutils/tests/scantime.at: New file.
* libmailutils/tests/scantime.c: New file.
* libmailutils/tests/Makefile.am (noinst_PROGRAMS): Add scantime.
(TESTSUITE_AT): Add scantime.at.
* libmailutils/tests/strftime.c (main): Call mu_set_program_name.
* libmailutils/tests/testsuite.at: Include scantime.at
* libmu_sieve/actions.c (mime_create_reason): Use mu_c_streamftime.
* imap4d/fetch.c (_frt_internaldate): Use mu_scan_datetime.
* imap4d/util.c (util_parse_internal_date): Likewise.
* libmu_scm/mu_message.c (mu-message-get-envelope-date): Likewise.
* libproto/imap/fetch.c (_date_mapper): Likewise.
* mail/from.c (hdr_date): Use mu_scan_datetime.
Diffstat (limited to 'libmailutils/base/date.c')
-rw-r--r-- | libmailutils/base/date.c | 416 |
1 files changed, 313 insertions, 103 deletions
diff --git a/libmailutils/base/date.c b/libmailutils/base/date.c index 1b2619031..f784bea43 100644 --- a/libmailutils/base/date.c +++ b/libmailutils/base/date.c @@ -26,6 +26,7 @@ #include <mailutils/stream.h> #include <mailutils/errno.h> #include <mailutils/cstr.h> +#include <mailutils/cctype.h> #define SECS_PER_DAY 86400 #define ADJUSTMENT -719162L @@ -528,140 +529,349 @@ mu_c_streamftime (mu_stream_t str, const char *fmt, struct tm *input_tm, return rc; } -int -mu_parse_imap_date_time (const char **p, struct tm *tm, mu_timezone *tz) +static int +_mu_short_weekday_string (const char *str) { - int year, mon, day, hour, min, sec; - char zone[6] = "+0000"; /* ( "+" / "-" ) hhmm */ - char month[5] = ""; - int hh = 0; - int mm = 0; - int sign = 1; - int scanned = 0, scanned3; int i; - int tzoffset; - - day = mon = year = hour = min = sec = 0; - - memset (tm, 0, sizeof (*tm)); - - switch (sscanf (*p, - "%2d-%3s-%4d%n %2d:%2d:%2d %5s%n", - &day, month, &year, &scanned3, &hour, &min, &sec, zone, - &scanned)) + + for (i = 0; i < 7; i++) { - case 3: - scanned = scanned3; - break; - case 7: - break; - default: - return -1; + if (mu_c_strncasecmp (str, short_wday[i], 3) == 0) + return i; } + return -1; +} - tm->tm_sec = sec; - tm->tm_min = min; - tm->tm_hour = hour; - tm->tm_mday = day; - - for (i = 0; i < 12; i++) +static int +_mu_full_weekday_string (const char *str, char **endp) +{ + int i; + + for (i = 0; i < 7; i++) { - if (mu_c_strncasecmp (month, short_month[i], 3) == 0) + if (mu_c_strcasecmp (str, full_wday[i]) == 0) { - mon = i; - break; + if (endp) + *endp = (char*) (str + strlen (full_wday[i])); + return i; } } - tm->tm_mon = mon; - tm->tm_year = (year > 1900) ? year - 1900 : year; - tm->tm_yday = 0; /* unknown. */ - tm->tm_wday = 0; /* unknown. */ -#if HAVE_STRUCT_TM_TM_ISDST - tm->tm_isdst = -1; /* unknown. */ -#endif - - hh = (zone[1] - '0') * 10 + (zone[2] - '0'); - mm = (zone[3] - '0') * 10 + (zone[4] - '0'); - sign = (zone[0] == '-') ? -1 : +1; - tzoffset = sign * (hh * 60 * 60 + mm * 60); + return -1; +} -#if HAVE_STRUCT_TM_TM_GMTOFF - tm->tm_gmtoff = tzoffset; -#endif - if (tz) +static int +_mu_short_month_string (const char *str) +{ + int i; + + for (i = 0; i < 12; i++) { - tz->utc_offset = tzoffset; - tz->tz_name = NULL; + if (mu_c_strncasecmp (str, short_month[i], 3) == 0) + return i; } + return -1; +} - *p += scanned; - - return 0; +static int +_mu_full_month_string (const char *str, char **endp) +{ + int i; + + for (i = 0; i < 12; i++) + { + if (mu_c_strcasecmp (str, full_month[i]) == 0) + { + if (endp) + *endp = (char*) (str + strlen (full_month[i])); + return i; + } + } + return -1; } -/* "ctime" format is: Thu Jul 01 15:58:27 1999, with no trailing \n. */ int -mu_parse_ctime_date_time (const char **p, struct tm *tm, mu_timezone *tz) +get_num (const char *str, char **endp, int ndig, int minval, int maxval, + int *pn) { - int wday = 0; - int year = 0; - int mon = 0; - int day = 0; - int hour = 0; - int min = 0; - int sec = 0; - int n = 0; + int x = 0; int i; - char weekday[5] = ""; - char month[5] = ""; + + errno = 0; + for (i = 0; i < ndig && *str && mu_isdigit (*str); str++, i++) + x = x * 10 + *str - '0'; - if (sscanf (*p, "%3s %3s %2d %2d:%2d:%2d %d%n\n", - weekday, month, &day, &hour, &min, &sec, &year, &n) != 7) + *endp = (char*) str; + if (i == 0) + return -1; + else if (pn) + *pn = i; + else if (i != ndig) return -1; + if (x < minval || x > maxval) + return -1; + return x; +} - *p += n; +#define DT_YEAR 0x01 +#define DT_MONTH 0x02 +#define DT_MDAY 0x04 +#define DT_WDAY 0x08 +#define DT_HOUR 0x10 +#define DT_MIN 0x20 +#define DT_SEC 0x40 - for (i = 0; i < 7; i++) +int +mu_scan_datetime (const char *input, const char *fmt, + struct tm *tm, struct mu_timezone *tz, char **endp) +{ + int rc = 0; + char *p; + int n; + int eof_ok = 0; + int datetime_parts = 0; + + memset (tm, 0, sizeof *tm); +#ifdef HAVE_STRUCT_TM_TM_ISDST + tm->tm_isdst = -1; /* unknown. */ +#endif + /* provide default timezone, in case it is not supplied in input */ + if (tz) { - if (mu_c_strncasecmp (weekday, short_wday[i], 3) == 0) - { - wday = i; - break; - } + memset (tz, 0, sizeof *tz); + tz->utc_offset = mu_utc_offset (); } - for (i = 0; i < 12; i++) + /* Skip leading whitespace */ + input = mu_str_skip_class (input, MU_CTYPE_BLANK); + for (; *fmt && rc == 0; fmt++) { - if (mu_c_strncasecmp (month, short_month[i], 3) == 0) + if (mu_isspace (*fmt)) { - mon = i; - break; + fmt = mu_str_skip_class (fmt, MU_CTYPE_BLANK); + input = mu_str_skip_class (input, MU_CTYPE_BLANK); + if (!*fmt) + break; } + eof_ok = 0; + + if (*fmt == '%') + { + switch (*++fmt) + { + case 'a': + /* The abbreviated weekday name. */ + n = _mu_short_weekday_string (input); + if (n == -1) + rc = MU_ERR_PARSE; + else + { + tm->tm_wday = n; + datetime_parts |= DT_WDAY; + input += 3; + } + break; + + case 'A': + /* The full weekday name. */ + n = _mu_full_weekday_string (input, &p); + if (n == -1) + rc = MU_ERR_PARSE; + else + { + tm->tm_wday = n; + datetime_parts |= DT_WDAY; + input = p; + } + break; + + case 'b': + /* The abbreviated month name. */ + n = _mu_short_month_string (input); + if (n == -1) + rc = MU_ERR_PARSE; + else + { + tm->tm_mon = n; + datetime_parts |= DT_MONTH; + input += 3; + } + break; + + case 'B': + /* The full month name. */ + n = _mu_full_month_string (input, &p); + if (n == -1) + rc = MU_ERR_PARSE; + else + { + tm->tm_mon = n; + datetime_parts |= DT_MONTH; + input = p; + } + break; + + case 'd': + /* The day of the month as a decimal number (range 01 to 31). */ + n = get_num (input, &p, 2, 1, 31, NULL); + if (n == -1) + rc = MU_ERR_PARSE; + else + { + tm->tm_mday = n; + datetime_parts |= DT_MDAY; + input = p; + } + break; + + case 'e': + /* Like %d, the day of the month as a decimal number, but a + leading zero is replaced by a space. */ + { + int ndig; + + n = get_num (input, &p, 2, 1, 31, &ndig); + if (n == -1) + rc = MU_ERR_PARSE; + else + { + tm->tm_mday = n; + datetime_parts |= DT_MDAY; + input = p; + } + } + break; + + case 'H': + /* The hour as a decimal number using a 24-hour clock (range + 00 to 23). */ + n = get_num (input, &p, 2, 0, 23, NULL); + if (n == -1) + rc = MU_ERR_PARSE; + else + { + tm->tm_hour = n; + datetime_parts |= DT_HOUR; + input = p; + } + break; + + case 'm': + /* The month as a decimal number (range 01 to 12). */ + n = get_num (input, &p, 2, 1, 12, NULL); + if (n == -1) + rc = MU_ERR_PARSE; + else + { + tm->tm_mon = n - 1; + datetime_parts |= DT_MONTH; + input = p; + } + break; + + case 'M': + /* The minute as a decimal number (range 00 to 59). */ + n = get_num (input, &p, 2, 0, 59, NULL); + if (n == -1) + rc = MU_ERR_PARSE; + else + { + tm->tm_min = n; + datetime_parts |= DT_MIN; + input = p; + } + break; + + case 'S': + /* The second as a decimal number (range 00 to 60) */ + n = get_num (input, &p, 2, 0, 60, NULL); + if (n == -1) + rc = MU_ERR_PARSE; + else + { + tm->tm_sec = n; + datetime_parts |= DT_SEC; + input = p; + } + break; + + case 'Y': + /* The year as a decimal number including the century. */ + errno = 0; + n = strtoul (input, &p, 10); + if (errno || p == input) + rc = MU_ERR_PARSE; + else + { + tm->tm_year = n - 1900; + datetime_parts |= DT_YEAR; + input = p; + } + break; + + case 'z': + /* The time-zone as hour offset from GMT */ + { + int sign = 1; + int hr; + + if (*input == '+') + input++; + else if (*input == '-') + { + input++; + sign = -1; + } + n = get_num (input, &p, 2, 0, 11, NULL); + if (n == -1) + rc = MU_ERR_PARSE; + else + { + input = p; + hr = n; + n = get_num (input, &p, 2, 0, 59, NULL); + if (n == -1) + rc = MU_ERR_PARSE; + else + { + input = p; + if (tz) + tz->utc_offset = sign * (hr * 60 + n) * 60; + } + } + } + break; + + case '%': + if (*input == '%') + input++; + else + rc = MU_ERR_PARSE; + break; + + case '?': + eof_ok = 1; + break; + } + if (eof_ok && rc == 0 && *input == 0) + break; + } + else if (*input != *fmt) + rc = MU_ERR_PARSE; + else + input++; } - if (tm) - { - memset (tm, 0, sizeof (struct tm)); - - tm->tm_sec = sec; - tm->tm_min = min; - tm->tm_hour = hour; - tm->tm_mday = day; - tm->tm_wday = wday; - tm->tm_mon = mon; - tm->tm_year = (year > 1900) ? year - 1900 : year; -#ifdef HAVE_STRUCT_TM_TM_ISDST - tm->tm_isdst = -1; /* unknown. */ -#endif - } + if (!eof_ok && rc == 0 && *input == 0 && *fmt) + rc = MU_ERR_PARSE; - /* ctime has no timezone information, set tz to local TZ if they ask. */ - if (tz) - { - tz->utc_offset = mu_utc_offset (); - tz->tz_name = NULL; - } + if (!(datetime_parts & DT_WDAY) && + (datetime_parts & (DT_YEAR|DT_MONTH|DT_MDAY)) == + (DT_YEAR|DT_MONTH|DT_MDAY)) + tm->tm_wday = dayofweek (tm->tm_year + 1900, tm->tm_mon, tm->tm_mday); - return 0; + if (endp) + *endp = (char*) input; + + return rc; } |