From 630684d3a130bb9381e5256b24d248d6a751d59f Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 5 Aug 2003 18:30:21 +0000 Subject: [PATCH] Improve documentation of ParseDateTime(). Reorder tests to prevent writing one more value into return arrays than will fit. This is potentially a stack smash, though I do not think it is a problem in current uses of the routine, since a failure return causes elog anyway. --- src/backend/utils/adt/datetime.c | 59 ++++++++++++++++++++------------ src/include/utils/datetime.h | 4 +-- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 7baa751895..c94f9bf6f9 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.111 2003/08/05 17:39:19 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.112 2003/08/05 18:30:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -699,7 +699,20 @@ TrimTrailingZeros(char *str) /* ParseDateTime() - * Break string into tokens based on a date/time context. + * Break string into tokens based on a date/time context. + * Returns 0 if successful, -1 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) + * 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. + * * Several field types are assigned: * DTK_NUMBER - digits and (possibly) a decimal point * DTK_DATE - digits and two delimiters, or digits and text @@ -707,22 +720,33 @@ TrimTrailingZeros(char *str) * DTK_STRING - text (no digits) * DTK_SPECIAL - leading "+" or "-" followed by text * DTK_TZ - leading "+" or "-" followed by digits + * * Note that some field types can hold unexpected items: * DTK_NUMBER can hold date fields (yy.ddd) * DTK_STRING can hold months (January) and time zones (PST) * DTK_DATE can hold Posix time zones (GMT-8) */ int -ParseDateTime(char *timestr, char *lowstr, +ParseDateTime(const char *timestr, char *lowstr, char **field, int *ftype, int maxfields, int *numfields) { int nf = 0; - char *cp = timestr; + const char *cp = timestr; char *lp = lowstr; /* outer loop through fields */ while (*cp != '\0') { + /* Ignore spaces between fields */ + if (isspace((unsigned char) *cp)) + { + cp++; + continue; + } + + /* Record start of current field */ + if (nf >= maxfields) + return -1; field[nf] = lp; /* leading digit? then date or time */ @@ -745,13 +769,13 @@ ParseDateTime(char *timestr, char *lowstr, else if ((*cp == '-') || (*cp == '/') || (*cp == '.')) { /* save delimiting character to use later */ - char *dp = cp; + char delim = *cp; *lp++ = *cp++; /* second field is all digits? then no embedded text month */ if (isdigit((unsigned char) *cp)) { - ftype[nf] = ((*dp == '.') ? DTK_NUMBER : DTK_DATE); + ftype[nf] = ((delim == '.') ? DTK_NUMBER : DTK_DATE); while (isdigit((unsigned char) *cp)) *lp++ = *cp++; @@ -759,18 +783,18 @@ ParseDateTime(char *timestr, char *lowstr, * insist that the delimiters match to get a * three-field date. */ - if (*cp == *dp) + if (*cp == delim) { ftype[nf] = DTK_DATE; *lp++ = *cp++; - while (isdigit((unsigned char) *cp) || (*cp == *dp)) + while (isdigit((unsigned char) *cp) || (*cp == delim)) *lp++ = *cp++; } } else { ftype[nf] = DTK_DATE; - while (isalnum((unsigned char) *cp) || (*cp == *dp)) + while (isalnum((unsigned char) *cp) || (*cp == delim)) *lp++ = tolower((unsigned char) *cp++); } } @@ -809,20 +833,14 @@ ParseDateTime(char *timestr, char *lowstr, */ if ((*cp == '-') || (*cp == '/') || (*cp == '.')) { - char *dp = cp; + char delim = *cp; ftype[nf] = DTK_DATE; *lp++ = *cp++; - while (isdigit((unsigned char) *cp) || (*cp == *dp)) + while (isdigit((unsigned char) *cp) || (*cp == delim)) *lp++ = *cp++; } } - /* skip leading spaces */ - else if (isspace((unsigned char) *cp)) - { - cp++; - continue; - } /* sign? then special or numeric timezone */ else if ((*cp == '+') || (*cp == '-')) { @@ -851,12 +869,11 @@ ParseDateTime(char *timestr, char *lowstr, else return -1; } - /* ignore punctuation but use as delimiter */ + /* ignore other punctuation but use as delimiter */ else if (ispunct((unsigned char) *cp)) { cp++; continue; - } /* otherwise, something is not right... */ else @@ -865,14 +882,12 @@ ParseDateTime(char *timestr, char *lowstr, /* force in a delimiter after each field */ *lp++ = '\0'; nf++; - if (nf > MAXDATEFIELDS) - return -1; } *numfields = nf; return 0; -} /* ParseDateTime() */ +} /* DecodeDateTime() diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index f81f5f8e9c..d5facdd8e0 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: datetime.h,v 1.43 2003/08/04 02:40:15 momjian Exp $ + * $Id: datetime.h,v 1.44 2003/08/05 18:30:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -277,7 +277,7 @@ extern void GetCurrentTimeUsec(struct tm * tm, fsec_t *fsec, int *tzp); extern void j2date(int jd, int *year, int *month, int *day); extern int date2j(int year, int month, int day); -extern int ParseDateTime(char *timestr, char *lowstr, +extern int ParseDateTime(const char *timestr, char *lowstr, char **field, int *ftype, int maxfields, int *numfields); extern int DecodeDateTime(char **field, int *ftype, -- 2.40.0