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