strptime.c revision 74412
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 55static const char rcsid[] = 56 "$FreeBSD: head/lib/libc/stdtime/strptime.c 74412 2001-03-18 11:58:15Z ache $"; 57#endif 58 59#ifndef lint 60#ifndef NOID 61static char copyright[] = 62"@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved."; 63static 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 76static char * _strptime(const char *, const char *, struct tm *); 77 78static pthread_mutex_t gotgmt_mutex = PTHREAD_MUTEX_INITIALIZER; 79static int got_GMT; 80 81#define asizeof(a) (sizeof (a) / sizeof ((a)[0])) 82 83static 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; 111label: 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 if (!Ealternative) 169 buf = _strptime(buf, "%Y-%m-%d", tm); 170 else 171 buf = _strptime(buf, 172 *(tptr->md_order) == 'd' ? 173 "%e %B" : "%B %e", tm); 174 if (buf == 0) 175 return 0; 176 break; 177 178 case 'f': 179 if (!Ealternative) 180 break; 181 buf = _strptime(buf, 182 *(tptr->md_order) == 'd' ? "%e %b" : "%b %e", 183 tm); 184 if (buf == 0) 185 return 0; 186 break; 187 188 case 'R': 189 buf = _strptime(buf, "%H:%M", tm); 190 if (buf == 0) 191 return 0; 192 break; 193 194 case 'r': 195 buf = _strptime(buf, tptr->ampm_fmt, tm); 196 if (buf == 0) 197 return 0; 198 break; 199 200 case 'T': 201 buf = _strptime(buf, "%H:%M:%S", tm); 202 if (buf == 0) 203 return 0; 204 break; 205 206 case 'X': 207 buf = _strptime(buf, tptr->X_fmt, tm); 208 if (buf == 0) 209 return 0; 210 break; 211 212 case 'x': 213 buf = _strptime(buf, tptr->x_fmt, tm); 214 if (buf == 0) 215 return 0; 216 break; 217 218 case 'j': 219 if (!isdigit((unsigned char)*buf)) 220 return 0; 221 222 len = 3; 223 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 224 i *= 10; 225 i += *buf - '0'; 226 len--; 227 } 228 if (i < 1 || i > 366) 229 return 0; 230 231 tm->tm_yday = i - 1; 232 break; 233 234 case 'M': 235 case 'S': 236 if (*buf == 0 || isspace((unsigned char)*buf)) 237 break; 238 239 if (!isdigit((unsigned char)*buf)) 240 return 0; 241 242 len = 2; 243 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 244 i *= 10; 245 i += *buf - '0'; 246 len--; 247 } 248 249 if (c == 'M') { 250 if (i > 59) 251 return 0; 252 tm->tm_min = i; 253 } else { 254 if (i > 60) 255 return 0; 256 tm->tm_sec = i; 257 } 258 259 if (*buf != 0 && isspace((unsigned char)*buf)) 260 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 261 ptr++; 262 break; 263 264 case 'H': 265 case 'I': 266 case 'k': 267 case 'l': 268 /* 269 * Of these, %l is the only specifier explicitly 270 * documented as not being zero-padded. However, 271 * there is no harm in allowing zero-padding. 272 * 273 * XXX The %l specifier may gobble one too many 274 * digits if used incorrectly. 275 */ 276 if (!isdigit((unsigned char)*buf)) 277 return 0; 278 279 len = 2; 280 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 281 i *= 10; 282 i += *buf - '0'; 283 len--; 284 } 285 if (c == 'H' || c == 'k') { 286 if (i > 23) 287 return 0; 288 } else if (i > 12) 289 return 0; 290 291 tm->tm_hour = i; 292 293 if (*buf != 0 && isspace((unsigned char)*buf)) 294 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 295 ptr++; 296 break; 297 298 case 'p': 299 /* 300 * XXX This is bogus if parsed before hour-related 301 * specifiers. 302 */ 303 len = strlen(tptr->am); 304 if (strncasecmp(buf, tptr->am, len) == 0) { 305 if (tm->tm_hour > 12) 306 return 0; 307 if (tm->tm_hour == 12) 308 tm->tm_hour = 0; 309 buf += len; 310 break; 311 } 312 313 len = strlen(tptr->pm); 314 if (strncasecmp(buf, tptr->pm, len) == 0) { 315 if (tm->tm_hour > 12) 316 return 0; 317 if (tm->tm_hour != 12) 318 tm->tm_hour += 12; 319 buf += len; 320 break; 321 } 322 323 return 0; 324 325 case 'A': 326 case 'a': 327 for (i = 0; i < asizeof(tptr->weekday); i++) { 328 len = strlen(tptr->weekday[i]); 329 if (strncasecmp(buf, tptr->weekday[i], 330 len) == 0) 331 break; 332 len = strlen(tptr->wday[i]); 333 if (strncasecmp(buf, tptr->wday[i], 334 len) == 0) 335 break; 336 } 337 if (i == asizeof(tptr->weekday)) 338 return 0; 339 340 tm->tm_wday = i; 341 buf += len; 342 break; 343 344 case 'U': 345 case 'W': 346 /* 347 * XXX This is bogus, as we can not assume any valid 348 * information present in the tm structure at this 349 * point to calculate a real value, so just check the 350 * range for now. 351 */ 352 if (!isdigit((unsigned char)*buf)) 353 return 0; 354 355 len = 2; 356 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 357 i *= 10; 358 i += *buf - '0'; 359 len--; 360 } 361 if (i > 53) 362 return 0; 363 364 if (*buf != 0 && isspace((unsigned char)*buf)) 365 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 366 ptr++; 367 break; 368 369 case 'w': 370 if (!isdigit((unsigned char)*buf)) 371 return 0; 372 373 i = *buf - '0'; 374 if (i > 6) 375 return 0; 376 377 tm->tm_wday = i; 378 379 if (*buf != 0 && isspace((unsigned char)*buf)) 380 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 381 ptr++; 382 break; 383 384 case 'd': 385 case 'e': 386 /* 387 * The %e specifier is explicitly documented as not 388 * being zero-padded but there is no harm in allowing 389 * such padding. 390 * 391 * XXX The %e specifier may gobble one too many 392 * digits if used incorrectly. 393 */ 394 if (!isdigit((unsigned char)*buf)) 395 return 0; 396 397 len = 2; 398 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 399 i *= 10; 400 i += *buf - '0'; 401 len--; 402 } 403 if (i > 31) 404 return 0; 405 406 tm->tm_mday = i; 407 408 if (*buf != 0 && isspace((unsigned char)*buf)) 409 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 410 ptr++; 411 break; 412 413 case 'B': 414 case 'b': 415 case 'h': 416 for (i = 0; i < asizeof(tptr->month); i++) { 417 if (Oalternative) { 418 if (c == 'B') { 419 len = strlen(tptr->alt_month[i]); 420 if (strncasecmp(buf, 421 tptr->alt_month[i], 422 len) == 0) 423 break; 424 } 425 } else { 426 len = strlen(tptr->month[i]); 427 if (strncasecmp(buf, tptr->month[i], 428 len) == 0) 429 break; 430 len = strlen(tptr->mon[i]); 431 if (strncasecmp(buf, tptr->mon[i], 432 len) == 0) 433 break; 434 } 435 } 436 if (i == asizeof(tptr->month)) 437 return 0; 438 439 tm->tm_mon = i; 440 buf += len; 441 break; 442 443 case 'm': 444 if (!isdigit((unsigned char)*buf)) 445 return 0; 446 447 len = 2; 448 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 449 i *= 10; 450 i += *buf - '0'; 451 len--; 452 } 453 if (i < 1 || i > 12) 454 return 0; 455 456 tm->tm_mon = i - 1; 457 458 if (*buf != 0 && isspace((unsigned char)*buf)) 459 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 460 ptr++; 461 break; 462 463 case 'Y': 464 case 'y': 465 if (*buf == 0 || isspace((unsigned char)*buf)) 466 break; 467 468 if (!isdigit((unsigned char)*buf)) 469 return 0; 470 471 len = (c == 'Y') ? 4 : 2; 472 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 473 i *= 10; 474 i += *buf - '0'; 475 len--; 476 } 477 if (c == 'Y') 478 i -= 1900; 479 if (c == 'y' && i < 69) 480 i += 100; 481 if (i < 0) 482 return 0; 483 484 tm->tm_year = i; 485 486 if (*buf != 0 && isspace((unsigned char)*buf)) 487 while (*ptr != 0 && !isspace((unsigned char)*ptr)) 488 ptr++; 489 break; 490 491 case 'Z': 492 { 493 const char *cp; 494 char *zonestr; 495 496 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/} 497 if (cp - buf) { 498 zonestr = alloca(cp - buf + 1); 499 strncpy(zonestr, buf, cp - buf); 500 zonestr[cp - buf] = '\0'; 501 tzset(); 502 if (0 == strcmp(zonestr, "GMT")) { 503 got_GMT = 1; 504 } else if (0 == strcmp(zonestr, tzname[0])) { 505 tm->tm_isdst = 0; 506 } else if (0 == strcmp(zonestr, tzname[1])) { 507 tm->tm_isdst = 1; 508 } else { 509 return 0; 510 } 511 buf += cp - buf; 512 } 513 } 514 break; 515 } 516 } 517 return (char *)buf; 518} 519 520 521char * 522strptime(const char *buf, const char *fmt, struct tm *tm) 523{ 524 char *ret; 525 526 if (__isthreaded) 527 _pthread_mutex_lock(&gotgmt_mutex); 528 529 got_GMT = 0; 530 ret = _strptime(buf, fmt, tm); 531 if (ret && got_GMT) { 532 time_t t = timegm(tm); 533 localtime_r(&t, tm); 534 got_GMT = 0; 535 } 536 537 if (__isthreaded) 538 _pthread_mutex_unlock(&gotgmt_mutex); 539 540 return ret; 541} 542