maint: update copyright
[gnulib.git] / lib / strptime.c
1 /* Copyright (C) 2002, 2004-2005, 2007, 2009-2014 Free Software Foundation,
2    Inc.
3    This file is part of the GNU C Library.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program 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 General Public License for more details.
14
15    You should have received a copy of the GNU General Public License along
16    with this program; if not, see <http://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 <stdbool.h>
32
33 #ifdef _LIBC
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 wday = (-473
206               + (365 * (tm->tm_year - 70))
207               + (corr_year / 4)
208               - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
209               + (((corr_year / 4) / 25) / 4)
210               + __mon_yday[0][tm->tm_mon]
211               + tm->tm_mday - 1);
212   tm->tm_wday = ((wday % 7) + 7) % 7;
213 }
214
215 /* Compute the day of the year.  */
216 static void
217 day_of_the_year (struct tm *tm)
218 {
219   tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
220                  + (tm->tm_mday - 1));
221 }
222
223
224 #ifdef _LIBC
225 char *
226 internal_function
227 #else
228 static char *
229 #endif
230 __strptime_internal (rp, fmt, tm, decided, era_cnt LOCALE_PARAM)
231      const char *rp;
232      const char *fmt;
233      struct tm *tm;
234      enum ptime_locale_status *decided;
235      int era_cnt;
236      LOCALE_PARAM_DECL
237 {
238 #ifdef _LIBC
239   struct locale_data *const current = locale->__locales[LC_TIME];
240 #endif
241
242   int cnt;
243   size_t val;
244   int have_I, is_pm;
245   int century, want_century;
246   int want_era;
247   int have_wday, want_xday;
248   int have_yday;
249   int have_mon, have_mday;
250   int have_uweek, have_wweek;
251   int week_no;
252 #ifdef _NL_CURRENT
253   size_t num_eras;
254   struct era_entry *era = NULL;
255   const char *rp_backup;
256 #endif
257
258   have_I = is_pm = 0;
259   century = -1;
260   want_century = 0;
261   want_era = 0;
262   week_no = 0;
263
264   have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
265   have_wweek = 0;
266
267   while (*fmt != '\0')
268     {
269       /* A white space in the format string matches 0 more or white
270          space in the input string.  */
271       if (ISSPACE (*fmt))
272         {
273           while (ISSPACE (*rp))
274             ++rp;
275           ++fmt;
276           continue;
277         }
278
279       /* Any character but '%' must be matched by the same character
280          in the iput string.  */
281       if (*fmt != '%')
282         {
283           match_char (*fmt++, *rp++);
284           continue;
285         }
286
287       ++fmt;
288 #ifndef _NL_CURRENT
289       /* We need this for handling the 'E' modifier.  */
290     start_over:
291 #else
292       /* Make back up of current processing pointer.  */
293       rp_backup = rp;
294 #endif
295
296       switch (*fmt++)
297         {
298         case '%':
299           /* Match the '%' character itself.  */
300           match_char ('%', *rp++);
301           break;
302         case 'a':
303         case 'A':
304           /* Match day of week.  */
305           for (cnt = 0; cnt < 7; ++cnt)
306             {
307 #ifdef _NL_CURRENT
308               if (*decided !=raw)
309                 {
310                   if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
311                     {
312                       if (*decided == not
313                           && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
314                                      weekday_name[cnt]))
315                         *decided = loc;
316                       break;
317                     }
318                   if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
319                     {
320                       if (*decided == not
321                           && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
322                                      ab_weekday_name[cnt]))
323                         *decided = loc;
324                       break;
325                     }
326                 }
327 #endif
328               if (*decided != loc
329                   && (match_string (weekday_name[cnt], rp)
330                       || match_string (ab_weekday_name[cnt], rp)))
331                 {
332                   *decided = raw;
333                   break;
334                 }
335             }
336           if (cnt == 7)
337             /* Does not match a weekday name.  */
338             return NULL;
339           tm->tm_wday = cnt;
340           have_wday = 1;
341           break;
342         case 'b':
343         case 'B':
344         case 'h':
345           /* Match month name.  */
346           for (cnt = 0; cnt < 12; ++cnt)
347             {
348 #ifdef _NL_CURRENT
349               if (*decided !=raw)
350                 {
351                   if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
352                     {
353                       if (*decided == not
354                           && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
355                                      month_name[cnt]))
356                         *decided = loc;
357                       break;
358                     }
359                   if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
360                     {
361                       if (*decided == not
362                           && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
363                                      ab_month_name[cnt]))
364                         *decided = loc;
365                       break;
366                     }
367                 }
368 #endif
369               if (match_string (month_name[cnt], rp)
370                   || match_string (ab_month_name[cnt], rp))
371                 {
372                   *decided = raw;
373                   break;
374                 }
375             }
376           if (cnt == 12)
377             /* Does not match a month name.  */
378             return NULL;
379           tm->tm_mon = cnt;
380           want_xday = 1;
381           break;
382         case 'c':
383           /* Match locale's date and time format.  */
384 #ifdef _NL_CURRENT
385           if (*decided != raw)
386             {
387               if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
388                 {
389                   if (*decided == loc)
390                     return NULL;
391                   else
392                     rp = rp_backup;
393                 }
394               else
395                 {
396                   if (*decided == not &&
397                       strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
398                     *decided = loc;
399                   want_xday = 1;
400                   break;
401                 }
402               *decided = raw;
403             }
404 #endif
405           if (!recursive (HERE_D_T_FMT))
406             return NULL;
407           want_xday = 1;
408           break;
409         case 'C':
410           /* Match century number.  */
411 #ifdef _NL_CURRENT
412         match_century:
413 #endif
414           get_number (0, 99, 2);
415           century = val;
416           want_xday = 1;
417           break;
418         case 'd':
419         case 'e':
420           /* Match day of month.  */
421           get_number (1, 31, 2);
422           tm->tm_mday = val;
423           have_mday = 1;
424           want_xday = 1;
425           break;
426         case 'F':
427           if (!recursive ("%Y-%m-%d"))
428             return NULL;
429           want_xday = 1;
430           break;
431         case 'x':
432 #ifdef _NL_CURRENT
433           if (*decided != raw)
434             {
435               if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
436                 {
437                   if (*decided == loc)
438                     return NULL;
439                   else
440                     rp = rp_backup;
441                 }
442               else
443                 {
444                   if (*decided == not
445                       && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
446                     *decided = loc;
447                   want_xday = 1;
448                   break;
449                 }
450               *decided = raw;
451             }
452 #endif
453           /* Fall through.  */
454         case 'D':
455           /* Match standard day format.  */
456           if (!recursive (HERE_D_FMT))
457             return NULL;
458           want_xday = 1;
459           break;
460         case 'k':
461         case 'H':
462           /* Match hour in 24-hour clock.  */
463           get_number (0, 23, 2);
464           tm->tm_hour = val;
465           have_I = 0;
466           break;
467         case 'l':
468           /* Match hour in 12-hour clock.  GNU extension.  */
469         case 'I':
470           /* Match hour in 12-hour clock.  */
471           get_number (1, 12, 2);
472           tm->tm_hour = val % 12;
473           have_I = 1;
474           break;
475         case 'j':
476           /* Match day number of year.  */
477           get_number (1, 366, 3);
478           tm->tm_yday = val - 1;
479           have_yday = 1;
480           break;
481         case 'm':
482           /* Match number of month.  */
483           get_number (1, 12, 2);
484           tm->tm_mon = val - 1;
485           have_mon = 1;
486           want_xday = 1;
487           break;
488         case 'M':
489           /* Match minute.  */
490           get_number (0, 59, 2);
491           tm->tm_min = val;
492           break;
493         case 'n':
494         case 't':
495           /* Match any white space.  */
496           while (ISSPACE (*rp))
497             ++rp;
498           break;
499         case 'p':
500           /* Match locale's equivalent of AM/PM.  */
501 #ifdef _NL_CURRENT
502           if (*decided != raw)
503             {
504               if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
505                 {
506                   if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
507                     *decided = loc;
508                   break;
509                 }
510               if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
511                 {
512                   if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
513                     *decided = loc;
514                   is_pm = 1;
515                   break;
516                 }
517               *decided = raw;
518             }
519 #endif
520           if (!match_string (HERE_AM_STR, rp))
521             {
522               if (match_string (HERE_PM_STR, rp))
523                 is_pm = 1;
524               else
525                 return NULL;
526             }
527           break;
528         case 'r':
529 #ifdef _NL_CURRENT
530           if (*decided != raw)
531             {
532               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
533                 {
534                   if (*decided == loc)
535                     return NULL;
536                   else
537                     rp = rp_backup;
538                 }
539               else
540                 {
541                   if (*decided == not &&
542                       strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
543                               HERE_T_FMT_AMPM))
544                     *decided = loc;
545                   break;
546                 }
547               *decided = raw;
548             }
549 #endif
550           if (!recursive (HERE_T_FMT_AMPM))
551             return NULL;
552           break;
553         case 'R':
554           if (!recursive ("%H:%M"))
555             return NULL;
556           break;
557         case 's':
558           {
559             /* The number of seconds may be very high so we cannot use
560                the 'get_number' macro.  Instead read the number
561                character for character and construct the result while
562                doing this.  */
563             time_t secs = 0;
564             if (*rp < '0' || *rp > '9')
565               /* We need at least one digit.  */
566               return NULL;
567
568             do
569               {
570                 secs *= 10;
571                 secs += *rp++ - '0';
572               }
573             while (*rp >= '0' && *rp <= '9');
574
575             if (localtime_r (&secs, tm) == NULL)
576               /* Error in function.  */
577               return NULL;
578           }
579           break;
580         case 'S':
581           get_number (0, 61, 2);
582           tm->tm_sec = val;
583           break;
584         case 'X':
585 #ifdef _NL_CURRENT
586           if (*decided != raw)
587             {
588               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
589                 {
590                   if (*decided == loc)
591                     return NULL;
592                   else
593                     rp = rp_backup;
594                 }
595               else
596                 {
597                   if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
598                     *decided = loc;
599                   break;
600                 }
601               *decided = raw;
602             }
603 #endif
604           /* Fall through.  */
605         case 'T':
606           if (!recursive (HERE_T_FMT))
607             return NULL;
608           break;
609         case 'u':
610           get_number (1, 7, 1);
611           tm->tm_wday = val % 7;
612           have_wday = 1;
613           break;
614         case 'g':
615           get_number (0, 99, 2);
616           /* XXX This cannot determine any field in TM.  */
617           break;
618         case 'G':
619           if (*rp < '0' || *rp > '9')
620             return NULL;
621           /* XXX Ignore the number since we would need some more
622              information to compute a real date.  */
623           do
624             ++rp;
625           while (*rp >= '0' && *rp <= '9');
626           break;
627         case 'U':
628           get_number (0, 53, 2);
629           week_no = val;
630           have_uweek = 1;
631           break;
632         case 'W':
633           get_number (0, 53, 2);
634           week_no = val;
635           have_wweek = 1;
636           break;
637         case 'V':
638           get_number (0, 53, 2);
639           /* XXX This cannot determine any field in TM without some
640              information.  */
641           break;
642         case 'w':
643           /* Match number of weekday.  */
644           get_number (0, 6, 1);
645           tm->tm_wday = val;
646           have_wday = 1;
647           break;
648         case 'y':
649 #ifdef _NL_CURRENT
650         match_year_in_century:
651 #endif
652           /* Match year within century.  */
653           get_number (0, 99, 2);
654           /* The "Year 2000: The Millennium Rollover" paper suggests that
655              values in the range 69-99 refer to the twentieth century.  */
656           tm->tm_year = val >= 69 ? val : val + 100;
657           /* Indicate that we want to use the century, if specified.  */
658           want_century = 1;
659           want_xday = 1;
660           break;
661         case 'Y':
662           /* Match year including century number.  */
663           get_number (0, 9999, 4);
664           tm->tm_year = val - 1900;
665           want_century = 0;
666           want_xday = 1;
667           break;
668         case 'Z':
669           /* XXX How to handle this?  */
670           break;
671         case 'z':
672           /* We recognize two formats: if two digits are given, these
673              specify hours.  If fours digits are used, minutes are
674              also specified.  */
675           {
676             bool neg _GL_UNUSED;
677             int n;
678
679             val = 0;
680             while (*rp == ' ')
681               ++rp;
682             if (*rp != '+' && *rp != '-')
683               return NULL;
684             neg = *rp++ == '-';
685             n = 0;
686             while (n < 4 && *rp >= '0' && *rp <= '9')
687               {
688                 val = val * 10 + *rp++ - '0';
689                 ++n;
690               }
691             if (n == 2)
692               val *= 100;
693             else if (n != 4)
694               /* Only two or four digits recognized.  */
695               return NULL;
696             else
697               {
698                 /* We have to convert the minutes into decimal.  */
699                 if (val % 100 >= 60)
700                   return NULL;
701                 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
702               }
703             if (val > 1200)
704               return NULL;
705 #if defined _LIBC || HAVE_TM_GMTOFF
706             tm->tm_gmtoff = (val * 3600) / 100;
707             if (neg)
708               tm->tm_gmtoff = -tm->tm_gmtoff;
709 #endif
710           }
711           break;
712         case 'E':
713 #ifdef _NL_CURRENT
714           switch (*fmt++)
715             {
716             case 'c':
717               /* Match locale's alternate date and time format.  */
718               if (*decided != raw)
719                 {
720                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
721
722                   if (*fmt == '\0')
723                     fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
724
725                   if (!recursive (fmt))
726                     {
727                       if (*decided == loc)
728                         return NULL;
729                       else
730                         rp = rp_backup;
731                     }
732                   else
733                     {
734                       if (strcmp (fmt, HERE_D_T_FMT))
735                         *decided = loc;
736                       want_xday = 1;
737                       break;
738                     }
739                   *decided = raw;
740                 }
741               /* The C locale has no era information, so use the
742                  normal representation.  */
743               if (!recursive (HERE_D_T_FMT))
744                 return NULL;
745               want_xday = 1;
746               break;
747             case 'C':
748               if (*decided != raw)
749                 {
750                   if (era_cnt >= 0)
751                     {
752                       era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
753                       if (era != NULL && match_string (era->era_name, rp))
754                         {
755                           *decided = loc;
756                           break;
757                         }
758                       else
759                         return NULL;
760                     }
761
762                   num_eras = _NL_CURRENT_WORD (LC_TIME,
763                                                _NL_TIME_ERA_NUM_ENTRIES);
764                   for (era_cnt = 0; era_cnt < (int) num_eras;
765                        ++era_cnt, rp = rp_backup)
766                     {
767                       era = _nl_select_era_entry (era_cnt
768                                                   HELPER_LOCALE_ARG);
769                       if (era != NULL && match_string (era->era_name, rp))
770                         {
771                           *decided = loc;
772                           break;
773                         }
774                     }
775                   if (era_cnt != (int) num_eras)
776                     break;
777
778                   era_cnt = -1;
779                   if (*decided == loc)
780                     return NULL;
781
782                   *decided = raw;
783                 }
784               /* The C locale has no era information, so use the
785                  normal representation.  */
786               goto match_century;
787             case 'y':
788               if (*decided != raw)
789                 {
790                   get_number(0, 9999, 4);
791                   tm->tm_year = val;
792                   want_era = 1;
793                   want_xday = 1;
794                   want_century = 1;
795
796                   if (era_cnt >= 0)
797                     {
798                       assert (*decided == loc);
799
800                       era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
801                       bool match = false;
802                       if (era != NULL)
803                         {
804                           int delta = ((tm->tm_year - era->offset)
805                                        * era->absolute_direction);
806                           match = (delta >= 0
807                                    && delta < (((int64_t) era->stop_date[0]
808                                                 - (int64_t) era->start_date[0])
809                                                * era->absolute_direction));
810                         }
811                       if (! match)
812                         return NULL;
813
814                       break;
815                     }
816
817                   num_eras = _NL_CURRENT_WORD (LC_TIME,
818                                                _NL_TIME_ERA_NUM_ENTRIES);
819                   for (era_cnt = 0; era_cnt < (int) num_eras; ++era_cnt)
820                     {
821                       era = _nl_select_era_entry (era_cnt
822                                                   HELPER_LOCALE_ARG);
823                       if (era != NULL)
824                         {
825                           int delta = ((tm->tm_year - era->offset)
826                                        * era->absolute_direction);
827                           if (delta >= 0
828                               && delta < (((int64_t) era->stop_date[0]
829                                            - (int64_t) era->start_date[0])
830                                           * era->absolute_direction))
831                             {
832                               *decided = loc;
833                               break;
834                             }
835                         }
836                     }
837                   if (era_cnt != (int) num_eras)
838                     break;
839
840                   era_cnt = -1;
841                   if (*decided == loc)
842                     return NULL;
843
844                   *decided = raw;
845                 }
846
847               goto match_year_in_century;
848             case 'Y':
849               if (*decided != raw)
850                 {
851                   num_eras = _NL_CURRENT_WORD (LC_TIME,
852                                                _NL_TIME_ERA_NUM_ENTRIES);
853                   for (era_cnt = 0; era_cnt < (int) num_eras;
854                        ++era_cnt, rp = rp_backup)
855                     {
856                       era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
857                       if (era != NULL && recursive (era->era_format))
858                         break;
859                     }
860                   if (era_cnt == (int) num_eras)
861                     {
862                       era_cnt = -1;
863                       if (*decided == loc)
864                         return NULL;
865                       else
866                         rp = rp_backup;
867                     }
868                   else
869                     {
870                       *decided = loc;
871                       era_cnt = -1;
872                       break;
873                     }
874
875                   *decided = raw;
876                 }
877               get_number (0, 9999, 4);
878               tm->tm_year = val - 1900;
879               want_century = 0;
880               want_xday = 1;
881               break;
882             case 'x':
883               if (*decided != raw)
884                 {
885                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
886
887                   if (*fmt == '\0')
888                     fmt = _NL_CURRENT (LC_TIME, D_FMT);
889
890                   if (!recursive (fmt))
891                     {
892                       if (*decided == loc)
893                         return NULL;
894                       else
895                         rp = rp_backup;
896                     }
897                   else
898                     {
899                       if (strcmp (fmt, HERE_D_FMT))
900                         *decided = loc;
901                       break;
902                     }
903                   *decided = raw;
904                 }
905               if (!recursive (HERE_D_FMT))
906                 return NULL;
907               break;
908             case 'X':
909               if (*decided != raw)
910                 {
911                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
912
913                   if (*fmt == '\0')
914                     fmt = _NL_CURRENT (LC_TIME, T_FMT);
915
916                   if (!recursive (fmt))
917                     {
918                       if (*decided == loc)
919                         return NULL;
920                       else
921                         rp = rp_backup;
922                     }
923                   else
924                     {
925                       if (strcmp (fmt, HERE_T_FMT))
926                         *decided = loc;
927                       break;
928                     }
929                   *decided = raw;
930                 }
931               if (!recursive (HERE_T_FMT))
932                 return NULL;
933               break;
934             default:
935               return NULL;
936             }
937           break;
938 #else
939           /* We have no information about the era format.  Just use
940              the normal format.  */
941           if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
942               && *fmt != 'x' && *fmt != 'X')
943             /* This is an illegal format.  */
944             return NULL;
945
946           goto start_over;
947 #endif
948         case 'O':
949           switch (*fmt++)
950             {
951             case 'd':
952             case 'e':
953               /* Match day of month using alternate numeric symbols.  */
954               get_alt_number (1, 31, 2);
955               tm->tm_mday = val;
956               have_mday = 1;
957               want_xday = 1;
958               break;
959             case 'H':
960               /* Match hour in 24-hour clock using alternate numeric
961                  symbols.  */
962               get_alt_number (0, 23, 2);
963               tm->tm_hour = val;
964               have_I = 0;
965               break;
966             case 'I':
967               /* Match hour in 12-hour clock using alternate numeric
968                  symbols.  */
969               get_alt_number (1, 12, 2);
970               tm->tm_hour = val % 12;
971               have_I = 1;
972               break;
973             case 'm':
974               /* Match month using alternate numeric symbols.  */
975               get_alt_number (1, 12, 2);
976               tm->tm_mon = val - 1;
977               have_mon = 1;
978               want_xday = 1;
979               break;
980             case 'M':
981               /* Match minutes using alternate numeric symbols.  */
982               get_alt_number (0, 59, 2);
983               tm->tm_min = val;
984               break;
985             case 'S':
986               /* Match seconds using alternate numeric symbols.  */
987               get_alt_number (0, 61, 2);
988               tm->tm_sec = val;
989               break;
990             case 'U':
991               get_alt_number (0, 53, 2);
992               week_no = val;
993               have_uweek = 1;
994               break;
995             case 'W':
996               get_alt_number (0, 53, 2);
997               week_no = val;
998               have_wweek = 1;
999               break;
1000             case 'V':
1001               get_alt_number (0, 53, 2);
1002               /* XXX This cannot determine any field in TM without
1003                  further information.  */
1004               break;
1005             case 'w':
1006               /* Match number of weekday using alternate numeric symbols.  */
1007               get_alt_number (0, 6, 1);
1008               tm->tm_wday = val;
1009               have_wday = 1;
1010               break;
1011             case 'y':
1012               /* Match year within century using alternate numeric symbols.  */
1013               get_alt_number (0, 99, 2);
1014               tm->tm_year = val >= 69 ? val : val + 100;
1015               want_xday = 1;
1016               break;
1017             default:
1018               return NULL;
1019             }
1020           break;
1021         default:
1022           return NULL;
1023         }
1024     }
1025
1026   if (have_I && is_pm)
1027     tm->tm_hour += 12;
1028
1029   if (century != -1)
1030     {
1031       if (want_century)
1032         tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
1033       else
1034         /* Only the century, but not the year.  Strange, but so be it.  */
1035         tm->tm_year = (century - 19) * 100;
1036     }
1037
1038   if (era_cnt != -1)
1039     {
1040 #ifdef _NL_CURRENT
1041       era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1042       if (era == NULL)
1043         return NULL;
1044       if (want_era)
1045         tm->tm_year = (era->start_date[0]
1046                        + ((tm->tm_year - era->offset)
1047                           * era->absolute_direction));
1048       else
1049         /* Era start year assumed.  */
1050         tm->tm_year = era->start_date[0];
1051 #endif
1052     }
1053   else
1054     if (want_era)
1055       {
1056         /* No era found but we have seen an E modifier.  Rectify some
1057            values.  */
1058         if (want_century && century == -1 && tm->tm_year < 69)
1059           tm->tm_year += 100;
1060       }
1061
1062   if (want_xday && !have_wday)
1063     {
1064       if ( !(have_mon && have_mday) && have_yday)
1065         {
1066           /* We don't have tm_mon and/or tm_mday, compute them.  */
1067           int t_mon = 0;
1068           while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1069               t_mon++;
1070           if (!have_mon)
1071               tm->tm_mon = t_mon - 1;
1072           if (!have_mday)
1073               tm->tm_mday =
1074                 (tm->tm_yday
1075                  - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1076         }
1077       day_of_the_week (tm);
1078     }
1079
1080   if (want_xday && !have_yday)
1081     day_of_the_year (tm);
1082
1083   if ((have_uweek || have_wweek) && have_wday)
1084     {
1085       int save_wday = tm->tm_wday;
1086       int save_mday = tm->tm_mday;
1087       int save_mon = tm->tm_mon;
1088       int w_offset = have_uweek ? 0 : 1;
1089
1090       tm->tm_mday = 1;
1091       tm->tm_mon = 0;
1092       day_of_the_week (tm);
1093       if (have_mday)
1094         tm->tm_mday = save_mday;
1095       if (have_mon)
1096         tm->tm_mon = save_mon;
1097
1098       if (!have_yday)
1099         tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1100                        + (week_no - 1) *7
1101                        + save_wday - w_offset);
1102
1103       if (!have_mday || !have_mon)
1104         {
1105           int t_mon = 0;
1106           while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1107                  <= tm->tm_yday)
1108             t_mon++;
1109           if (!have_mon)
1110             tm->tm_mon = t_mon - 1;
1111           if (!have_mday)
1112               tm->tm_mday =
1113                 (tm->tm_yday
1114                  - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1115         }
1116
1117       tm->tm_wday = save_wday;
1118     }
1119
1120   return (char *) rp;
1121 }
1122
1123
1124 char *
1125 strptime (buf, format, tm LOCALE_PARAM)
1126      const char *restrict buf;
1127      const char *restrict format;
1128      struct tm *restrict tm;
1129      LOCALE_PARAM_DECL
1130 {
1131   enum ptime_locale_status decided;
1132
1133 #ifdef _NL_CURRENT
1134   decided = not;
1135 #else
1136   decided = raw;
1137 #endif
1138   return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG);
1139 }
1140
1141 #ifdef _LIBC
1142 weak_alias (__strptime_l, strptime_l)
1143 #endif