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 buf = _strptime(buf, Locale->c_fmt, tm); 147 if (buf == 0) 148 return 0; 149 break; 150 151 case 'D': 152 buf = _strptime(buf, "%m/%d/%y", tm); 153 if (buf == 0) 154 return 0; 155 break; 156 157 case 'E': 158 if (Ealternative || Oalternative) 159 break; 160 Ealternative++; 161 goto label; 162 163 case 'O': 164 if (Ealternative || Oalternative) 165 break; 166 Oalternative++; 167 goto label; 168 169 case 'F': 170 case 'f': 171 if (!Ealternative) 172 break; 173 buf = _strptime(buf, (c == 'f') ? Locale->Ef_fmt : Locale->EF_fmt, 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, "%I:%M:%S %p", 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, Locale->X_fmt, tm); 198 if (buf == 0) 199 return 0; 200 break; 201 202 case 'x': 203 buf = _strptime(buf, Locale->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(Locale->am); 294 if (strncasecmp(buf, Locale->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(Locale->pm); 304 if (strncasecmp(buf, Locale->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(Locale->weekday); i++) { 318 if (c == 'A') { 319 len = strlen(Locale->weekday[i]); 320 if (strncasecmp(buf, 321 Locale->weekday[i], 322 len) == 0) 323 break; 324 } else { 325 len = strlen(Locale->wday[i]); 326 if (strncasecmp(buf, 327 Locale->wday[i], 328 len) == 0) 329 break; 330 } 331 } 332 if (i == asizeof(Locale->weekday)) 333 return 0; 334 335 tm->tm_wday = i; 336 buf += len; 337 break; 338 339 case 'U': 340 case 'W': 341 /* 342 * XXX This is bogus, as we can not assume any valid 343 * information present in the tm structure at this 344 * point to calculate a real value, so just check the 345 * range for now. 346 */ 347 if (!isdigit((unsigned char)*buf)) 348 return 0; 349 350 len = 2; 351 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 352 i *= 10; 353 i += *buf - '0'; 354 len--; 355 } 356 if (i > 53) 357 return 0; 358 359 if (*buf != 0 && isspace((unsigned char)*buf)) 360 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 361 ptr++; 362 break; 363 364 case 'w': 365 if (!isdigit((unsigned char)*buf)) 366 return 0; 367 368 i = *buf - '0'; 369 if (i > 6) 370 return 0; 371 372 tm->tm_wday = i; 373 374 if (*buf != 0 && isspace((unsigned char)*buf)) 375 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 376 ptr++; 377 break; 378 379 case 'd': 380 case 'e': 381 /* 382 * The %e specifier is explicitly documented as not 383 * being zero-padded but there is no harm in allowing 384 * such padding. 385 * 386 * XXX The %e specifier may gobble one too many 387 * digits if used incorrectly. 388 */ 389 if (!isdigit((unsigned char)*buf)) 390 return 0; 391 392 len = 2; 393 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 394 i *= 10; 395 i += *buf - '0'; 396 len--; 397 } 398 if (i > 31) 399 return 0; 400 401 tm->tm_mday = i; 402 403 if (*buf != 0 && isspace((unsigned char)*buf)) 404 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 405 ptr++; 406 break; 407 408 case 'B': 409 case 'b': 410 case 'h': 411 for (i = 0; i < asizeof(Locale->month); i++) { 412 if (Oalternative) { 413 if (c == 'B') { 414 len = strlen(Locale->alt_month[i]); 415 if (strncasecmp(buf, 416 Locale->alt_month[i], 417 len) == 0) 418 break; 419 } 420 } else { 421 if (c == 'B') { 422 len = strlen(Locale->month[i]); 423 if (strncasecmp(buf, 424 Locale->month[i], 425 len) == 0) 426 break; 427 } else { 428 len = strlen(Locale->mon[i]); 429 if (strncasecmp(buf, 430 Locale->mon[i], 431 len) == 0) 432 break; 433 } 434 } 435 } 436 if (i == asizeof(Locale->month)) 437 return 0; 438 439 tm->tm_mon = i; 440 buf += len; 441 break; 442 443 case 'm': 444 if (!isdigit((unsigned char)*buf)) 445 return 0; 446 447 len = 2; 448 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 449 i *= 10; 450 i += *buf - '0'; 451 len--; 452 } 453 if (i < 1 || i > 12) 454 return 0; 455 456 tm->tm_mon = i - 1; 457 458 if (*buf != 0 && isspace((unsigned char)*buf)) 459 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 460 ptr++; 461 break; 462 463 case 'Y': 464 case 'y': 465 if (*buf == 0 || isspace((unsigned char)*buf)) 466 break; 467 468 if (!isdigit((unsigned char)*buf)) 469 return 0; 470 471 len = (c == 'Y') ? 4 : 2; 472 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 473 i *= 10; 474 i += *buf - '0'; 475 len--; 476 } 477 if (c == 'Y') 478 i -= 1900; 479 if (c == 'y' && i < 69) 480 i += 100; 481 if (i < 0) 482 return 0; 483 484 tm->tm_year = i; 485 486 if (*buf != 0 && isspace((unsigned char)*buf)) 487 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 488 ptr++; 489 break; 490 491 case 'Z': 492 { 493 const char *cp; 494 char *zonestr; 495 496 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/} 497 if (cp - buf) { 498 zonestr = alloca(cp - buf + 1); 499 strncpy(zonestr, buf, cp - buf); 500 zonestr[cp - buf] = '\0'; 501 tzset(); 502 if (0 == strcmp(zonestr, "GMT")) { 503 got_GMT = 1; 504 } else if (0 == strcmp(zonestr, tzname[0])) { 505 tm->tm_isdst = 0; 506 } else if (0 == strcmp(zonestr, tzname[1])) { 507 tm->tm_isdst = 1; 508 } else { 509 return 0; 510 } 511 buf += cp - buf; 512 } 513 } 514 break; 515 } 516 } 517 return (char *)buf; 518 } 519 520 521 char * 522 strptime(const char *buf, const char *fmt, struct tm *tm) 523 { 524 char *ret; 525 526 #ifdef _THREAD_SAFE 527 pthread_mutex_lock(&gotgmt_mutex); 528 #endif 529 530 got_GMT = 0; 531 ret = _strptime(buf, fmt, tm); 532 if (ret && got_GMT) { 533 time_t t = timegm(tm); 534 localtime_r(&t, tm); 535 got_GMT = 0; 536 } 537 538 #ifdef _THREAD_SAFE 539 pthread_mutex_unlock(&gotgmt_mutex); 540 #endif 541 542 return ret; 543 } 544