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 "namespace.h" 68 #include <time.h> 69 #include <ctype.h> 70 #include <limits.h> 71 #include <stdlib.h> 72 #include <string.h> 73 #include <pthread.h> 74 #include "un-namespace.h" 75 #include "libc_private.h" 76 #include "timelocal.h" 77 78 static char * _strptime(const char *, const char *, struct tm *); 79 80 static pthread_mutex_t gotgmt_mutex = PTHREAD_MUTEX_INITIALIZER; 81 static int got_GMT; 82 83 #define asizeof(a) (sizeof (a) / sizeof ((a)[0])) 84 85 static char * 86 _strptime(const char *buf, const char *fmt, struct tm *tm) 87 { 88 char c; 89 const char *ptr; 90 int i, 91 len; 92 int Ealternative, Oalternative; 93 struct lc_time_T *tptr = __get_current_time_locale(); 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, tptr->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, tptr->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 buf = _strptime(buf, "%Y-%m-%d", tm); 171 if (buf == 0) 172 return 0; 173 break; 174 175 case 'R': 176 buf = _strptime(buf, "%H:%M", tm); 177 if (buf == 0) 178 return 0; 179 break; 180 181 case 'r': 182 buf = _strptime(buf, tptr->ampm_fmt, tm); 183 if (buf == 0) 184 return 0; 185 break; 186 187 case 'T': 188 buf = _strptime(buf, "%H:%M:%S", tm); 189 if (buf == 0) 190 return 0; 191 break; 192 193 case 'X': 194 buf = _strptime(buf, tptr->X_fmt, tm); 195 if (buf == 0) 196 return 0; 197 break; 198 199 case 'x': 200 buf = _strptime(buf, tptr->x_fmt, tm); 201 if (buf == 0) 202 return 0; 203 break; 204 205 case 'j': 206 if (!isdigit((unsigned char)*buf)) 207 return 0; 208 209 len = 3; 210 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 211 i *= 10; 212 i += *buf - '0'; 213 len--; 214 } 215 if (i < 1 || i > 366) 216 return 0; 217 218 tm->tm_yday = i - 1; 219 break; 220 221 case 'M': 222 case 'S': 223 if (*buf == 0 || isspace((unsigned char)*buf)) 224 break; 225 226 if (!isdigit((unsigned char)*buf)) 227 return 0; 228 229 len = 2; 230 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 231 i *= 10; 232 i += *buf - '0'; 233 len--; 234 } 235 236 if (c == 'M') { 237 if (i > 59) 238 return 0; 239 tm->tm_min = i; 240 } else { 241 if (i > 60) 242 return 0; 243 tm->tm_sec = i; 244 } 245 246 if (*buf != 0 && isspace((unsigned char)*buf)) 247 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 248 ptr++; 249 break; 250 251 case 'H': 252 case 'I': 253 case 'k': 254 case 'l': 255 /* 256 * Of these, %l is the only specifier explicitly 257 * documented as not being zero-padded. However, 258 * there is no harm in allowing zero-padding. 259 * 260 * XXX The %l specifier may gobble one too many 261 * digits if used incorrectly. 262 */ 263 if (!isdigit((unsigned char)*buf)) 264 return 0; 265 266 len = 2; 267 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 268 i *= 10; 269 i += *buf - '0'; 270 len--; 271 } 272 if (c == 'H' || c == 'k') { 273 if (i > 23) 274 return 0; 275 } else if (i > 12) 276 return 0; 277 278 tm->tm_hour = i; 279 280 if (*buf != 0 && isspace((unsigned char)*buf)) 281 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 282 ptr++; 283 break; 284 285 case 'p': 286 /* 287 * XXX This is bogus if parsed before hour-related 288 * specifiers. 289 */ 290 len = strlen(tptr->am); 291 if (strncasecmp(buf, tptr->am, len) == 0) { 292 if (tm->tm_hour > 12) 293 return 0; 294 if (tm->tm_hour == 12) 295 tm->tm_hour = 0; 296 buf += len; 297 break; 298 } 299 300 len = strlen(tptr->pm); 301 if (strncasecmp(buf, tptr->pm, len) == 0) { 302 if (tm->tm_hour > 12) 303 return 0; 304 if (tm->tm_hour != 12) 305 tm->tm_hour += 12; 306 buf += len; 307 break; 308 } 309 310 return 0; 311 312 case 'A': 313 case 'a': 314 for (i = 0; i < asizeof(tptr->weekday); i++) { 315 len = strlen(tptr->weekday[i]); 316 if (strncasecmp(buf, tptr->weekday[i], 317 len) == 0) 318 break; 319 len = strlen(tptr->wday[i]); 320 if (strncasecmp(buf, tptr->wday[i], 321 len) == 0) 322 break; 323 } 324 if (i == asizeof(tptr->weekday)) 325 return 0; 326 327 tm->tm_wday = i; 328 buf += len; 329 break; 330 331 case 'U': 332 case 'W': 333 /* 334 * XXX This is bogus, as we can not assume any valid 335 * information present in the tm structure at this 336 * point to calculate a real value, so just check the 337 * range for now. 338 */ 339 if (!isdigit((unsigned char)*buf)) 340 return 0; 341 342 len = 2; 343 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 344 i *= 10; 345 i += *buf - '0'; 346 len--; 347 } 348 if (i > 53) 349 return 0; 350 351 if (*buf != 0 && isspace((unsigned char)*buf)) 352 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 353 ptr++; 354 break; 355 356 case 'w': 357 if (!isdigit((unsigned char)*buf)) 358 return 0; 359 360 i = *buf - '0'; 361 if (i > 6) 362 return 0; 363 364 tm->tm_wday = i; 365 366 if (*buf != 0 && isspace((unsigned char)*buf)) 367 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 368 ptr++; 369 break; 370 371 case 'd': 372 case 'e': 373 /* 374 * The %e specifier is explicitly documented as not 375 * being zero-padded but there is no harm in allowing 376 * such padding. 377 * 378 * XXX The %e specifier may gobble one too many 379 * digits if used incorrectly. 380 */ 381 if (!isdigit((unsigned char)*buf)) 382 return 0; 383 384 len = 2; 385 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); 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 && !isspace((unsigned char)*ptr)) 397 ptr++; 398 break; 399 400 case 'B': 401 case 'b': 402 case 'h': 403 for (i = 0; i < asizeof(tptr->month); i++) { 404 if (Oalternative) { 405 if (c == 'B') { 406 len = strlen(tptr->alt_month[i]); 407 if (strncasecmp(buf, 408 tptr->alt_month[i], 409 len) == 0) 410 break; 411 } 412 } else { 413 len = strlen(tptr->month[i]); 414 if (strncasecmp(buf, tptr->month[i], 415 len) == 0) 416 break; 417 len = strlen(tptr->mon[i]); 418 if (strncasecmp(buf, tptr->mon[i], 419 len) == 0) 420 break; 421 } 422 } 423 if (i == asizeof(tptr->month)) 424 return 0; 425 426 tm->tm_mon = i; 427 buf += len; 428 break; 429 430 case 'm': 431 if (!isdigit((unsigned char)*buf)) 432 return 0; 433 434 len = 2; 435 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); 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 && !isspace((unsigned char)*ptr)) 447 ptr++; 448 break; 449 450 case 's': 451 { 452 char *cp; 453 time_t t; 454 455 t = strtol(buf, &cp, 10); 456 if (t == LONG_MAX) 457 return 0; 458 buf = cp; 459 gmtime_r(&t, tm); 460 got_GMT = 1; 461 } 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 if (__isthreaded) 528 _pthread_mutex_lock(&gotgmt_mutex); 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 if (__isthreaded) 539 _pthread_mutex_unlock(&gotgmt_mutex); 540 541 return ret; 542 } 543