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