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