1 /* 2 * Powerdog Industries kindly requests feedback from anyone modifying 3 * this function: 4 * 5 * Date: Thu, 05 Jun 1997 23:17:17 -0400 6 * From: Kevin Ruddy <kevin.ruddy@powerdog.com> 7 * To: James FitzGibbon <james@nexis.net> 8 * Subject: Re: Use of your strptime(3) code (fwd) 9 * 10 * The reason for the "no mod" clause was so that modifications would 11 * come back and we could integrate them and reissue so that a wider 12 * audience could use it (thereby spreading the wealth). This has 13 * made it possible to get strptime to work on many operating systems. 14 * I'm not sure why that's "plain unacceptable" to the FreeBSD team. 15 * 16 * Anyway, you can change it to "with or without modification" as 17 * you see fit. Enjoy. 18 * 19 * Kevin Ruddy 20 * Powerdog Industries, Inc. 21 */ 22 /* 23 * Copyright (c) 1994 Powerdog Industries. All rights reserved. 24 * 25 * Copyright (c) 2011 The FreeBSD Foundation 26 * All rights reserved. 27 * Portions of this software were developed by David Chisnall 28 * under sponsorship from the FreeBSD Foundation. 29 * 30 * Redistribution and use in source and binary forms, with or without 31 * modification, are permitted provided that the following conditions 32 * are met: 33 * 1. Redistributions of source code must retain the above copyright 34 * notice, this list of conditions and the following disclaimer. 35 * 2. Redistributions in binary form must reproduce the above copyright 36 * notice, this list of conditions and the following disclaimer 37 * in the documentation and/or other materials provided with the 38 * distribution. 39 * 3. All advertising materials mentioning features or use of this 40 * software must display the following acknowledgement: 41 * This product includes software developed by Powerdog Industries. 42 * 4. The name of Powerdog Industries may not be used to endorse or 43 * promote products derived from this software without specific prior 44 * written permission. 45 * 46 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY 47 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 49 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE 50 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 51 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 52 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 53 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 54 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 55 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 56 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 57 * 58 * @(#)strptime.c 0.1 (Powerdog) 94/03/27 59 * @(#) Copyright (c) 1994 Powerdog Industries. All rights reserved. 60 * $FreeBSD: head/lib/libc/stdtime/strptime.c 227753 2011-11-20 14:45:42Z theraven $ 61 */ 62 63 #include "namespace.h" 64 #include <time.h> 65 #include <ctype.h> 66 #include <errno.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <pthread.h> 70 #include "un-namespace.h" 71 #include "libc_private.h" 72 #include "timelocal.h" 73 74 static char * _strptime(const char *, const char *, struct tm *, int *, locale_t); 75 76 #define asizeof(a) (sizeof (a) / sizeof ((a)[0])) 77 78 static char * 79 _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp, 80 locale_t locale) 81 { 82 char c; 83 const char *ptr; 84 int i, 85 len; 86 int Ealternative, Oalternative; 87 struct lc_time_T *tptr = __get_current_time_locale(locale); 88 89 ptr = fmt; 90 while (*ptr != 0) { 91 if (*buf == 0) 92 break; 93 94 c = *ptr++; 95 96 if (c != '%') { 97 if (isspace_l((unsigned char)c, locale)) 98 while (*buf != 0 && 99 isspace_l((unsigned char)*buf, locale)) 100 buf++; 101 else if (c != *buf++) 102 return 0; 103 continue; 104 } 105 106 Ealternative = 0; 107 Oalternative = 0; 108 label: 109 c = *ptr++; 110 switch (c) { 111 case 0: 112 case '%': 113 if (*buf++ != '%') 114 return 0; 115 break; 116 117 case '+': 118 buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale); 119 if (buf == 0) 120 return 0; 121 break; 122 123 case 'C': 124 if (!isdigit_l((unsigned char)*buf, locale)) 125 return 0; 126 127 /* XXX This will break for 3-digit centuries. */ 128 len = 2; 129 for (i = 0; len && *buf != 0 && 130 isdigit_l((unsigned char)*buf, locale); buf++) { 131 i *= 10; 132 i += *buf - '0'; 133 len--; 134 } 135 if (i < 19) 136 return 0; 137 138 tm->tm_year = i * 100 - 1900; 139 break; 140 141 case 'c': 142 buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale); 143 if (buf == 0) 144 return 0; 145 break; 146 147 case 'D': 148 buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale); 149 if (buf == 0) 150 return 0; 151 break; 152 153 case 'E': 154 if (Ealternative || Oalternative) 155 break; 156 Ealternative++; 157 goto label; 158 159 case 'O': 160 if (Ealternative || Oalternative) 161 break; 162 Oalternative++; 163 goto label; 164 165 case 'F': 166 buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale); 167 if (buf == 0) 168 return 0; 169 break; 170 171 case 'R': 172 buf = _strptime(buf, "%H:%M", tm, GMTp, locale); 173 if (buf == 0) 174 return 0; 175 break; 176 177 case 'r': 178 buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale); 179 if (buf == 0) 180 return 0; 181 break; 182 183 case 'T': 184 buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale); 185 if (buf == 0) 186 return 0; 187 break; 188 189 case 'X': 190 buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale); 191 if (buf == 0) 192 return 0; 193 break; 194 195 case 'x': 196 buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale); 197 if (buf == 0) 198 return 0; 199 break; 200 201 case 'j': 202 if (!isdigit_l((unsigned char)*buf, locale)) 203 return 0; 204 205 len = 3; 206 for (i = 0; len && *buf != 0 && 207 isdigit_l((unsigned char)*buf, locale); buf++){ 208 i *= 10; 209 i += *buf - '0'; 210 len--; 211 } 212 if (i < 1 || i > 366) 213 return 0; 214 215 tm->tm_yday = i - 1; 216 break; 217 218 case 'M': 219 case 'S': 220 if (*buf == 0 || 221 isspace_l((unsigned char)*buf, locale)) 222 break; 223 224 if (!isdigit_l((unsigned char)*buf, locale)) 225 return 0; 226 227 len = 2; 228 for (i = 0; len && *buf != 0 && 229 isdigit_l((unsigned char)*buf, locale); buf++){ 230 i *= 10; 231 i += *buf - '0'; 232 len--; 233 } 234 235 if (c == 'M') { 236 if (i > 59) 237 return 0; 238 tm->tm_min = i; 239 } else { 240 if (i > 60) 241 return 0; 242 tm->tm_sec = i; 243 } 244 245 if (*buf != 0 && 246 isspace_l((unsigned char)*buf, locale)) 247 while (*ptr != 0 && 248 !isspace_l((unsigned char)*ptr, locale)) 249 ptr++; 250 break; 251 252 case 'H': 253 case 'I': 254 case 'k': 255 case 'l': 256 /* 257 * Of these, %l is the only specifier explicitly 258 * documented as not being zero-padded. However, 259 * there is no harm in allowing zero-padding. 260 * 261 * XXX The %l specifier may gobble one too many 262 * digits if used incorrectly. 263 */ 264 if (!isdigit_l((unsigned char)*buf, locale)) 265 return 0; 266 267 len = 2; 268 for (i = 0; len && *buf != 0 && 269 isdigit_l((unsigned char)*buf, locale); buf++) { 270 i *= 10; 271 i += *buf - '0'; 272 len--; 273 } 274 if (c == 'H' || c == 'k') { 275 if (i > 23) 276 return 0; 277 } else if (i > 12) 278 return 0; 279 280 tm->tm_hour = i; 281 282 if (*buf != 0 && 283 isspace_l((unsigned char)*buf, locale)) 284 while (*ptr != 0 && 285 !isspace_l((unsigned char)*ptr, locale)) 286 ptr++; 287 break; 288 289 case 'p': 290 /* 291 * XXX This is bogus if parsed before hour-related 292 * specifiers. 293 */ 294 len = strlen(tptr->am); 295 if (strncasecmp_l(buf, tptr->am, len, locale) == 0) { 296 if (tm->tm_hour > 12) 297 return 0; 298 if (tm->tm_hour == 12) 299 tm->tm_hour = 0; 300 buf += len; 301 break; 302 } 303 304 len = strlen(tptr->pm); 305 if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) { 306 if (tm->tm_hour > 12) 307 return 0; 308 if (tm->tm_hour != 12) 309 tm->tm_hour += 12; 310 buf += len; 311 break; 312 } 313 314 return 0; 315 316 case 'A': 317 case 'a': 318 for (i = 0; i < asizeof(tptr->weekday); i++) { 319 len = strlen(tptr->weekday[i]); 320 if (strncasecmp_l(buf, tptr->weekday[i], 321 len, locale) == 0) 322 break; 323 len = strlen(tptr->wday[i]); 324 if (strncasecmp_l(buf, tptr->wday[i], 325 len, locale) == 0) 326 break; 327 } 328 if (i == asizeof(tptr->weekday)) 329 return 0; 330 331 tm->tm_wday = i; 332 buf += len; 333 break; 334 335 case 'U': 336 case 'W': 337 /* 338 * XXX This is bogus, as we can not assume any valid 339 * information present in the tm structure at this 340 * point to calculate a real value, so just check the 341 * range for now. 342 */ 343 if (!isdigit_l((unsigned char)*buf, locale)) 344 return 0; 345 346 len = 2; 347 for (i = 0; len && *buf != 0 && 348 isdigit_l((unsigned char)*buf, locale); buf++) { 349 i *= 10; 350 i += *buf - '0'; 351 len--; 352 } 353 if (i > 53) 354 return 0; 355 356 if (*buf != 0 && 357 isspace_l((unsigned char)*buf, locale)) 358 while (*ptr != 0 && 359 !isspace_l((unsigned char)*ptr, locale)) 360 ptr++; 361 break; 362 363 case 'w': 364 if (!isdigit_l((unsigned char)*buf, locale)) 365 return 0; 366 367 i = *buf - '0'; 368 if (i > 6) 369 return 0; 370 371 tm->tm_wday = i; 372 373 if (*buf != 0 && 374 isspace_l((unsigned char)*buf, locale)) 375 while (*ptr != 0 && 376 !isspace_l((unsigned char)*ptr, locale)) 377 ptr++; 378 break; 379 380 case 'd': 381 case 'e': 382 /* 383 * The %e specifier is explicitly documented as not 384 * being zero-padded but there is no harm in allowing 385 * such padding. 386 * 387 * XXX The %e specifier may gobble one too many 388 * digits if used incorrectly. 389 */ 390 if (!isdigit_l((unsigned char)*buf, locale)) 391 return 0; 392 393 len = 2; 394 for (i = 0; len && *buf != 0 && 395 isdigit_l((unsigned char)*buf, locale); buf++) { 396 i *= 10; 397 i += *buf - '0'; 398 len--; 399 } 400 if (i > 31) 401 return 0; 402 403 tm->tm_mday = i; 404 405 if (*buf != 0 && 406 isspace_l((unsigned char)*buf, locale)) 407 while (*ptr != 0 && 408 !isspace_l((unsigned char)*ptr, locale)) 409 ptr++; 410 break; 411 412 case 'B': 413 case 'b': 414 case 'h': 415 for (i = 0; i < asizeof(tptr->month); i++) { 416 if (Oalternative) { 417 if (c == 'B') { 418 len = strlen(tptr->alt_month[i]); 419 if (strncasecmp_l(buf, 420 tptr->alt_month[i], 421 len, locale) == 0) 422 break; 423 } 424 } else { 425 len = strlen(tptr->month[i]); 426 if (strncasecmp_l(buf, tptr->month[i], 427 len, locale) == 0) 428 break; 429 } 430 } 431 /* 432 * Try the abbreviated month name if the full name 433 * wasn't found and Oalternative was not requested. 434 */ 435 if (i == asizeof(tptr->month) && !Oalternative) { 436 for (i = 0; i < asizeof(tptr->month); i++) { 437 len = strlen(tptr->mon[i]); 438 if (strncasecmp_l(buf, tptr->mon[i], 439 len, locale) == 0) 440 break; 441 } 442 } 443 if (i == asizeof(tptr->month)) 444 return 0; 445 446 tm->tm_mon = i; 447 buf += len; 448 break; 449 450 case 'm': 451 if (!isdigit_l((unsigned char)*buf, locale)) 452 return 0; 453 454 len = 2; 455 for (i = 0; len && *buf != 0 && 456 isdigit_l((unsigned char)*buf, locale); buf++) { 457 i *= 10; 458 i += *buf - '0'; 459 len--; 460 } 461 if (i < 1 || i > 12) 462 return 0; 463 464 tm->tm_mon = i - 1; 465 466 if (*buf != 0 && 467 isspace_l((unsigned char)*buf, locale)) 468 while (*ptr != 0 && 469 !isspace_l((unsigned char)*ptr, locale)) 470 ptr++; 471 break; 472 473 case 's': 474 { 475 char *cp; 476 int sverrno; 477 long n; 478 time_t t; 479 480 sverrno = errno; 481 errno = 0; 482 n = strtol_l(buf, &cp, 10, locale); 483 if (errno == ERANGE || (long)(t = n) != n) { 484 errno = sverrno; 485 return 0; 486 } 487 errno = sverrno; 488 buf = cp; 489 gmtime_r(&t, tm); 490 *GMTp = 1; 491 } 492 break; 493 494 case 'Y': 495 case 'y': 496 if (*buf == 0 || 497 isspace_l((unsigned char)*buf, locale)) 498 break; 499 500 if (!isdigit_l((unsigned char)*buf, locale)) 501 return 0; 502 503 len = (c == 'Y') ? 4 : 2; 504 for (i = 0; len && *buf != 0 && 505 isdigit_l((unsigned char)*buf, locale); buf++) { 506 i *= 10; 507 i += *buf - '0'; 508 len--; 509 } 510 if (c == 'Y') 511 i -= 1900; 512 if (c == 'y' && i < 69) 513 i += 100; 514 if (i < 0) 515 return 0; 516 517 tm->tm_year = i; 518 519 if (*buf != 0 && 520 isspace_l((unsigned char)*buf, locale)) 521 while (*ptr != 0 && 522 !isspace_l((unsigned char)*ptr, locale)) 523 ptr++; 524 break; 525 526 case 'Z': 527 { 528 const char *cp; 529 char *zonestr; 530 531 for (cp = buf; *cp && 532 isupper_l((unsigned char)*cp, locale); ++cp) { 533 /*empty*/} 534 if (cp - buf) { 535 zonestr = alloca(cp - buf + 1); 536 strncpy(zonestr, buf, cp - buf); 537 zonestr[cp - buf] = '\0'; 538 tzset(); 539 if (0 == strcmp(zonestr, "GMT")) { 540 *GMTp = 1; 541 } else if (0 == strcmp(zonestr, tzname[0])) { 542 tm->tm_isdst = 0; 543 } else if (0 == strcmp(zonestr, tzname[1])) { 544 tm->tm_isdst = 1; 545 } else { 546 return 0; 547 } 548 buf += cp - buf; 549 } 550 } 551 break; 552 553 case 'z': 554 { 555 int sign = 1; 556 557 if (*buf != '+') { 558 if (*buf == '-') 559 sign = -1; 560 else 561 return 0; 562 } 563 564 buf++; 565 i = 0; 566 for (len = 4; len > 0; len--) { 567 if (isdigit_l((unsigned char)*buf, locale)) { 568 i *= 10; 569 i += *buf - '0'; 570 buf++; 571 } else 572 return 0; 573 } 574 575 tm->tm_hour -= sign * (i / 100); 576 tm->tm_min -= sign * (i % 100); 577 *GMTp = 1; 578 } 579 break; 580 } 581 } 582 return (char *)buf; 583 } 584 585 586 char * 587 strptime_l(const char * __restrict buf, const char * __restrict fmt, 588 struct tm * __restrict tm, locale_t loc) 589 { 590 char *ret; 591 int gmt; 592 FIX_LOCALE(loc); 593 594 gmt = 0; 595 ret = _strptime(buf, fmt, tm, &gmt, loc); 596 if (ret && gmt) { 597 time_t t = timegm(tm); 598 localtime_r(&t, tm); 599 } 600 601 return (ret); 602 } 603 char * 604 strptime(const char * __restrict buf, const char * __restrict fmt, 605 struct tm * __restrict tm) 606 { 607 return strptime_l(buf, fmt, tm, __get_locale()); 608 } 609