root/lib/replace/strptime.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. localtime_r
  2. day_of_the_week
  3. day_of_the_year
  4. strptime_internal
  5. rep_strptime

   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
  43 static struct tm *localtime_r __P ((const time_t *, struct tm *));
  44 static struct tm *
  45 localtime_r (t, tp)
     /* [<][>][^][v][top][bottom][index][help] */
  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.  */
 146 extern const struct locale_data _nl_C_LC_TIME;
 147 extern 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
 164 static char const weekday_name[][10] =
 165   {
 166     "Sunday", "Monday", "Tuesday", "Wednesday",
 167     "Thursday", "Friday", "Saturday"
 168   };
 169 static char const ab_weekday_name[][4] =
 170   {
 171     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
 172   };
 173 static char const month_name[][10] =
 174   {
 175     "January", "February", "March", "April", "May", "June",
 176     "July", "August", "September", "October", "November", "December"
 177   };
 178 static 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 
 190 static 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?  */
 200 enum 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.  */
 211 static void
 212 day_of_the_week (struct tm *tm)
     /* [<][>][^][v][top][bottom][index][help] */
 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.  */
 229 static void
 230 day_of_the_year (struct tm *tm)
     /* [<][>][^][v][top][bottom][index][help] */
 231 {
 232   tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
 233                  + (tm->tm_mday - 1));
 234 }
 235 
 236 static char *
 237 #ifdef _LIBC
 238 internal_function
 239 #endif
 240 strptime_internal __P ((const char *rp, const char *fmt, struct tm *tm,
 241                         enum locale_status *decided, int era_cnt));
 242 
 243 static char *
 244 #ifdef _LIBC
 245 internal_function
 246 #endif
 247 strptime_internal (rp, fmt, tm, decided, era_cnt)
     /* [<][>][^][v][top][bottom][index][help] */
 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 
 980 char *rep_strptime(const char *buf, const char *format, struct tm *tm)
     /* [<][>][^][v][top][bottom][index][help] */
 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 }

/* [<][>][^][v][top][bottom][index][help] */