1 /* 2 * Copyright 2010, 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 'd': 343 case 'e': 344 /* 345 * The %e specifier is explicitly documented as not 346 * being zero-padded but there is no harm in allowing 347 * such padding. 348 * 349 * XXX The %e specifier may gobble one too many 350 * digits if used incorrectly. 351 */ 352 if (!isdigit(*buf)) 353 return (NULL); 354 355 len = 2; 356 for (i = 0; len && isdigit(*buf); buf++) { 357 i *= 10; 358 i += *buf - '0'; 359 len--; 360 } 361 if (i > 31) 362 return (NULL); 363 364 tm->tm_mday = i; 365 366 if (isspace(*buf)) 367 while (*ptr != 0 && !isspace(*ptr)) 368 ptr++; 369 break; 370 371 case 'B': 372 case 'b': 373 case 'h': 374 for (i = 0; i < asizeof(tptr->month); i++) { 375 len = strlen(tptr->month[i]); 376 if (strncasecmp(buf, tptr->month[i], len) == 0) 377 break; 378 } 379 /* 380 * Try the abbreviated month name if the full name 381 * wasn't found. 382 */ 383 if (i == asizeof(tptr->month)) { 384 for (i = 0; i < asizeof(tptr->month); i++) { 385 len = strlen(tptr->mon[i]); 386 if (strncasecmp(buf, tptr->mon[i], 387 len) == 0) 388 break; 389 } 390 } 391 if (i == asizeof(tptr->month)) 392 return (NULL); 393 394 tm->tm_mon = i; 395 buf += len; 396 break; 397 398 case 'm': 399 if (!isdigit(*buf)) 400 return (NULL); 401 402 len = 2; 403 for (i = 0; len && isdigit(*buf); buf++) { 404 i *= 10; 405 i += *buf - '0'; 406 len--; 407 } 408 if (i < 1 || i > 12) 409 return (NULL); 410 411 tm->tm_mon = i - 1; 412 413 if (isspace(*buf)) 414 while (*ptr != NULL && !isspace(*ptr)) 415 ptr++; 416 break; 417 418 case 's': 419 { 420 char *cp; 421 int sverrno; 422 time_t t; 423 424 sverrno = errno; 425 errno = 0; 426 t = strtol(buf, &cp, 10); 427 if (errno == ERANGE) { 428 errno = sverrno; 429 return (NULL); 430 } 431 errno = sverrno; 432 buf = cp; 433 (void) gmtime_r(&t, tm); 434 *flagsp |= F_GMT; 435 } 436 break; 437 438 case 'Y': 439 case 'y': 440 if (*buf == NULL || isspace(*buf)) 441 break; 442 443 if (!isdigit(*buf)) 444 return (NULL); 445 446 len = (c == 'Y') ? 4 : 2; 447 for (i = 0; len && isdigit(*buf); buf++) { 448 i *= 10; 449 i += *buf - '0'; 450 len--; 451 } 452 if (c == 'Y') 453 i -= 1900; 454 if (c == 'y' && i < 69) 455 i += 100; 456 if (i < 0) 457 return (NULL); 458 459 tm->tm_year = i; 460 461 if (isspace(*buf)) 462 while (*ptr != 0 && !isspace(*ptr)) 463 ptr++; 464 break; 465 466 case 'Z': 467 { 468 const char *cp = buf; 469 char *zonestr; 470 471 while (isupper(*cp)) 472 ++cp; 473 if (cp - buf) { 474 zonestr = alloca(cp - buf + 1); 475 (void) strncpy(zonestr, buf, cp - buf); 476 zonestr[cp - buf] = '\0'; 477 tzset(); 478 if (strcmp(zonestr, "GMT") == 0) { 479 *flagsp |= F_GMT; 480 } else if (0 == strcmp(zonestr, tzname[0])) { 481 tm->tm_isdst = 0; 482 } else if (0 == strcmp(zonestr, tzname[1])) { 483 tm->tm_isdst = 1; 484 } else { 485 return (NULL); 486 } 487 buf += cp - buf; 488 } 489 } 490 break; 491 492 case 'z': 493 { 494 int sign = 1; 495 496 if (*buf != '+') { 497 if (*buf == '-') 498 sign = -1; 499 else 500 return (NULL); 501 } 502 buf++; 503 i = 0; 504 for (len = 4; len > 0; len--) { 505 if (!isdigit(*buf)) 506 return (NULL); 507 i *= 10; 508 i += *buf - '0'; 509 buf++; 510 } 511 512 tm->tm_hour -= sign * (i / 100); 513 tm->tm_min -= sign * (i % 100); 514 *flagsp |= F_GMT; 515 } 516 break; 517 } 518 } 519 520 if (!recurse) { 521 if (buf && (*flagsp & F_GMT)) { 522 time_t t = timegm(tm); 523 (void) localtime_r(&t, tm); 524 } 525 } 526 527 return ((char *)buf); 528 } 529 530 char * 531 strptime(const char *buf, const char *fmt, struct tm *tm) 532 { 533 int flags = F_ZERO; 534 535 return (__strptime(buf, fmt, tm, &flags)); 536 } 537 538 /* 539 * This is used by Solaris, and is a variant that does not clear the 540 * incoming tm. It is triggered by -D_STRPTIME_DONTZERO. 541 */ 542 char * 543 __strptime_dontzero(const char *buf, const char *fmt, struct tm *tm) 544 { 545 int flags = 0; 546 547 return (__strptime(buf, fmt, tm, &flags)); 548 } 549