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