(root)/
glibc-2.38/
time/
strptime_l.c
       1  /* Copyright (C) 2002-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #ifdef HAVE_CONFIG_H
      19  # include <config.h>
      20  #endif
      21  
      22  #include <assert.h>
      23  #include <ctype.h>
      24  #include <langinfo.h>
      25  #include <limits.h>
      26  #include <string.h>
      27  #include <time.h>
      28  #include <stdbool.h>
      29  
      30  #ifdef _LIBC
      31  # define HAVE_LOCALTIME_R 0
      32  # include "../locale/localeinfo.h"
      33  
      34  # define time_t __time64_t
      35  # define __localtime_r(t, tp) __localtime64_r (t, tp)
      36  #endif
      37  
      38  #if ! HAVE_LOCALTIME_R && ! defined localtime_r
      39  # ifdef _LIBC
      40  #  define localtime_r __localtime_r
      41  # else
      42  /* Approximate localtime_r as best we can in its absence.  */
      43  #  define localtime_r my_localtime_r
      44  static struct tm *localtime_r (const time_t *, struct tm *);
      45  static struct tm *
      46  localtime_r (const time_t *t, struct tm *tp)
      47  {
      48    struct tm *l = localtime (t);
      49    if (! l)
      50      return 0;
      51    *tp = *l;
      52    return tp;
      53  }
      54  # endif /* ! _LIBC */
      55  #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
      56  
      57  
      58  #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
      59  #if defined __GNUC__ && __GNUC__ >= 2
      60  # define match_string(cs1, s2) \
      61    ({ size_t len = strlen (cs1);						      \
      62       int result = __strncasecmp_l ((cs1), (s2), len, locale) == 0;	      \
      63       if (result) (s2) += len;						      \
      64       result; })
      65  #else
      66  /* Oh come on.  Get a reasonable compiler.  */
      67  # define match_string(cs1, s2) \
      68    (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
      69  #endif
      70  /* We intentionally do not use isdigit() for testing because this will
      71     lead to problems with the wide character version.  */
      72  #define get_number(from, to, n) \
      73    do {									      \
      74      int __n = n;							      \
      75      val = 0;								      \
      76      while (ISSPACE (*rp))						      \
      77        ++rp;								      \
      78      if (*rp < '0' || *rp > '9')						      \
      79        return NULL;							      \
      80      do {								      \
      81        val *= 10;							      \
      82        val += *rp++ - '0';						      \
      83      } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9');	      \
      84      if (val < from || val > to)						      \
      85        return NULL;							      \
      86    } while (0)
      87  #ifdef _NL_CURRENT
      88  # define get_alt_number(from, to, n) \
      89    ({									      \
      90       __label__ do_normal;						      \
      91  									      \
      92       if (s.decided != raw)						      \
      93         {								      \
      94  	 val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG);		      \
      95  	 if (val == -1 && s.decided != loc)				      \
      96  	   {								      \
      97  	     s.decided = loc;						      \
      98  	     goto do_normal;						      \
      99  	   }								      \
     100  	if (val < from || val > to)					      \
     101  	  return NULL;							      \
     102         }								      \
     103       else								      \
     104         {								      \
     105         do_normal:							      \
     106  	 get_number (from, to, n);					      \
     107         }								      \
     108      0;									      \
     109    })
     110  #else
     111  # define get_alt_number(from, to, n) \
     112    /* We don't have the alternate representation.  */			      \
     113    get_number(from, to, n)
     114  #endif
     115  #define recursive(new_fmt) \
     116    (*(new_fmt) != '\0'							      \
     117     && (rp = __strptime_internal (rp, (new_fmt), tm, &s LOCALE_ARG)) != NULL)
     118  
     119  
     120  #ifdef _LIBC
     121  /* This is defined in locale/C-time.c in the GNU libc.  */
     122  extern const struct __locale_data _nl_C_LC_TIME attribute_hidden;
     123  
     124  # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
     125  # define ab_weekday_name \
     126    (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
     127  # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
     128  # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
     129  # define alt_month_name \
     130    (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ALTMON_1)].string)
     131  # define ab_alt_month_name \
     132    (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (_NL_ABALTMON_1)].string)
     133  # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
     134  # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
     135  # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
     136  # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
     137  # define HERE_T_FMT_AMPM \
     138    (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
     139  # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
     140  
     141  # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
     142  #else
     143  static char const weekday_name[][10] =
     144    {
     145      "Sunday", "Monday", "Tuesday", "Wednesday",
     146      "Thursday", "Friday", "Saturday"
     147    };
     148  static char const ab_weekday_name[][4] =
     149    {
     150      "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
     151    };
     152  static char const month_name[][10] =
     153    {
     154      "January", "February", "March", "April", "May", "June",
     155      "July", "August", "September", "October", "November", "December"
     156    };
     157  static char const ab_month_name[][4] =
     158    {
     159      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
     160      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
     161    };
     162  # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
     163  # define HERE_D_FMT "%m/%d/%y"
     164  # define HERE_AM_STR "AM"
     165  # define HERE_PM_STR "PM"
     166  # define HERE_T_FMT_AMPM "%I:%M:%S %p"
     167  # define HERE_T_FMT "%H:%M:%S"
     168  
     169  static const unsigned short int __mon_yday[2][13] =
     170    {
     171      /* Normal years.  */
     172      { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
     173      /* Leap years.  */
     174      { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
     175    };
     176  #endif
     177  
     178  #if defined _LIBC
     179  /* We use this code also for the extended locale handling where the
     180     function gets as an additional argument the locale which has to be
     181     used.  To access the values we have to redefine the _NL_CURRENT
     182     macro.  */
     183  # define strptime		__strptime_l
     184  # undef _NL_CURRENT
     185  # define _NL_CURRENT(category, item) \
     186    (current->values[_NL_ITEM_INDEX (item)].string)
     187  # undef _NL_CURRENT_WORD
     188  # define _NL_CURRENT_WORD(category, item) \
     189    (current->values[_NL_ITEM_INDEX (item)].word)
     190  # define LOCALE_PARAM , locale_t locale
     191  # define LOCALE_ARG , locale
     192  # define HELPER_LOCALE_ARG , current
     193  # define ISSPACE(Ch) __isspace_l (Ch, locale)
     194  #else
     195  # define LOCALE_PARAM
     196  # define LOCALE_ARG
     197  # define HELPER_LOCALE_ARG
     198  # define ISSPACE(Ch) isspace (Ch)
     199  #endif
     200  
     201  
     202  
     203  
     204  #ifndef __isleap
     205  /* Nonzero if YEAR is a leap year (every 4 years,
     206     except every 100th isn't, and every 400th is).  */
     207  # define __isleap(year)	\
     208    ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
     209  #endif
     210  
     211  /* Compute the day of the week.  */
     212  static void
     213  day_of_the_week (struct tm *tm)
     214  {
     215    /* We know that January 1st 1970 was a Thursday (= 4).  Compute the
     216       difference between this data in the one on TM and so determine
     217       the weekday.  */
     218    int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
     219    int wday = (-473
     220  	      + (365 * (tm->tm_year - 70))
     221  	      + (corr_year / 4)
     222  	      - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
     223  	      + (((corr_year / 4) / 25) / 4)
     224  	      + __mon_yday[0][tm->tm_mon]
     225  	      + tm->tm_mday - 1);
     226    tm->tm_wday = ((wday % 7) + 7) % 7;
     227  }
     228  
     229  /* Compute the day of the year.  */
     230  static void
     231  day_of_the_year (struct tm *tm)
     232  {
     233    tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
     234  		 + (tm->tm_mday - 1));
     235  }
     236  
     237  
     238  #ifdef _LIBC
     239  char *
     240  #else
     241  static char *
     242  #endif
     243  __strptime_internal (const char *rp, const char *fmt, struct tm *tmp,
     244  		     void *statep LOCALE_PARAM)
     245  {
     246  #ifdef _LIBC
     247    struct __locale_data *const current = locale->__locales[LC_TIME];
     248  #endif
     249  
     250    const char *rp_backup;
     251    const char *rp_longest;
     252    int cnt;
     253    int cnt_longest;
     254    size_t val;
     255    size_t num_eras;
     256    struct era_entry *era = NULL;
     257    enum ptime_locale_status { not, loc, raw } decided_longest;
     258    struct __strptime_state
     259    {
     260      unsigned int have_I : 1;
     261      unsigned int have_wday : 1;
     262      unsigned int have_yday : 1;
     263      unsigned int have_mon : 1;
     264      unsigned int have_mday : 1;
     265      unsigned int have_uweek : 1;
     266      unsigned int have_wweek : 1;
     267      unsigned int is_pm : 1;
     268      unsigned int want_century : 1;
     269      unsigned int want_era : 1;
     270      unsigned int want_xday : 1;
     271      enum ptime_locale_status decided : 2;
     272      signed char week_no;
     273      signed char century;
     274      int era_cnt;
     275    } s;
     276    struct tm tmb;
     277    struct tm *tm;
     278  
     279    if (statep == NULL)
     280      {
     281        memset (&s, 0, sizeof (s));
     282        s.century = -1;
     283        s.era_cnt = -1;
     284  #ifdef _NL_CURRENT
     285        s.decided = not;
     286  #else
     287        s.decided = raw;
     288  #endif
     289        tm = tmp;
     290      }
     291    else
     292      {
     293        s = *(struct __strptime_state *) statep;
     294        tmb = *tmp;
     295        tm = &tmb;
     296      }
     297  
     298    while (*fmt != '\0')
     299      {
     300        /* A white space in the format string matches 0 more or white
     301  	 space in the input string.  */
     302        if (ISSPACE (*fmt))
     303  	{
     304  	  while (ISSPACE (*rp))
     305  	    ++rp;
     306  	  ++fmt;
     307  	  continue;
     308  	}
     309  
     310        /* Any character but `%' must be matched by the same character
     311  	 in the input string.  */
     312        if (*fmt != '%')
     313  	{
     314  	  match_char (*fmt++, *rp++);
     315  	  continue;
     316  	}
     317  
     318        ++fmt;
     319        /* We discard strftime modifiers.  */
     320        while (*fmt == '-' || *fmt == '_' || *fmt == '0'
     321  	     || *fmt == '^' || *fmt == '#')
     322  	++fmt;
     323  
     324        /* And field width.  */
     325        while (*fmt >= '0' && *fmt <= '9')
     326  	++fmt;
     327  
     328        /* In some cases, modifiers are handled by adjusting state and
     329           then restarting the switch statement below.  */
     330      start_over:
     331  
     332        /* Make back up of current processing pointer.  */
     333        rp_backup = rp;
     334  
     335        switch (*fmt++)
     336  	{
     337  	case '%':
     338  	  /* Match the `%' character itself.  */
     339  	  match_char ('%', *rp++);
     340  	  break;
     341  	case 'a':
     342  	case 'A':
     343  	  /* Match day of week.  */
     344  	  rp_longest = NULL;
     345  	  decided_longest = s.decided;
     346  	  cnt_longest = -1;
     347  	  for (cnt = 0; cnt < 7; ++cnt)
     348  	    {
     349  	      const char *trp;
     350  #ifdef _NL_CURRENT
     351  	      if (s.decided !=raw)
     352  		{
     353  		  trp = rp;
     354  		  if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), trp)
     355  		      && trp > rp_longest)
     356  		    {
     357  		      rp_longest = trp;
     358  		      cnt_longest = cnt;
     359  		      if (s.decided == not
     360  			  && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
     361  				     weekday_name[cnt]))
     362  			decided_longest = loc;
     363  		    }
     364  		  trp = rp;
     365  		  if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), trp)
     366  		      && trp > rp_longest)
     367  		    {
     368  		      rp_longest = trp;
     369  		      cnt_longest = cnt;
     370  		      if (s.decided == not
     371  			  && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
     372  				     ab_weekday_name[cnt]))
     373  			decided_longest = loc;
     374  		    }
     375  		}
     376  #endif
     377  	      if (s.decided != loc
     378  		  && (((trp = rp, match_string (weekday_name[cnt], trp))
     379  		       && trp > rp_longest)
     380  		      || ((trp = rp, match_string (ab_weekday_name[cnt], rp))
     381  			  && trp > rp_longest)))
     382  		{
     383  		  rp_longest = trp;
     384  		  cnt_longest = cnt;
     385  		  decided_longest = raw;
     386  		}
     387  	    }
     388  	  if (rp_longest == NULL)
     389  	    /* Does not match a weekday name.  */
     390  	    return NULL;
     391  	  rp = rp_longest;
     392  	  s.decided = decided_longest;
     393  	  tm->tm_wday = cnt_longest;
     394  	  s.have_wday = 1;
     395  	  break;
     396  	case 'b':
     397  	case 'B':
     398  	case 'h':
     399  	  /* Match month name.  */
     400  	  rp_longest = NULL;
     401  	  decided_longest = s.decided;
     402  	  cnt_longest = -1;
     403  	  for (cnt = 0; cnt < 12; ++cnt)
     404  	    {
     405  	      const char *trp;
     406  #ifdef _NL_CURRENT
     407  	      if (s.decided !=raw)
     408  		{
     409  		  trp = rp;
     410  		  if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), trp)
     411  		      && trp > rp_longest)
     412  		    {
     413  		      rp_longest = trp;
     414  		      cnt_longest = cnt;
     415  		      if (s.decided == not
     416  			  && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
     417  				     month_name[cnt]))
     418  			decided_longest = loc;
     419  		    }
     420  		  trp = rp;
     421  		  if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), trp)
     422  		      && trp > rp_longest)
     423  		    {
     424  		      rp_longest = trp;
     425  		      cnt_longest = cnt;
     426  		      if (s.decided == not
     427  			  && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
     428  				     ab_month_name[cnt]))
     429  			decided_longest = loc;
     430  		    }
     431  #ifdef _LIBC
     432  		  /* Now check the alt month.  */
     433  		  trp = rp;
     434  		  if (match_string (_NL_CURRENT (LC_TIME, ALTMON_1 + cnt), trp)
     435  		      && trp > rp_longest)
     436  		    {
     437  		      rp_longest = trp;
     438  		      cnt_longest = cnt;
     439  		      if (s.decided == not
     440  			  && strcmp (_NL_CURRENT (LC_TIME, ALTMON_1 + cnt),
     441  				     alt_month_name[cnt]))
     442  			decided_longest = loc;
     443  		    }
     444  		  trp = rp;
     445  		  if (match_string (_NL_CURRENT (LC_TIME, _NL_ABALTMON_1 + cnt),
     446  				    trp)
     447  		      && trp > rp_longest)
     448  		    {
     449  		      rp_longest = trp;
     450  		      cnt_longest = cnt;
     451  		      if (s.decided == not
     452  			  && strcmp (_NL_CURRENT (LC_TIME, _NL_ABALTMON_1 + cnt),
     453  				     alt_month_name[cnt]))
     454  			decided_longest = loc;
     455  		    }
     456  #endif
     457  		}
     458  #endif
     459  	      if (s.decided != loc
     460  		  && (((trp = rp, match_string (month_name[cnt], trp))
     461  		       && trp > rp_longest)
     462  		      || ((trp = rp, match_string (ab_month_name[cnt], trp))
     463  			  && trp > rp_longest)
     464  #ifdef _LIBC
     465  		      || ((trp = rp, match_string (alt_month_name[cnt], trp))
     466  			  && trp > rp_longest)
     467  		      || ((trp = rp, match_string (ab_alt_month_name[cnt], trp))
     468  			  && trp > rp_longest)
     469  #endif
     470  	      ))
     471  		{
     472  		  rp_longest = trp;
     473  		  cnt_longest = cnt;
     474  		  decided_longest = raw;
     475  		}
     476  	    }
     477  	  if (rp_longest == NULL)
     478  	    /* Does not match a month name.  */
     479  	    return NULL;
     480  	  rp = rp_longest;
     481  	  s.decided = decided_longest;
     482  	  tm->tm_mon = cnt_longest;
     483  	  s.have_mon = 1;
     484  	  s.want_xday = 1;
     485  	  break;
     486  	case 'c':
     487  	  /* Match locale's date and time format.  */
     488  #ifdef _NL_CURRENT
     489  	  if (s.decided != raw)
     490  	    {
     491  	      if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
     492  		{
     493  		  if (s.decided == loc)
     494  		    return NULL;
     495  		  else
     496  		    rp = rp_backup;
     497  		}
     498  	      else
     499  		{
     500  		  if (s.decided == not
     501  		      && strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
     502  		    s.decided = loc;
     503  		  s.want_xday = 1;
     504  		  break;
     505  		}
     506  	      s.decided = raw;
     507  	    }
     508  #endif
     509  	  if (!recursive (HERE_D_T_FMT))
     510  	    return NULL;
     511  	  s.want_xday = 1;
     512  	  break;
     513  	case 'C':
     514  	  /* Match century number.  */
     515  	match_century:
     516  	  get_number (0, 99, 2);
     517  	  s.century = val;
     518  	  s.want_xday = 1;
     519  	  break;
     520  	case 'd':
     521  	case 'e':
     522  	  /* Match day of month.  */
     523  	  get_number (1, 31, 2);
     524  	  tm->tm_mday = val;
     525  	  s.have_mday = 1;
     526  	  s.want_xday = 1;
     527  	  break;
     528  	case 'F':
     529  	  if (!recursive ("%Y-%m-%d"))
     530  	    return NULL;
     531  	  s.want_xday = 1;
     532  	  break;
     533  	case 'x':
     534  #ifdef _NL_CURRENT
     535  	  if (s.decided != raw)
     536  	    {
     537  	      if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
     538  		{
     539  		  if (s.decided == loc)
     540  		    return NULL;
     541  		  else
     542  		    rp = rp_backup;
     543  		}
     544  	      else
     545  		{
     546  		  if (s.decided == not
     547  		      && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
     548  		    s.decided = loc;
     549  		  s.want_xday = 1;
     550  		  break;
     551  		}
     552  	      s.decided = raw;
     553  	    }
     554  #endif
     555  	  /* Fall through.  */
     556  	case 'D':
     557  	  /* Match standard day format.  */
     558  	  if (!recursive (HERE_D_FMT))
     559  	    return NULL;
     560  	  s.want_xday = 1;
     561  	  break;
     562  	case 'k':
     563  	case 'H':
     564  	  /* Match hour in 24-hour clock.  */
     565  	  get_number (0, 23, 2);
     566  	  tm->tm_hour = val;
     567  	  s.have_I = 0;
     568  	  break;
     569  	case 'l':
     570  	  /* Match hour in 12-hour clock.  GNU extension.  */
     571  	case 'I':
     572  	  /* Match hour in 12-hour clock.  */
     573  	  get_number (1, 12, 2);
     574  	  tm->tm_hour = val % 12;
     575  	  s.have_I = 1;
     576  	  break;
     577  	case 'j':
     578  	  /* Match day number of year.  */
     579  	  get_number (1, 366, 3);
     580  	  tm->tm_yday = val - 1;
     581  	  s.have_yday = 1;
     582  	  break;
     583  	case 'm':
     584  	  /* Match number of month.  */
     585  	  get_number (1, 12, 2);
     586  	  tm->tm_mon = val - 1;
     587  	  s.have_mon = 1;
     588  	  s.want_xday = 1;
     589  	  break;
     590  	case 'M':
     591  	  /* Match minute.  */
     592  	  get_number (0, 59, 2);
     593  	  tm->tm_min = val;
     594  	  break;
     595  	case 'n':
     596  	case 't':
     597  	  /* Match any white space.  */
     598  	  while (ISSPACE (*rp))
     599  	    ++rp;
     600  	  break;
     601  	case 'p':
     602  	  /* Match locale's equivalent of AM/PM.  */
     603  #ifdef _NL_CURRENT
     604  	  if (s.decided != raw)
     605  	    {
     606  	      if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
     607  		{
     608  		  if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
     609  		    s.decided = loc;
     610  		  s.is_pm = 0;
     611  		  break;
     612  		}
     613  	      if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
     614  		{
     615  		  if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
     616  		    s.decided = loc;
     617  		  s.is_pm = 1;
     618  		  break;
     619  		}
     620  	      s.decided = raw;
     621  	    }
     622  #endif
     623  	  if (!match_string (HERE_AM_STR, rp))
     624  	    {
     625  	      if (match_string (HERE_PM_STR, rp))
     626  		s.is_pm = 1;
     627  	      else
     628  		return NULL;
     629  	    }
     630  	  else
     631  	    s.is_pm = 0;
     632  	  break;
     633  	case 'r':
     634  #ifdef _NL_CURRENT
     635  	  if (s.decided != raw)
     636  	    {
     637  	      if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
     638  		{
     639  		  if (s.decided == loc)
     640  		    return NULL;
     641  		  else
     642  		    rp = rp_backup;
     643  		}
     644  	      else
     645  		{
     646  		  if (s.decided == not
     647  		      && strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
     648  				 HERE_T_FMT_AMPM))
     649  		    s.decided = loc;
     650  		  break;
     651  		}
     652  	      s.decided = raw;
     653  	    }
     654  #endif
     655  	  if (!recursive (HERE_T_FMT_AMPM))
     656  	    return NULL;
     657  	  break;
     658  	case 'R':
     659  	  if (!recursive ("%H:%M"))
     660  	    return NULL;
     661  	  break;
     662  	case 's':
     663  	  {
     664  	    /* The number of seconds may be very high so we cannot use
     665  	       the `get_number' macro.  Instead read the number
     666  	       character for character and construct the result while
     667  	       doing this.  */
     668  	    time_t secs = 0;
     669  	    if (*rp < '0' || *rp > '9')
     670  	      /* We need at least one digit.  */
     671  	      return NULL;
     672  
     673  	    do
     674  	      {
     675  		secs *= 10;
     676  		secs += *rp++ - '0';
     677  	      }
     678  	    while (*rp >= '0' && *rp <= '9');
     679  
     680  	    if (localtime_r (&secs, tm) == NULL)
     681  	      /* Error in function.  */
     682  	      return NULL;
     683  	  }
     684  	  break;
     685  	case 'S':
     686  	  get_number (0, 61, 2);
     687  	  tm->tm_sec = val;
     688  	  break;
     689  	case 'X':
     690  #ifdef _NL_CURRENT
     691  	  if (s.decided != raw)
     692  	    {
     693  	      if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
     694  		{
     695  		  if (s.decided == loc)
     696  		    return NULL;
     697  		  else
     698  		    rp = rp_backup;
     699  		}
     700  	      else
     701  		{
     702  		  if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
     703  		    s.decided = loc;
     704  		  break;
     705  		}
     706  	      s.decided = raw;
     707  	    }
     708  #endif
     709  	  /* Fall through.  */
     710  	case 'T':
     711  	  if (!recursive (HERE_T_FMT))
     712  	    return NULL;
     713  	  break;
     714  	case 'u':
     715  	  get_number (1, 7, 1);
     716  	  tm->tm_wday = val % 7;
     717  	  s.have_wday = 1;
     718  	  break;
     719  	case 'g':
     720  	  get_number (0, 99, 2);
     721  	  /* XXX This cannot determine any field in TM.  */
     722  	  break;
     723  	case 'G':
     724  	  if (*rp < '0' || *rp > '9')
     725  	    return NULL;
     726  	  /* XXX Ignore the number since we would need some more
     727  	     information to compute a real date.  */
     728  	  do
     729  	    ++rp;
     730  	  while (*rp >= '0' && *rp <= '9');
     731  	  break;
     732  	case 'U':
     733  	  get_number (0, 53, 2);
     734  	  s.week_no = val;
     735  	  s.have_uweek = 1;
     736  	  break;
     737  	case 'W':
     738  	  get_number (0, 53, 2);
     739  	  s.week_no = val;
     740  	  s.have_wweek = 1;
     741  	  break;
     742  	case 'V':
     743  	  get_number (0, 53, 2);
     744  	  /* XXX This cannot determine any field in TM without some
     745  	     information.  */
     746  	  break;
     747  	case 'w':
     748  	  /* Match number of weekday.  */
     749  	  get_number (0, 6, 1);
     750  	  tm->tm_wday = val;
     751  	  s.have_wday = 1;
     752  	  break;
     753  	case 'y':
     754  	match_year_in_century:
     755  	  /* Match year within century.  */
     756  	  get_number (0, 99, 2);
     757  	  /* The "Year 2000: The Millennium Rollover" paper suggests that
     758  	     values in the range 69-99 refer to the twentieth century.  */
     759  	  tm->tm_year = val >= 69 ? val : val + 100;
     760  	  /* Indicate that we want to use the century, if specified.  */
     761  	  s.want_century = 1;
     762  	  s.want_xday = 1;
     763  	  break;
     764  	case 'Y':
     765  	  /* Match year including century number.  */
     766  	  get_number (0, 9999, 4);
     767  	  tm->tm_year = val - 1900;
     768  	  s.want_century = 0;
     769  	  s.want_xday = 1;
     770  	  break;
     771  	case 'Z':
     772  	  /* Read timezone but perform no conversion.  */
     773  	  while (ISSPACE (*rp))
     774  	    rp++;
     775  	  while (!ISSPACE (*rp) && *rp != '\0')
     776  	    rp++;
     777  	  break;
     778  	case 'z':
     779  	  /* We recognize four formats:
     780  	     1. Two digits specify hours.
     781  	     2. Four digits specify hours and minutes.
     782  	     3. Two digits, ':', and two digits specify hours and minutes.
     783  	     4. 'Z' is equivalent to +0000.  */
     784  	  {
     785  	    val = 0;
     786  	    while (ISSPACE (*rp))
     787  	      ++rp;
     788  	    if (*rp == 'Z')
     789  	      {
     790  		++rp;
     791  		tm->tm_gmtoff = 0;
     792  		break;
     793  	      }
     794  	    if (*rp != '+' && *rp != '-')
     795  	      return NULL;
     796  	    bool neg = *rp++ == '-';
     797  	    int n = 0;
     798  	    while (n < 4 && *rp >= '0' && *rp <= '9')
     799  	      {
     800  		val = val * 10 + *rp++ - '0';
     801  		++n;
     802  		if (*rp == ':' && n == 2 && isdigit (*(rp + 1)))
     803  		  ++rp;
     804  	      }
     805  	    if (n == 2)
     806  	      val *= 100;
     807  	    else if (n != 4)
     808  	      /* Only two or four digits recognized.  */
     809  	      return NULL;
     810  	    else if (val % 100 >= 60)
     811  	      /* Minutes valid range is 0 through 59.  */
     812  	      return NULL;
     813  	    tm->tm_gmtoff = (val / 100) * 3600 + (val % 100) * 60;
     814  	    if (neg)
     815  	      tm->tm_gmtoff = -tm->tm_gmtoff;
     816  	  }
     817  	  break;
     818  	case 'E':
     819  #ifdef _NL_CURRENT
     820  	  switch (*fmt++)
     821  	    {
     822  	    case 'c':
     823  	      /* Match locale's alternate date and time format.  */
     824  	      if (s.decided != raw)
     825  		{
     826  		  const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
     827  
     828  		  if (*fmt == '\0')
     829  		    fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
     830  
     831  		  if (!recursive (fmt))
     832  		    {
     833  		      if (s.decided == loc)
     834  			return NULL;
     835  		      else
     836  			rp = rp_backup;
     837  		    }
     838  		  else
     839  		    {
     840  		      if (strcmp (fmt, HERE_D_T_FMT))
     841  			s.decided = loc;
     842  		      s.want_xday = 1;
     843  		      break;
     844  		    }
     845  		  s.decided = raw;
     846  		}
     847  	      /* The C locale has no era information, so use the
     848  		 normal representation.  */
     849  	      if (!recursive (HERE_D_T_FMT))
     850  		return NULL;
     851  	      s.want_xday = 1;
     852  	      break;
     853  	    case 'C':
     854  	      if (s.decided != raw)
     855  		{
     856  		  if (s.era_cnt >= 0)
     857  		    {
     858  		      era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
     859  		      if (era != NULL && match_string (era->era_name, rp))
     860  			{
     861  			  s.decided = loc;
     862  			  break;
     863  			}
     864  		      else
     865  			return NULL;
     866  		    }
     867  
     868  		  num_eras = _NL_CURRENT_WORD (LC_TIME,
     869  					       _NL_TIME_ERA_NUM_ENTRIES);
     870  		  for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
     871  		       ++s.era_cnt, rp = rp_backup)
     872  		    {
     873  		      era = _nl_select_era_entry (s.era_cnt
     874  						  HELPER_LOCALE_ARG);
     875  		      if (era != NULL && match_string (era->era_name, rp))
     876  			{
     877  			  s.decided = loc;
     878  			  break;
     879  			}
     880  		    }
     881  		  if (s.era_cnt != (int) num_eras)
     882  		    break;
     883  
     884  		  s.era_cnt = -1;
     885  		  if (s.decided == loc)
     886  		    return NULL;
     887  
     888  		  s.decided = raw;
     889  		}
     890  	      /* The C locale has no era information, so use the
     891  		 normal representation.  */
     892  	      goto match_century;
     893   	    case 'y':
     894  	      if (s.decided != raw)
     895  		{
     896  		  get_number(0, 9999, 4);
     897  		  tm->tm_year = val;
     898  		  s.want_era = 1;
     899  		  s.want_xday = 1;
     900  		  s.want_century = 1;
     901  
     902  		  if (s.era_cnt >= 0)
     903  		    {
     904  		      assert (s.decided == loc);
     905  
     906  		      era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
     907  		      bool match = false;
     908  		      if (era != NULL)
     909  			{
     910  			  int delta = ((tm->tm_year - era->offset)
     911  				       * era->absolute_direction);
     912  			  /* The difference between two sets of years
     913  			     does not include the final year itself,
     914  			     therefore add 1 to the difference to
     915  			     account for that final year.  */
     916  			  match = (delta >= 0
     917  				   && delta < (((int64_t) era->stop_date[0]
     918  						- (int64_t) era->start_date[0])
     919  					       * era->absolute_direction
     920  					       + 1));
     921  			}
     922  		      if (! match)
     923  			return NULL;
     924  
     925  		      break;
     926  		    }
     927  
     928  		  num_eras = _NL_CURRENT_WORD (LC_TIME,
     929  					       _NL_TIME_ERA_NUM_ENTRIES);
     930  		  for (s.era_cnt = 0; s.era_cnt < (int) num_eras; ++s.era_cnt)
     931  		    {
     932  		      era = _nl_select_era_entry (s.era_cnt
     933  						  HELPER_LOCALE_ARG);
     934  		      if (era != NULL)
     935  			{
     936  			  int delta = ((tm->tm_year - era->offset)
     937  				       * era->absolute_direction);
     938  			  /* See comment above about year difference + 1.  */
     939  			  if (delta >= 0
     940  			      && delta < (((int64_t) era->stop_date[0]
     941  					   - (int64_t) era->start_date[0])
     942  					  * era->absolute_direction
     943  					  + 1))
     944  			    {
     945  			      s.decided = loc;
     946  			      break;
     947  			    }
     948  			}
     949  		    }
     950  		  if (s.era_cnt != (int) num_eras)
     951  		    break;
     952  
     953  		  s.era_cnt = -1;
     954  		  if (s.decided == loc)
     955  		    return NULL;
     956  
     957  		  s.decided = raw;
     958  		}
     959  
     960  	      goto match_year_in_century;
     961  	    case 'Y':
     962  	      if (s.decided != raw)
     963  		{
     964  		  num_eras = _NL_CURRENT_WORD (LC_TIME,
     965  					       _NL_TIME_ERA_NUM_ENTRIES);
     966  		  for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
     967  		       ++s.era_cnt, rp = rp_backup)
     968  		    {
     969  		      era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
     970  		      if (era != NULL && recursive (era->era_format))
     971  			break;
     972  		    }
     973  		  if (s.era_cnt == (int) num_eras)
     974  		    {
     975  		      s.era_cnt = -1;
     976  		      if (s.decided == loc)
     977  			return NULL;
     978  		      else
     979  			rp = rp_backup;
     980  		    }
     981  		  else
     982  		    {
     983  		      s.decided = loc;
     984  		      break;
     985  		    }
     986  
     987  		  s.decided = raw;
     988  		}
     989  	      get_number (0, 9999, 4);
     990  	      tm->tm_year = val - 1900;
     991  	      s.want_century = 0;
     992  	      s.want_xday = 1;
     993  	      break;
     994  	    case 'x':
     995  	      if (s.decided != raw)
     996  		{
     997  		  const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
     998  
     999  		  if (*fmt == '\0')
    1000  		    fmt = _NL_CURRENT (LC_TIME, D_FMT);
    1001  
    1002  		  if (!recursive (fmt))
    1003  		    {
    1004  		      if (s.decided == loc)
    1005  			return NULL;
    1006  		      else
    1007  			rp = rp_backup;
    1008  		    }
    1009  		  else
    1010  		    {
    1011  		      if (strcmp (fmt, HERE_D_FMT))
    1012  			s.decided = loc;
    1013  		      break;
    1014  		    }
    1015  		  s.decided = raw;
    1016  		}
    1017  	      if (!recursive (HERE_D_FMT))
    1018  		return NULL;
    1019  	      break;
    1020  	    case 'X':
    1021  	      if (s.decided != raw)
    1022  		{
    1023  		  const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
    1024  
    1025  		  if (*fmt == '\0')
    1026  		    fmt = _NL_CURRENT (LC_TIME, T_FMT);
    1027  
    1028  		  if (!recursive (fmt))
    1029  		    {
    1030  		      if (s.decided == loc)
    1031  			return NULL;
    1032  		      else
    1033  			rp = rp_backup;
    1034  		    }
    1035  		  else
    1036  		    {
    1037  		      if (strcmp (fmt, HERE_T_FMT))
    1038  			s.decided = loc;
    1039  		      break;
    1040  		    }
    1041  		  s.decided = raw;
    1042  		}
    1043  	      if (!recursive (HERE_T_FMT))
    1044  		return NULL;
    1045  	      break;
    1046  	    default:
    1047  	      return NULL;
    1048  	    }
    1049  	  break;
    1050  #else
    1051  	  /* We have no information about the era format.  Just use
    1052  	     the normal format.  */
    1053  	  if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
    1054  	      && *fmt != 'x' && *fmt != 'X')
    1055  	    /* This is an illegal format.  */
    1056  	    return NULL;
    1057  
    1058  	  goto start_over;
    1059  #endif
    1060  	case 'O':
    1061  	  switch (*fmt++)
    1062  	    {
    1063  	    case 'b':
    1064  	    case 'B':
    1065  	    case 'h':
    1066  	      /* Match month name.  Reprocess as plain 'B'.  */
    1067  	      fmt--;
    1068  	      goto start_over;
    1069  	    case 'd':
    1070  	    case 'e':
    1071  	      /* Match day of month using alternate numeric symbols.  */
    1072  	      get_alt_number (1, 31, 2);
    1073  	      tm->tm_mday = val;
    1074  	      s.have_mday = 1;
    1075  	      s.want_xday = 1;
    1076  	      break;
    1077  	    case 'H':
    1078  	      /* Match hour in 24-hour clock using alternate numeric
    1079  		 symbols.  */
    1080  	      get_alt_number (0, 23, 2);
    1081  	      tm->tm_hour = val;
    1082  	      s.have_I = 0;
    1083  	      break;
    1084  	    case 'I':
    1085  	      /* Match hour in 12-hour clock using alternate numeric
    1086  		 symbols.  */
    1087  	      get_alt_number (1, 12, 2);
    1088  	      tm->tm_hour = val % 12;
    1089  	      s.have_I = 1;
    1090  	      break;
    1091  	    case 'm':
    1092  	      /* Match month using alternate numeric symbols.  */
    1093  	      get_alt_number (1, 12, 2);
    1094  	      tm->tm_mon = val - 1;
    1095  	      s.have_mon = 1;
    1096  	      s.want_xday = 1;
    1097  	      break;
    1098  	    case 'M':
    1099  	      /* Match minutes using alternate numeric symbols.  */
    1100  	      get_alt_number (0, 59, 2);
    1101  	      tm->tm_min = val;
    1102  	      break;
    1103  	    case 'S':
    1104  	      /* Match seconds using alternate numeric symbols.  */
    1105  	      get_alt_number (0, 61, 2);
    1106  	      tm->tm_sec = val;
    1107  	      break;
    1108  	    case 'U':
    1109  	      get_alt_number (0, 53, 2);
    1110  	      s.week_no = val;
    1111  	      s.have_uweek = 1;
    1112  	      break;
    1113  	    case 'W':
    1114  	      get_alt_number (0, 53, 2);
    1115  	      s.week_no = val;
    1116  	      s.have_wweek = 1;
    1117  	      break;
    1118  	    case 'V':
    1119  	      get_alt_number (0, 53, 2);
    1120  	      /* XXX This cannot determine any field in TM without
    1121  		 further information.  */
    1122  	      break;
    1123  	    case 'w':
    1124  	      /* Match number of weekday using alternate numeric symbols.  */
    1125  	      get_alt_number (0, 6, 1);
    1126  	      tm->tm_wday = val;
    1127  	      s.have_wday = 1;
    1128  	      break;
    1129  	    case 'y':
    1130  	      /* Match year within century using alternate numeric symbols.  */
    1131  	      get_alt_number (0, 99, 2);
    1132  	      tm->tm_year = val >= 69 ? val : val + 100;
    1133  	      s.want_xday = 1;
    1134  	      break;
    1135  	    default:
    1136  	      return NULL;
    1137  	    }
    1138  	  break;
    1139  	default:
    1140  	  return NULL;
    1141  	}
    1142      }
    1143  
    1144    if (statep != NULL)
    1145      {
    1146        /* Recursive invocation, returning success, so
    1147  	 update parent's struct tm and state.  */
    1148        *(struct __strptime_state *) statep = s;
    1149        *tmp = tmb;
    1150        return (char *) rp;
    1151      }
    1152  
    1153    if (s.have_I && s.is_pm)
    1154      tm->tm_hour += 12;
    1155  
    1156    if (s.century != -1)
    1157      {
    1158        if (s.want_century)
    1159  	tm->tm_year = tm->tm_year % 100 + (s.century - 19) * 100;
    1160        else
    1161  	/* Only the century, but not the year.  Strange, but so be it.  */
    1162  	tm->tm_year = (s.century - 19) * 100;
    1163      }
    1164  
    1165    if (s.era_cnt != -1)
    1166      {
    1167        era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
    1168        if (era == NULL)
    1169  	return NULL;
    1170        if (s.want_era)
    1171  	tm->tm_year = (era->start_date[0]
    1172  		       + ((tm->tm_year - era->offset)
    1173  			  * era->absolute_direction));
    1174        else
    1175  	/* Era start year assumed.  */
    1176  	tm->tm_year = era->start_date[0];
    1177      }
    1178    else
    1179      if (s.want_era)
    1180        {
    1181  	/* No era found but we have seen an E modifier.  Rectify some
    1182  	   values.  */
    1183  	if (s.want_century && s.century == -1 && tm->tm_year < 69)
    1184  	  tm->tm_year += 100;
    1185        }
    1186  
    1187    if (s.want_xday && !s.have_wday)
    1188      {
    1189        if ( !(s.have_mon && s.have_mday) && s.have_yday)
    1190  	{
    1191  	  /* We don't have tm_mon and/or tm_mday, compute them.  */
    1192  	  int t_mon = 0;
    1193  	  while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
    1194  	      t_mon++;
    1195  	  if (!s.have_mon)
    1196  	      tm->tm_mon = t_mon - 1;
    1197  	  if (!s.have_mday)
    1198  	      tm->tm_mday =
    1199  		(tm->tm_yday
    1200  		 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
    1201  	  s.have_mon = 1;
    1202  	  s.have_mday = 1;
    1203  	}
    1204        /* Don't crash in day_of_the_week if tm_mon is uninitialized.  */
    1205        if (s.have_mon || (unsigned) tm->tm_mon <= 11)
    1206  	day_of_the_week (tm);
    1207      }
    1208  
    1209    if (s.want_xday && !s.have_yday && (s.have_mon || (unsigned) tm->tm_mon <= 11))
    1210      day_of_the_year (tm);
    1211  
    1212    if ((s.have_uweek || s.have_wweek) && s.have_wday)
    1213      {
    1214        int save_wday = tm->tm_wday;
    1215        int save_mday = tm->tm_mday;
    1216        int save_mon = tm->tm_mon;
    1217        int w_offset = s.have_uweek ? 0 : 1;
    1218  
    1219        tm->tm_mday = 1;
    1220        tm->tm_mon = 0;
    1221        day_of_the_week (tm);
    1222        if (s.have_mday)
    1223  	tm->tm_mday = save_mday;
    1224        if (s.have_mon)
    1225  	tm->tm_mon = save_mon;
    1226  
    1227        if (!s.have_yday)
    1228  	tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
    1229  		       + (s.week_no - 1) * 7
    1230  		       + (save_wday - w_offset + 7) % 7);
    1231  
    1232        if (!s.have_mday || !s.have_mon)
    1233  	{
    1234  	  int t_mon = 0;
    1235  	  while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
    1236  		 <= tm->tm_yday)
    1237  	    t_mon++;
    1238  	  if (!s.have_mon)
    1239  	    tm->tm_mon = t_mon - 1;
    1240  	  if (!s.have_mday)
    1241  	      tm->tm_mday =
    1242  		(tm->tm_yday
    1243  		 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
    1244  	}
    1245  
    1246        tm->tm_wday = save_wday;
    1247      }
    1248  
    1249    return (char *) rp;
    1250  }
    1251  
    1252  
    1253  char *
    1254  strptime (const char *buf, const char *format, struct tm *tm LOCALE_PARAM)
    1255  {
    1256    return __strptime_internal (buf, format, tm, NULL LOCALE_ARG);
    1257  }
    1258  
    1259  #ifdef _LIBC
    1260  weak_alias (__strptime_l, strptime_l)
    1261  #endif