summaryrefslogtreecommitdiff
path: root/libexslt/date.c
diff options
context:
space:
mode:
Diffstat (limited to 'libexslt/date.c')
-rw-r--r--libexslt/date.c432
1 files changed, 246 insertions, 186 deletions
diff --git a/libexslt/date.c b/libexslt/date.c
index 60d353d5..bb4a8ec7 100644
--- a/libexslt/date.c
+++ b/libexslt/date.c
@@ -38,6 +38,7 @@
#include "exslt.h"
+#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
@@ -52,6 +53,12 @@
#include <time.h>
+#if defined(_MSC_VER) && _MSC_VER >= 1400 || \
+ defined(_WIN32) && \
+ defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 4
+ #define HAVE_MSVCRT
+#endif
+
/*
* types of date and/or time (from schema datatypes)
* somewhat ordered from least specific to most specific (i.e.
@@ -227,37 +234,41 @@ _exsltDateParseGYear (exsltDateValPtr dt, const xmlChar **str)
}
/**
- * FORMAT_GYEAR:
+ * exsltFormatGYear:
+ * @cur: a pointer to a pointer to an allocated buffer
+ * @end: a pointer to the end of @cur buffer
* @yr: the year to format
- * @cur: a pointer to an allocated buffer
*
* Formats @yr in xsl:gYear format. Result is appended to @cur and
* @cur is updated to point after the xsl:gYear.
*/
-#define FORMAT_GYEAR(yr, cur) \
- if (yr <= 0) { \
- *cur = '-'; \
- cur++; \
- } \
- { \
- long year = (yr <= 0) ? -yr + 1 : yr; \
- xmlChar tmp_buf[100], *tmp = tmp_buf; \
- /* result is in reverse-order */ \
- while (year > 0) { \
- *tmp = '0' + (xmlChar)(year % 10); \
- year /= 10; \
- tmp++; \
- } \
- /* virtually adds leading zeros */ \
- while ((tmp - tmp_buf) < 4) \
- *tmp++ = '0'; \
- /* restore the correct order */ \
- while (tmp > tmp_buf) { \
- tmp--; \
- *cur = *tmp; \
- cur++; \
- } \
- }
+static void
+exsltFormatGYear(xmlChar **cur, xmlChar *end, long yr)
+{
+ long year;
+ xmlChar tmp_buf[100], *tmp = tmp_buf, *tmp_end = tmp_buf + 99;
+
+ if (yr <= 0 && *cur < end) {
+ *(*cur)++ = '-';
+ }
+
+ year = (yr <= 0) ? -yr + 1 : yr;
+ /* result is in reverse-order */
+ while (year > 0 && tmp < tmp_end) {
+ *tmp++ = '0' + (xmlChar)(year % 10);
+ year /= 10;
+ }
+
+ /* virtually adds leading zeros */
+ while ((tmp - tmp_buf) < 4)
+ *tmp++ = '0';
+
+ /* restore the correct order */
+ while (tmp > tmp_buf && *cur < end) {
+ tmp--;
+ *(*cur)++ = *tmp;
+ }
+}
/**
* PARSE_2_DIGITS:
@@ -286,18 +297,22 @@ _exsltDateParseGYear (exsltDateValPtr dt, const xmlChar **str)
cur += 2;
/**
- * FORMAT_2_DIGITS:
- * @num: the integer to format
- * @cur: a pointer to an allocated buffer
+ * exsltFormat2Digits:
+ * @cur: a pointer to a pointer to an allocated buffer
+ * @end: a pointer to the end of @cur buffer
+ * @num: the integer to format
*
* Formats a 2-digits integer. Result is appended to @cur and
* @cur is updated to point after the integer.
*/
-#define FORMAT_2_DIGITS(num, cur) \
- *cur = '0' + ((num / 10) % 10); \
- cur++; \
- *cur = '0' + (num % 10); \
- cur++;
+static void
+exsltFormat2Digits(xmlChar **cur, xmlChar *end, unsigned int num)
+{
+ if (*cur < end)
+ *(*cur)++ = '0' + ((num / 10) % 10);
+ if (*cur < end)
+ *(*cur)++ = '0' + (num % 10);
+}
/**
* PARSE_FLOAT:
@@ -326,29 +341,6 @@ _exsltDateParseGYear (exsltDateValPtr dt, const xmlChar **str)
}
/**
- * FORMAT_FLOAT:
- * @num: the double to format
- * @cur: a pointer to an allocated buffer
- * @pad: a flag for padding to 2 integer digits
- *
- * Formats a float. Result is appended to @cur and @cur is updated to
- * point after the integer. If the @pad flag is non-zero, then the
- * float representation has a minimum 2-digits integer part. The
- * fractional part is formatted if @num has a fractional value.
- */
-#define FORMAT_FLOAT(num, cur, pad) \
- { \
- xmlChar *sav, *str; \
- if ((pad) && (num < 10.0)) \
- *cur++ = '0'; \
- str = xmlXPathCastNumberToString(num); \
- sav = str; \
- while (*str != 0) \
- *cur++ = *str++; \
- xmlFree(sav); \
- }
-
-/**
* _exsltDateParseGMonth:
* @dt: pointer to a date structure
* @str: pointer to the string to analyze
@@ -380,17 +372,6 @@ _exsltDateParseGMonth (exsltDateValPtr dt, const xmlChar **str)
}
/**
- * FORMAT_GMONTH:
- * @mon: the month to format
- * @cur: a pointer to an allocated buffer
- *
- * Formats @mon in xsl:gMonth format. Result is appended to @cur and
- * @cur is updated to point after the xsl:gMonth.
- */
-#define FORMAT_GMONTH(mon, cur) \
- FORMAT_2_DIGITS(mon, cur)
-
-/**
* _exsltDateParseGDay:
* @dt: pointer to a date structure
* @str: pointer to the string to analyze
@@ -422,32 +403,25 @@ _exsltDateParseGDay (exsltDateValPtr dt, const xmlChar **str)
}
/**
- * FORMAT_GDAY:
- * @dt: the #exsltDateVal to format
- * @cur: a pointer to an allocated buffer
- *
- * Formats @dt in xsl:gDay format. Result is appended to @cur and
- * @cur is updated to point after the xsl:gDay.
- */
-#define FORMAT_GDAY(dt, cur) \
- FORMAT_2_DIGITS(dt->day, cur)
-
-/**
- * FORMAT_DATE:
+ * exsltFormatYearMonthDay:
+ * @cur: a pointer to a pointer to an allocated buffer
+ * @end: a pointer to the end of @cur buffer
* @dt: the #exsltDateVal to format
- * @cur: a pointer to an allocated buffer
*
* Formats @dt in xsl:date format. Result is appended to @cur and
* @cur is updated to point after the xsl:date.
*/
-#define FORMAT_DATE(dt, cur) \
- FORMAT_GYEAR(dt->year, cur); \
- *cur = '-'; \
- cur++; \
- FORMAT_GMONTH(dt->mon, cur); \
- *cur = '-'; \
- cur++; \
- FORMAT_GDAY(dt, cur);
+static void
+exsltFormatYearMonthDay(xmlChar **cur, xmlChar *end, const exsltDateValPtr dt)
+{
+ exsltFormatGYear(cur, end, dt->year);
+ if (*cur < end)
+ *(*cur)++ = '-';
+ exsltFormat2Digits(cur, end, dt->mon);
+ if (*cur < end)
+ *(*cur)++ = '-';
+ exsltFormat2Digits(cur, end, dt->day);
+}
/**
* _exsltDateParseTime:
@@ -506,23 +480,6 @@ _exsltDateParseTime (exsltDateValPtr dt, const xmlChar **str)
}
/**
- * FORMAT_TIME:
- * @dt: the #exsltDateVal to format
- * @cur: a pointer to an allocated buffer
- *
- * Formats @dt in xsl:time format. Result is appended to @cur and
- * @cur is updated to point after the xsl:time.
- */
-#define FORMAT_TIME(dt, cur) \
- FORMAT_2_DIGITS(dt->hour, cur); \
- *cur = ':'; \
- cur++; \
- FORMAT_2_DIGITS(dt->min, cur); \
- *cur = ':'; \
- cur++; \
- FORMAT_FLOAT(dt->sec, cur, 1);
-
-/**
* _exsltDateParseTimeZone:
* @dt: pointer to a date structure
* @str: pointer to the string to analyze
@@ -600,27 +557,31 @@ _exsltDateParseTimeZone (exsltDateValPtr dt, const xmlChar **str)
}
/**
- * FORMAT_TZ:
- * @tzo: the timezone offset to format
- * @cur: a pointer to an allocated buffer
+ * exsltFormatTimeZone:
+ * @cur: a pointer to a pointer to an allocated buffer
+ * @end: a pointer to the end of @cur buffer
+ * @tzo: the timezone offset to format
*
* Formats @tzo timezone. Result is appended to @cur and
* @cur is updated to point after the timezone.
*/
-#define FORMAT_TZ(tzo, cur) \
- if (tzo == 0) { \
- *cur = 'Z'; \
- cur++; \
- } else { \
- int aTzo = (tzo < 0) ? - tzo : tzo; \
- int tzHh = aTzo / 60, tzMm = aTzo % 60; \
- *cur = (tzo < 0) ? '-' : '+' ; \
- cur++; \
- FORMAT_2_DIGITS(tzHh, cur); \
- *cur = ':'; \
- cur++; \
- FORMAT_2_DIGITS(tzMm, cur); \
- }
+static void
+exsltFormatTimeZone(xmlChar **cur, xmlChar *end, int tzo)
+{
+ if (tzo == 0) {
+ if (*cur < end)
+ *(*cur)++ = 'Z';
+ } else {
+ unsigned int aTzo = (tzo < 0) ? -tzo : tzo;
+ unsigned int tzHh = aTzo / 60, tzMm = aTzo % 60;
+ if (*cur < end)
+ *(*cur)++ = (tzo < 0) ? '-' : '+';
+ exsltFormat2Digits(cur, end, tzHh);
+ if (*cur < end)
+ *(*cur)++ = ':';
+ exsltFormat2Digits(cur, end, tzMm);
+ }
+}
/****************************************************************
* *
@@ -718,7 +679,7 @@ static exsltDateValPtr
exsltDateCurrent (void)
{
struct tm localTm, gmTm;
-#ifndef HAVE_GMTIME_R
+#if !defined(HAVE_GMTIME_R) && !defined(HAVE_MSVCRT)
struct tm *tb = NULL;
#endif
time_t secs;
@@ -740,7 +701,11 @@ exsltDateCurrent (void)
errno = 0;
secs = (time_t) strtol (source_date_epoch, NULL, 10);
if (errno == 0) {
-#if HAVE_GMTIME_R
+#ifdef HAVE_MSVCRT
+ struct tm *gm = gmtime_s(&localTm, &secs) ? NULL : &localTm;
+ if (gm != NULL)
+ override = 1;
+#elif HAVE_GMTIME_R
if (gmtime_r(&secs, &localTm) != NULL)
override = 1;
#else
@@ -757,7 +722,9 @@ exsltDateCurrent (void)
/* get current time */
secs = time(NULL);
-#if HAVE_LOCALTIME_R
+#ifdef HAVE_MSVCRT
+ localtime_s(&localTm, &secs);
+#elif HAVE_LOCALTIME_R
localtime_r(&secs, &localTm);
#else
localTm = *localtime(&secs);
@@ -776,7 +743,9 @@ exsltDateCurrent (void)
ret->sec = (double) localTm.tm_sec;
/* determine the time zone offset from local to gm time */
-#if HAVE_GMTIME_R
+#ifdef HAVE_MSVCRT
+ gmtime_s(&gmTm, &secs);
+#elif HAVE_GMTIME_R
gmtime_r(&secs, &gmTm);
#else
tb = gmtime(&secs);
@@ -1138,21 +1107,41 @@ error:
return NULL;
}
-/**
- * FORMAT_ITEM:
- * @num: number to format
- * @cur: current location to convert number
- * @limit: max value
- * @item: char designator
- *
- */
-#define FORMAT_ITEM(num, cur, limit, item) \
- if (num >= limit) { \
- double comp = floor(num / limit); \
- FORMAT_FLOAT(comp, cur, 0); \
- *cur++ = item; \
- num -= comp * limit; \
+static void
+exsltFormatLong(xmlChar **cur, xmlChar *end, long num) {
+ xmlChar buf[20];
+ int i = 0;
+
+ while (i < 20) {
+ buf[i++] = '0' + num % 10;
+ num /= 10;
+ if (num == 0)
+ break;
+ }
+
+ while (i > 0) {
+ if (*cur < end)
+ *(*cur)++ = buf[--i];
+ }
+}
+
+static void
+exsltFormatNanoseconds(xmlChar **cur, xmlChar *end, long nsecs) {
+ long p10, digit;
+
+ if (nsecs > 0) {
+ if (*cur < end)
+ *(*cur)++ = '.';
+ p10 = 100000000;
+ while (nsecs > 0) {
+ digit = nsecs / p10;
+ if (*cur < end)
+ *(*cur)++ = '0' + digit;
+ nsecs -= digit * p10;
+ p10 /= 10;
}
+ }
+}
/**
* exsltDateFormatDuration:
@@ -1165,9 +1154,9 @@ error:
static xmlChar *
exsltDateFormatDuration (const exsltDateDurValPtr dur)
{
- xmlChar buf[100], *cur = buf;
- double secs, days;
- double years, months;
+ xmlChar buf[100], *cur = buf, *end = buf + 99;
+ double secs, tmp;
+ long days, months, intSecs, nsecs;
if (dur == NULL)
return NULL;
@@ -1177,9 +1166,8 @@ exsltDateFormatDuration (const exsltDateDurValPtr dur)
return xmlStrdup((xmlChar*)"P0D");
secs = dur->sec;
- days = (double)dur->day;
- years = (double)(dur->mon / 12);
- months = (double)(dur->mon % 12);
+ days = dur->day;
+ months = dur->mon;
*cur = '\0';
if (days < 0) {
@@ -1190,10 +1178,6 @@ exsltDateFormatDuration (const exsltDateDurValPtr dur)
days = -days;
*cur = '-';
}
- if (years < 0) {
- years = -years;
- *cur = '-';
- }
if (months < 0) {
months = -months;
*cur = '-';
@@ -1203,23 +1187,64 @@ exsltDateFormatDuration (const exsltDateDurValPtr dur)
*cur++ = 'P';
- if (years != 0.0) {
- FORMAT_ITEM(years, cur, 1, 'Y');
+ if (months >= 12) {
+ long years = months / 12;
+
+ months -= years * 12;
+ exsltFormatLong(&cur, end, years);
+ if (cur < end)
+ *cur++ = 'Y';
}
- if (months != 0.0) {
- FORMAT_ITEM(months, cur, 1, 'M');
+ if (months != 0) {
+ exsltFormatLong(&cur, end, months);
+ if (cur < end)
+ *cur++ = 'M';
}
- FORMAT_ITEM(days, cur, 1, 'D');
- if (secs > 0.0) {
- *cur++ = 'T';
+ if (days != 0) {
+ exsltFormatLong(&cur, end, days);
+ if (cur < end)
+ *cur++ = 'D';
}
- FORMAT_ITEM(secs, cur, SECS_PER_HOUR, 'H');
- FORMAT_ITEM(secs, cur, SECS_PER_MIN, 'M');
- if (secs > 0.0) {
- FORMAT_FLOAT(secs, cur, 0);
- *cur++ = 'S';
+
+ tmp = floor(secs);
+ intSecs = (long) tmp;
+ /* Round to nearest to avoid issues with floating point precision */
+ nsecs = (long) floor((secs - tmp) * 1000000000 + 0.5);
+ if (nsecs >= 1000000000) {
+ nsecs -= 1000000000;
+ intSecs += 1;
+ }
+
+ if ((intSecs > 0) || (nsecs > 0)) {
+ if (cur < end)
+ *cur++ = 'T';
+
+ if (intSecs >= SECS_PER_HOUR) {
+ long hours = intSecs / SECS_PER_HOUR;
+
+ intSecs -= hours * SECS_PER_HOUR;
+ exsltFormatLong(&cur, end, hours);
+ if (cur < end)
+ *cur++ = 'H';
+ }
+
+ if (intSecs >= SECS_PER_MIN) {
+ long mins = intSecs / SECS_PER_MIN;
+
+ intSecs -= mins * SECS_PER_MIN;
+ exsltFormatLong(&cur, end, mins);
+ if (cur < end)
+ *cur++ = 'M';
+ }
+
+ if ((intSecs > 0) || (nsecs > 0)) {
+ exsltFormatLong(&cur, end, intSecs);
+ exsltFormatNanoseconds(&cur, end, nsecs);
+ if (cur < end)
+ *cur++ = 'S';
+ }
}
*cur = 0;
@@ -1227,6 +1252,42 @@ exsltDateFormatDuration (const exsltDateDurValPtr dur)
return xmlStrdup(buf);
}
+static void
+exsltFormatTwoDigits(xmlChar **cur, xmlChar *end, int num) {
+ if (num < 0 || num >= 100)
+ return;
+ if (*cur < end)
+ *(*cur)++ = '0' + num / 10;
+ if (*cur < end)
+ *(*cur)++ = '0' + num % 10;
+}
+
+static void
+exsltFormatTime(xmlChar **cur, xmlChar *end, exsltDateValPtr dt) {
+ double tmp;
+ long intSecs, nsecs;
+
+ exsltFormatTwoDigits(cur, end, dt->hour);
+ if (*cur < end)
+ *(*cur)++ = ':';
+
+ exsltFormatTwoDigits(cur, end, dt->min);
+ if (*cur < end)
+ *(*cur)++ = ':';
+
+ tmp = floor(dt->sec);
+ intSecs = (long) tmp;
+ /*
+ * Round to nearest to avoid issues with floating point precision,
+ * but don't carry over so seconds stay below 60.
+ */
+ nsecs = (long) floor((dt->sec - tmp) * 1000000000 + 0.5);
+ if (nsecs > 999999999)
+ nsecs = 999999999;
+ exsltFormatTwoDigits(cur, end, intSecs);
+ exsltFormatNanoseconds(cur, end, nsecs);
+}
+
/**
* exsltDateFormatDateTime:
* @dt: an #exsltDateValPtr
@@ -1238,16 +1299,16 @@ exsltDateFormatDuration (const exsltDateDurValPtr dur)
static xmlChar *
exsltDateFormatDateTime (const exsltDateValPtr dt)
{
- xmlChar buf[100], *cur = buf;
+ xmlChar buf[100], *cur = buf, *end = buf + 99;
if ((dt == NULL) || !VALID_DATETIME(dt))
return NULL;
- FORMAT_DATE(dt, cur);
- *cur = 'T';
- cur++;
- FORMAT_TIME(dt, cur);
- FORMAT_TZ(dt->tzo, cur);
+ exsltFormatYearMonthDay(&cur, end, dt);
+ if (cur < end)
+ *cur++ = 'T';
+ exsltFormatTime(&cur, end, dt);
+ exsltFormatTimeZone(&cur, end, dt->tzo);
*cur = 0;
return xmlStrdup(buf);
@@ -1264,14 +1325,14 @@ exsltDateFormatDateTime (const exsltDateValPtr dt)
static xmlChar *
exsltDateFormatDate (const exsltDateValPtr dt)
{
- xmlChar buf[100], *cur = buf;
+ xmlChar buf[100], *cur = buf, *end = buf + 99;
if ((dt == NULL) || !VALID_DATETIME(dt))
return NULL;
- FORMAT_DATE(dt, cur);
+ exsltFormatYearMonthDay(&cur, end, dt);
if (dt->tz_flag || (dt->tzo != 0)) {
- FORMAT_TZ(dt->tzo, cur);
+ exsltFormatTimeZone(&cur, end, dt->tzo);
}
*cur = 0;
@@ -1289,14 +1350,14 @@ exsltDateFormatDate (const exsltDateValPtr dt)
static xmlChar *
exsltDateFormatTime (const exsltDateValPtr dt)
{
- xmlChar buf[100], *cur = buf;
+ xmlChar buf[100], *cur = buf, *end = buf + 99;
if ((dt == NULL) || !VALID_TIME(dt))
return NULL;
- FORMAT_TIME(dt, cur);
+ exsltFormatTime(&cur, end, dt);
if (dt->tz_flag || (dt->tzo != 0)) {
- FORMAT_TZ(dt->tzo, cur);
+ exsltFormatTimeZone(&cur, end, dt->tzo);
}
*cur = 0;
@@ -1316,7 +1377,6 @@ exsltDateFormatTime (const exsltDateValPtr dt)
static xmlChar *
exsltDateFormat (const exsltDateValPtr dt)
{
-
if (dt == NULL)
return NULL;
@@ -1332,17 +1392,17 @@ exsltDateFormat (const exsltDateValPtr dt)
}
if (dt->type & XS_GYEAR) {
- xmlChar buf[100], *cur = buf;
+ xmlChar buf[100], *cur = buf, *end = buf + 99;
- FORMAT_GYEAR(dt->year, cur);
+ exsltFormatGYear(&cur, end, dt->year);
if (dt->type == XS_GYEARMONTH) {
- *cur = '-';
- cur++;
- FORMAT_GMONTH(dt->mon, cur);
+ if (cur < end)
+ *cur++ = '-';
+ exsltFormat2Digits(&cur, end, dt->mon);
}
if (dt->tz_flag || (dt->tzo != 0)) {
- FORMAT_TZ(dt->tzo, cur);
+ exsltFormatTimeZone(&cur, end, dt->tzo);
}
*cur = 0;
return xmlStrdup(buf);
@@ -1689,16 +1749,16 @@ _exsltDateAddDurCalc (exsltDateDurValPtr ret, exsltDateDurValPtr x,
exsltDateDurValPtr y)
{
/* months */
- if ((x->mon > 0 && y->mon > LONG_MAX - x->mon) ||
- (x->mon < 0 && y->mon < LONG_MIN - x->mon)) {
+ if ((x->mon > 0 && y->mon > LONG_MAX - x->mon) ||
+ (x->mon < 0 && y->mon <= LONG_MIN - x->mon)) {
/* Overflow */
return 0;
}
ret->mon = x->mon + y->mon;
/* days */
- if ((x->day > 0 && y->day > LONG_MAX - x->day) ||
- (x->day < 0 && y->day < LONG_MIN - x->day)) {
+ if ((x->day > 0 && y->day > LONG_MAX - x->day) ||
+ (x->day < 0 && y->day <= LONG_MIN - x->day)) {
/* Overflow */
return 0;
}