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