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