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 #ifdef LIBC_RCS 55 static const char rcsid[] = 56 "$FreeBSD: src/lib/libc/stdtime/strptime.c,v 1.17.2.3 2002/03/12 17:24:54 phantom Exp $"; 57 #endif 58 59 #ifndef lint 60 #ifndef NOID 61 static char copyright[] = 62 "@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved."; 63 static char sccsid[] = "@(#)strptime.c 0.1 (Powerdog) 94/03/27"; 64 #endif /* !defined NOID */ 65 #endif /* not lint */ 66 67 #include <time.h> 68 #include <ctype.h> 69 #include <limits.h> 70 #include <stdlib.h> 71 #include <string.h> 72 #ifdef _THREAD_SAFE 73 #include <pthread.h> 74 #include "pthread_private.h" 75 #endif 76 #include "timelocal.h" 77 78 static char * _strptime(const char *, const char *, struct tm *); 79 80 #ifdef _THREAD_SAFE 81 static struct pthread_mutex _gotgmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER; 82 static pthread_mutex_t gotgmt_mutex = &_gotgmt_mutexd; 83 #endif 84 static int got_GMT; 85 86 #define asizeof(a) (sizeof (a) / sizeof ((a)[0])) 87 88 static char * 89 _strptime(const char *buf, const char *fmt, struct tm *tm) 90 { 91 char c; 92 const char *ptr; 93 int i, 94 len; 95 int Ealternative, Oalternative; 96 struct lc_time_T *tptr = __get_current_time_locale(); 97 98 ptr = fmt; 99 while (*ptr != 0) { 100 if (*buf == 0) 101 break; 102 103 c = *ptr++; 104 105 if (c != '%') { 106 if (isspace((unsigned char)c)) 107 while (*buf != 0 && isspace((unsigned char)*buf)) 108 buf++; 109 else if (c != *buf++) 110 return 0; 111 continue; 112 } 113 114 Ealternative = 0; 115 Oalternative = 0; 116 label: 117 c = *ptr++; 118 switch (c) { 119 case 0: 120 case '%': 121 if (*buf++ != '%') 122 return 0; 123 break; 124 125 case '+': 126 buf = _strptime(buf, tptr->date_fmt, tm); 127 if (buf == 0) 128 return 0; 129 break; 130 131 case 'C': 132 if (!isdigit((unsigned char)*buf)) 133 return 0; 134 135 /* XXX This will break for 3-digit centuries. */ 136 len = 2; 137 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 138 i *= 10; 139 i += *buf - '0'; 140 len--; 141 } 142 if (i < 19) 143 return 0; 144 145 tm->tm_year = i * 100 - 1900; 146 break; 147 148 case 'c': 149 buf = _strptime(buf, tptr->c_fmt, tm); 150 if (buf == 0) 151 return 0; 152 break; 153 154 case 'D': 155 buf = _strptime(buf, "%m/%d/%y", tm); 156 if (buf == 0) 157 return 0; 158 break; 159 160 case 'E': 161 if (Ealternative || Oalternative) 162 break; 163 Ealternative++; 164 goto label; 165 166 case 'O': 167 if (Ealternative || Oalternative) 168 break; 169 Oalternative++; 170 goto label; 171 172 case 'F': 173 buf = _strptime(buf, "%Y-%m-%d", tm); 174 if (buf == 0) 175 return 0; 176 break; 177 178 case 'R': 179 buf = _strptime(buf, "%H:%M", tm); 180 if (buf == 0) 181 return 0; 182 break; 183 184 case 'r': 185 buf = _strptime(buf, tptr->ampm_fmt, tm); 186 if (buf == 0) 187 return 0; 188 break; 189 190 case 'T': 191 buf = _strptime(buf, "%H:%M:%S", tm); 192 if (buf == 0) 193 return 0; 194 break; 195 196 case 'X': 197 buf = _strptime(buf, tptr->X_fmt, tm); 198 if (buf == 0) 199 return 0; 200 break; 201 202 case 'x': 203 buf = _strptime(buf, tptr->x_fmt, tm); 204 if (buf == 0) 205 return 0; 206 break; 207 208 case 'j': 209 if (!isdigit((unsigned char)*buf)) 210 return 0; 211 212 len = 3; 213 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 214 i *= 10; 215 i += *buf - '0'; 216 len--; 217 } 218 if (i < 1 || i > 366) 219 return 0; 220 221 tm->tm_yday = i - 1; 222 break; 223 224 case 'M': 225 case 'S': 226 if (*buf == 0 || isspace((unsigned char)*buf)) 227 break; 228 229 if (!isdigit((unsigned char)*buf)) 230 return 0; 231 232 len = 2; 233 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 234 i *= 10; 235 i += *buf - '0'; 236 len--; 237 } 238 239 if (c == 'M') { 240 if (i > 59) 241 return 0; 242 tm->tm_min = i; 243 } else { 244 if (i > 60) 245 return 0; 246 tm->tm_sec = i; 247 } 248 249 if (*buf != 0 && isspace((unsigned char)*buf)) 250 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 251 ptr++; 252 break; 253 254 case 'H': 255 case 'I': 256 case 'k': 257 case 'l': 258 /* 259 * Of these, %l is the only specifier explicitly 260 * documented as not being zero-padded. However, 261 * there is no harm in allowing zero-padding. 262 * 263 * XXX The %l specifier may gobble one too many 264 * digits if used incorrectly. 265 */ 266 if (!isdigit((unsigned char)*buf)) 267 return 0; 268 269 len = 2; 270 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 271 i *= 10; 272 i += *buf - '0'; 273 len--; 274 } 275 if (c == 'H' || c == 'k') { 276 if (i > 23) 277 return 0; 278 } else if (i > 12) 279 return 0; 280 281 tm->tm_hour = i; 282 283 if (*buf != 0 && isspace((unsigned char)*buf)) 284 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 285 ptr++; 286 break; 287 288 case 'p': 289 /* 290 * XXX This is bogus if parsed before hour-related 291 * specifiers. 292 */ 293 len = strlen(tptr->am); 294 if (strncasecmp(buf, tptr->am, len) == 0) { 295 if (tm->tm_hour > 12) 296 return 0; 297 if (tm->tm_hour == 12) 298 tm->tm_hour = 0; 299 buf += len; 300 break; 301 } 302 303 len = strlen(tptr->pm); 304 if (strncasecmp(buf, tptr->pm, len) == 0) { 305 if (tm->tm_hour > 12) 306 return 0; 307 if (tm->tm_hour != 12) 308 tm->tm_hour += 12; 309 buf += len; 310 break; 311 } 312 313 return 0; 314 315 case 'A': 316 case 'a': 317 for (i = 0; i < asizeof(tptr->weekday); i++) { 318 len = strlen(tptr->weekday[i]); 319 if (strncasecmp(buf, tptr->weekday[i], 320 len) == 0) 321 break; 322 len = strlen(tptr->wday[i]); 323 if (strncasecmp(buf, tptr->wday[i], 324 len) == 0) 325 break; 326 } 327 if (i == asizeof(tptr->weekday)) 328 return 0; 329 330 tm->tm_wday = i; 331 buf += len; 332 break; 333 334 case 'U': 335 case 'W': 336 /* 337 * XXX This is bogus, as we can not assume any valid 338 * information present in the tm structure at this 339 * point to calculate a real value, so just check the 340 * range for now. 341 */ 342 if (!isdigit((unsigned char)*buf)) 343 return 0; 344 345 len = 2; 346 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 347 i *= 10; 348 i += *buf - '0'; 349 len--; 350 } 351 if (i > 53) 352 return 0; 353 354 if (*buf != 0 && isspace((unsigned char)*buf)) 355 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 356 ptr++; 357 break; 358 359 case 'w': 360 if (!isdigit((unsigned char)*buf)) 361 return 0; 362 363 i = *buf - '0'; 364 if (i > 6) 365 return 0; 366 367 tm->tm_wday = i; 368 369 if (*buf != 0 && isspace((unsigned char)*buf)) 370 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 371 ptr++; 372 break; 373 374 case 'd': 375 case 'e': 376 /* 377 * The %e specifier is explicitly documented as not 378 * being zero-padded but there is no harm in allowing 379 * such padding. 380 * 381 * XXX The %e specifier may gobble one too many 382 * digits if used incorrectly. 383 */ 384 if (!isdigit((unsigned char)*buf)) 385 return 0; 386 387 len = 2; 388 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 389 i *= 10; 390 i += *buf - '0'; 391 len--; 392 } 393 if (i > 31) 394 return 0; 395 396 tm->tm_mday = i; 397 398 if (*buf != 0 && isspace((unsigned char)*buf)) 399 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 400 ptr++; 401 break; 402 403 case 'B': 404 case 'b': 405 case 'h': 406 for (i = 0; i < asizeof(tptr->month); i++) { 407 if (Oalternative) { 408 if (c == 'B') { 409 len = strlen(tptr->alt_month[i]); 410 if (strncasecmp(buf, 411 tptr->alt_month[i], 412 len) == 0) 413 break; 414 } 415 } else { 416 len = strlen(tptr->month[i]); 417 if (strncasecmp(buf, tptr->month[i], 418 len) == 0) 419 break; 420 len = strlen(tptr->mon[i]); 421 if (strncasecmp(buf, tptr->mon[i], 422 len) == 0) 423 break; 424 } 425 } 426 if (i == asizeof(tptr->month)) 427 return 0; 428 429 tm->tm_mon = i; 430 buf += len; 431 break; 432 433 case 'm': 434 if (!isdigit((unsigned char)*buf)) 435 return 0; 436 437 len = 2; 438 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 439 i *= 10; 440 i += *buf - '0'; 441 len--; 442 } 443 if (i < 1 || i > 12) 444 return 0; 445 446 tm->tm_mon = i - 1; 447 448 if (*buf != 0 && isspace((unsigned char)*buf)) 449 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 450 ptr++; 451 break; 452 453 case 's': 454 { 455 char *cp; 456 time_t t; 457 458 t = strtol(buf, &cp, 10); 459 if (t == LONG_MAX) 460 return 0; 461 buf = cp; 462 gmtime_r(&t, tm); 463 got_GMT = 1; 464 } 465 break; 466 467 case 'Y': 468 case 'y': 469 if (*buf == 0 || isspace((unsigned char)*buf)) 470 break; 471 472 if (!isdigit((unsigned char)*buf)) 473 return 0; 474 475 len = (c == 'Y') ? 4 : 2; 476 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 477 i *= 10; 478 i += *buf - '0'; 479 len--; 480 } 481 if (c == 'Y') 482 i -= 1900; 483 if (c == 'y' && i < 69) 484 i += 100; 485 if (i < 0) 486 return 0; 487 488 tm->tm_year = i; 489 490 if (*buf != 0 && isspace((unsigned char)*buf)) 491 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 492 ptr++; 493 break; 494 495 case 'Z': 496 { 497 const char *cp; 498 char *zonestr; 499 500 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/} 501 if (cp - buf) { 502 zonestr = alloca(cp - buf + 1); 503 strncpy(zonestr, buf, cp - buf); 504 zonestr[cp - buf] = '\0'; 505 tzset(); 506 if (0 == strcmp(zonestr, "GMT")) { 507 got_GMT = 1; 508 } else if (0 == strcmp(zonestr, tzname[0])) { 509 tm->tm_isdst = 0; 510 } else if (0 == strcmp(zonestr, tzname[1])) { 511 tm->tm_isdst = 1; 512 } else { 513 return 0; 514 } 515 buf += cp - buf; 516 } 517 } 518 break; 519 } 520 } 521 return (char *)buf; 522 } 523 524 525 char * 526 strptime(const char *buf, const char *fmt, struct tm *tm) 527 { 528 char *ret; 529 530 #ifdef _THREAD_SAFE 531 pthread_mutex_lock(&gotgmt_mutex); 532 #endif 533 534 got_GMT = 0; 535 ret = _strptime(buf, fmt, tm); 536 if (ret && got_GMT) { 537 time_t t = timegm(tm); 538 localtime_r(&t, tm); 539 got_GMT = 0; 540 } 541 542 #ifdef _THREAD_SAFE 543 pthread_mutex_unlock(&gotgmt_mutex); 544 #endif 545 546 return ret; 547 } 548