strptime.c revision 74412
1219820Sjeff/* 2219820Sjeff * Powerdog Industries kindly requests feedback from anyone modifying 3219820Sjeff * this function: 4219820Sjeff * 5219820Sjeff * Date: Thu, 05 Jun 1997 23:17:17 -0400 6219820Sjeff * From: Kevin Ruddy <kevin.ruddy@powerdog.com> 7219820Sjeff * To: James FitzGibbon <james@nexis.net> 8219820Sjeff * Subject: Re: Use of your strptime(3) code (fwd) 9219820Sjeff * 10219820Sjeff * The reason for the "no mod" clause was so that modifications would 11219820Sjeff * come back and we could integrate them and reissue so that a wider 12219820Sjeff * audience could use it (thereby spreading the wealth). This has 13219820Sjeff * made it possible to get strptime to work on many operating systems. 14219820Sjeff * I'm not sure why that's "plain unacceptable" to the FreeBSD team. 15219820Sjeff * 16219820Sjeff * Anyway, you can change it to "with or without modification" as 17219820Sjeff * you see fit. Enjoy. 18219820Sjeff * 19219820Sjeff * Kevin Ruddy 20219820Sjeff * Powerdog Industries, Inc. 21219820Sjeff */ 22219820Sjeff/* 23219820Sjeff * Copyright (c) 1994 Powerdog Industries. All rights reserved. 24219820Sjeff * 25219820Sjeff * Redistribution and use in source and binary forms, with or without 26219820Sjeff * modification, are permitted provided that the following conditions 27219820Sjeff * are met: 28219820Sjeff * 1. Redistributions of source code must retain the above copyright 29219820Sjeff * notice, this list of conditions and the following disclaimer. 30219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright 31219820Sjeff * notice, this list of conditions and the following disclaimer 32219820Sjeff * in the documentation and/or other materials provided with the 33219820Sjeff * distribution. 34219820Sjeff * 3. All advertising materials mentioning features or use of this 35219820Sjeff * software must display the following acknowledgement: 36219820Sjeff * This product includes software developed by Powerdog Industries. 37219820Sjeff * 4. The name of Powerdog Industries may not be used to endorse or 38219820Sjeff * promote products derived from this software without specific prior 39219820Sjeff * written permission. 40219820Sjeff * 41219820Sjeff * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY 42219820Sjeff * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 43219820Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 44219820Sjeff * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE 45219820Sjeff * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 46219820Sjeff * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 47219820Sjeff * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 48219820Sjeff * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 49219820Sjeff * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 50219820Sjeff * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 51219820Sjeff * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 52219820Sjeff */ 53219820Sjeff 54219820Sjeff#ifdef LIBC_RCS 55219820Sjeffstatic const char rcsid[] = 56219820Sjeff "$FreeBSD: head/lib/libc/stdtime/strptime.c 74412 2001-03-18 11:58:15Z ache $"; 57219820Sjeff#endif 58219820Sjeff 59219820Sjeff#ifndef lint 60219820Sjeff#ifndef NOID 61219820Sjeffstatic char copyright[] = 62219820Sjeff"@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved."; 63219820Sjeffstatic char sccsid[] = "@(#)strptime.c 0.1 (Powerdog) 94/03/27"; 64219820Sjeff#endif /* !defined NOID */ 65219820Sjeff#endif /* not lint */ 66219820Sjeff 67219820Sjeff#include "namespace.h" 68219820Sjeff#include <time.h> 69219820Sjeff#include <ctype.h> 70219820Sjeff#include <string.h> 71219820Sjeff#include <pthread.h> 72219820Sjeff#include "un-namespace.h" 73219820Sjeff#include "libc_private.h" 74219820Sjeff#include "timelocal.h" 75219820Sjeff 76219820Sjeffstatic char * _strptime(const char *, const char *, struct tm *); 77219820Sjeff 78219820Sjeffstatic pthread_mutex_t gotgmt_mutex = PTHREAD_MUTEX_INITIALIZER; 79219820Sjeffstatic int got_GMT; 80219820Sjeff 81219820Sjeff#define asizeof(a) (sizeof (a) / sizeof ((a)[0])) 82219820Sjeff 83219820Sjeffstatic char * 84219820Sjeff_strptime(const char *buf, const char *fmt, struct tm *tm) 85219820Sjeff{ 86219820Sjeff char c; 87219820Sjeff const char *ptr; 88219820Sjeff int i, 89219820Sjeff len; 90219820Sjeff int Ealternative, Oalternative; 91219820Sjeff struct lc_time_T *tptr = __get_current_time_locale(); 92219820Sjeff 93219820Sjeff ptr = fmt; 94219820Sjeff while (*ptr != 0) { 95219820Sjeff if (*buf == 0) 96219820Sjeff break; 97219820Sjeff 98219820Sjeff c = *ptr++; 99219820Sjeff 100219820Sjeff if (c != '%') { 101219820Sjeff if (isspace((unsigned char)c)) 102219820Sjeff while (*buf != 0 && isspace((unsigned char)*buf)) 103219820Sjeff buf++; 104219820Sjeff else if (c != *buf++) 105219820Sjeff return 0; 106219820Sjeff continue; 107219820Sjeff } 108219820Sjeff 109219820Sjeff Ealternative = 0; 110219820Sjeff Oalternative = 0; 111219820Sjefflabel: 112219820Sjeff c = *ptr++; 113219820Sjeff switch (c) { 114219820Sjeff case 0: 115219820Sjeff case '%': 116219820Sjeff if (*buf++ != '%') 117219820Sjeff return 0; 118219820Sjeff break; 119219820Sjeff 120219820Sjeff case '+': 121219820Sjeff buf = _strptime(buf, tptr->date_fmt, tm); 122219820Sjeff if (buf == 0) 123219820Sjeff return 0; 124219820Sjeff break; 125219820Sjeff 126219820Sjeff case 'C': 127219820Sjeff if (!isdigit((unsigned char)*buf)) 128219820Sjeff return 0; 129219820Sjeff 130219820Sjeff /* XXX This will break for 3-digit centuries. */ 131219820Sjeff len = 2; 132219820Sjeff for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 133219820Sjeff i *= 10; 134219820Sjeff i += *buf - '0'; 135219820Sjeff len--; 136219820Sjeff } 137219820Sjeff if (i < 19) 138219820Sjeff return 0; 139219820Sjeff 140219820Sjeff tm->tm_year = i * 100 - 1900; 141219820Sjeff break; 142219820Sjeff 143219820Sjeff case 'c': 144219820Sjeff buf = _strptime(buf, tptr->c_fmt, tm); 145219820Sjeff if (buf == 0) 146219820Sjeff return 0; 147219820Sjeff break; 148219820Sjeff 149219820Sjeff case 'D': 150219820Sjeff buf = _strptime(buf, "%m/%d/%y", tm); 151219820Sjeff if (buf == 0) 152219820Sjeff return 0; 153219820Sjeff break; 154219820Sjeff 155219820Sjeff case 'E': 156219820Sjeff if (Ealternative || Oalternative) 157219820Sjeff break; 158219820Sjeff Ealternative++; 159219820Sjeff goto label; 160219820Sjeff 161219820Sjeff case 'O': 162219820Sjeff if (Ealternative || Oalternative) 163219820Sjeff break; 164219820Sjeff Oalternative++; 165219820Sjeff goto label; 166219820Sjeff 167219820Sjeff case 'F': 168219820Sjeff if (!Ealternative) 169219820Sjeff buf = _strptime(buf, "%Y-%m-%d", tm); 170219820Sjeff else 171219820Sjeff buf = _strptime(buf, 172219820Sjeff *(tptr->md_order) == 'd' ? 173219820Sjeff "%e %B" : "%B %e", tm); 174219820Sjeff if (buf == 0) 175219820Sjeff return 0; 176219820Sjeff break; 177219820Sjeff 178219820Sjeff case 'f': 179219820Sjeff if (!Ealternative) 180219820Sjeff break; 181219820Sjeff buf = _strptime(buf, 182219820Sjeff *(tptr->md_order) == 'd' ? "%e %b" : "%b %e", 183219820Sjeff tm); 184219820Sjeff if (buf == 0) 185219820Sjeff return 0; 186219820Sjeff break; 187219820Sjeff 188219820Sjeff case 'R': 189219820Sjeff buf = _strptime(buf, "%H:%M", tm); 190219820Sjeff if (buf == 0) 191219820Sjeff return 0; 192219820Sjeff break; 193219820Sjeff 194219820Sjeff case 'r': 195219820Sjeff buf = _strptime(buf, tptr->ampm_fmt, tm); 196219820Sjeff if (buf == 0) 197219820Sjeff return 0; 198219820Sjeff break; 199219820Sjeff 200219820Sjeff case 'T': 201219820Sjeff buf = _strptime(buf, "%H:%M:%S", tm); 202219820Sjeff if (buf == 0) 203219820Sjeff return 0; 204219820Sjeff break; 205219820Sjeff 206219820Sjeff case 'X': 207219820Sjeff buf = _strptime(buf, tptr->X_fmt, tm); 208219820Sjeff if (buf == 0) 209219820Sjeff return 0; 210219820Sjeff break; 211219820Sjeff 212219820Sjeff case 'x': 213219820Sjeff buf = _strptime(buf, tptr->x_fmt, tm); 214219820Sjeff if (buf == 0) 215219820Sjeff return 0; 216219820Sjeff break; 217219820Sjeff 218219820Sjeff case 'j': 219219820Sjeff if (!isdigit((unsigned char)*buf)) 220219820Sjeff return 0; 221219820Sjeff 222219820Sjeff len = 3; 223219820Sjeff for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 224219820Sjeff i *= 10; 225219820Sjeff i += *buf - '0'; 226219820Sjeff len--; 227219820Sjeff } 228219820Sjeff if (i < 1 || i > 366) 229219820Sjeff return 0; 230219820Sjeff 231219820Sjeff tm->tm_yday = i - 1; 232219820Sjeff break; 233219820Sjeff 234219820Sjeff case 'M': 235219820Sjeff case 'S': 236219820Sjeff if (*buf == 0 || isspace((unsigned char)*buf)) 237219820Sjeff break; 238219820Sjeff 239219820Sjeff if (!isdigit((unsigned char)*buf)) 240219820Sjeff return 0; 241219820Sjeff 242219820Sjeff len = 2; 243219820Sjeff for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 244219820Sjeff i *= 10; 245219820Sjeff i += *buf - '0'; 246219820Sjeff len--; 247219820Sjeff } 248219820Sjeff 249219820Sjeff if (c == 'M') { 250219820Sjeff if (i > 59) 251219820Sjeff return 0; 252219820Sjeff tm->tm_min = i; 253219820Sjeff } else { 254219820Sjeff if (i > 60) 255219820Sjeff return 0; 256219820Sjeff tm->tm_sec = i; 257219820Sjeff } 258219820Sjeff 259219820Sjeff if (*buf != 0 && isspace((unsigned char)*buf)) 260219820Sjeff while (*ptr != 0 && !isspace((unsigned char)*ptr)) 261219820Sjeff ptr++; 262219820Sjeff break; 263219820Sjeff 264219820Sjeff case 'H': 265219820Sjeff case 'I': 266219820Sjeff case 'k': 267219820Sjeff case 'l': 268219820Sjeff /* 269219820Sjeff * Of these, %l is the only specifier explicitly 270219820Sjeff * documented as not being zero-padded. However, 271219820Sjeff * there is no harm in allowing zero-padding. 272219820Sjeff * 273219820Sjeff * XXX The %l specifier may gobble one too many 274219820Sjeff * digits if used incorrectly. 275219820Sjeff */ 276219820Sjeff if (!isdigit((unsigned char)*buf)) 277219820Sjeff return 0; 278219820Sjeff 279219820Sjeff len = 2; 280219820Sjeff for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 281219820Sjeff i *= 10; 282219820Sjeff i += *buf - '0'; 283219820Sjeff len--; 284219820Sjeff } 285219820Sjeff if (c == 'H' || c == 'k') { 286219820Sjeff if (i > 23) 287219820Sjeff return 0; 288219820Sjeff } else if (i > 12) 289219820Sjeff return 0; 290219820Sjeff 291219820Sjeff tm->tm_hour = i; 292219820Sjeff 293219820Sjeff if (*buf != 0 && isspace((unsigned char)*buf)) 294219820Sjeff while (*ptr != 0 && !isspace((unsigned char)*ptr)) 295219820Sjeff ptr++; 296219820Sjeff break; 297219820Sjeff 298219820Sjeff case 'p': 299219820Sjeff /* 300219820Sjeff * XXX This is bogus if parsed before hour-related 301219820Sjeff * specifiers. 302219820Sjeff */ 303219820Sjeff len = strlen(tptr->am); 304219820Sjeff if (strncasecmp(buf, tptr->am, len) == 0) { 305219820Sjeff if (tm->tm_hour > 12) 306219820Sjeff return 0; 307219820Sjeff if (tm->tm_hour == 12) 308219820Sjeff tm->tm_hour = 0; 309219820Sjeff buf += len; 310219820Sjeff break; 311219820Sjeff } 312219820Sjeff 313219820Sjeff len = strlen(tptr->pm); 314219820Sjeff if (strncasecmp(buf, tptr->pm, len) == 0) { 315219820Sjeff if (tm->tm_hour > 12) 316219820Sjeff return 0; 317219820Sjeff if (tm->tm_hour != 12) 318219820Sjeff tm->tm_hour += 12; 319219820Sjeff buf += len; 320219820Sjeff break; 321219820Sjeff } 322219820Sjeff 323219820Sjeff return 0; 324219820Sjeff 325219820Sjeff case 'A': 326219820Sjeff case 'a': 327219820Sjeff for (i = 0; i < asizeof(tptr->weekday); i++) { 328219820Sjeff len = strlen(tptr->weekday[i]); 329219820Sjeff if (strncasecmp(buf, tptr->weekday[i], 330219820Sjeff len) == 0) 331 break; 332 len = strlen(tptr->wday[i]); 333 if (strncasecmp(buf, tptr->wday[i], 334 len) == 0) 335 break; 336 } 337 if (i == asizeof(tptr->weekday)) 338 return 0; 339 340 tm->tm_wday = i; 341 buf += len; 342 break; 343 344 case 'U': 345 case 'W': 346 /* 347 * XXX This is bogus, as we can not assume any valid 348 * information present in the tm structure at this 349 * point to calculate a real value, so just check the 350 * range for now. 351 */ 352 if (!isdigit((unsigned char)*buf)) 353 return 0; 354 355 len = 2; 356 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 357 i *= 10; 358 i += *buf - '0'; 359 len--; 360 } 361 if (i > 53) 362 return 0; 363 364 if (*buf != 0 && isspace((unsigned char)*buf)) 365 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 366 ptr++; 367 break; 368 369 case 'w': 370 if (!isdigit((unsigned char)*buf)) 371 return 0; 372 373 i = *buf - '0'; 374 if (i > 6) 375 return 0; 376 377 tm->tm_wday = i; 378 379 if (*buf != 0 && isspace((unsigned char)*buf)) 380 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 381 ptr++; 382 break; 383 384 case 'd': 385 case 'e': 386 /* 387 * The %e specifier is explicitly documented as not 388 * being zero-padded but there is no harm in allowing 389 * such padding. 390 * 391 * XXX The %e specifier may gobble one too many 392 * digits if used incorrectly. 393 */ 394 if (!isdigit((unsigned char)*buf)) 395 return 0; 396 397 len = 2; 398 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 399 i *= 10; 400 i += *buf - '0'; 401 len--; 402 } 403 if (i > 31) 404 return 0; 405 406 tm->tm_mday = i; 407 408 if (*buf != 0 && isspace((unsigned char)*buf)) 409 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 410 ptr++; 411 break; 412 413 case 'B': 414 case 'b': 415 case 'h': 416 for (i = 0; i < asizeof(tptr->month); i++) { 417 if (Oalternative) { 418 if (c == 'B') { 419 len = strlen(tptr->alt_month[i]); 420 if (strncasecmp(buf, 421 tptr->alt_month[i], 422 len) == 0) 423 break; 424 } 425 } else { 426 len = strlen(tptr->month[i]); 427 if (strncasecmp(buf, tptr->month[i], 428 len) == 0) 429 break; 430 len = strlen(tptr->mon[i]); 431 if (strncasecmp(buf, tptr->mon[i], 432 len) == 0) 433 break; 434 } 435 } 436 if (i == asizeof(tptr->month)) 437 return 0; 438 439 tm->tm_mon = i; 440 buf += len; 441 break; 442 443 case 'm': 444 if (!isdigit((unsigned char)*buf)) 445 return 0; 446 447 len = 2; 448 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 449 i *= 10; 450 i += *buf - '0'; 451 len--; 452 } 453 if (i < 1 || i > 12) 454 return 0; 455 456 tm->tm_mon = i - 1; 457 458 if (*buf != 0 && isspace((unsigned char)*buf)) 459 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 460 ptr++; 461 break; 462 463 case 'Y': 464 case 'y': 465 if (*buf == 0 || isspace((unsigned char)*buf)) 466 break; 467 468 if (!isdigit((unsigned char)*buf)) 469 return 0; 470 471 len = (c == 'Y') ? 4 : 2; 472 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 473 i *= 10; 474 i += *buf - '0'; 475 len--; 476 } 477 if (c == 'Y') 478 i -= 1900; 479 if (c == 'y' && i < 69) 480 i += 100; 481 if (i < 0) 482 return 0; 483 484 tm->tm_year = i; 485 486 if (*buf != 0 && isspace((unsigned char)*buf)) 487 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 488 ptr++; 489 break; 490 491 case 'Z': 492 { 493 const char *cp; 494 char *zonestr; 495 496 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/} 497 if (cp - buf) { 498 zonestr = alloca(cp - buf + 1); 499 strncpy(zonestr, buf, cp - buf); 500 zonestr[cp - buf] = '\0'; 501 tzset(); 502 if (0 == strcmp(zonestr, "GMT")) { 503 got_GMT = 1; 504 } else if (0 == strcmp(zonestr, tzname[0])) { 505 tm->tm_isdst = 0; 506 } else if (0 == strcmp(zonestr, tzname[1])) { 507 tm->tm_isdst = 1; 508 } else { 509 return 0; 510 } 511 buf += cp - buf; 512 } 513 } 514 break; 515 } 516 } 517 return (char *)buf; 518} 519 520 521char * 522strptime(const char *buf, const char *fmt, struct tm *tm) 523{ 524 char *ret; 525 526 if (__isthreaded) 527 _pthread_mutex_lock(&gotgmt_mutex); 528 529 got_GMT = 0; 530 ret = _strptime(buf, fmt, tm); 531 if (ret && got_GMT) { 532 time_t t = timegm(tm); 533 localtime_r(&t, tm); 534 got_GMT = 0; 535 } 536 537 if (__isthreaded) 538 _pthread_mutex_unlock(&gotgmt_mutex); 539 540 return ret; 541} 542