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