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