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