root/maint/gnulib/lib/strptime.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. day_of_the_week
  2. day_of_the_year
  3. __strptime_internal
  4. strptime

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

/* [previous][next][first][last][top][bottom][index][help] */