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 * @(#) Copyright (c) 1994 Powerdog Industries. All rights reserved. 23 * @(#)strptime.c 0.1 (Powerdog) 94/03/27 24 * $FreeBSD: src/lib/libc/stdtime/strptime.c,v 1.17.2.3 2002/03/12 17:24:54 phantom Exp $ 25 * $DragonFly: src/lib/libc/stdtime/strptime.c,v 1.3 2005/01/31 22:29:44 dillon Exp $ 26 */ 27 /* 28 * Copyright (c) 1994 Powerdog Industries. All rights reserved. 29 * 30 * Redistribution and use in source and binary forms, with or without 31 * modification, are permitted provided that the following conditions 32 * are met: 33 * 1. Redistributions of source code must retain the above copyright 34 * notice, this list of conditions and the following disclaimer. 35 * 2. Redistributions in binary form must reproduce the above copyright 36 * notice, this list of conditions and the following disclaimer 37 * in the documentation and/or other materials provided with the 38 * distribution. 39 * 3. All advertising materials mentioning features or use of this 40 * software must display the following acknowledgement: 41 * This product includes software developed by Powerdog Industries. 42 * 4. The name of Powerdog Industries may not be used to endorse or 43 * promote products derived from this software without specific prior 44 * written permission. 45 * 46 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY 47 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 49 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE 50 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 51 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 52 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 53 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 54 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 55 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 56 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 57 */ 58 59 #include "namespace.h" 60 #include <time.h> 61 #include <ctype.h> 62 #include <limits.h> 63 #include <stdlib.h> 64 #include <string.h> 65 #include <pthread.h> 66 #include "un-namespace.h" 67 #include "libc_private.h" 68 #include "timelocal.h" 69 70 static char * _strptime(const char *, const char *, struct tm *); 71 72 static pthread_mutex_t gotgmt_mutex = PTHREAD_MUTEX_INITIALIZER; 73 static int got_GMT; 74 75 #define asizeof(a) (sizeof (a) / sizeof ((a)[0])) 76 77 static char * 78 _strptime(const char *buf, const char *fmt, struct tm *tm) 79 { 80 char c; 81 const char *ptr; 82 int i, 83 len; 84 int Ealternative, Oalternative; 85 struct lc_time_T *tptr = __get_current_time_locale(); 86 87 ptr = fmt; 88 while (*ptr != 0) { 89 if (*buf == 0) 90 break; 91 92 c = *ptr++; 93 94 if (c != '%') { 95 if (isspace((unsigned char)c)) 96 while (*buf != 0 && isspace((unsigned char)*buf)) 97 buf++; 98 else if (c != *buf++) 99 return 0; 100 continue; 101 } 102 103 Ealternative = 0; 104 Oalternative = 0; 105 label: 106 c = *ptr++; 107 switch (c) { 108 case 0: 109 case '%': 110 if (*buf++ != '%') 111 return 0; 112 break; 113 114 case '+': 115 buf = _strptime(buf, tptr->date_fmt, tm); 116 if (buf == 0) 117 return 0; 118 break; 119 120 case 'C': 121 if (!isdigit((unsigned char)*buf)) 122 return 0; 123 124 /* XXX This will break for 3-digit centuries. */ 125 len = 2; 126 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 127 i *= 10; 128 i += *buf - '0'; 129 len--; 130 } 131 if (i < 19) 132 return 0; 133 134 tm->tm_year = i * 100 - 1900; 135 break; 136 137 case 'c': 138 buf = _strptime(buf, tptr->c_fmt, tm); 139 if (buf == 0) 140 return 0; 141 break; 142 143 case 'D': 144 buf = _strptime(buf, "%m/%d/%y", tm); 145 if (buf == 0) 146 return 0; 147 break; 148 149 case 'E': 150 if (Ealternative || Oalternative) 151 break; 152 Ealternative++; 153 goto label; 154 155 case 'O': 156 if (Ealternative || Oalternative) 157 break; 158 Oalternative++; 159 goto label; 160 161 case 'F': 162 buf = _strptime(buf, "%Y-%m-%d", tm); 163 if (buf == 0) 164 return 0; 165 break; 166 167 case 'R': 168 buf = _strptime(buf, "%H:%M", tm); 169 if (buf == 0) 170 return 0; 171 break; 172 173 case 'r': 174 buf = _strptime(buf, tptr->ampm_fmt, tm); 175 if (buf == 0) 176 return 0; 177 break; 178 179 case 'T': 180 buf = _strptime(buf, "%H:%M:%S", tm); 181 if (buf == 0) 182 return 0; 183 break; 184 185 case 'X': 186 buf = _strptime(buf, tptr->X_fmt, tm); 187 if (buf == 0) 188 return 0; 189 break; 190 191 case 'x': 192 buf = _strptime(buf, tptr->x_fmt, tm); 193 if (buf == 0) 194 return 0; 195 break; 196 197 case 'j': 198 if (!isdigit((unsigned char)*buf)) 199 return 0; 200 201 len = 3; 202 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 203 i *= 10; 204 i += *buf - '0'; 205 len--; 206 } 207 if (i < 1 || i > 366) 208 return 0; 209 210 tm->tm_yday = i - 1; 211 break; 212 213 case 'M': 214 case 'S': 215 if (*buf == 0 || isspace((unsigned char)*buf)) 216 break; 217 218 if (!isdigit((unsigned char)*buf)) 219 return 0; 220 221 len = 2; 222 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 223 i *= 10; 224 i += *buf - '0'; 225 len--; 226 } 227 228 if (c == 'M') { 229 if (i > 59) 230 return 0; 231 tm->tm_min = i; 232 } else { 233 if (i > 60) 234 return 0; 235 tm->tm_sec = i; 236 } 237 238 if (*buf != 0 && isspace((unsigned char)*buf)) 239 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 240 ptr++; 241 break; 242 243 case 'H': 244 case 'I': 245 case 'k': 246 case 'l': 247 /* 248 * Of these, %l is the only specifier explicitly 249 * documented as not being zero-padded. However, 250 * there is no harm in allowing zero-padding. 251 * 252 * XXX The %l specifier may gobble one too many 253 * digits if used incorrectly. 254 */ 255 if (!isdigit((unsigned char)*buf)) 256 return 0; 257 258 len = 2; 259 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 260 i *= 10; 261 i += *buf - '0'; 262 len--; 263 } 264 if (c == 'H' || c == 'k') { 265 if (i > 23) 266 return 0; 267 } else if (i > 12) 268 return 0; 269 270 tm->tm_hour = i; 271 272 if (*buf != 0 && isspace((unsigned char)*buf)) 273 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 274 ptr++; 275 break; 276 277 case 'p': 278 /* 279 * XXX This is bogus if parsed before hour-related 280 * specifiers. 281 */ 282 len = strlen(tptr->am); 283 if (strncasecmp(buf, tptr->am, len) == 0) { 284 if (tm->tm_hour > 12) 285 return 0; 286 if (tm->tm_hour == 12) 287 tm->tm_hour = 0; 288 buf += len; 289 break; 290 } 291 292 len = strlen(tptr->pm); 293 if (strncasecmp(buf, tptr->pm, len) == 0) { 294 if (tm->tm_hour > 12) 295 return 0; 296 if (tm->tm_hour != 12) 297 tm->tm_hour += 12; 298 buf += len; 299 break; 300 } 301 302 return 0; 303 304 case 'A': 305 case 'a': 306 for (i = 0; i < asizeof(tptr->weekday); i++) { 307 len = strlen(tptr->weekday[i]); 308 if (strncasecmp(buf, tptr->weekday[i], 309 len) == 0) 310 break; 311 len = strlen(tptr->wday[i]); 312 if (strncasecmp(buf, tptr->wday[i], 313 len) == 0) 314 break; 315 } 316 if (i == asizeof(tptr->weekday)) 317 return 0; 318 319 tm->tm_wday = i; 320 buf += len; 321 break; 322 323 case 'U': 324 case 'W': 325 /* 326 * XXX This is bogus, as we can not assume any valid 327 * information present in the tm structure at this 328 * point to calculate a real value, so just check the 329 * range for now. 330 */ 331 if (!isdigit((unsigned char)*buf)) 332 return 0; 333 334 len = 2; 335 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 336 i *= 10; 337 i += *buf - '0'; 338 len--; 339 } 340 if (i > 53) 341 return 0; 342 343 if (*buf != 0 && isspace((unsigned char)*buf)) 344 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 345 ptr++; 346 break; 347 348 case 'w': 349 if (!isdigit((unsigned char)*buf)) 350 return 0; 351 352 i = *buf - '0'; 353 if (i > 6) 354 return 0; 355 356 tm->tm_wday = i; 357 358 if (*buf != 0 && isspace((unsigned char)*buf)) 359 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 360 ptr++; 361 break; 362 363 case 'd': 364 case 'e': 365 /* 366 * The %e specifier is explicitly documented as not 367 * being zero-padded but there is no harm in allowing 368 * such padding. 369 * 370 * XXX The %e specifier may gobble one too many 371 * digits if used incorrectly. 372 */ 373 if (!isdigit((unsigned char)*buf)) 374 return 0; 375 376 len = 2; 377 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 378 i *= 10; 379 i += *buf - '0'; 380 len--; 381 } 382 if (i > 31) 383 return 0; 384 385 tm->tm_mday = i; 386 387 if (*buf != 0 && isspace((unsigned char)*buf)) 388 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 389 ptr++; 390 break; 391 392 case 'B': 393 case 'b': 394 case 'h': 395 for (i = 0; i < asizeof(tptr->month); i++) { 396 if (Oalternative) { 397 if (c == 'B') { 398 len = strlen(tptr->alt_month[i]); 399 if (strncasecmp(buf, 400 tptr->alt_month[i], 401 len) == 0) 402 break; 403 } 404 } else { 405 len = strlen(tptr->month[i]); 406 if (strncasecmp(buf, tptr->month[i], 407 len) == 0) 408 break; 409 len = strlen(tptr->mon[i]); 410 if (strncasecmp(buf, tptr->mon[i], 411 len) == 0) 412 break; 413 } 414 } 415 if (i == asizeof(tptr->month)) 416 return 0; 417 418 tm->tm_mon = i; 419 buf += len; 420 break; 421 422 case 'm': 423 if (!isdigit((unsigned char)*buf)) 424 return 0; 425 426 len = 2; 427 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 428 i *= 10; 429 i += *buf - '0'; 430 len--; 431 } 432 if (i < 1 || i > 12) 433 return 0; 434 435 tm->tm_mon = i - 1; 436 437 if (*buf != 0 && isspace((unsigned char)*buf)) 438 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 439 ptr++; 440 break; 441 442 case 's': 443 { 444 char *cp; 445 time_t t; 446 447 t = strtol(buf, &cp, 10); 448 if (t == LONG_MAX) 449 return 0; 450 buf = cp; 451 gmtime_r(&t, tm); 452 got_GMT = 1; 453 } 454 break; 455 456 case 'Y': 457 case 'y': 458 if (*buf == 0 || isspace((unsigned char)*buf)) 459 break; 460 461 if (!isdigit((unsigned char)*buf)) 462 return 0; 463 464 len = (c == 'Y') ? 4 : 2; 465 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 466 i *= 10; 467 i += *buf - '0'; 468 len--; 469 } 470 if (c == 'Y') 471 i -= 1900; 472 if (c == 'y' && i < 69) 473 i += 100; 474 if (i < 0) 475 return 0; 476 477 tm->tm_year = i; 478 479 if (*buf != 0 && isspace((unsigned char)*buf)) 480 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 481 ptr++; 482 break; 483 484 case 'Z': 485 { 486 const char *cp; 487 char *zonestr; 488 489 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/} 490 if (cp - buf) { 491 zonestr = alloca(cp - buf + 1); 492 strncpy(zonestr, buf, cp - buf); 493 zonestr[cp - buf] = '\0'; 494 tzset(); 495 if (0 == strcmp(zonestr, "GMT")) { 496 got_GMT = 1; 497 } else if (0 == strcmp(zonestr, tzname[0])) { 498 tm->tm_isdst = 0; 499 } else if (0 == strcmp(zonestr, tzname[1])) { 500 tm->tm_isdst = 1; 501 } else { 502 return 0; 503 } 504 buf += cp - buf; 505 } 506 } 507 break; 508 } 509 } 510 return (char *)buf; 511 } 512 513 514 char * 515 strptime(const char *buf, const char *fmt, struct tm *tm) 516 { 517 char *ret; 518 519 520 if (__isthreaded) 521 _pthread_mutex_lock(&gotgmt_mutex); 522 got_GMT = 0; 523 ret = _strptime(buf, fmt, tm); 524 if (ret && got_GMT) { 525 time_t t = timegm(tm); 526 localtime_r(&t, tm); 527 got_GMT = 0; 528 } 529 530 if (__isthreaded) 531 _pthread_mutex_unlock(&gotgmt_mutex); 532 return ret; 533 } 534