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