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