1/* Convert a string representation of time to a time value. 2 Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. 3 This file is part of the GNU C Library. 4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. 5 6 The GNU C Library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public License as 8 published by the Free Software Foundation; either version 3 of the 9 License, or (at your option) any later version. 10 11 The GNU C Library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Library General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with the GNU C Library; see the file COPYING.LIB. If not, 18 see <http://www.gnu.org/licenses/>. */ 19 20/* XXX This version of the implementation is not really complete. 21 Some of the fields cannot add information alone. But if seeing 22 some of them in the same format (such as year, week and weekday) 23 this is enough information for determining the date. */ 24 25#include "replace.h" 26#include "system/locale.h" 27#include "system/time.h" 28 29#ifndef __P 30# if defined (__GNUC__) || (defined (__STDC__) && __STDC__) 31# define __P(args) args 32# else 33# define __P(args) () 34# endif /* GCC. */ 35#endif /* Not __P. */ 36 37#if ! HAVE_LOCALTIME_R && ! defined localtime_r 38# ifdef _LIBC 39# define localtime_r __localtime_r 40# else 41/* Approximate localtime_r as best we can in its absence. */ 42# define localtime_r my_localtime_r 43static struct tm *localtime_r __P ((const time_t *, struct tm *)); 44static struct tm * 45localtime_r (t, tp) 46 const time_t *t; 47 struct tm *tp; 48{ 49 struct tm *l = localtime (t); 50 if (! l) 51 return 0; 52 *tp = *l; 53 return tp; 54} 55# endif /* ! _LIBC */ 56#endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */ 57 58 59#define match_char(ch1, ch2) if (ch1 != ch2) return NULL 60#if defined __GNUC__ && __GNUC__ >= 2 61# define match_string(cs1, s2) \ 62 ({ size_t len = strlen (cs1); \ 63 int result = strncasecmp ((cs1), (s2), len) == 0; \ 64 if (result) (s2) += len; \ 65 result; }) 66#else 67/* Oh come on. Get a reasonable compiler. */ 68# define match_string(cs1, s2) \ 69 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1)) 70#endif 71/* We intentionally do not use isdigit() for testing because this will 72 lead to problems with the wide character version. */ 73#define get_number(from, to, n) \ 74 do { \ 75 int __n = n; \ 76 val = 0; \ 77 while (*rp == ' ') \ 78 ++rp; \ 79 if (*rp < '0' || *rp > '9') \ 80 return NULL; \ 81 do { \ 82 val *= 10; \ 83 val += *rp++ - '0'; \ 84 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \ 85 if (val < from || val > to) \ 86 return NULL; \ 87 } while (0) 88#ifdef _NL_CURRENT 89# define get_alt_number(from, to, n) \ 90 ({ \ 91 __label__ do_normal; \ 92 if (*decided != raw) \ 93 { \ 94 const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS); \ 95 int __n = n; \ 96 int any = 0; \ 97 while (*rp == ' ') \ 98 ++rp; \ 99 val = 0; \ 100 do { \ 101 val *= 10; \ 102 while (*alts != '\0') \ 103 { \ 104 size_t len = strlen (alts); \ 105 if (strncasecmp (alts, rp, len) == 0) \ 106 break; \ 107 alts += len + 1; \ 108 ++val; \ 109 } \ 110 if (*alts == '\0') \ 111 { \ 112 if (*decided == not && ! any) \ 113 goto do_normal; \ 114 /* If we haven't read anything it's an error. */ \ 115 if (! any) \ 116 return NULL; \ 117 /* Correct the premature multiplication. */ \ 118 val /= 10; \ 119 break; \ 120 } \ 121 else \ 122 *decided = loc; \ 123 } while (--__n > 0 && val * 10 <= to); \ 124 if (val < from || val > to) \ 125 return NULL; \ 126 } \ 127 else \ 128 { \ 129 do_normal: \ 130 get_number (from, to, n); \ 131 } \ 132 0; \ 133 }) 134#else 135# define get_alt_number(from, to, n) \ 136 /* We don't have the alternate representation. */ \ 137 get_number(from, to, n) 138#endif 139#define recursive(new_fmt) \ 140 (*(new_fmt) != '\0' \ 141 && (rp = strptime_internal (rp, (new_fmt), tm, decided, era_cnt)) != NULL) 142 143 144#ifdef _LIBC 145/* This is defined in locale/C-time.c in the GNU libc. */ 146extern const struct locale_data _nl_C_LC_TIME; 147extern const unsigned short int __mon_yday[2][13]; 148 149# define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string) 150# define ab_weekday_name \ 151 (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string) 152# define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string) 153# define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string) 154# define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string) 155# define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string) 156# define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string) 157# define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string) 158# define HERE_T_FMT_AMPM \ 159 (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string) 160# define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string) 161 162# define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n) 163#else 164static char const weekday_name[][10] = 165 { 166 "Sunday", "Monday", "Tuesday", "Wednesday", 167 "Thursday", "Friday", "Saturday" 168 }; 169static char const ab_weekday_name[][4] = 170 { 171 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 172 }; 173static char const month_name[][10] = 174 { 175 "January", "February", "March", "April", "May", "June", 176 "July", "August", "September", "October", "November", "December" 177 }; 178static char const ab_month_name[][4] = 179 { 180 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 181 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 182 }; 183# define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y" 184# define HERE_D_FMT "%m/%d/%y" 185# define HERE_AM_STR "AM" 186# define HERE_PM_STR "PM" 187# define HERE_T_FMT_AMPM "%I:%M:%S %p" 188# define HERE_T_FMT "%H:%M:%S" 189 190static const unsigned short int __mon_yday[2][13] = 191 { 192 /* Normal years. */ 193 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 194 /* Leap years. */ 195 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } 196 }; 197#endif 198 199/* Status of lookup: do we use the locale data or the raw data? */ 200enum locale_status { not, loc, raw }; 201 202 203#ifndef __isleap 204/* Nonzero if YEAR is a leap year (every 4 years, 205 except every 100th isn't, and every 400th is). */ 206# define __isleap(year) \ 207 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) 208#endif 209 210/* Compute the day of the week. */ 211static void 212day_of_the_week (struct tm *tm) 213{ 214 /* We know that January 1st 1970 was a Thursday (= 4). Compute the 215 the difference between this data in the one on TM and so determine 216 the weekday. */ 217 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2); 218 int wday = (-473 219 + (365 * (tm->tm_year - 70)) 220 + (corr_year / 4) 221 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0) 222 + (((corr_year / 4) / 25) / 4) 223 + __mon_yday[0][tm->tm_mon] 224 + tm->tm_mday - 1); 225 tm->tm_wday = ((wday % 7) + 7) % 7; 226} 227 228/* Compute the day of the year. */ 229static void 230day_of_the_year (struct tm *tm) 231{ 232 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon] 233 + (tm->tm_mday - 1)); 234} 235 236static char * 237#ifdef _LIBC 238internal_function 239#endif 240strptime_internal __P ((const char *rp, const char *fmt, struct tm *tm, 241 enum locale_status *decided, int era_cnt)); 242 243static char * 244#ifdef _LIBC 245internal_function 246#endif 247strptime_internal (rp, fmt, tm, decided, era_cnt) 248 const char *rp; 249 const char *fmt; 250 struct tm *tm; 251 enum locale_status *decided; 252 int era_cnt; 253{ 254 const char *rp_backup; 255 int cnt; 256 size_t val; 257 int have_I, is_pm; 258 int century, want_century; 259 int want_era; 260 int have_wday, want_xday; 261 int have_yday; 262 int have_mon, have_mday; 263#ifdef _NL_CURRENT 264 size_t num_eras; 265#endif 266 struct era_entry *era; 267 268 have_I = is_pm = 0; 269 century = -1; 270 want_century = 0; 271 want_era = 0; 272 era = NULL; 273 274 have_wday = want_xday = have_yday = have_mon = have_mday = 0; 275 276 while (*fmt != '\0') 277 { 278 /* A white space in the format string matches 0 more or white 279 space in the input string. */ 280 if (isspace (*fmt)) 281 { 282 while (isspace (*rp)) 283 ++rp; 284 ++fmt; 285 continue; 286 } 287 288 /* Any character but `%' must be matched by the same character 289 in the iput string. */ 290 if (*fmt != '%') 291 { 292 match_char (*fmt++, *rp++); 293 continue; 294 } 295 296 ++fmt; 297#ifndef _NL_CURRENT 298 /* We need this for handling the `E' modifier. */ 299 start_over: 300#endif 301 302 /* Make back up of current processing pointer. */ 303 rp_backup = rp; 304 305 switch (*fmt++) 306 { 307 case '%': 308 /* Match the `%' character itself. */ 309 match_char ('%', *rp++); 310 break; 311 case 'a': 312 case 'A': 313 /* Match day of week. */ 314 for (cnt = 0; cnt < 7; ++cnt) 315 { 316#ifdef _NL_CURRENT 317 if (*decided !=raw) 318 { 319 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp)) 320 { 321 if (*decided == not 322 && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt), 323 weekday_name[cnt])) 324 *decided = loc; 325 break; 326 } 327 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp)) 328 { 329 if (*decided == not 330 && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), 331 ab_weekday_name[cnt])) 332 *decided = loc; 333 break; 334 } 335 } 336#endif 337 if (*decided != loc 338 && (match_string (weekday_name[cnt], rp) 339 || match_string (ab_weekday_name[cnt], rp))) 340 { 341 *decided = raw; 342 break; 343 } 344 } 345 if (cnt == 7) 346 /* Does not match a weekday name. */ 347 return NULL; 348 tm->tm_wday = cnt; 349 have_wday = 1; 350 break; 351 case 'b': 352 case 'B': 353 case 'h': 354 /* Match month name. */ 355 for (cnt = 0; cnt < 12; ++cnt) 356 { 357#ifdef _NL_CURRENT 358 if (*decided !=raw) 359 { 360 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp)) 361 { 362 if (*decided == not 363 && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt), 364 month_name[cnt])) 365 *decided = loc; 366 break; 367 } 368 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp)) 369 { 370 if (*decided == not 371 && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), 372 ab_month_name[cnt])) 373 *decided = loc; 374 break; 375 } 376 } 377#endif 378 if (match_string (month_name[cnt], rp) 379 || match_string (ab_month_name[cnt], rp)) 380 { 381 *decided = raw; 382 break; 383 } 384 } 385 if (cnt == 12) 386 /* Does not match a month name. */ 387 return NULL; 388 tm->tm_mon = cnt; 389 want_xday = 1; 390 break; 391 case 'c': 392 /* Match locale's date and time format. */ 393#ifdef _NL_CURRENT 394 if (*decided != raw) 395 { 396 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT))) 397 { 398 if (*decided == loc) 399 return NULL; 400 else 401 rp = rp_backup; 402 } 403 else 404 { 405 if (*decided == not && 406 strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT)) 407 *decided = loc; 408 want_xday = 1; 409 break; 410 } 411 *decided = raw; 412 } 413#endif 414 if (!recursive (HERE_D_T_FMT)) 415 return NULL; 416 want_xday = 1; 417 break; 418 case 'C': 419 /* Match century number. */ 420#ifdef _NL_CURRENT 421 match_century: 422#endif 423 get_number (0, 99, 2); 424 century = val; 425 want_xday = 1; 426 break; 427 case 'd': 428 case 'e': 429 /* Match day of month. */ 430 get_number (1, 31, 2); 431 tm->tm_mday = val; 432 have_mday = 1; 433 want_xday = 1; 434 break; 435 case 'F': 436 if (!recursive ("%Y-%m-%d")) 437 return NULL; 438 want_xday = 1; 439 break; 440 case 'x': 441#ifdef _NL_CURRENT 442 if (*decided != raw) 443 { 444 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT))) 445 { 446 if (*decided == loc) 447 return NULL; 448 else 449 rp = rp_backup; 450 } 451 else 452 { 453 if (*decided == not 454 && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT)) 455 *decided = loc; 456 want_xday = 1; 457 break; 458 } 459 *decided = raw; 460 } 461#endif 462 /* Fall through. */ 463 case 'D': 464 /* Match standard day format. */ 465 if (!recursive (HERE_D_FMT)) 466 return NULL; 467 want_xday = 1; 468 break; 469 case 'k': 470 case 'H': 471 /* Match hour in 24-hour clock. */ 472 get_number (0, 23, 2); 473 tm->tm_hour = val; 474 have_I = 0; 475 break; 476 case 'I': 477 /* Match hour in 12-hour clock. */ 478 get_number (1, 12, 2); 479 tm->tm_hour = val % 12; 480 have_I = 1; 481 break; 482 case 'j': 483 /* Match day number of year. */ 484 get_number (1, 366, 3); 485 tm->tm_yday = val - 1; 486 have_yday = 1; 487 break; 488 case 'm': 489 /* Match number of month. */ 490 get_number (1, 12, 2); 491 tm->tm_mon = val - 1; 492 have_mon = 1; 493 want_xday = 1; 494 break; 495 case 'M': 496 /* Match minute. */ 497 get_number (0, 59, 2); 498 tm->tm_min = val; 499 break; 500 case 'n': 501 case 't': 502 /* Match any white space. */ 503 while (isspace (*rp)) 504 ++rp; 505 break; 506 case 'p': 507 /* Match locale's equivalent of AM/PM. */ 508#ifdef _NL_CURRENT 509 if (*decided != raw) 510 { 511 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp)) 512 { 513 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR)) 514 *decided = loc; 515 break; 516 } 517 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp)) 518 { 519 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR)) 520 *decided = loc; 521 is_pm = 1; 522 break; 523 } 524 *decided = raw; 525 } 526#endif 527 if (!match_string (HERE_AM_STR, rp)) { 528 if (match_string (HERE_PM_STR, rp)) { 529 is_pm = 1; 530 } else { 531 return NULL; 532 } 533 } 534 break; 535 case 'r': 536#ifdef _NL_CURRENT 537 if (*decided != raw) 538 { 539 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM))) 540 { 541 if (*decided == loc) 542 return NULL; 543 else 544 rp = rp_backup; 545 } 546 else 547 { 548 if (*decided == not && 549 strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM), 550 HERE_T_FMT_AMPM)) 551 *decided = loc; 552 break; 553 } 554 *decided = raw; 555 } 556#endif 557 if (!recursive (HERE_T_FMT_AMPM)) 558 return NULL; 559 break; 560 case 'R': 561 if (!recursive ("%H:%M")) 562 return NULL; 563 break; 564 case 's': 565 { 566 /* The number of seconds may be very high so we cannot use 567 the `get_number' macro. Instead read the number 568 character for character and construct the result while 569 doing this. */ 570 time_t secs = 0; 571 if (*rp < '0' || *rp > '9') 572 /* We need at least one digit. */ 573 return NULL; 574 575 do 576 { 577 secs *= 10; 578 secs += *rp++ - '0'; 579 } 580 while (*rp >= '0' && *rp <= '9'); 581 582 if (localtime_r (&secs, tm) == NULL) 583 /* Error in function. */ 584 return NULL; 585 } 586 break; 587 case 'S': 588 get_number (0, 61, 2); 589 tm->tm_sec = val; 590 break; 591 case 'X': 592#ifdef _NL_CURRENT 593 if (*decided != raw) 594 { 595 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT))) 596 { 597 if (*decided == loc) 598 return NULL; 599 else 600 rp = rp_backup; 601 } 602 else 603 { 604 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT)) 605 *decided = loc; 606 break; 607 } 608 *decided = raw; 609 } 610#endif 611 /* Fall through. */ 612 case 'T': 613 if (!recursive (HERE_T_FMT)) 614 return NULL; 615 break; 616 case 'u': 617 get_number (1, 7, 1); 618 tm->tm_wday = val % 7; 619 have_wday = 1; 620 break; 621 case 'g': 622 get_number (0, 99, 2); 623 /* XXX This cannot determine any field in TM. */ 624 break; 625 case 'G': 626 if (*rp < '0' || *rp > '9') 627 return NULL; 628 /* XXX Ignore the number since we would need some more 629 information to compute a real date. */ 630 do 631 ++rp; 632 while (*rp >= '0' && *rp <= '9'); 633 break; 634 case 'U': 635 case 'V': 636 case 'W': 637 get_number (0, 53, 2); 638 /* XXX This cannot determine any field in TM without some 639 information. */ 640 break; 641 case 'w': 642 /* Match number of weekday. */ 643 get_number (0, 6, 1); 644 tm->tm_wday = val; 645 have_wday = 1; 646 break; 647 case 'y': 648#ifdef _NL_CURRENT 649 match_year_in_century: 650#endif 651 /* Match year within century. */ 652 get_number (0, 99, 2); 653 /* The "Year 2000: The Millennium Rollover" paper suggests that 654 values in the range 69-99 refer to the twentieth century. */ 655 tm->tm_year = val >= 69 ? val : val + 100; 656 /* Indicate that we want to use the century, if specified. */ 657 want_century = 1; 658 want_xday = 1; 659 break; 660 case 'Y': 661 /* Match year including century number. */ 662 get_number (0, 9999, 4); 663 tm->tm_year = val - 1900; 664 want_century = 0; 665 want_xday = 1; 666 break; 667 case 'Z': 668 /* XXX How to handle this? */ 669 break; 670 case 'E': 671#ifdef _NL_CURRENT 672 switch (*fmt++) 673 { 674 case 'c': 675 /* Match locale's alternate date and time format. */ 676 if (*decided != raw) 677 { 678 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT); 679 680 if (*fmt == '\0') 681 fmt = _NL_CURRENT (LC_TIME, D_T_FMT); 682 683 if (!recursive (fmt)) 684 { 685 if (*decided == loc) 686 return NULL; 687 else 688 rp = rp_backup; 689 } 690 else 691 { 692 if (strcmp (fmt, HERE_D_T_FMT)) 693 *decided = loc; 694 want_xday = 1; 695 break; 696 } 697 *decided = raw; 698 } 699 /* The C locale has no era information, so use the 700 normal representation. */ 701 if (!recursive (HERE_D_T_FMT)) 702 return NULL; 703 want_xday = 1; 704 break; 705 case 'C': 706 if (*decided != raw) 707 { 708 if (era_cnt >= 0) 709 { 710 era = _nl_select_era_entry (era_cnt); 711 if (match_string (era->era_name, rp)) 712 { 713 *decided = loc; 714 break; 715 } 716 else 717 return NULL; 718 } 719 else 720 { 721 num_eras = _NL_CURRENT_WORD (LC_TIME, 722 _NL_TIME_ERA_NUM_ENTRIES); 723 for (era_cnt = 0; era_cnt < (int) num_eras; 724 ++era_cnt, rp = rp_backup) 725 { 726 era = _nl_select_era_entry (era_cnt); 727 if (match_string (era->era_name, rp)) 728 { 729 *decided = loc; 730 break; 731 } 732 } 733 if (era_cnt == (int) num_eras) 734 { 735 era_cnt = -1; 736 if (*decided == loc) 737 return NULL; 738 } 739 else 740 break; 741 } 742 743 *decided = raw; 744 } 745 /* The C locale has no era information, so use the 746 normal representation. */ 747 goto match_century; 748 case 'y': 749 if (*decided == raw) 750 goto match_year_in_century; 751 752 get_number(0, 9999, 4); 753 tm->tm_year = val; 754 want_era = 1; 755 want_xday = 1; 756 break; 757 case 'Y': 758 if (*decided != raw) 759 { 760 num_eras = _NL_CURRENT_WORD (LC_TIME, 761 _NL_TIME_ERA_NUM_ENTRIES); 762 for (era_cnt = 0; era_cnt < (int) num_eras; 763 ++era_cnt, rp = rp_backup) 764 { 765 era = _nl_select_era_entry (era_cnt); 766 if (recursive (era->era_format)) 767 break; 768 } 769 if (era_cnt == (int) num_eras) 770 { 771 era_cnt = -1; 772 if (*decided == loc) 773 return NULL; 774 else 775 rp = rp_backup; 776 } 777 else 778 { 779 *decided = loc; 780 era_cnt = -1; 781 break; 782 } 783 784 *decided = raw; 785 } 786 get_number (0, 9999, 4); 787 tm->tm_year = val - 1900; 788 want_century = 0; 789 want_xday = 1; 790 break; 791 case 'x': 792 if (*decided != raw) 793 { 794 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT); 795 796 if (*fmt == '\0') 797 fmt = _NL_CURRENT (LC_TIME, D_FMT); 798 799 if (!recursive (fmt)) 800 { 801 if (*decided == loc) 802 return NULL; 803 else 804 rp = rp_backup; 805 } 806 else 807 { 808 if (strcmp (fmt, HERE_D_FMT)) 809 *decided = loc; 810 break; 811 } 812 *decided = raw; 813 } 814 if (!recursive (HERE_D_FMT)) 815 return NULL; 816 break; 817 case 'X': 818 if (*decided != raw) 819 { 820 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT); 821 822 if (*fmt == '\0') 823 fmt = _NL_CURRENT (LC_TIME, T_FMT); 824 825 if (!recursive (fmt)) 826 { 827 if (*decided == loc) 828 return NULL; 829 else 830 rp = rp_backup; 831 } 832 else 833 { 834 if (strcmp (fmt, HERE_T_FMT)) 835 *decided = loc; 836 break; 837 } 838 *decided = raw; 839 } 840 if (!recursive (HERE_T_FMT)) 841 return NULL; 842 break; 843 default: 844 return NULL; 845 } 846 break; 847#else 848 /* We have no information about the era format. Just use 849 the normal format. */ 850 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y' 851 && *fmt != 'x' && *fmt != 'X') 852 /* This is an illegal format. */ 853 return NULL; 854 855 goto start_over; 856#endif 857 case 'O': 858 switch (*fmt++) 859 { 860 case 'd': 861 case 'e': 862 /* Match day of month using alternate numeric symbols. */ 863 get_alt_number (1, 31, 2); 864 tm->tm_mday = val; 865 have_mday = 1; 866 want_xday = 1; 867 break; 868 case 'H': 869 /* Match hour in 24-hour clock using alternate numeric 870 symbols. */ 871 get_alt_number (0, 23, 2); 872 tm->tm_hour = val; 873 have_I = 0; 874 break; 875 case 'I': 876 /* Match hour in 12-hour clock using alternate numeric 877 symbols. */ 878 get_alt_number (1, 12, 2); 879 tm->tm_hour = val - 1; 880 have_I = 1; 881 break; 882 case 'm': 883 /* Match month using alternate numeric symbols. */ 884 get_alt_number (1, 12, 2); 885 tm->tm_mon = val - 1; 886 have_mon = 1; 887 want_xday = 1; 888 break; 889 case 'M': 890 /* Match minutes using alternate numeric symbols. */ 891 get_alt_number (0, 59, 2); 892 tm->tm_min = val; 893 break; 894 case 'S': 895 /* Match seconds using alternate numeric symbols. */ 896 get_alt_number (0, 61, 2); 897 tm->tm_sec = val; 898 break; 899 case 'U': 900 case 'V': 901 case 'W': 902 get_alt_number (0, 53, 2); 903 /* XXX This cannot determine any field in TM without 904 further information. */ 905 break; 906 case 'w': 907 /* Match number of weekday using alternate numeric symbols. */ 908 get_alt_number (0, 6, 1); 909 tm->tm_wday = val; 910 have_wday = 1; 911 break; 912 case 'y': 913 /* Match year within century using alternate numeric symbols. */ 914 get_alt_number (0, 99, 2); 915 tm->tm_year = val >= 69 ? val : val + 100; 916 want_xday = 1; 917 break; 918 default: 919 return NULL; 920 } 921 break; 922 default: 923 return NULL; 924 } 925 } 926 927 if (have_I && is_pm) 928 tm->tm_hour += 12; 929 930 if (century != -1) 931 { 932 if (want_century) 933 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100; 934 else 935 /* Only the century, but not the year. Strange, but so be it. */ 936 tm->tm_year = (century - 19) * 100; 937 } 938 939#ifdef _NL_CURRENT 940 if (era_cnt != -1) 941 { 942 era = _nl_select_era_entry(era_cnt); 943 if (want_era) 944 tm->tm_year = (era->start_date[0] 945 + ((tm->tm_year - era->offset) 946 * era->absolute_direction)); 947 else 948 /* Era start year assumed. */ 949 tm->tm_year = era->start_date[0]; 950 } 951 else 952#endif 953 if (want_era) 954 return NULL; 955 956 if (want_xday && !have_wday) 957 { 958 if ( !(have_mon && have_mday) && have_yday) 959 { 960 /* We don't have tm_mon and/or tm_mday, compute them. */ 961 int t_mon = 0; 962 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday) 963 t_mon++; 964 if (!have_mon) 965 tm->tm_mon = t_mon - 1; 966 if (!have_mday) 967 tm->tm_mday = 968 (tm->tm_yday 969 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1); 970 } 971 day_of_the_week (tm); 972 } 973 if (want_xday && !have_yday) 974 day_of_the_year (tm); 975 976 return discard_const_p(char, rp); 977} 978 979 980char *rep_strptime(const char *buf, const char *format, struct tm *tm) 981{ 982 enum locale_status decided; 983 984#ifdef _NL_CURRENT 985 decided = not; 986#else 987 decided = raw; 988#endif 989 return strptime_internal (buf, format, tm, &decided, -1); 990} 991