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