Back to index

glibc  2.9
strptime_l.c
Go to the documentation of this file.
00001 /* Copyright (C) 2002, 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003 
00004    The GNU C Library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Lesser General Public
00006    License as published by the Free Software Foundation; either
00007    version 2.1 of the License, or (at your option) any later version.
00008 
00009    The GNU C Library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Lesser General Public License for more details.
00013 
00014    You should have received a copy of the GNU Lesser General Public
00015    License along with the GNU C Library; if not, write to the Free
00016    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00017    02111-1307 USA.  */
00018 
00019 #ifdef HAVE_CONFIG_H
00020 # include <config.h>
00021 #endif
00022 
00023 #include <assert.h>
00024 #include <ctype.h>
00025 #include <langinfo.h>
00026 #include <limits.h>
00027 #include <string.h>
00028 #include <time.h>
00029 #include <stdbool.h>
00030 
00031 #ifdef _LIBC
00032 # include "../locale/localeinfo.h"
00033 #endif
00034 
00035 
00036 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
00037 # ifdef _LIBC
00038 #  define localtime_r __localtime_r
00039 # else
00040 /* Approximate localtime_r as best we can in its absence.  */
00041 #  define localtime_r my_localtime_r
00042 static struct tm *localtime_r (const time_t *, struct tm *);
00043 static struct tm *
00044 localtime_r (t, tp)
00045      const time_t *t;
00046      struct tm *tp;
00047 {
00048   struct tm *l = localtime (t);
00049   if (! l)
00050     return 0;
00051   *tp = *l;
00052   return tp;
00053 }
00054 # endif /* ! _LIBC */
00055 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
00056 
00057 
00058 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
00059 #if defined __GNUC__ && __GNUC__ >= 2
00060 # define match_string(cs1, s2) \
00061   ({ size_t len = strlen (cs1);                                             \
00062      int result = __strncasecmp_l ((cs1), (s2), len, locale) == 0;          \
00063      if (result) (s2) += len;                                               \
00064      result; })
00065 #else
00066 /* Oh come on.  Get a reasonable compiler.  */
00067 # define match_string(cs1, s2) \
00068   (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
00069 #endif
00070 /* We intentionally do not use isdigit() for testing because this will
00071    lead to problems with the wide character version.  */
00072 #define get_number(from, to, n) \
00073   do {                                                               \
00074     int __n = n;                                                     \
00075     val = 0;                                                         \
00076     while (*rp == ' ')                                                      \
00077       ++rp;                                                          \
00078     if (*rp < '0' || *rp > '9')                                             \
00079       return NULL;                                                   \
00080     do {                                                             \
00081       val *= 10;                                                     \
00082       val += *rp++ - '0';                                            \
00083     } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9');             \
00084     if (val < from || val > to)                                             \
00085       return NULL;                                                   \
00086   } while (0)
00087 #ifdef _NL_CURRENT
00088 # define get_alt_number(from, to, n) \
00089   ({                                                                 \
00090      __label__ do_normal;                                            \
00091                                                                      \
00092      if (s.decided != raw)                                           \
00093        {                                                             \
00094         val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG);                  \
00095         if (val == -1 && s.decided != loc)                                  \
00096           {                                                          \
00097             s.decided = loc;                                                \
00098             goto do_normal;                                          \
00099           }                                                          \
00100        if (val < from || val > to)                                   \
00101          return NULL;                                                       \
00102        }                                                             \
00103      else                                                            \
00104        {                                                             \
00105        do_normal:                                                    \
00106         get_number (from, to, n);                                    \
00107        }                                                             \
00108     0;                                                               \
00109   })
00110 #else
00111 # define get_alt_number(from, to, n) \
00112   /* We don't have the alternate representation.  */                        \
00113   get_number(from, to, n)
00114 #endif
00115 #define recursive(new_fmt) \
00116   (*(new_fmt) != '\0'                                                       \
00117    && (rp = __strptime_internal (rp, (new_fmt), tm, &s LOCALE_ARG)) != NULL)
00118 
00119 
00120 #ifdef _LIBC
00121 /* This is defined in locale/C-time.c in the GNU libc.  */
00122 extern const struct locale_data _nl_C_LC_TIME attribute_hidden;
00123 
00124 # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
00125 # define ab_weekday_name \
00126   (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
00127 # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
00128 # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
00129 # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
00130 # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
00131 # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
00132 # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
00133 # define HERE_T_FMT_AMPM \
00134   (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
00135 # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
00136 
00137 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
00138 #else
00139 static char const weekday_name[][10] =
00140   {
00141     "Sunday", "Monday", "Tuesday", "Wednesday",
00142     "Thursday", "Friday", "Saturday"
00143   };
00144 static char const ab_weekday_name[][4] =
00145   {
00146     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
00147   };
00148 static char const month_name[][10] =
00149   {
00150     "January", "February", "March", "April", "May", "June",
00151     "July", "August", "September", "October", "November", "December"
00152   };
00153 static char const ab_month_name[][4] =
00154   {
00155     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
00156     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
00157   };
00158 # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
00159 # define HERE_D_FMT "%m/%d/%y"
00160 # define HERE_AM_STR "AM"
00161 # define HERE_PM_STR "PM"
00162 # define HERE_T_FMT_AMPM "%I:%M:%S %p"
00163 # define HERE_T_FMT "%H:%M:%S"
00164 
00165 static const unsigned short int __mon_yday[2][13] =
00166   {
00167     /* Normal years.  */
00168     { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
00169     /* Leap years.  */
00170     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
00171   };
00172 #endif
00173 
00174 #if defined _LIBC
00175 /* We use this code also for the extended locale handling where the
00176    function gets as an additional argument the locale which has to be
00177    used.  To access the values we have to redefine the _NL_CURRENT
00178    macro.  */
00179 # define strptime           __strptime_l
00180 # undef _NL_CURRENT
00181 # define _NL_CURRENT(category, item) \
00182   (current->values[_NL_ITEM_INDEX (item)].string)
00183 # undef _NL_CURRENT_WORD
00184 # define _NL_CURRENT_WORD(category, item) \
00185   (current->values[_NL_ITEM_INDEX (item)].word)
00186 # define LOCALE_PARAM , locale
00187 # define LOCALE_ARG , locale
00188 # define LOCALE_PARAM_PROTO , __locale_t locale
00189 # define LOCALE_PARAM_DECL __locale_t locale;
00190 # define HELPER_LOCALE_ARG , current
00191 # define ISSPACE(Ch) __isspace_l (Ch, locale)
00192 #else
00193 # define LOCALE_PARAM
00194 # define LOCALE_ARG
00195 # define LOCALE_PARAM_DECL
00196 # define LOCALE_PARAM_PROTO
00197 # define HELPER_LOCALE_ARG
00198 # define ISSPACE(Ch) isspace (Ch)
00199 #endif
00200 
00201 
00202 
00203 
00204 #ifndef __isleap
00205 /* Nonzero if YEAR is a leap year (every 4 years,
00206    except every 100th isn't, and every 400th is).  */
00207 # define __isleap(year)     \
00208   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
00209 #endif
00210 
00211 /* Compute the day of the week.  */
00212 static void
00213 day_of_the_week (struct tm *tm)
00214 {
00215   /* We know that January 1st 1970 was a Thursday (= 4).  Compute the
00216      the difference between this data in the one on TM and so determine
00217      the weekday.  */
00218   int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
00219   int wday = (-473
00220              + (365 * (tm->tm_year - 70))
00221              + (corr_year / 4)
00222              - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
00223              + (((corr_year / 4) / 25) / 4)
00224              + __mon_yday[0][tm->tm_mon]
00225              + tm->tm_mday - 1);
00226   tm->tm_wday = ((wday % 7) + 7) % 7;
00227 }
00228 
00229 /* Compute the day of the year.  */
00230 static void
00231 day_of_the_year (struct tm *tm)
00232 {
00233   tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
00234                + (tm->tm_mday - 1));
00235 }
00236 
00237 
00238 #ifdef _LIBC
00239 char *
00240 internal_function
00241 #else
00242 static char *
00243 #endif
00244 __strptime_internal (rp, fmt, tmp, statep LOCALE_PARAM)
00245      const char *rp;
00246      const char *fmt;
00247      struct tm *tmp;
00248      void *statep;
00249      LOCALE_PARAM_DECL
00250 {
00251 #ifdef _LIBC
00252   struct locale_data *const current = locale->__locales[LC_TIME];
00253 #endif
00254 
00255   const char *rp_backup;
00256   const char *rp_longest;
00257   int cnt;
00258   int cnt_longest;
00259   size_t val;
00260   size_t num_eras;
00261   struct era_entry *era = NULL;
00262   enum ptime_locale_status { not, loc, raw } decided_longest;
00263   struct __strptime_state
00264   {
00265     unsigned int have_I : 1;
00266     unsigned int have_wday : 1;
00267     unsigned int have_yday : 1;
00268     unsigned int have_mon : 1;
00269     unsigned int have_mday : 1;
00270     unsigned int have_uweek : 1;
00271     unsigned int have_wweek : 1;
00272     unsigned int is_pm : 1;
00273     unsigned int want_century : 1;
00274     unsigned int want_era : 1;
00275     unsigned int want_xday : 1;
00276     enum ptime_locale_status decided : 2;
00277     signed char week_no;
00278     signed char century;
00279     int era_cnt;
00280   } s;
00281   struct tm tmb;
00282   struct tm *tm;
00283 
00284   if (statep == NULL)
00285     {
00286       memset (&s, 0, sizeof (s));
00287       s.century = -1;
00288       s.era_cnt = -1;
00289 #ifdef _NL_CURRENT
00290       s.decided = not;
00291 #else
00292       s.decided = raw;
00293 #endif
00294       tm = tmp;
00295     }
00296   else
00297     {
00298       s = *(struct __strptime_state *) statep;
00299       tmb = *tmp;
00300       tm = &tmb;
00301     }
00302 
00303   while (*fmt != '\0')
00304     {
00305       /* A white space in the format string matches 0 more or white
00306         space in the input string.  */
00307       if (ISSPACE (*fmt))
00308        {
00309          while (ISSPACE (*rp))
00310            ++rp;
00311          ++fmt;
00312          continue;
00313        }
00314 
00315       /* Any character but `%' must be matched by the same character
00316         in the iput string.  */
00317       if (*fmt != '%')
00318        {
00319          match_char (*fmt++, *rp++);
00320          continue;
00321        }
00322 
00323       ++fmt;
00324       if (statep != NULL)
00325        {
00326          /* In recursive calls silently discard strftime modifiers.  */
00327          while (*fmt == '-' || *fmt == '_' || *fmt == '0'
00328                || *fmt == '^' || *fmt == '#')
00329            ++fmt;
00330 
00331          /* And field width.  */
00332          while (*fmt >= '0' && *fmt <= '9')
00333            ++fmt;
00334        }
00335 
00336 #ifndef _NL_CURRENT
00337       /* We need this for handling the `E' modifier.  */
00338     start_over:
00339 #endif
00340 
00341       /* Make back up of current processing pointer.  */
00342       rp_backup = rp;
00343 
00344       switch (*fmt++)
00345        {
00346        case '%':
00347          /* Match the `%' character itself.  */
00348          match_char ('%', *rp++);
00349          break;
00350        case 'a':
00351        case 'A':
00352          /* Match day of week.  */
00353          rp_longest = NULL;
00354          decided_longest = s.decided;
00355          cnt_longest = -1;
00356          for (cnt = 0; cnt < 7; ++cnt)
00357            {
00358              const char *trp;
00359 #ifdef _NL_CURRENT
00360              if (s.decided !=raw)
00361               {
00362                 trp = rp;
00363                 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), trp)
00364                     && trp > rp_longest)
00365                   {
00366                     rp_longest = trp;
00367                     cnt_longest = cnt;
00368                     if (s.decided == not
00369                        && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
00370                                  weekday_name[cnt]))
00371                      decided_longest = loc;
00372                   }
00373                 trp = rp;
00374                 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), trp)
00375                     && trp > rp_longest)
00376                   {
00377                     rp_longest = trp;
00378                     cnt_longest = cnt;
00379                     if (s.decided == not
00380                        && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
00381                                  ab_weekday_name[cnt]))
00382                      decided_longest = loc;
00383                   }
00384               }
00385 #endif
00386              if (s.decided != loc
00387                 && (((trp = rp, match_string (weekday_name[cnt], trp))
00388                      && trp > rp_longest)
00389                     || ((trp = rp, match_string (ab_weekday_name[cnt], rp))
00390                        && trp > rp_longest)))
00391               {
00392                 rp_longest = trp;
00393                 cnt_longest = cnt;
00394                 decided_longest = raw;
00395               }
00396            }
00397          if (rp_longest == NULL)
00398            /* Does not match a weekday name.  */
00399            return NULL;
00400          rp = rp_longest;
00401          s.decided = decided_longest;
00402          tm->tm_wday = cnt_longest;
00403          s.have_wday = 1;
00404          break;
00405        case 'b':
00406        case 'B':
00407        case 'h':
00408          /* Match month name.  */
00409          rp_longest = NULL;
00410          decided_longest = s.decided;
00411          cnt_longest = -1;
00412          for (cnt = 0; cnt < 12; ++cnt)
00413            {
00414              const char *trp;
00415 #ifdef _NL_CURRENT
00416              if (s.decided !=raw)
00417               {
00418                 trp = rp;
00419                 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), trp)
00420                     && trp > rp_longest)
00421                   {
00422                     rp_longest = trp;
00423                     cnt_longest = cnt;
00424                     if (s.decided == not
00425                        && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
00426                                  month_name[cnt]))
00427                      decided_longest = loc;
00428                   }
00429                 trp = rp;
00430                 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), trp)
00431                     && trp > rp_longest)
00432                   {
00433                     rp_longest = trp;
00434                     cnt_longest = cnt;
00435                     if (s.decided == not
00436                        && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
00437                                  ab_month_name[cnt]))
00438                      decided_longest = loc;
00439                   }
00440               }
00441 #endif
00442              if (s.decided != loc
00443                 && (((trp = rp, match_string (month_name[cnt], trp))
00444                      && trp > rp_longest)
00445                     || ((trp = rp, match_string (ab_month_name[cnt], trp))
00446                        && trp > rp_longest)))
00447               {
00448                 rp_longest = trp;
00449                 cnt_longest = cnt;
00450                 decided_longest = raw;
00451               }
00452            }
00453          if (rp_longest == NULL)
00454            /* Does not match a month name.  */
00455            return NULL;
00456          rp = rp_longest;
00457          s.decided = decided_longest;
00458          tm->tm_mon = cnt_longest;
00459          s.have_mon = 1;
00460          s.want_xday = 1;
00461          break;
00462        case 'c':
00463          /* Match locale's date and time format.  */
00464 #ifdef _NL_CURRENT
00465          if (s.decided != raw)
00466            {
00467              if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
00468               {
00469                 if (s.decided == loc)
00470                   return NULL;
00471                 else
00472                   rp = rp_backup;
00473               }
00474              else
00475               {
00476                 if (s.decided == not &&
00477                     strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
00478                   s.decided = loc;
00479                 s.want_xday = 1;
00480                 break;
00481               }
00482              s.decided = raw;
00483            }
00484 #endif
00485          if (!recursive (HERE_D_T_FMT))
00486            return NULL;
00487          s.want_xday = 1;
00488          break;
00489        case 'C':
00490          /* Match century number.  */
00491        match_century:
00492          get_number (0, 99, 2);
00493          s.century = val;
00494          s.want_xday = 1;
00495          break;
00496        case 'd':
00497        case 'e':
00498          /* Match day of month.  */
00499          get_number (1, 31, 2);
00500          tm->tm_mday = val;
00501          s.have_mday = 1;
00502          s.want_xday = 1;
00503          break;
00504        case 'F':
00505          if (!recursive ("%Y-%m-%d"))
00506            return NULL;
00507          s.want_xday = 1;
00508          break;
00509        case 'x':
00510 #ifdef _NL_CURRENT
00511          if (s.decided != raw)
00512            {
00513              if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
00514               {
00515                 if (s.decided == loc)
00516                   return NULL;
00517                 else
00518                   rp = rp_backup;
00519               }
00520              else
00521               {
00522                 if (s.decided == not
00523                     && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
00524                   s.decided = loc;
00525                 s.want_xday = 1;
00526                 break;
00527               }
00528              s.decided = raw;
00529            }
00530 #endif
00531          /* Fall through.  */
00532        case 'D':
00533          /* Match standard day format.  */
00534          if (!recursive (HERE_D_FMT))
00535            return NULL;
00536          s.want_xday = 1;
00537          break;
00538        case 'k':
00539        case 'H':
00540          /* Match hour in 24-hour clock.  */
00541          get_number (0, 23, 2);
00542          tm->tm_hour = val;
00543          s.have_I = 0;
00544          break;
00545        case 'l':
00546          /* Match hour in 12-hour clock.  GNU extension.  */
00547        case 'I':
00548          /* Match hour in 12-hour clock.  */
00549          get_number (1, 12, 2);
00550          tm->tm_hour = val % 12;
00551          s.have_I = 1;
00552          break;
00553        case 'j':
00554          /* Match day number of year.  */
00555          get_number (1, 366, 3);
00556          tm->tm_yday = val - 1;
00557          s.have_yday = 1;
00558          break;
00559        case 'm':
00560          /* Match number of month.  */
00561          get_number (1, 12, 2);
00562          tm->tm_mon = val - 1;
00563          s.have_mon = 1;
00564          s.want_xday = 1;
00565          break;
00566        case 'M':
00567          /* Match minute.  */
00568          get_number (0, 59, 2);
00569          tm->tm_min = val;
00570          break;
00571        case 'n':
00572        case 't':
00573          /* Match any white space.  */
00574          while (ISSPACE (*rp))
00575            ++rp;
00576          break;
00577        case 'p':
00578          /* Match locale's equivalent of AM/PM.  */
00579 #ifdef _NL_CURRENT
00580          if (s.decided != raw)
00581            {
00582              if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
00583               {
00584                 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
00585                   s.decided = loc;
00586                 s.is_pm = 0;
00587                 break;
00588               }
00589              if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
00590               {
00591                 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
00592                   s.decided = loc;
00593                 s.is_pm = 1;
00594                 break;
00595               }
00596              s.decided = raw;
00597            }
00598 #endif
00599          if (!match_string (HERE_AM_STR, rp))
00600            {
00601              if (match_string (HERE_PM_STR, rp))
00602               s.is_pm = 1;
00603              else
00604               return NULL;
00605            }
00606          else
00607            s.is_pm = 0;
00608          break;
00609        case 'r':
00610 #ifdef _NL_CURRENT
00611          if (s.decided != raw)
00612            {
00613              if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
00614               {
00615                 if (s.decided == loc)
00616                   return NULL;
00617                 else
00618                   rp = rp_backup;
00619               }
00620              else
00621               {
00622                 if (s.decided == not &&
00623                     strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
00624                            HERE_T_FMT_AMPM))
00625                   s.decided = loc;
00626                 break;
00627               }
00628              s.decided = raw;
00629            }
00630 #endif
00631          if (!recursive (HERE_T_FMT_AMPM))
00632            return NULL;
00633          break;
00634        case 'R':
00635          if (!recursive ("%H:%M"))
00636            return NULL;
00637          break;
00638        case 's':
00639          {
00640            /* The number of seconds may be very high so we cannot use
00641               the `get_number' macro.  Instead read the number
00642               character for character and construct the result while
00643               doing this.  */
00644            time_t secs = 0;
00645            if (*rp < '0' || *rp > '9')
00646              /* We need at least one digit.  */
00647              return NULL;
00648 
00649            do
00650              {
00651               secs *= 10;
00652               secs += *rp++ - '0';
00653              }
00654            while (*rp >= '0' && *rp <= '9');
00655 
00656            if (localtime_r (&secs, tm) == NULL)
00657              /* Error in function.  */
00658              return NULL;
00659          }
00660          break;
00661        case 'S':
00662          get_number (0, 61, 2);
00663          tm->tm_sec = val;
00664          break;
00665        case 'X':
00666 #ifdef _NL_CURRENT
00667          if (s.decided != raw)
00668            {
00669              if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
00670               {
00671                 if (s.decided == loc)
00672                   return NULL;
00673                 else
00674                   rp = rp_backup;
00675               }
00676              else
00677               {
00678                 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
00679                   s.decided = loc;
00680                 break;
00681               }
00682              s.decided = raw;
00683            }
00684 #endif
00685          /* Fall through.  */
00686        case 'T':
00687          if (!recursive (HERE_T_FMT))
00688            return NULL;
00689          break;
00690        case 'u':
00691          get_number (1, 7, 1);
00692          tm->tm_wday = val % 7;
00693          s.have_wday = 1;
00694          break;
00695        case 'g':
00696          get_number (0, 99, 2);
00697          /* XXX This cannot determine any field in TM.  */
00698          break;
00699        case 'G':
00700          if (*rp < '0' || *rp > '9')
00701            return NULL;
00702          /* XXX Ignore the number since we would need some more
00703             information to compute a real date.  */
00704          do
00705            ++rp;
00706          while (*rp >= '0' && *rp <= '9');
00707          break;
00708        case 'U':
00709          get_number (0, 53, 2);
00710          s.week_no = val;
00711          s.have_uweek = 1;
00712          break;
00713        case 'W':
00714          get_number (0, 53, 2);
00715          s.week_no = val;
00716          s.have_wweek = 1;
00717          break;
00718        case 'V':
00719          get_number (0, 53, 2);
00720          /* XXX This cannot determine any field in TM without some
00721             information.  */
00722          break;
00723        case 'w':
00724          /* Match number of weekday.  */
00725          get_number (0, 6, 1);
00726          tm->tm_wday = val;
00727          s.have_wday = 1;
00728          break;
00729        case 'y':
00730        match_year_in_century:
00731          /* Match year within century.  */
00732          get_number (0, 99, 2);
00733          /* The "Year 2000: The Millennium Rollover" paper suggests that
00734             values in the range 69-99 refer to the twentieth century.  */
00735          tm->tm_year = val >= 69 ? val : val + 100;
00736          /* Indicate that we want to use the century, if specified.  */
00737          s.want_century = 1;
00738          s.want_xday = 1;
00739          break;
00740        case 'Y':
00741          /* Match year including century number.  */
00742          get_number (0, 9999, 4);
00743          tm->tm_year = val - 1900;
00744          s.want_century = 0;
00745          s.want_xday = 1;
00746          break;
00747        case 'Z':
00748          /* XXX How to handle this?  */
00749          break;
00750        case 'z':
00751          /* We recognize two formats: if two digits are given, these
00752             specify hours.  If fours digits are used, minutes are
00753             also specified.  */
00754          {
00755            val = 0;
00756            while (*rp == ' ')
00757              ++rp;
00758            if (*rp != '+' && *rp != '-')
00759              return NULL;
00760            bool neg = *rp++ == '-';
00761            int n = 0;
00762            while (n < 4 && *rp >= '0' && *rp <= '9')
00763              {
00764               val = val * 10 + *rp++ - '0';
00765               ++n;
00766              }
00767            if (n == 2)
00768              val *= 100;
00769            else if (n != 4)
00770              /* Only two or four digits recognized.  */
00771              return NULL;
00772            else
00773              {
00774               /* We have to convert the minutes into decimal.  */
00775               if (val % 100 >= 60)
00776                 return NULL;
00777               val = (val / 100) * 100 + ((val % 100) * 50) / 30;
00778              }
00779            if (val > 1200)
00780              return NULL;
00781            tm->tm_gmtoff = (val * 3600) / 100;
00782            if (neg)
00783              tm->tm_gmtoff = -tm->tm_gmtoff;
00784          }
00785          break;
00786        case 'E':
00787 #ifdef _NL_CURRENT
00788          switch (*fmt++)
00789            {
00790            case 'c':
00791              /* Match locale's alternate date and time format.  */
00792              if (s.decided != raw)
00793               {
00794                 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
00795 
00796                 if (*fmt == '\0')
00797                   fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
00798 
00799                 if (!recursive (fmt))
00800                   {
00801                     if (s.decided == loc)
00802                      return NULL;
00803                     else
00804                      rp = rp_backup;
00805                   }
00806                 else
00807                   {
00808                     if (strcmp (fmt, HERE_D_T_FMT))
00809                      s.decided = loc;
00810                     s.want_xday = 1;
00811                     break;
00812                   }
00813                 s.decided = raw;
00814               }
00815              /* The C locale has no era information, so use the
00816                normal representation.  */
00817              if (!recursive (HERE_D_T_FMT))
00818               return NULL;
00819              s.want_xday = 1;
00820              break;
00821            case 'C':
00822              if (s.decided != raw)
00823               {
00824                 if (s.era_cnt >= 0)
00825                   {
00826                     era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
00827                     if (era != NULL && match_string (era->era_name, rp))
00828                      {
00829                        s.decided = loc;
00830                        break;
00831                      }
00832                     else
00833                      return NULL;
00834                   }
00835 
00836                 num_eras = _NL_CURRENT_WORD (LC_TIME,
00837                                           _NL_TIME_ERA_NUM_ENTRIES);
00838                 for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
00839                      ++s.era_cnt, rp = rp_backup)
00840                   {
00841                     era = _nl_select_era_entry (s.era_cnt
00842                                             HELPER_LOCALE_ARG);
00843                     if (era != NULL && match_string (era->era_name, rp))
00844                      {
00845                        s.decided = loc;
00846                        break;
00847                      }
00848                   }
00849                 if (s.era_cnt != (int) num_eras)
00850                   break;
00851 
00852                 s.era_cnt = -1;
00853                 if (s.decided == loc)
00854                   return NULL;
00855 
00856                 s.decided = raw;
00857               }
00858              /* The C locale has no era information, so use the
00859                normal representation.  */
00860              goto match_century;
00861            case 'y':
00862              if (s.decided != raw)
00863               {
00864                 get_number(0, 9999, 4);
00865                 tm->tm_year = val;
00866                 s.want_era = 1;
00867                 s.want_xday = 1;
00868                 s.want_century = 1;
00869 
00870                 if (s.era_cnt >= 0)
00871                   {
00872                     assert (s.decided == loc);
00873 
00874                     era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
00875                     bool match = false;
00876                     if (era != NULL)
00877                      {
00878                        int delta = ((tm->tm_year - era->offset)
00879                                    * era->absolute_direction);
00880                        match = (delta >= 0
00881                                && delta < (((int64_t) era->stop_date[0]
00882                                           - (int64_t) era->start_date[0])
00883                                           * era->absolute_direction));
00884                      }
00885                     if (! match)
00886                      return NULL;
00887 
00888                     break;
00889                   }
00890 
00891                 num_eras = _NL_CURRENT_WORD (LC_TIME,
00892                                           _NL_TIME_ERA_NUM_ENTRIES);
00893                 for (s.era_cnt = 0; s.era_cnt < (int) num_eras; ++s.era_cnt)
00894                   {
00895                     era = _nl_select_era_entry (s.era_cnt
00896                                             HELPER_LOCALE_ARG);
00897                     if (era != NULL)
00898                      {
00899                        int delta = ((tm->tm_year - era->offset)
00900                                    * era->absolute_direction);
00901                        if (delta >= 0
00902                            && delta < (((int64_t) era->stop_date[0]
00903                                       - (int64_t) era->start_date[0])
00904                                      * era->absolute_direction))
00905                          {
00906                            s.decided = loc;
00907                            break;
00908                          }
00909                      }
00910                   }
00911                 if (s.era_cnt != (int) num_eras)
00912                   break;
00913 
00914                 s.era_cnt = -1;
00915                 if (s.decided == loc)
00916                   return NULL;
00917 
00918                 s.decided = raw;
00919               }
00920 
00921              goto match_year_in_century;
00922            case 'Y':
00923              if (s.decided != raw)
00924               {
00925                 num_eras = _NL_CURRENT_WORD (LC_TIME,
00926                                           _NL_TIME_ERA_NUM_ENTRIES);
00927                 for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
00928                      ++s.era_cnt, rp = rp_backup)
00929                   {
00930                     era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
00931                     if (era != NULL && recursive (era->era_format))
00932                      break;
00933                   }
00934                 if (s.era_cnt == (int) num_eras)
00935                   {
00936                     s.era_cnt = -1;
00937                     if (s.decided == loc)
00938                      return NULL;
00939                     else
00940                      rp = rp_backup;
00941                   }
00942                 else
00943                   {
00944                     s.decided = loc;
00945                     break;
00946                   }
00947 
00948                 s.decided = raw;
00949               }
00950              get_number (0, 9999, 4);
00951              tm->tm_year = val - 1900;
00952              s.want_century = 0;
00953              s.want_xday = 1;
00954              break;
00955            case 'x':
00956              if (s.decided != raw)
00957               {
00958                 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
00959 
00960                 if (*fmt == '\0')
00961                   fmt = _NL_CURRENT (LC_TIME, D_FMT);
00962 
00963                 if (!recursive (fmt))
00964                   {
00965                     if (s.decided == loc)
00966                      return NULL;
00967                     else
00968                      rp = rp_backup;
00969                   }
00970                 else
00971                   {
00972                     if (strcmp (fmt, HERE_D_FMT))
00973                      s.decided = loc;
00974                     break;
00975                   }
00976                 s.decided = raw;
00977               }
00978              if (!recursive (HERE_D_FMT))
00979               return NULL;
00980              break;
00981            case 'X':
00982              if (s.decided != raw)
00983               {
00984                 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
00985 
00986                 if (*fmt == '\0')
00987                   fmt = _NL_CURRENT (LC_TIME, T_FMT);
00988 
00989                 if (!recursive (fmt))
00990                   {
00991                     if (s.decided == loc)
00992                      return NULL;
00993                     else
00994                      rp = rp_backup;
00995                   }
00996                 else
00997                   {
00998                     if (strcmp (fmt, HERE_T_FMT))
00999                      s.decided = loc;
01000                     break;
01001                   }
01002                 s.decided = raw;
01003               }
01004              if (!recursive (HERE_T_FMT))
01005               return NULL;
01006              break;
01007            default:
01008              return NULL;
01009            }
01010          break;
01011 #else
01012          /* We have no information about the era format.  Just use
01013             the normal format.  */
01014          if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
01015              && *fmt != 'x' && *fmt != 'X')
01016            /* This is an illegal format.  */
01017            return NULL;
01018 
01019          goto start_over;
01020 #endif
01021        case 'O':
01022          switch (*fmt++)
01023            {
01024            case 'd':
01025            case 'e':
01026              /* Match day of month using alternate numeric symbols.  */
01027              get_alt_number (1, 31, 2);
01028              tm->tm_mday = val;
01029              s.have_mday = 1;
01030              s.want_xday = 1;
01031              break;
01032            case 'H':
01033              /* Match hour in 24-hour clock using alternate numeric
01034                symbols.  */
01035              get_alt_number (0, 23, 2);
01036              tm->tm_hour = val;
01037              s.have_I = 0;
01038              break;
01039            case 'I':
01040              /* Match hour in 12-hour clock using alternate numeric
01041                symbols.  */
01042              get_alt_number (1, 12, 2);
01043              tm->tm_hour = val % 12;
01044              s.have_I = 1;
01045              break;
01046            case 'm':
01047              /* Match month using alternate numeric symbols.  */
01048              get_alt_number (1, 12, 2);
01049              tm->tm_mon = val - 1;
01050              s.have_mon = 1;
01051              s.want_xday = 1;
01052              break;
01053            case 'M':
01054              /* Match minutes using alternate numeric symbols.  */
01055              get_alt_number (0, 59, 2);
01056              tm->tm_min = val;
01057              break;
01058            case 'S':
01059              /* Match seconds using alternate numeric symbols.  */
01060              get_alt_number (0, 61, 2);
01061              tm->tm_sec = val;
01062              break;
01063            case 'U':
01064              get_alt_number (0, 53, 2);
01065              s.week_no = val;
01066              s.have_uweek = 1;
01067              break;
01068            case 'W':
01069              get_alt_number (0, 53, 2);
01070              s.week_no = val;
01071              s.have_wweek = 1;
01072              break;
01073            case 'V':
01074              get_alt_number (0, 53, 2);
01075              /* XXX This cannot determine any field in TM without
01076                further information.  */
01077              break;
01078            case 'w':
01079              /* Match number of weekday using alternate numeric symbols.  */
01080              get_alt_number (0, 6, 1);
01081              tm->tm_wday = val;
01082              s.have_wday = 1;
01083              break;
01084            case 'y':
01085              /* Match year within century using alternate numeric symbols.  */
01086              get_alt_number (0, 99, 2);
01087              tm->tm_year = val >= 69 ? val : val + 100;
01088              s.want_xday = 1;
01089              break;
01090            default:
01091              return NULL;
01092            }
01093          break;
01094        default:
01095          return NULL;
01096        }
01097     }
01098 
01099   if (statep != NULL)
01100     {
01101       /* Recursive invocation, returning success, so
01102         update parent's struct tm and state.  */
01103       *(struct __strptime_state *) statep = s;
01104       *tmp = tmb;
01105       return (char *) rp;
01106     }
01107 
01108   if (s.have_I && s.is_pm)
01109     tm->tm_hour += 12;
01110 
01111   if (s.century != -1)
01112     {
01113       if (s.want_century)
01114        tm->tm_year = tm->tm_year % 100 + (s.century - 19) * 100;
01115       else
01116        /* Only the century, but not the year.  Strange, but so be it.  */
01117        tm->tm_year = (s.century - 19) * 100;
01118     }
01119 
01120   if (s.era_cnt != -1)
01121     {
01122       era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
01123       if (era == NULL)
01124        return NULL;
01125       if (s.want_era)
01126        tm->tm_year = (era->start_date[0]
01127                      + ((tm->tm_year - era->offset)
01128                        * era->absolute_direction));
01129       else
01130        /* Era start year assumed.  */
01131        tm->tm_year = era->start_date[0];
01132     }
01133   else
01134     if (s.want_era)
01135       {
01136        /* No era found but we have seen an E modifier.  Rectify some
01137           values.  */
01138        if (s.want_century && s.century == -1 && tm->tm_year < 69)
01139          tm->tm_year += 100;
01140       }
01141 
01142   if (s.want_xday && !s.have_wday)
01143     {
01144       if ( !(s.have_mon && s.have_mday) && s.have_yday)
01145        {
01146          /* We don't have tm_mon and/or tm_mday, compute them.  */
01147          int t_mon = 0;
01148          while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
01149              t_mon++;
01150          if (!s.have_mon)
01151              tm->tm_mon = t_mon - 1;
01152          if (!s.have_mday)
01153              tm->tm_mday =
01154               (tm->tm_yday
01155                - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
01156          s.have_mon = 1;
01157          s.have_mday = 1;
01158        }
01159       /* Don't crash in day_of_the_week if tm_mon is uninitialized.  */
01160       if (s.have_mon || (unsigned) tm->tm_mon <= 11)
01161        day_of_the_week (tm);
01162     }
01163 
01164   if (s.want_xday && !s.have_yday && (s.have_mon || (unsigned) tm->tm_mon <= 11))
01165     day_of_the_year (tm);
01166 
01167   if ((s.have_uweek || s.have_wweek) && s.have_wday)
01168     {
01169       int save_wday = tm->tm_wday;
01170       int save_mday = tm->tm_mday;
01171       int save_mon = tm->tm_mon;
01172       int w_offset = s.have_uweek ? 0 : 1;
01173 
01174       tm->tm_mday = 1;
01175       tm->tm_mon = 0;
01176       day_of_the_week (tm);
01177       if (s.have_mday)
01178        tm->tm_mday = save_mday;
01179       if (s.have_mon)
01180        tm->tm_mon = save_mon;
01181 
01182       if (!s.have_yday)
01183        tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
01184                      + (s.week_no - 1) *7
01185                      + save_wday - w_offset);
01186 
01187       if (!s.have_mday || !s.have_mon)
01188        {
01189          int t_mon = 0;
01190          while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
01191                <= tm->tm_yday)
01192            t_mon++;
01193          if (!s.have_mon)
01194            tm->tm_mon = t_mon - 1;
01195          if (!s.have_mday)
01196              tm->tm_mday =
01197               (tm->tm_yday
01198                - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
01199        }
01200 
01201       tm->tm_wday = save_wday;
01202     }
01203 
01204   return (char *) rp;
01205 }
01206 
01207 
01208 char *
01209 strptime (buf, format, tm LOCALE_PARAM)
01210      const char *buf;
01211      const char *format;
01212      struct tm *tm;
01213      LOCALE_PARAM_DECL
01214 {
01215   return __strptime_internal (buf, format, tm, NULL LOCALE_ARG);
01216 }
01217 
01218 #ifdef _LIBC
01219 weak_alias (__strptime_l, strptime_l)
01220 #endif