parsedate: parse strings without using sscanf()

- sscanf is slow and complex, avoid it
- give up already if the string is 12 bytes or longer as no valid string
  can be that long
- this can now be done without copy

Closes #10547
This commit is contained in:
Daniel Stenberg 2023-02-17 10:04:31 +01:00
parent cbf5717615
commit ba9a4e9bd8
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2

View file

@ -212,56 +212,55 @@ static int checkday(const char *check, size_t len)
{ {
int i; int i;
const char * const *what; const char * const *what;
bool found = FALSE;
if(len > 3) if(len > 3)
what = &weekday[0]; what = &weekday[0];
else else if(len == 3)
what = &Curl_wkday[0]; what = &Curl_wkday[0];
else
return -1; /* too short */
for(i = 0; i<7; i++) { for(i = 0; i<7; i++) {
if(strcasecompare(check, what[0])) { size_t ilen = strlen(what[0]);
found = TRUE; if((ilen == len) &&
break; strncasecompare(check, what[0], len))
} return i;
what++; what++;
} }
return found?i:-1; return -1;
} }
static int checkmonth(const char *check) static int checkmonth(const char *check, size_t len)
{ {
int i; int i;
const char * const *what; const char * const *what = &Curl_month[0];
bool found = FALSE; if(len != 3)
return -1; /* not a month */
what = &Curl_month[0];
for(i = 0; i<12; i++) { for(i = 0; i<12; i++) {
if(strcasecompare(check, what[0])) { if(strncasecompare(check, what[0], 3))
found = TRUE; return i;
break;
}
what++; what++;
} }
return found?i:-1; /* return the offset or -1, no real offset is -1 */ return -1; /* return the offset or -1, no real offset is -1 */
} }
/* return the time zone offset between GMT and the input one, in number /* return the time zone offset between GMT and the input one, in number
of seconds or -1 if the timezone wasn't found/legal */ of seconds or -1 if the timezone wasn't found/legal */
static int checktz(const char *check) static int checktz(const char *check, size_t len)
{ {
unsigned int i; unsigned int i;
const struct tzinfo *what; const struct tzinfo *what = tz;
bool found = FALSE; if(len > 4) /* longer than any valid timezone */
return -1;
what = tz;
for(i = 0; i< sizeof(tz)/sizeof(tz[0]); i++) { for(i = 0; i< sizeof(tz)/sizeof(tz[0]); i++) {
if(strcasecompare(check, what->name)) { size_t ilen = strlen(what->name);
found = TRUE; if((ilen == len) &&
break; strncasecompare(check, what->name, len))
} return what->offset*60;
what++; what++;
} }
return found?what->offset*60:-1; return -1;
} }
static void skip(const char **date) static void skip(const char **date)
@ -305,6 +304,9 @@ static time_t time2epoch(int sec, int min, int hour,
* PARSEDATE_SOONER - time underflow at the low end of time_t * PARSEDATE_SOONER - time underflow at the low end of time_t
*/ */
/* Wednesday is the longest name this parser knows about */
#define NAME_LEN 12
static int parsedate(const char *date, time_t *output) static int parsedate(const char *date, time_t *output)
{ {
time_t t = 0; time_t t = 0;
@ -327,32 +329,32 @@ static int parsedate(const char *date, time_t *output)
if(ISALPHA(*date)) { if(ISALPHA(*date)) {
/* a name coming up */ /* a name coming up */
char buf[32]=""; size_t len = 0;
size_t len; const char *p = date;
if(sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZ" while(ISALPHA(*p) && (len < NAME_LEN)) {
"abcdefghijklmnopqrstuvwxyz]", buf)) p++;
len = strlen(buf); len++;
else
len = 0;
if(wdaynum == -1) {
wdaynum = checkday(buf, len);
if(wdaynum != -1)
found = TRUE;
}
if(!found && (monnum == -1)) {
monnum = checkmonth(buf);
if(monnum != -1)
found = TRUE;
} }
if(!found && (tzoff == -1)) { if(len != NAME_LEN) {
/* this just must be a time zone string */ if(wdaynum == -1) {
tzoff = checktz(buf); wdaynum = checkday(date, len);
if(tzoff != -1) if(wdaynum != -1)
found = TRUE; found = TRUE;
} }
if(!found && (monnum == -1)) {
monnum = checkmonth(date, len);
if(monnum != -1)
found = TRUE;
}
if(!found && (tzoff == -1)) {
/* this just must be a time zone string */
tzoff = checktz(date, len);
if(tzoff != -1)
found = TRUE;
}
}
if(!found) if(!found)
return PARSEDATE_FAIL; /* bad string */ return PARSEDATE_FAIL; /* bad string */