summaryrefslogtreecommitdiff
path: root/libmailutils
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2011-12-08 17:04:29 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2011-12-08 18:33:56 +0200
commitacec689ef648adaa5c420049bfb775953e184bda (patch)
treeac5a96099e5a646fc24986650b4f9073c9e83642 /libmailutils
parent5af1d3a7bd8149139ad59baa11501e3d11b138a7 (diff)
downloadmailutils-acec689ef648adaa5c420049bfb775953e184bda.tar.gz
mailutils-acec689ef648adaa5c420049bfb775953e184bda.tar.bz2
Revamp date/time calculations.
* configure.ac: Build libmailutils/datetime/Makefile.am * include/mailutils/datetime.h (mu_datetime_julianday) (mu_datetime_dayofweek,mu_datetime_dayofyear) (mu_datetime_year_days): New protos. * libmailutils/Makefile.am (SUBDIRS): Add datetime (libmailutils_la_LIBADD): Add libdatetime.la * libmailutils/base/Makefile.am (libbase_la_SOURCES): Remove date.c and strftime.c. * libmailutils/base/date.c: Remove. * libmailutils/base/strftime.c: Move to libmailutils/datetime * libmailutils/datetime/Makefile.am: New file. * libmailutils/datetime/dow.c: New file. * libmailutils/datetime/doy.c: New file. * libmailutils/datetime/jd.c: New file. * libmailutils/datetime/scantime.c: New file. * libmailutils/datetime/streamftime.c: New file. * libmailutils/datetime/tab.c: New file. * libmailutils/datetime/unixtime.c: New file. * libmailutils/datetime/utcoff.c: New file. * libmailutils/datetime/yd.c: New file. * libmailutils/tests/scantime.at: Fix yday numbers.
Diffstat (limited to 'libmailutils')
-rw-r--r--libmailutils/Makefile.am3
-rw-r--r--libmailutils/base/Makefile.am2
-rw-r--r--libmailutils/base/date.c1163
-rw-r--r--libmailutils/datetime/Makefile.am32
-rw-r--r--libmailutils/datetime/dow.c29
-rw-r--r--libmailutils/datetime/doy.c53
-rw-r--r--libmailutils/datetime/jd.c32
-rw-r--r--libmailutils/datetime/scantime.c660
-rw-r--r--libmailutils/datetime/streamftime.c411
-rw-r--r--libmailutils/datetime/strftime.c (renamed from libmailutils/base/strftime.c)0
-rw-r--r--libmailutils/datetime/tab.c45
-rw-r--r--libmailutils/datetime/unixtime.c38
-rw-r--r--libmailutils/datetime/utcoff.c32
-rw-r--r--libmailutils/datetime/yd.c29
-rw-r--r--libmailutils/tests/scantime.at58
-rw-r--r--libmailutils/tests/scantime.c2
16 files changed, 1392 insertions, 1197 deletions
diff --git a/libmailutils/Makefile.am b/libmailutils/Makefile.am
index b32138250..c01b32176 100644
--- a/libmailutils/Makefile.am
+++ b/libmailutils/Makefile.am
@@ -19,7 +19,7 @@
SUBDIRS = \
auth base address list sockaddr cidr cfg diag\
filter mailbox mailer mime server string stream stdstream\
- property url imapio . tests
+ property url imapio datetime . tests
lib_LTLIBRARIES = libmailutils.la
@@ -33,6 +33,7 @@ libmailutils_la_LIBADD = \
sockaddr/libsockaddr.la\
cidr/libcidr.la\
cfg/libcfg.la\
+ datetime/libdatetime.la\
diag/libdiag.la\
filter/libfilter.la\
imapio/libimapio.la\
diff --git a/libmailutils/base/Makefile.am b/libmailutils/base/Makefile.am
index a2e5ac5b6..4b9a3759d 100644
--- a/libmailutils/base/Makefile.am
+++ b/libmailutils/base/Makefile.am
@@ -26,7 +26,6 @@ libbase_la_SOURCES = \
assoc.c\
filesafety.c\
daemon.c\
- date.c\
fdwait.c\
fgetpwent.c\
filename.c\
@@ -61,7 +60,6 @@ libbase_la_SOURCES = \
sha1.c\
secret.c\
spawnvp.c\
- strftime.c\
symlink.c\
tempfile.c\
ticket.c\
diff --git a/libmailutils/base/date.c b/libmailutils/base/date.c
deleted file mode 100644
index b3182538d..000000000
--- a/libmailutils/base/date.c
+++ /dev/null
@@ -1,1163 +0,0 @@
-/* GNU Mailutils -- a suite of utilities for electronic mail
- Copyright (C) 1999, 2000, 2001, 2002, 2007, 2009, 2010, 2011 Free
- Software Foundation, Inc.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General
- Public License along with this library. If not, see
- <http://www.gnu.org/licenses/>. */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <mailutils/diag.h>
-#include <mailutils/datetime.h>
-#include <mailutils/util.h>
-#include <mailutils/stream.h>
-#include <mailutils/errno.h>
-#include <mailutils/cstr.h>
-#include <mailutils/cctype.h>
-
-#define SECS_PER_DAY 86400
-#define ADJUSTMENT -719162L
-
-/* Julian day is the number of days since Jan 1, 4713 BC (ouch!).
- Eg. Jan 1, 1900 is: */
-#define JULIAN_1900 1721425L
-
-/* Computes the number of days elapsed since January, 1 1900 to the
- January, 1 of the given year */
-static unsigned
-jan1st (int year)
-{
- year--; /* Do not consider the current year */
- return year * 365L
- + year/4L /* Years divisible by 4 are leap years */
- + year/400L /* Years divisible by 400 are always leap years */
- - year/100L; /* Years divisible by 100 but not 400 aren't */
-}
-
-static int month_start[]=
- { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
- /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
- 31 28 31 30 31 30 31 31 30 31 30 31
- */
-
-#define leap_year(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
-
-static int
-dayofyear (int year, int month, int day)
-{
- int leap, month_days;
-
- if (year < 0 || month < 0 || month > 11)
- return -1;
-
- leap = leap_year (year);
-
- month_days = month_start[month + 1] - month_start[month]
- + ((month == 2) ? leap : 0);
-
- if (day < 0 || day > month_days)
- return -1; /* Illegal Date */
-
- if (month <= 2)
- leap = 0;
-
- return month_start[month] + day + leap;
-}
-
-/* Returns number of days in a given year */
-static int
-year_days (int year)
-{
- return dayofyear (year, 11, 31);
-}
-
-static int
-julianday (unsigned *pd, int year, int month, int day)
-{
- int total = dayofyear (year, month, day);
- if (total == -1)
- return -1;
- *pd = JULIAN_1900 + total + jan1st (year);
- return 0;
-}
-
-static int
-dayofweek (int year, int month, int day)
-{
- unsigned jd;
-
- if (julianday (&jd, year, month, day))
- return -1;
-
- /* January 1, 1900 was Monday, hence +1 */
- return (jd + 1) % 7;
-}
-
-#define ISO_8601_START_WDAY 1 /* Monday */
-#define ISO_8601_MAX_WDAY 4 /* Thursday */
-#define MAXDAYS 366 /* Max. number of days in a year */
-#define ISO_8601_OFF ((MAXDAYS / 7 + 2) * 7)
-
-int
-ISO_8601_weekdays (int yday, int wday)
-{
- return (yday
- - (yday - wday + ISO_8601_MAX_WDAY + ISO_8601_OFF) % 7
- + ISO_8601_MAX_WDAY - ISO_8601_START_WDAY);
-}
-
-/* Convert struct tm into time_t, taking into account timezone offset. */
-/* FIXME: It does not take DST into account */
-time_t
-mu_tm2time (struct tm *tm, struct mu_timezone *tz)
-{
- time_t t;
- int day;
-
- day = dayofyear (tm->tm_year, tm->tm_mon, tm->tm_mday - 1);
- if (day == -1)
- return -1;
- t = (day + ADJUSTMENT + jan1st (1900 + tm->tm_year)) * SECS_PER_DAY
- + (tm->tm_hour * 60 + tm->tm_min) * 60 + tm->tm_sec
- - (tz ? tz->utc_offset : 0);
- return t;
-}
-
-/* Convert time 0 at UTC to our localtime, that tells us the offset
- of our current timezone from UTC. */
-time_t
-mu_utc_offset (void)
-{
- time_t t = 0;
- struct tm *tm = gmtime (&t);
-
- return - mktime (tm);
-}
-
-static const char *short_month[] =
-{
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
-static const char *full_month[] =
-{
- "January", "February", "March", "April",
- "May", "June", "July", "August",
- "September", "October", "November", "December"
-};
-
-static const char *short_wday[] =
-{
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
-};
-
-static const char *full_wday[] =
-{
- "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
- "Friday", "Saturday"
-};
-
-int
-mu_c_streamftime (mu_stream_t str, const char *fmt, struct tm *input_tm,
- struct mu_timezone *tz)
-{
- int rc = 0;
- struct tm tm;
-
- /* Copy input TM because it might have been received from gmtime and
- the fmt might result in further calls to gmtime which will clobber
- it. */
- tm = *input_tm;
- while (*fmt && rc == 0)
- {
- size_t len = strcspn (fmt, "%");
- if (len)
- {
- rc = mu_stream_write (str, fmt, len, NULL);
- if (rc)
- break;
- }
-
- fmt += len;
-
- restart:
- if (!*fmt || !*++fmt)
- break;
-
- switch (*fmt)
- {
- case 'a':
- /* The abbreviated weekday name. */
- if (tm.tm_wday < 0 || tm.tm_wday > 6)
- rc = ERANGE;
- else
- rc = mu_stream_write (str, short_wday[tm.tm_wday],
- strlen (short_wday[tm.tm_wday]), NULL);
- break;
-
- case 'A':
- /* The full weekday name. */
- if (tm.tm_wday < 0 || tm.tm_wday > 6)
- rc = ERANGE;
- else
- rc = mu_stream_write (str, full_wday[tm.tm_wday],
- strlen (full_wday[tm.tm_wday]), NULL);
- break;
-
- case 'b':
- case 'h':
- /* The abbreviated month name. */
- if (tm.tm_mon < 0 || tm.tm_mon > 11)
- rc = ERANGE;
- else
- rc = mu_stream_write (str, short_month[tm.tm_mon],
- strlen (short_month[tm.tm_mon]), NULL);
- break;
-
- case 'B':
- /* The full month name. */
- if (tm.tm_mon < 0 || tm.tm_mon > 11)
- rc = ERANGE;
- else
- rc = mu_stream_write (str, full_month[tm.tm_mon],
- strlen (full_month[tm.tm_mon]), NULL);
- break;
-
- case 'c':
- /* The preferred date and time representation. */
- rc = mu_c_streamftime (str, "%a %b %e %H:%M:%S %Y", &tm, tz);
- break;
-
- case 'C':
- /* The century number (year/100) as a 2-digit integer. */
- rc = mu_stream_printf (str, "%02d", (tm.tm_year + 1900) / 100);
- break;
-
- case 'd':
- /* The day of the month as a decimal number (range 01 to 31). */
- if (tm.tm_mday < 1 || tm.tm_mday > 31)
- rc = ERANGE;
- else
- rc = mu_stream_printf (str, "%02d", tm.tm_mday);
- break;
-
- case 'D':
- /* Equivalent to %m/%d/%y. */
- rc = mu_c_streamftime (str, "%m/%d/%y", &tm, tz);
- break;
-
- case 'e':
- /* Like %d, the day of the month as a decimal number, but a leading
- zero is replaced by a space. */
- if (tm.tm_mday < 1 || tm.tm_mday > 31)
- rc = ERANGE;
- else
- rc = mu_stream_printf (str, "%2d", tm.tm_mday);
- break;
-
- case 'E':
- /* Modifier. The Single Unix Specification mentions %Ec, %EC, %Ex,
- %EX, %Ey, and %EY, which are supposed to use a corresponding
- locale-dependent alternative representation.
- A no-op in POSIX locale */
- goto restart;
-
- case 'F':
- /* Equivalent to %Y-%m-%d (the ISO 8601 date format). */
- rc = mu_c_streamftime (str, "%Y-%m-%d", &tm, tz);
- break;
-
- case 'V':
- /* The ISO 8601:1988 week number of the current year as a decimal
- number range 01 to 53, where week 1 is the first week that has
- at least 4 days in the current year, and with Monday as the
- first day of the week.
- */
- case 'G':
- /* The ISO 8601 year with century as a decimal number. The 4-digit
- year corresponding to the ISO week number (see %V). This has
- the same format and value as %y, except that if the ISO week
- number belongs to the previous or next year, that year is used
- instead.
- */
- case 'g':
- /* Like %G, but without century, that is, with a 2-digit year
- (00-99). */
- {
- int year = tm.tm_year + 1900;
- int days = ISO_8601_weekdays (tm.tm_yday, tm.tm_wday);
-
- if (days < 0)
- {
- days = ISO_8601_weekdays (tm.tm_yday + year_days (year - 1),
- tm.tm_wday);
- year--;
- }
- else
- {
- int d = ISO_8601_weekdays (tm.tm_yday - year_days (year),
- tm.tm_wday);
- if (d >= 0)
- {
- year++;
- days = d;
- }
- }
-
- switch (*fmt)
- {
- case 'V':
- rc = mu_stream_printf (str, "%02d", days / 7 + 1);
- break;
-
- case 'G':
- rc = mu_stream_printf (str, "%4d", year);
- break;
-
- case 'g':
- rc = mu_stream_printf (str, "%02d", year % 100);
- }
- }
- break;
-
- case 'H':
- /* The hour as a decimal number using a 24-hour clock (range 00 to
- 23). */
- rc = mu_stream_printf (str, "%02d", tm.tm_hour);
- break;
-
- case 'I':
- /* The hour as a decimal number using a 12-hour clock (range 01 to
- 12). */
- {
- unsigned n = tm.tm_hour % 12;
- rc = mu_stream_printf (str, "%02d", n == 0 ? 12 : n);
- }
- break;
-
- case 'j':
- /* The day of the year as a decimal number (range 001 to 366). */
- rc = mu_stream_printf (str, "%03d", tm.tm_yday + 1);
- break;
-
- case 'k':
- /* The hour (24-hour clock) as a decimal number (range 0 to 23);
- single digits are preceded by a blank. */
- rc = mu_stream_printf (str, "%2d", tm.tm_hour);
- break;
-
- case 'l':
- /* The hour (12-hour clock) as a decimal number (range 1 to 12);
- single digits are preceded by a blank. */
- {
- unsigned n = tm.tm_hour % 12;
- rc = mu_stream_printf (str, "%2d", n == 0 ? 12 : n);
- }
- break;
-
- case 'm':
- /* The month as a decimal number (range 01 to 12). */
- rc = mu_stream_printf (str, "%02d", tm.tm_mon + 1);
- break;
-
- case 'M':
- /* The minute as a decimal number (range 00 to 59). */
- rc = mu_stream_printf (str, "%02d", tm.tm_min);
- break;
-
- case 'n':
- /* A newline character. */
- rc = mu_stream_write (str, "\n", 1, NULL);
- break;
-
- case 'O':
- /* Modifier. The Single Unix Specification mentions %Od, %Oe, %OH,
- %OI, %Om, %OM, %OS, %Ou, %OU, %OV, %Ow, %OW, and %Oy, which are
- supposed to use alternative numeric symbols.
-
- Hardly of any use for our purposes, hence a no-op. */
- goto restart;
-
- case 'p':
- /* Either "AM" or "PM" according to the given time value.
- Noon is treated as "PM" and midnight as "AM". */
- rc = mu_stream_write (str,
- tm.tm_hour < 12 ? "AM" : "PM",
- 2, NULL);
- break;
-
- case 'P':
- /* Like %p but in lowercase: "am" or "pm". */
- rc = mu_stream_write (str,
- tm.tm_hour < 12 ? "am" : "pm",
- 2, NULL);
- break;
-
- case 'r':
- /* The time in a.m. or p.m. notation, i.e. %I:%M:%S %p. */
- rc = mu_c_streamftime (str, "%I:%M:%S %p", &tm, tz);
- break;
-
- case 'R':
- /* The time in 24-hour notation (%H:%M) */
- rc = mu_c_streamftime (str, "%H:%M", &tm, tz);
- break;
-
- case 's':
- /* The number of seconds since the Epoch */
- rc = mu_stream_printf (str, "%lu",
- (unsigned long) mu_tm2time (&tm, tz));
- break;
-
- case 'S':
- /* The second as a decimal number (range 00 to 60) */
- rc = mu_stream_printf (str, "%02d", tm.tm_sec);
- break;
-
- case 't':
- /* A tab character. */
- rc = mu_stream_write (str, "\t", 1, NULL);
- break;
-
- case 'T':
- /* The time in 24-hour notation (%H:%M:%S) */
- rc = mu_c_streamftime (str, "%H:%M:%S", &tm, tz);
- break;
-
- case 'u':
- /* The day of the week as a decimal, range 1 to 7, Monday being 1.
- */
- rc = mu_stream_printf (str, "%1d",
- tm.tm_wday == 0 ? 7 : tm.tm_wday);
- break;
-
- case 'U':
- /* The week number of the current year as a decimal number, range
- 00 to 53, starting with the first Sunday as the first day of
- week 01.
- */
- rc = mu_stream_printf (str, "%02d",
- (tm.tm_yday - tm.tm_wday + 7) / 7);
- break;
-
- case 'w':
- /* The day of the week as a decimal, range 0 to 6, Sunday being 0.
- */
- rc = mu_stream_printf (str, "%01d", tm.tm_wday);
- break;
-
- case 'W':
- /* The week number of the current year as a decimal number, range
- 00 to 53, starting with the first Monday as the first day of
- week 01. */
- rc = mu_stream_printf (str, "%02d",
- (tm.tm_yday - (tm.tm_wday - 1 + 7) % 7 + 7) / 7);
- break;
-
- /* The preferred date representation without the time:
- equivalent to %D */
- case 'x':
- rc = mu_c_streamftime (str, "%m/%d/%y", &tm, tz);
- break;
-
- case 'X':
- /* The preferred date representation without the date */
- rc = mu_c_streamftime (str, "%H:%M:%S", &tm, tz);
- break;
-
- case 'y':
- /* The year as a decimal number without a century (range 00 to 99).
- */
- rc = mu_stream_printf (str, "%02d", (tm.tm_year + 1900) % 100);
- break;
-
- case 'Y':
- /* The year as a decimal number including the century. */
- rc = mu_stream_printf (str, "%d", tm.tm_year + 1900);
- break;
-
- case 'z':
- case 'Z':
- /* The time-zone as hour offset from GMT, for formatting RFC-822
- dates (e.g. "%a, %d %b %Y %H:%M:%S %z") */
- {
- int utc_off = tz ? tz->utc_offset : mu_utc_offset ();
- int sign;
- if (utc_off < 0)
- {
- sign = '-';
- utc_off = - utc_off;
- }
- else
- sign = '+';
- utc_off /= 60;
- rc = mu_stream_printf (str, "%c%02u%02u", sign,
- utc_off / 60, utc_off % 60);
- }
- break;
-
- case '%':
- /* A literal '%' character. */
- rc = mu_stream_write (str, "%", 1, NULL);
- break;
-
- case '$':
- /* Ignored for compatibilty with mu_scan_datetime */
- break;
-
- case '+':
- /* Not supported (date and time in date(1) format. */
- default:
- rc = mu_stream_write (str, fmt-1, 2, NULL);
- break;
- }
- fmt++;
- }
- /* Restore input tm */
- *input_tm = tm;
- return rc;
-}
-
-static int
-_mu_short_weekday_string (const char *str)
-{
- int i;
-
- for (i = 0; i < 7; i++)
- {
- if (mu_c_strncasecmp (str, short_wday[i], 3) == 0)
- return i;
- }
- return -1;
-}
-
-static int
-_mu_full_weekday_string (const char *str, char **endp)
-{
- int i;
-
- for (i = 0; i < 7; i++)
- {
- if (mu_c_strcasecmp (str, full_wday[i]) == 0)
- {
- if (endp)
- *endp = (char*) (str + strlen (full_wday[i]));
- return i;
- }
- }
- return -1;
-}
-
-
-static int
-_mu_short_month_string (const char *str)
-{
- int i;
-
- for (i = 0; i < 12; i++)
- {
- if (mu_c_strncasecmp (str, short_month[i], 3) == 0)
- return i;
- }
- return -1;
-}
-
-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;
-}
-
-int
-get_num (const char *str, char **endp, int ndig, int minval, int maxval,
- int *pn)
-{
- int x = 0;
- int i;
-
- errno = 0;
- for (i = 0; i < ndig && *str && mu_isdigit (*str); str++, i++)
- x = x * 10 + *str - '0';
-
- *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;
-}
-
-#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
-
-#define ST_NON -1
-#define ST_OPT 0
-#define ST_ALT 1
-
-struct save_input
-{
- int state;
- const char *input;
-};
-
-static int
-push_input (mu_list_t *plist, int state, const char *input)
-{
- mu_list_t list = *plist;
- struct save_input *inp = malloc (sizeof (*inp));
- if (!inp)
- return ENOMEM;
- if (!list)
- {
- int rc = mu_list_create (&list);
- if (rc)
- {
- free (inp);
- return rc;
- }
- mu_list_set_destroy_item (list, mu_list_free_item);
- *plist = list;
- }
- inp->state = state;
- inp->input = input;
- return mu_list_push (list, (void*)inp);
-}
-
-static int
-peek_state (mu_list_t list, int *state, const char **input)
-{
- int rc;
- struct save_input *inp;
-
- rc = mu_list_tail (list, (void**)&inp);
- if (rc)
- return rc;
- *state = inp->state;
- if (input)
- *input = inp->input;
- return 0;
-}
-
-static int
-pop_input (mu_list_t list, int *state, const char **input)
-{
- int rc;
- struct save_input *inp;
-
- rc = mu_list_pop (list, (void**)&inp);
- if (rc)
- return rc;
- *state = inp->state;
- if (input)
- *input = inp->input;
- return 0;
-}
-
-static int
-bracket_to_state (int c)
-{
- switch (c)
- {
- case '[':
- case ']':
- return ST_OPT;
- case '(':
- case ')':
- return ST_ALT;
- }
- return ST_NON;
-}
-
-static int
-state_to_closing_bracket (int st)
-{
- switch (st)
- {
- case ST_OPT:
- return ']';
- case ST_ALT:
- return ')';
- }
- return '?';
-}
-
-static int
-scan_recovery (const char *fmt, mu_list_t *plist, int skip_alt,
- const char **endp,
- const char **input)
-{
- int c, rc = 0;
- int nesting_level = 1;
- int st;
- const char *p;
-
- while (*fmt)
- {
- c = *fmt++;
-
- if (c == '%')
- {
- c = *fmt++;
- if (!c)
- {
- mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
- ("%s:%d: error in format: %% at the end of input",
- __FILE__, __LINE__));
- rc = MU_ERR_FORMAT;
- break;
- }
-
- switch (c)
- {
- case '[':
- case '(':
- nesting_level++;
- rc = push_input (plist, bracket_to_state (c), NULL);
- break;
-
- case ')':
- case ']':
- rc = pop_input (*plist, &st, &p);
- if (rc || st != bracket_to_state (c))
- {
- mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
- ("%s:%d: error in format: %%%c out of context",
- __FILE__, __LINE__, c));
- rc = MU_ERR_FORMAT;
- break;
- }
- if (--nesting_level == 0)
- {
- *endp = fmt;
- if (skip_alt)
- return 0;
- *input = p;
- if (st == ST_ALT)
- {
- if (*fmt == '%' && (fmt[1] == '|' || fmt[1] == ']'))
- return 0;
- return MU_ERR_PARSE; /* No match found */
- }
- return 0;
- }
- break;
-
- case '|':
- if (skip_alt)
- continue;
- if (nesting_level == 1)
- {
- *endp = fmt;
- return peek_state (*plist, &st, input);
- }
- break;
-
- case '\\':
- if (*++fmt == 0)
- {
- peek_state (*plist, &st, NULL);
- mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
- ("%s:%d: error in format: missing closing %%%c",
- __FILE__, __LINE__,
- state_to_closing_bracket (st)));
- return MU_ERR_FORMAT;
- }
- }
- }
- }
-
- peek_state (*plist, &st, NULL);
- mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
- ("%s:%d: error in format: missing closing %%%c",
- __FILE__, __LINE__,
- state_to_closing_bracket (st)));
- return MU_ERR_FORMAT;
-}
-
-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 c;
- int st;
- int recovery = 0;
- int eof_ok = 0;
- int datetime_parts = 0;
- mu_list_t save_input_list = NULL;
-
- 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)
- {
- memset (tz, 0, sizeof *tz);
- tz->utc_offset = mu_utc_offset ();
- }
-
- /* Skip leading whitespace */
- input = mu_str_skip_class (input, MU_CTYPE_BLANK);
- for (; *fmt && rc == 0; fmt++)
- {
- if (mu_isspace (*fmt))
- {
- 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 == '%')
- {
- c = *++fmt;
- if (!c)
- {
- mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
- ("%s:%d: error in format: %% at the end of input",
- __FILE__, __LINE__));
- rc = MU_ERR_FORMAT;
- break;
- }
-
- switch (c)
- {
- 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;
-
- rc = push_input (&save_input_list, ST_ALT, (void*)input);
- break;
-
- case '(':
- case '[':
- rc = push_input (&save_input_list, bracket_to_state (c),
- (void*)input);
- break;
-
- case ')':
- case ']':
- if (pop_input (save_input_list, &st, NULL))
- {
- mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
- ("%s:%d: error in format: unbalanced %%%c near %s",
- __FILE__, __LINE__, c, fmt));
- rc = MU_ERR_FORMAT;
- }
- else if (st != bracket_to_state (c))
- {
- mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
- ("%s:%d: error in format: %%%c out of context",
- __FILE__, __LINE__, c));
- rc = MU_ERR_FORMAT;
- }
- break;
-
- case '|':
- rc = scan_recovery (fmt, &save_input_list, 1, &fmt, NULL);
- if (rc == 0)
- fmt--;
- break;
-
- case '$':
- eof_ok = 1;
- break;
-
- case '\\':
- c = *++fmt;
- if (!c)
- {
- mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
- ("%s:%d: error in format: %% at the end of input",
- __FILE__, __LINE__));
- rc = MU_ERR_FORMAT;
- }
- else if (c == *input)
- input++;
- else
- rc = MU_ERR_PARSE;
- break;
-
- case '?':
- input++;
- break;
-
- default:
- mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
- ("%s:%d: error in format: unrecognized conversion type"
- " near %s",
- __FILE__, __LINE__, fmt));
- rc = MU_ERR_FORMAT;
- break;
- }
-
- if (eof_ok && rc == 0 && *input == 0)
- break;
- }
- else if (!recovery && *input != *fmt)
- rc = MU_ERR_PARSE;
- else
- input++;
-
- if (rc == MU_ERR_PARSE && !mu_list_is_empty (save_input_list))
- {
- rc = scan_recovery (fmt, &save_input_list, 0, &fmt, &input);
- if (rc == 0)
- --fmt;
- }
- }
-
- if (!mu_list_is_empty (save_input_list))
- {
- mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
- ("%s:%d: error in format: closing bracket missing",
- __FILE__, __LINE__));
- rc = MU_ERR_FORMAT;
- }
- mu_list_destroy (&save_input_list);
-
- if (rc == 0 && recovery)
- rc = MU_ERR_PARSE;
-
- if (!eof_ok && rc == 0 && *input == 0 && *fmt)
- rc = MU_ERR_PARSE;
-
- if ((datetime_parts & (DT_YEAR|DT_MONTH|DT_MDAY)) ==
- (DT_YEAR|DT_MONTH|DT_MDAY))
- {
- if (!(datetime_parts & DT_WDAY))
- tm->tm_wday = dayofweek (tm->tm_year + 1900, tm->tm_mon, tm->tm_mday);
- tm->tm_yday = dayofyear (tm->tm_year + 1900, tm->tm_mon, tm->tm_mday);
- }
-
- if (endp)
- *endp = (char*) input;
-
- return rc;
-}
diff --git a/libmailutils/datetime/Makefile.am b/libmailutils/datetime/Makefile.am
new file mode 100644
index 000000000..f9a4f451b
--- /dev/null
+++ b/libmailutils/datetime/Makefile.am
@@ -0,0 +1,32 @@
+# GNU Mailutils -- a suite of utilities for electronic mail
+# Copyright (C) 2011 Free Software Foundation, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General
+# Public License along with this library. If not, see
+# <http://www.gnu.org/licenses/>.
+
+noinst_LTLIBRARIES = libdatetime.la
+
+libdatetime_la_SOURCES = \
+ dow.c\
+ doy.c\
+ jd.c\
+ scantime.c\
+ streamftime.c\
+ strftime.c\
+ tab.c\
+ unixtime.c\
+ utcoff.c\
+ yd.c
+
+INCLUDES = @MU_LIB_COMMON_INCLUDES@ -I/libmailutils
diff --git a/libmailutils/datetime/dow.c b/libmailutils/datetime/dow.c
new file mode 100644
index 000000000..9f94a6803
--- /dev/null
+++ b/libmailutils/datetime/dow.c
@@ -0,0 +1,29 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <mailutils/datetime.h>
+
+/* Compute day of week (0 - Sunday) */
+int
+mu_datetime_dayofweek (int year, int month, int day)
+{
+ return (mu_datetime_julianday (year, month, day) + 1) % 7;
+}
diff --git a/libmailutils/datetime/doy.c b/libmailutils/datetime/doy.c
new file mode 100644
index 000000000..49883cd32
--- /dev/null
+++ b/libmailutils/datetime/doy.c
@@ -0,0 +1,53 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <mailutils/datetime.h>
+
+static int month_start[]=
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
+ /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
+ 31 28 31 30 31 30 31 31 30 31 30 31
+ */
+
+#define leap_year(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
+
+/* Compute number of days since January 1 to the given day of year. */
+int
+mu_datetime_dayofyear (int year, int month, int day)
+{
+ int leap, month_days;
+
+ if (year < 0 || month < 1 || month > 12 || day < 1)
+ return -1;
+
+ leap = leap_year (year);
+
+ month_days = month_start[month] - month_start[month - 1]
+ + ((month == 2) ? leap : 0);
+
+ if (day > month_days)
+ return -1;
+
+ if (month <= 2)
+ leap = 0;
+
+ return month_start[month-1] + day + leap;
+}
diff --git a/libmailutils/datetime/jd.c b/libmailutils/datetime/jd.c
new file mode 100644
index 000000000..39fdf8014
--- /dev/null
+++ b/libmailutils/datetime/jd.c
@@ -0,0 +1,32 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <mailutils/datetime.h>
+
+int
+mu_datetime_julianday (int year, int month, int day)
+{
+ int a = (14 - month) / 12;
+ int y = year + 4800 - a;
+ int m = month + 12*a - 3;
+
+ return day + (153*m + 2)/5 + 365*y + y/4 - y/100 + y/400 - 32045;
+}
diff --git a/libmailutils/datetime/scantime.c b/libmailutils/datetime/scantime.c
new file mode 100644
index 000000000..670052bb3
--- /dev/null
+++ b/libmailutils/datetime/scantime.c
@@ -0,0 +1,660 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2002, 2007, 2009, 2010, 2011 Free
+ Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/diag.h>
+#include <mailutils/datetime.h>
+#include <mailutils/util.h>
+#include <mailutils/stream.h>
+#include <mailutils/errno.h>
+#include <mailutils/cstr.h>
+#include <mailutils/cctype.h>
+
+static int
+_mu_short_weekday_string (const char *str)
+{
+ int i;
+
+ for (i = 0; i < 7; i++)
+ {
+ if (mu_c_strncasecmp (str, _mu_datetime_short_wday[i], 3) == 0)
+ return i;
+ }
+ return -1;
+}
+
+static int
+_mu_full_weekday_string (const char *str, char **endp)
+{
+ int i;
+
+ for (i = 0; i < 7; i++)
+ {
+ if (mu_c_strcasecmp (str, _mu_datetime_full_wday[i]) == 0)
+ {
+ if (endp)
+ *endp = (char*) (str + strlen (_mu_datetime_full_wday[i]));
+ return i;
+ }
+ }
+ return -1;
+}
+
+static int
+_mu_short_month_string (const char *str)
+{
+ int i;
+
+ for (i = 0; i < 12; i++)
+ {
+ if (mu_c_strncasecmp (str, _mu_datetime_short_month[i], 3) == 0)
+ return i;
+ }
+ return -1;
+}
+
+static int
+_mu_full_month_string (const char *str, char **endp)
+{
+ int i;
+
+ for (i = 0; i < 12; i++)
+ {
+ if (mu_c_strcasecmp (str, _mu_datetime_full_month[i]) == 0)
+ {
+ if (endp)
+ *endp = (char*) (str + strlen (_mu_datetime_full_month[i]));
+ return i;
+ }
+ }
+ return -1;
+}
+
+int
+get_num (const char *str, char **endp, int ndig, int minval, int maxval,
+ int *pn)
+{
+ int x = 0;
+ int i;
+
+ errno = 0;
+ for (i = 0; i < ndig && *str && mu_isdigit (*str); str++, i++)
+ x = x * 10 + *str - '0';
+
+ *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;
+}
+
+#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
+
+#define ST_NON -1
+#define ST_OPT 0
+#define ST_ALT 1
+
+struct save_input
+{
+ int state;
+ const char *input;
+};
+
+static int
+push_input (mu_list_t *plist, int state, const char *input)
+{
+ mu_list_t list = *plist;
+ struct save_input *inp = malloc (sizeof (*inp));
+ if (!inp)
+ return ENOMEM;
+ if (!list)
+ {
+ int rc = mu_list_create (&list);
+ if (rc)
+ {
+ free (inp);
+ return rc;
+ }
+ mu_list_set_destroy_item (list, mu_list_free_item);
+ *plist = list;
+ }
+ inp->state = state;
+ inp->input = input;
+ return mu_list_push (list, (void*)inp);
+}
+
+static int
+peek_state (mu_list_t list, int *state, const char **input)
+{
+ int rc;
+ struct save_input *inp;
+
+ rc = mu_list_tail (list, (void**)&inp);
+ if (rc)
+ return rc;
+ *state = inp->state;
+ if (input)
+ *input = inp->input;
+ return 0;
+}
+
+static int
+pop_input (mu_list_t list, int *state, const char **input)
+{
+ int rc;
+ struct save_input *inp;
+
+ rc = mu_list_pop (list, (void**)&inp);
+ if (rc)
+ return rc;
+ *state = inp->state;
+ if (input)
+ *input = inp->input;
+ return 0;
+}
+
+static int
+bracket_to_state (int c)
+{
+ switch (c)
+ {
+ case '[':
+ case ']':
+ return ST_OPT;
+ case '(':
+ case ')':
+ return ST_ALT;
+ }
+ return ST_NON;
+}
+
+static int
+state_to_closing_bracket (int st)
+{
+ switch (st)
+ {
+ case ST_OPT:
+ return ']';
+ case ST_ALT:
+ return ')';
+ }
+ return '?';
+}
+
+static int
+scan_recovery (const char *fmt, mu_list_t *plist, int skip_alt,
+ const char **endp,
+ const char **input)
+{
+ int c, rc = 0;
+ int nesting_level = 1;
+ int st;
+ const char *p;
+
+ while (*fmt)
+ {
+ c = *fmt++;
+
+ if (c == '%')
+ {
+ c = *fmt++;
+ if (!c)
+ {
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s:%d: error in format: %% at the end of input",
+ __FILE__, __LINE__));
+ rc = MU_ERR_FORMAT;
+ break;
+ }
+
+ switch (c)
+ {
+ case '[':
+ case '(':
+ nesting_level++;
+ rc = push_input (plist, bracket_to_state (c), NULL);
+ break;
+
+ case ')':
+ case ']':
+ rc = pop_input (*plist, &st, &p);
+ if (rc || st != bracket_to_state (c))
+ {
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s:%d: error in format: %%%c out of context",
+ __FILE__, __LINE__, c));
+ rc = MU_ERR_FORMAT;
+ break;
+ }
+ if (--nesting_level == 0)
+ {
+ *endp = fmt;
+ if (skip_alt)
+ return 0;
+ *input = p;
+ if (st == ST_ALT)
+ {
+ if (*fmt == '%' && (fmt[1] == '|' || fmt[1] == ']'))
+ return 0;
+ return MU_ERR_PARSE; /* No match found */
+ }
+ return 0;
+ }
+ break;
+
+ case '|':
+ if (skip_alt)
+ continue;
+ if (nesting_level == 1)
+ {
+ *endp = fmt;
+ return peek_state (*plist, &st, input);
+ }
+ break;
+
+ case '\\':
+ if (*++fmt == 0)
+ {
+ peek_state (*plist, &st, NULL);
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s:%d: error in format: missing closing %%%c",
+ __FILE__, __LINE__,
+ state_to_closing_bracket (st)));
+ return MU_ERR_FORMAT;
+ }
+ }
+ }
+ }
+
+ peek_state (*plist, &st, NULL);
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s:%d: error in format: missing closing %%%c",
+ __FILE__, __LINE__,
+ state_to_closing_bracket (st)));
+ return MU_ERR_FORMAT;
+}
+
+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 c;
+ int st;
+ int recovery = 0;
+ int eof_ok = 0;
+ int datetime_parts = 0;
+ mu_list_t save_input_list = NULL;
+
+ 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)
+ {
+ memset (tz, 0, sizeof *tz);
+ tz->utc_offset = mu_utc_offset ();
+ }
+
+ /* Skip leading whitespace */
+ input = mu_str_skip_class (input, MU_CTYPE_BLANK);
+ for (; *fmt && rc == 0; fmt++)
+ {
+ if (mu_isspace (*fmt))
+ {
+ 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 == '%')
+ {
+ c = *++fmt;
+ if (!c)
+ {
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s:%d: error in format: %% at the end of input",
+ __FILE__, __LINE__));
+ rc = MU_ERR_FORMAT;
+ break;
+ }
+
+ switch (c)
+ {
+ 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;
+
+ rc = push_input (&save_input_list, ST_ALT, (void*)input);
+ break;
+
+ case '(':
+ case '[':
+ rc = push_input (&save_input_list, bracket_to_state (c),
+ (void*)input);
+ break;
+
+ case ')':
+ case ']':
+ if (pop_input (save_input_list, &st, NULL))
+ {
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s:%d: error in format: unbalanced %%%c near %s",
+ __FILE__, __LINE__, c, fmt));
+ rc = MU_ERR_FORMAT;
+ }
+ else if (st != bracket_to_state (c))
+ {
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s:%d: error in format: %%%c out of context",
+ __FILE__, __LINE__, c));
+ rc = MU_ERR_FORMAT;
+ }
+ break;
+
+ case '|':
+ rc = scan_recovery (fmt, &save_input_list, 1, &fmt, NULL);
+ if (rc == 0)
+ fmt--;
+ break;
+
+ case '$':
+ eof_ok = 1;
+ break;
+
+ case '\\':
+ c = *++fmt;
+ if (!c)
+ {
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s:%d: error in format: %% at the end of input",
+ __FILE__, __LINE__));
+ rc = MU_ERR_FORMAT;
+ }
+ else if (c == *input)
+ input++;
+ else
+ rc = MU_ERR_PARSE;
+ break;
+
+ case '?':
+ input++;
+ break;
+
+ default:
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s:%d: error in format: unrecognized conversion type"
+ " near %s",
+ __FILE__, __LINE__, fmt));
+ rc = MU_ERR_FORMAT;
+ break;
+ }
+
+ if (eof_ok && rc == 0 && *input == 0)
+ break;
+ }
+ else if (!recovery && *input != *fmt)
+ rc = MU_ERR_PARSE;
+ else
+ input++;
+
+ if (rc == MU_ERR_PARSE && !mu_list_is_empty (save_input_list))
+ {
+ rc = scan_recovery (fmt, &save_input_list, 0, &fmt, &input);
+ if (rc == 0)
+ --fmt;
+ }
+ }
+
+ if (!mu_list_is_empty (save_input_list))
+ {
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s:%d: error in format: closing bracket missing",
+ __FILE__, __LINE__));
+ rc = MU_ERR_FORMAT;
+ }
+ mu_list_destroy (&save_input_list);
+
+ if (rc == 0 && recovery)
+ rc = MU_ERR_PARSE;
+
+ if (!eof_ok && rc == 0 && *input == 0 && *fmt)
+ rc = MU_ERR_PARSE;
+
+ if ((datetime_parts & (DT_YEAR|DT_MONTH|DT_MDAY)) ==
+ (DT_YEAR|DT_MONTH|DT_MDAY))
+ {
+ if (!(datetime_parts & DT_WDAY))
+ tm->tm_wday = mu_datetime_dayofweek (tm->tm_year + 1900,
+ tm->tm_mon + 1, tm->tm_mday);
+ tm->tm_yday = mu_datetime_dayofyear (tm->tm_year + 1900,
+ tm->tm_mon + 1, tm->tm_mday) - 1;
+ }
+
+ if (endp)
+ *endp = (char*) input;
+
+ return rc;
+}
diff --git a/libmailutils/datetime/streamftime.c b/libmailutils/datetime/streamftime.c
new file mode 100644
index 000000000..e67d7d4d0
--- /dev/null
+++ b/libmailutils/datetime/streamftime.c
@@ -0,0 +1,411 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2002, 2007, 2009, 2010, 2011 Free
+ Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/diag.h>
+#include <mailutils/datetime.h>
+#include <mailutils/util.h>
+#include <mailutils/stream.h>
+#include <mailutils/errno.h>
+#include <mailutils/cstr.h>
+#include <mailutils/cctype.h>
+
+#define ISO_8601_START_WDAY 1 /* Monday */
+#define ISO_8601_MAX_WDAY 4 /* Thursday */
+#define MAXDAYS 366 /* Max. number of days in a year */
+#define ISO_8601_OFF ((MAXDAYS / 7 + 2) * 7)
+
+int
+ISO_8601_weekdays (int yday, int wday)
+{
+ return (yday
+ - (yday - wday + ISO_8601_MAX_WDAY + ISO_8601_OFF) % 7
+ + ISO_8601_MAX_WDAY - ISO_8601_START_WDAY);
+}
+
+int
+mu_c_streamftime (mu_stream_t str, const char *fmt, struct tm *input_tm,
+ struct mu_timezone *tz)
+{
+ int rc = 0;
+ struct tm tm;
+
+ /* Copy input TM because it might have been received from gmtime and
+ the fmt might result in further calls to gmtime which will clobber
+ it. */
+ tm = *input_tm;
+ while (*fmt && rc == 0)
+ {
+ size_t len = strcspn (fmt, "%");
+ if (len)
+ {
+ rc = mu_stream_write (str, fmt, len, NULL);
+ if (rc)
+ break;
+ }
+
+ fmt += len;
+
+ restart:
+ if (!*fmt || !*++fmt)
+ break;
+
+ switch (*fmt)
+ {
+ case 'a':
+ /* The abbreviated weekday name. */
+ if (tm.tm_wday < 0 || tm.tm_wday > 6)
+ rc = ERANGE;
+ else
+ rc = mu_stream_write (str, _mu_datetime_short_wday[tm.tm_wday],
+ strlen (_mu_datetime_short_wday[tm.tm_wday]),
+ NULL);
+ break;
+
+ case 'A':
+ /* The full weekday name. */
+ if (tm.tm_wday < 0 || tm.tm_wday > 6)
+ rc = ERANGE;
+ else
+ rc = mu_stream_write (str, _mu_datetime_full_wday[tm.tm_wday],
+ strlen (_mu_datetime_full_wday[tm.tm_wday]),
+ NULL);
+ break;
+
+ case 'b':
+ case 'h':
+ /* The abbreviated month name. */
+ if (tm.tm_mon < 0 || tm.tm_mon > 11)
+ rc = ERANGE;
+ else
+ rc = mu_stream_write (str, _mu_datetime_short_month[tm.tm_mon],
+ strlen (_mu_datetime_short_month[tm.tm_mon]),
+ NULL);
+ break;
+
+ case 'B':
+ /* The full month name. */
+ if (tm.tm_mon < 0 || tm.tm_mon > 11)
+ rc = ERANGE;
+ else
+ rc = mu_stream_write (str, _mu_datetime_full_month[tm.tm_mon],
+ strlen (_mu_datetime_full_month[tm.tm_mon]),
+ NULL);
+ break;
+
+ case 'c':
+ /* The preferred date and time representation. */
+ rc = mu_c_streamftime (str, "%a %b %e %H:%M:%S %Y", &tm, tz);
+ break;
+
+ case 'C':
+ /* The century number (year/100) as a 2-digit integer. */
+ rc = mu_stream_printf (str, "%02d", (tm.tm_year + 1900) / 100);
+ break;
+
+ case 'd':
+ /* The day of the month as a decimal number (range 01 to 31). */
+ if (tm.tm_mday < 1 || tm.tm_mday > 31)
+ rc = ERANGE;
+ else
+ rc = mu_stream_printf (str, "%02d", tm.tm_mday);
+ break;
+
+ case 'D':
+ /* Equivalent to %m/%d/%y. */
+ rc = mu_c_streamftime (str, "%m/%d/%y", &tm, tz);
+ break;
+
+ case 'e':
+ /* Like %d, the day of the month as a decimal number, but a leading
+ zero is replaced by a space. */
+ if (tm.tm_mday < 1 || tm.tm_mday > 31)
+ rc = ERANGE;
+ else
+ rc = mu_stream_printf (str, "%2d", tm.tm_mday);
+ break;
+
+ case 'E':
+ /* Modifier. The Single Unix Specification mentions %Ec, %EC, %Ex,
+ %EX, %Ey, and %EY, which are supposed to use a corresponding
+ locale-dependent alternative representation.
+ A no-op in POSIX locale */
+ goto restart;
+
+ case 'F':
+ /* Equivalent to %Y-%m-%d (the ISO 8601 date format). */
+ rc = mu_c_streamftime (str, "%Y-%m-%d", &tm, tz);
+ break;
+
+ case 'V':
+ /* The ISO 8601:1988 week number of the current year as a decimal
+ number range 01 to 53, where week 1 is the first week that has
+ at least 4 days in the current year, and with Monday as the
+ first day of the week.
+ */
+ case 'G':
+ /* The ISO 8601 year with century as a decimal number. The 4-digit
+ year corresponding to the ISO week number (see %V). This has
+ the same format and value as %y, except that if the ISO week
+ number belongs to the previous or next year, that year is used
+ instead.
+ */
+ case 'g':
+ /* Like %G, but without century, that is, with a 2-digit year
+ (00-99). */
+ {
+ int year = tm.tm_year + 1900;
+ int days = ISO_8601_weekdays (tm.tm_yday, tm.tm_wday);
+
+ if (days < 0)
+ {
+ days = ISO_8601_weekdays (tm.tm_yday +
+ mu_datetime_year_days (year - 1),
+ tm.tm_wday);
+ year--;
+ }
+ else
+ {
+ int d = ISO_8601_weekdays (tm.tm_yday -
+ mu_datetime_year_days (year),
+ tm.tm_wday);
+ if (d >= 0)
+ {
+ year++;
+ days = d;
+ }
+ }
+
+ switch (*fmt)
+ {
+ case 'V':
+ rc = mu_stream_printf (str, "%02d", days / 7 + 1);
+ break;
+
+ case 'G':
+ rc = mu_stream_printf (str, "%4d", year);
+ break;
+
+ case 'g':
+ rc = mu_stream_printf (str, "%02d", year % 100);
+ }
+ }
+ break;
+
+ case 'H':
+ /* The hour as a decimal number using a 24-hour clock (range 00 to
+ 23). */
+ rc = mu_stream_printf (str, "%02d", tm.tm_hour);
+ break;
+
+ case 'I':
+ /* The hour as a decimal number using a 12-hour clock (range 01 to
+ 12). */
+ {
+ unsigned n = tm.tm_hour % 12;
+ rc = mu_stream_printf (str, "%02d", n == 0 ? 12 : n);
+ }
+ break;
+
+ case 'j':
+ /* The day of the year as a decimal number (range 001 to 366). */
+ rc = mu_stream_printf (str, "%03d", tm.tm_yday + 1);
+ break;
+
+ case 'k':
+ /* The hour (24-hour clock) as a decimal number (range 0 to 23);
+ single digits are preceded by a blank. */
+ rc = mu_stream_printf (str, "%2d", tm.tm_hour);
+ break;
+
+ case 'l':
+ /* The hour (12-hour clock) as a decimal number (range 1 to 12);
+ single digits are preceded by a blank. */
+ {
+ unsigned n = tm.tm_hour % 12;
+ rc = mu_stream_printf (str, "%2d", n == 0 ? 12 : n);
+ }
+ break;
+
+ case 'm':
+ /* The month as a decimal number (range 01 to 12). */
+ rc = mu_stream_printf (str, "%02d", tm.tm_mon + 1);
+ break;
+
+ case 'M':
+ /* The minute as a decimal number (range 00 to 59). */
+ rc = mu_stream_printf (str, "%02d", tm.tm_min);
+ break;
+
+ case 'n':
+ /* A newline character. */
+ rc = mu_stream_write (str, "\n", 1, NULL);
+ break;
+
+ case 'O':
+ /* Modifier. The Single Unix Specification mentions %Od, %Oe, %OH,
+ %OI, %Om, %OM, %OS, %Ou, %OU, %OV, %Ow, %OW, and %Oy, which are
+ supposed to use alternative numeric symbols.
+
+ Hardly of any use for our purposes, hence a no-op. */
+ goto restart;
+
+ case 'p':
+ /* Either "AM" or "PM" according to the given time value.
+ Noon is treated as "PM" and midnight as "AM". */
+ rc = mu_stream_write (str,
+ tm.tm_hour < 12 ? "AM" : "PM",
+ 2, NULL);
+ break;
+
+ case 'P':
+ /* Like %p but in lowercase: "am" or "pm". */
+ rc = mu_stream_write (str,
+ tm.tm_hour < 12 ? "am" : "pm",
+ 2, NULL);
+ break;
+
+ case 'r':
+ /* The time in a.m. or p.m. notation, i.e. %I:%M:%S %p. */
+ rc = mu_c_streamftime (str, "%I:%M:%S %p", &tm, tz);
+ break;
+
+ case 'R':
+ /* The time in 24-hour notation (%H:%M) */
+ rc = mu_c_streamftime (str, "%H:%M", &tm, tz);
+ break;
+
+ case 's':
+ /* The number of seconds since the Epoch */
+ rc = mu_stream_printf (str, "%lu",
+ (unsigned long) mu_tm2time (&tm, tz));
+ break;
+
+ case 'S':
+ /* The second as a decimal number (range 00 to 60) */
+ rc = mu_stream_printf (str, "%02d", tm.tm_sec);
+ break;
+
+ case 't':
+ /* A tab character. */
+ rc = mu_stream_write (str, "\t", 1, NULL);
+ break;
+
+ case 'T':
+ /* The time in 24-hour notation (%H:%M:%S) */
+ rc = mu_c_streamftime (str, "%H:%M:%S", &tm, tz);
+ break;
+
+ case 'u':
+ /* The day of the week as a decimal, range 1 to 7, Monday being 1.
+ */
+ rc = mu_stream_printf (str, "%1d",
+ tm.tm_wday == 0 ? 7 : tm.tm_wday);
+ break;
+
+ case 'U':
+ /* The week number of the current year as a decimal number, range
+ 00 to 53, starting with the first Sunday as the first day of
+ week 01.
+ */
+ rc = mu_stream_printf (str, "%02d",
+ (tm.tm_yday - tm.tm_wday + 7) / 7);
+ break;
+
+ case 'w':
+ /* The day of the week as a decimal, range 0 to 6, Sunday being 0.
+ */
+ rc = mu_stream_printf (str, "%01d", tm.tm_wday);
+ break;
+
+ case 'W':
+ /* The week number of the current year as a decimal number, range
+ 00 to 53, starting with the first Monday as the first day of
+ week 01. */
+ rc = mu_stream_printf (str, "%02d",
+ (tm.tm_yday - (tm.tm_wday - 1 + 7) % 7 + 7) / 7);
+ break;
+
+ /* The preferred date representation without the time:
+ equivalent to %D */
+ case 'x':
+ rc = mu_c_streamftime (str, "%m/%d/%y", &tm, tz);
+ break;
+
+ case 'X':
+ /* The preferred date representation without the date */
+ rc = mu_c_streamftime (str, "%H:%M:%S", &tm, tz);
+ break;
+
+ case 'y':
+ /* The year as a decimal number without a century (range 00 to 99).
+ */
+ rc = mu_stream_printf (str, "%02d", (tm.tm_year + 1900) % 100);
+ break;
+
+ case 'Y':
+ /* The year as a decimal number including the century. */
+ rc = mu_stream_printf (str, "%d", tm.tm_year + 1900);
+ break;
+
+ case 'z':
+ case 'Z':
+ /* The time-zone as hour offset from GMT, for formatting RFC-822
+ dates (e.g. "%a, %d %b %Y %H:%M:%S %z") */
+ {
+ int utc_off = tz ? tz->utc_offset : mu_utc_offset ();
+ int sign;
+ if (utc_off < 0)
+ {
+ sign = '-';
+ utc_off = - utc_off;
+ }
+ else
+ sign = '+';
+ utc_off /= 60;
+ rc = mu_stream_printf (str, "%c%02u%02u", sign,
+ utc_off / 60, utc_off % 60);
+ }
+ break;
+
+ case '%':
+ /* A literal '%' character. */
+ rc = mu_stream_write (str, "%", 1, NULL);
+ break;
+
+ case '$':
+ /* Ignored for compatibilty with mu_scan_datetime */
+ break;
+
+ case '+':
+ /* Not supported (date and time in date(1) format. */
+ default:
+ rc = mu_stream_write (str, fmt-1, 2, NULL);
+ break;
+ }
+ fmt++;
+ }
+ /* Restore input tm */
+ *input_tm = tm;
+ return rc;
+}
diff --git a/libmailutils/base/strftime.c b/libmailutils/datetime/strftime.c
index 01b6faf32..01b6faf32 100644
--- a/libmailutils/base/strftime.c
+++ b/libmailutils/datetime/strftime.c
diff --git a/libmailutils/datetime/tab.c b/libmailutils/datetime/tab.c
new file mode 100644
index 000000000..2ff92935b
--- /dev/null
+++ b/libmailutils/datetime/tab.c
@@ -0,0 +1,45 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2002, 2007, 2009, 2010, 2011 Free
+ Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+const char *_mu_datetime_short_month[] =
+{
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+const char *_mu_datetime_full_month[] =
+{
+ "January", "February", "March", "April",
+ "May", "June", "July", "August",
+ "September", "October", "November", "December"
+};
+
+const char *_mu_datetime_short_wday[] =
+{
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+const char *_mu_datetime_full_wday[] =
+{
+ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
+ "Friday", "Saturday"
+};
diff --git a/libmailutils/datetime/unixtime.c b/libmailutils/datetime/unixtime.c
new file mode 100644
index 000000000..a8931176a
--- /dev/null
+++ b/libmailutils/datetime/unixtime.c
@@ -0,0 +1,38 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2007, 2009, 2010, 2011 Free Software
+ Foundation, Inc.
+
+ GNU Mailutils is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Mailutils 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 GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <time.h>
+#include <mailutils/datetime.h>
+
+#define SECS_PER_DAY 86400
+
+#define JD_OF_EPOCH 2440588
+
+/* Convert struct tm into time_t, taking into account timezone offset. */
+/* FIXME: Ignores DST */
+time_t
+mu_tm2time (struct tm *tm, struct mu_timezone *tz)
+{
+ int jd = mu_datetime_julianday (tm->tm_year + 1900, tm->tm_mon + 1,
+ tm->tm_mday);
+ return (jd - JD_OF_EPOCH) * SECS_PER_DAY +
+ (tm->tm_hour * 60 + tm->tm_min) * 60 + tm->tm_sec
+ - (tz ? tz->utc_offset : 0);
+}
diff --git a/libmailutils/datetime/utcoff.c b/libmailutils/datetime/utcoff.c
new file mode 100644
index 000000000..cfa3b894c
--- /dev/null
+++ b/libmailutils/datetime/utcoff.c
@@ -0,0 +1,32 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2007, 2009, 2010, 2011 Free Software
+ Foundation, Inc.
+
+ GNU Mailutils is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Mailutils 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 GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <time.h>
+
+/* Convert time 0 at UTC to our localtime, that tells us the offset
+ of our current timezone from UTC. */
+time_t
+mu_utc_offset (void)
+{
+ time_t t = 0;
+ struct tm *tm = gmtime (&t);
+
+ return - mktime (tm);
+}
diff --git a/libmailutils/datetime/yd.c b/libmailutils/datetime/yd.c
new file mode 100644
index 000000000..abdee160c
--- /dev/null
+++ b/libmailutils/datetime/yd.c
@@ -0,0 +1,29 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <mailutils/datetime.h>
+
+/* Return number of days in year */
+int
+mu_datetime_year_days (int year)
+{
+ return mu_datetime_dayofyear (year, 12, 31);
+}
diff --git a/libmailutils/tests/scantime.at b/libmailutils/tests/scantime.at
index 2db854087..a7a6f85be 100644
--- a/libmailutils/tests/scantime.at
+++ b/libmailutils/tests/scantime.at
@@ -34,30 +34,30 @@ SCANTIME([Envelope (From) time],[envelope],
[%a %b %e %H:%M:%S %Y],
[Tue May 3 13:25:26 2011
Fri Nov 11 11:55:01 2011],
-[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=0
-sec=1,min=55,hour=11,mday=11,mon=10,year=111,wday=5,yday=315,tz=0
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=0
+sec=1,min=55,hour=11,mday=11,mon=10,year=111,wday=5,yday=314,tz=0
])
SCANTIME([IMAP INTERNALDATE],[imap-internaldate],
[%d-%b-%Y %H:%M:%S %z],
[03-May-2011 13:25:26 +0100
11-Nov-2011 11:55:01 +0100],
-[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=3600
-sec=1,min=55,hour=11,mday=11,mon=10,year=111,wday=5,yday=315,tz=3600
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=3600
+sec=1,min=55,hour=11,mday=11,mon=10,year=111,wday=5,yday=314,tz=3600
])
SCANTIME([IMAP INTERNALDATE (optional time)],[imap-search],
[%d-%b-%Y%$ %H:%M:%S %z],
[03-May-2011 13:25:26 +0100
03-May-2011],
-[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=3600
-sec=0,min=0,hour=0,mday=3,mon=4,year=111,wday=2,yday=123,tz=0
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=3600
+sec=0,min=0,hour=0,mday=3,mon=4,year=111,wday=2,yday=122,tz=0
])
SCANTIME([RFC-822 Strict],[rfc822-strict],
[%a, %d %b %Y %H:%M:%S %z],
[Tue, 03 May 2011 13:25:26 +0200],
-[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
])
SCANTIME([RFC-822],[rfc822],
@@ -66,10 +66,10 @@ SCANTIME([RFC-822],[rfc822],
03 May 2011 13:25:26 +0200
Tue, 03 May 2011 13:25 +0200
03 May 2011 13:25 +0200],
-[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
-sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
-sec=0,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
-sec=0,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
+sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
+sec=0,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
+sec=0,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
])
SCANTIME([Any char],[anychar],
@@ -78,23 +78,23 @@ SCANTIME([Any char],[anychar],
Tue: 03 May 2011 13:25:26 +0200
Tue; 03 May 2011 13:25:26 +0200
Tue 03 May 2011 13:25:26 +0200],
-[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
-sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
-sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
-sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
+sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
+sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
+sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
])
SCANTIME([Percent],[percent],
[%d%%%b%%%Y %H:%M:%S %z],
[03%May%2011 13:25:26 +0100],
-[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=3600
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=3600
])
SCANTIME([Fixed WS],[fixws],
[%d-%b-%Y%\ %H:%M:%S %z],
[03-May-2011 13:25:26 +0100
03-May-2011 13:25:26 +0100],
-[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=3600
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=3600
],
[scantime: 2: parse failed near 13:25:26 +0100
])
@@ -104,15 +104,15 @@ SCANTIME([endp return],[endp],
[Tue, 03 May 2011 13:25:26 +0100 other data
],
[# 1: stopped at other data
-sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=3600
+sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=3600
])
SCANTIME([Optional blocks],[opt],
[[%[%a, %]%d %b %Y %H:%M:%S %z]],
[Tue, 03 May 2011 13:25:26 +0200
03 May 2011 13:25:26 +0200],
-[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
-sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
+sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
])
SCANTIME([Nested optional blocks],[nested-opt],
@@ -120,9 +120,9 @@ SCANTIME([Nested optional blocks],[nested-opt],
[Tue, 03 May 2011 13:25:26 +0200
Tue 03 May 2011 13:25:26 +0200
03 May 2011 13:25:26 +0200],
-[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
-sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
-sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
+sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
+sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
])
SCANTIME([Optional alternatives],[opt-alt],
@@ -131,9 +131,9 @@ SCANTIME([Optional alternatives],[opt-alt],
Tue: 03 May 2011 13:25:26 +0200
Tue 03 May 2011 13:25:26 +0200
Tue; 03 May 2011 13:25:26 +0200],
-[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
-sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
-sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
+sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
+sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
],
[scantime: 4: parse failed near ; 03 May 2011 13:25:26 +0200
])
@@ -145,9 +145,9 @@ Tue: 03 May 2011 13:25:26 +0200
Tue/ 03 May 2011 13:25:26 +0200
Tue 03 May 2011 13:25:26 +0200
Tue; 03 May 2011 13:25:26 +0200],
-[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
-sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
-sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=123,tz=7200
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
+sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
+sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,yday=122,tz=7200
],
[scantime: 4: parse failed near 03 May 2011 13:25:26 +0200
scantime: 5: parse failed near ; 03 May 2011 13:25:26 +0200
diff --git a/libmailutils/tests/scantime.c b/libmailutils/tests/scantime.c
index c9c016bbb..d889add5d 100644
--- a/libmailutils/tests/scantime.c
+++ b/libmailutils/tests/scantime.c
@@ -92,8 +92,6 @@ main (int argc, char **argv)
mu_printf ("sec=%d,min=%d,hour=%d,mday=%d,mon=%d,year=%d,wday=%d,yday=%d,tz=%d\n",
tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mday, tm.tm_mon,
tm.tm_year, tm.tm_wday, tm.tm_yday, tz.utc_offset);
-
- /*mu_c_streamftime (mu_strout, "%c %z%n", &tm, &tz);*/
}
if (rc)

Return to:

Send suggestions and report system problems to the System administrator.