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