}
}
-
/* ParseDateTime()
* Break string into tokens based on a date/time context.
* Returns 0 if successful, DTERR code if bogus input detected.
*
* timestr - the input string
- * lowstr - workspace for field string storage (must be large enough for
- * a copy of the input string, including trailing null)
+ * workbuf - workspace for field string storage. This must be
+ * larger than the largest legal input for this datetime type --
+ * some additional space will be needed to NUL terminate fields.
+ * buflen - the size of workbuf
* field[] - pointers to field strings are returned in this array
* ftype[] - field type indicators are returned in this array
* maxfields - dimensions of the above two arrays
* *numfields - set to the actual number of fields detected
*
- * The fields extracted from the input are stored as separate, null-terminated
- * strings in the workspace at lowstr. Any text is converted to lower case.
+ * The fields extracted from the input are stored as separate,
+ * null-terminated strings in the workspace at workbuf. Any text is
+ * converted to lower case.
*
* Several field types are assigned:
* DTK_NUMBER - digits and (possibly) a decimal point
* DTK_DATE can hold Posix time zones (GMT-8)
*/
int
-ParseDateTime(const char *timestr, char *lowstr,
+ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
char **field, int *ftype, int maxfields, int *numfields)
{
int nf = 0;
const char *cp = timestr;
- char *lp = lowstr;
+ char *bufp = workbuf;
+ const char *bufend = workbuf + buflen;
+
+ /*
+ * Set the character pointed-to by "bufptr" to "newchar", and
+ * increment "bufptr". "end" gives the end of the buffer -- we
+ * return an error if there is no space left to append a character
+ * to the buffer. Note that "bufptr" is evaluated twice.
+ */
+#define APPEND_CHAR(bufptr, end, newchar) \
+ do \
+ { \
+ if (((bufptr) + 1) >= (end)) \
+ return DTERR_BAD_FORMAT; \
+ *(bufptr)++ = newchar; \
+ } while (0)
/* outer loop through fields */
while (*cp != '\0')
/* Record start of current field */
if (nf >= maxfields)
return DTERR_BAD_FORMAT;
- field[nf] = lp;
+ field[nf] = bufp;
/* leading digit? then date or time */
if (isdigit((unsigned char) *cp))
{
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
while (isdigit((unsigned char) *cp))
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
/* time field? */
if (*cp == ':')
{
ftype[nf] = DTK_TIME;
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
while (isdigit((unsigned char) *cp) ||
(*cp == ':') || (*cp == '.'))
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
}
/* date field? allow embedded text month */
else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
/* save delimiting character to use later */
char delim = *cp;
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
/* second field is all digits? then no embedded text month */
if (isdigit((unsigned char) *cp))
{
ftype[nf] = ((delim == '.') ? DTK_NUMBER : DTK_DATE);
while (isdigit((unsigned char) *cp))
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
/*
* insist that the delimiters match to get a
if (*cp == delim)
{
ftype[nf] = DTK_DATE;
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
while (isdigit((unsigned char) *cp) || (*cp == delim))
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
}
}
else
{
ftype[nf] = DTK_DATE;
while (isalnum((unsigned char) *cp) || (*cp == delim))
- *lp++ = pg_tolower((unsigned char) *cp++);
+ APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
}
}
/* Leading decimal point? Then fractional seconds... */
else if (*cp == '.')
{
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
while (isdigit((unsigned char) *cp))
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
ftype[nf] = DTK_NUMBER;
}
else if (isalpha((unsigned char) *cp))
{
ftype[nf] = DTK_STRING;
- *lp++ = pg_tolower((unsigned char) *cp++);
+ APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
while (isalpha((unsigned char) *cp))
- *lp++ = pg_tolower((unsigned char) *cp++);
+ APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
/*
* Full date string with leading text month? Could also be a
char delim = *cp;
ftype[nf] = DTK_DATE;
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
while (isdigit((unsigned char) *cp) || (*cp == delim))
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
}
}
/* sign? then special or numeric timezone */
else if ((*cp == '+') || (*cp == '-'))
{
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
/* soak up leading whitespace */
while (isspace((unsigned char) *cp))
cp++;
if (isdigit((unsigned char) *cp))
{
ftype[nf] = DTK_TZ;
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
while (isdigit((unsigned char) *cp) ||
(*cp == ':') || (*cp == '.'))
- *lp++ = *cp++;
+ APPEND_CHAR(bufp, bufend, *cp++);
}
/* special? */
else if (isalpha((unsigned char) *cp))
{
ftype[nf] = DTK_SPECIAL;
- *lp++ = pg_tolower((unsigned char) *cp++);
+ APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
while (isalpha((unsigned char) *cp))
- *lp++ = pg_tolower((unsigned char) *cp++);
+ APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
}
/* otherwise something wrong... */
else
return DTERR_BAD_FORMAT;
/* force in a delimiter after each field */
- *lp++ = '\0';
+ *bufp++ = '\0';
nf++;
}
if (tzp == NULL)
return DTERR_BAD_FORMAT;
+ errno = 0;
val = strtol(field[i], &cp, 10);
+ if (errno == ERANGE)
+ return DTERR_FIELD_OVERFLOW;
j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
/* Get the time zone from the end of the string */
char *cp;
int val;
+ errno = 0;
val = strtol(field[i], &cp, 10);
+ if (errno == ERANGE)
+ return DTERR_FIELD_OVERFLOW;
/*
* only a few kinds are allowed to have an embedded
break;
}
+ errno = 0;
val = strtol(field[i], &cp, 10);
+ if (errno == ERANGE)
+ return DTERR_FIELD_OVERFLOW;
/*
* only a few kinds are allowed to have an embedded
*tmask = DTK_TIME_M;
+ errno = 0;
tm->tm_hour = strtol(str, &cp, 10);
+ if (errno == ERANGE)
+ return DTERR_FIELD_OVERFLOW;
if (*cp != ':')
return DTERR_BAD_FORMAT;
str = cp + 1;
+ errno = 0;
tm->tm_min = strtol(str, &cp, 10);
+ if (errno == ERANGE)
+ return DTERR_FIELD_OVERFLOW;
if (*cp == '\0')
{
tm->tm_sec = 0;
else
{
str = cp + 1;
+ errno = 0;
tm->tm_sec = strtol(str, &cp, 10);
+ if (errno == ERANGE)
+ return DTERR_FIELD_OVERFLOW;
if (*cp == '\0')
*fsec = 0;
else if (*cp == '.')
*tmask = 0;
+ errno = 0;
val = strtol(str, &cp, 10);
+ if (errno == ERANGE)
+ return DTERR_FIELD_OVERFLOW;
if (cp == str)
return DTERR_BAD_FORMAT;
if (*str != '+' && *str != '-')
return DTERR_BAD_FORMAT;
+ errno = 0;
hr = strtol((str + 1), &cp, 10);
+ if (errno == ERANGE)
+ return DTERR_TZDISP_OVERFLOW;
/* explicit delimiter? */
if (*cp == ':')
+ {
+ errno = 0;
min = strtol((cp + 1), &cp, 10);
+ if (errno == ERANGE)
+ return DTERR_TZDISP_OVERFLOW;
+ }
/* otherwise, might have run things together... */
else if ((*cp == '\0') && (strlen(str) > 3))
{
case DTK_DATE:
case DTK_NUMBER:
+ errno = 0;
val = strtol(field[i], &cp, 10);
+ if (errno == ERANGE)
+ return DTERR_FIELD_OVERFLOW;
if (type == IGNORE_DTF)
type = DTK_SECOND;
/* fractional seconds? */
if (fsec != 0)
{
+ fsec_t sec;
+
#ifdef HAVE_INT64_TIMESTAMP
+ sec = fsec;
if (is_before || ((!is_nonzero) && (tm->tm_sec < 0)))
+ {
tm->tm_sec = -tm->tm_sec;
+ sec = -sec;
+ is_before = TRUE;
+ }
+ else if ((!is_nonzero) && (tm->tm_sec == 0) && (fsec < 0))
+ {
+ sec = -sec;
+ is_before = TRUE;
+ }
sprintf(cp, "%s%d.%02d secs", (is_nonzero ? " " : ""),
- tm->tm_sec, (((int) fsec) / 10000));
+ tm->tm_sec, (((int) sec) / 10000));
cp += strlen(cp);
- if (!is_nonzero)
- is_before = (fsec < 0);
#else
- fsec_t sec;
-
fsec += tm->tm_sec;
sec = fsec;
if (is_before || ((!is_nonzero) && (fsec < 0)))
is_before = (fsec < 0);
#endif
is_nonzero = TRUE;
-
- /* otherwise, integer seconds only? */
}
+ /* otherwise, integer seconds only? */
else if (tm->tm_sec != 0)
{
int sec = tm->tm_sec;