6994ec40faccf98e93b1cc8b732a0ddd134e0df3
[platform/upstream/diffutils.git] / lib / strptime.c
1 /* Copyright (C) 2002, 2004-2005, 2007, 2009-2018 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 3, 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 <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 <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 'q':
529           /* Match quarter of year.  GNU extension.  */
530           get_number (1, 4, 1);
531           tm->tm_mon = (val - 1) * 3;
532           tm->tm_mday = 1;
533           have_mon = 1;
534           have_mday = 1;
535           want_xday = 1;
536           break;
537         case 'r':
538 #ifdef _NL_CURRENT
539           if (*decided != raw)
540             {
541               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
542                 {
543                   if (*decided == loc)
544                     return NULL;
545                   else
546                     rp = rp_backup;
547                 }
548               else
549                 {
550                   if (*decided == not &&
551                       strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
552                               HERE_T_FMT_AMPM))
553                     *decided = loc;
554                   break;
555                 }
556               *decided = raw;
557             }
558 #endif
559           if (!recursive (HERE_T_FMT_AMPM))
560             return NULL;
561           break;
562         case 'R':
563           if (!recursive ("%H:%M"))
564             return NULL;
565           break;
566         case 's':
567           {
568             /* The number of seconds may be very high so we cannot use
569                the 'get_number' macro.  Instead read the number
570                character for character and construct the result while
571                doing this.  */
572             time_t secs = 0;
573             if (*rp < '0' || *rp > '9')
574               /* We need at least one digit.  */
575               return NULL;
576
577             do
578               {
579                 secs *= 10;
580                 secs += *rp++ - '0';
581               }
582             while (*rp >= '0' && *rp <= '9');
583
584             if (localtime_r (&secs, tm) == NULL)
585               /* Error in function.  */
586               return NULL;
587           }
588           break;
589         case 'S':
590           get_number (0, 61, 2);
591           tm->tm_sec = val;
592           break;
593         case 'X':
594 #ifdef _NL_CURRENT
595           if (*decided != raw)
596             {
597               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
598                 {
599                   if (*decided == loc)
600                     return NULL;
601                   else
602                     rp = rp_backup;
603                 }
604               else
605                 {
606                   if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
607                     *decided = loc;
608                   break;
609                 }
610               *decided = raw;
611             }
612 #endif
613           /* Fall through.  */
614         case 'T':
615           if (!recursive (HERE_T_FMT))
616             return NULL;
617           break;
618         case 'u':
619           get_number (1, 7, 1);
620           tm->tm_wday = val % 7;
621           have_wday = 1;
622           break;
623         case 'g':
624           get_number (0, 99, 2);
625           /* XXX This cannot determine any field in TM.  */
626           break;
627         case 'G':
628           if (*rp < '0' || *rp > '9')
629             return NULL;
630           /* XXX Ignore the number since we would need some more
631              information to compute a real date.  */
632           do
633             ++rp;
634           while (*rp >= '0' && *rp <= '9');
635           break;
636         case 'U':
637           get_number (0, 53, 2);
638           week_no = val;
639           have_uweek = 1;
640           break;
641         case 'W':
642           get_number (0, 53, 2);
643           week_no = val;
644           have_wweek = 1;
645           break;
646         case 'V':
647           get_number (0, 53, 2);
648           /* XXX This cannot determine any field in TM without some
649              information.  */
650           break;
651         case 'w':
652           /* Match number of weekday.  */
653           get_number (0, 6, 1);
654           tm->tm_wday = val;
655           have_wday = 1;
656           break;
657         case 'y':
658 #ifdef _NL_CURRENT
659         match_year_in_century:
660 #endif
661           /* Match year within century.  */
662           get_number (0, 99, 2);
663           /* The "Year 2000: The Millennium Rollover" paper suggests that
664              values in the range 69-99 refer to the twentieth century.  */
665           tm->tm_year = val >= 69 ? val : val + 100;
666           /* Indicate that we want to use the century, if specified.  */
667           want_century = 1;
668           want_xday = 1;
669           break;
670         case 'Y':
671           /* Match year including century number.  */
672           get_number (0, 9999, 4);
673           tm->tm_year = val - 1900;
674           want_century = 0;
675           want_xday = 1;
676           break;
677         case 'Z':
678           /* XXX How to handle this?  */
679           break;
680         case 'z':
681           /* We recognize two formats: if two digits are given, these
682              specify hours.  If fours digits are used, minutes are
683              also specified.  */
684           {
685             bool neg _GL_UNUSED;
686             int n;
687
688             val = 0;
689             while (*rp == ' ')
690               ++rp;
691             if (*rp != '+' && *rp != '-')
692               return NULL;
693             neg = *rp++ == '-';
694             n = 0;
695             while (n < 4 && *rp >= '0' && *rp <= '9')
696               {
697                 val = val * 10 + *rp++ - '0';
698                 ++n;
699               }
700             if (n == 2)
701               val *= 100;
702             else if (n != 4)
703               /* Only two or four digits recognized.  */
704               return NULL;
705             else
706               {
707                 /* We have to convert the minutes into decimal.  */
708                 if (val % 100 >= 60)
709                   return NULL;
710                 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
711               }
712             if (val > 1200)
713               return NULL;
714 #if defined _LIBC || HAVE_TM_GMTOFF
715             tm->tm_gmtoff = (val * 3600) / 100;
716             if (neg)
717               tm->tm_gmtoff = -tm->tm_gmtoff;
718 #endif
719           }
720           break;
721         case 'E':
722 #ifdef _NL_CURRENT
723           switch (*fmt++)
724             {
725             case 'c':
726               /* Match locale's alternate date and time format.  */
727               if (*decided != raw)
728                 {
729                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
730
731                   if (*fmt == '\0')
732                     fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
733
734                   if (!recursive (fmt))
735                     {
736                       if (*decided == loc)
737                         return NULL;
738                       else
739                         rp = rp_backup;
740                     }
741                   else
742                     {
743                       if (strcmp (fmt, HERE_D_T_FMT))
744                         *decided = loc;
745                       want_xday = 1;
746                       break;
747                     }
748                   *decided = raw;
749                 }
750               /* The C locale has no era information, so use the
751                  normal representation.  */
752               if (!recursive (HERE_D_T_FMT))
753                 return NULL;
754               want_xday = 1;
755               break;
756             case 'C':
757               if (*decided != raw)
758                 {
759                   if (era_cnt >= 0)
760                     {
761                       era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
762                       if (era != NULL && match_string (era->era_name, rp))
763                         {
764                           *decided = loc;
765                           break;
766                         }
767                       else
768                         return NULL;
769                     }
770
771                   num_eras = _NL_CURRENT_WORD (LC_TIME,
772                                                _NL_TIME_ERA_NUM_ENTRIES);
773                   for (era_cnt = 0; era_cnt < (int) num_eras;
774                        ++era_cnt, rp = rp_backup)
775                     {
776                       era = _nl_select_era_entry (era_cnt
777                                                   HELPER_LOCALE_ARG);
778                       if (era != NULL && match_string (era->era_name, rp))
779                         {
780                           *decided = loc;
781                           break;
782                         }
783                     }
784                   if (era_cnt != (int) num_eras)
785                     break;
786
787                   era_cnt = -1;
788                   if (*decided == loc)
789                     return NULL;
790
791                   *decided = raw;
792                 }
793               /* The C locale has no era information, so use the
794                  normal representation.  */
795               goto match_century;
796             case 'y':
797               if (*decided != raw)
798                 {
799                   get_number(0, 9999, 4);
800                   tm->tm_year = val;
801                   want_era = 1;
802                   want_xday = 1;
803                   want_century = 1;
804
805                   if (era_cnt >= 0)
806                     {
807                       assert (*decided == loc);
808
809                       era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
810                       bool match = false;
811                       if (era != NULL)
812                         {
813                           int delta = ((tm->tm_year - era->offset)
814                                        * era->absolute_direction);
815                           match = (delta >= 0
816                                    && delta < (((int64_t) era->stop_date[0]
817                                                 - (int64_t) era->start_date[0])
818                                                * era->absolute_direction));
819                         }
820                       if (! match)
821                         return NULL;
822
823                       break;
824                     }
825
826                   num_eras = _NL_CURRENT_WORD (LC_TIME,
827                                                _NL_TIME_ERA_NUM_ENTRIES);
828                   for (era_cnt = 0; era_cnt < (int) num_eras; ++era_cnt)
829                     {
830                       era = _nl_select_era_entry (era_cnt
831                                                   HELPER_LOCALE_ARG);
832                       if (era != NULL)
833                         {
834                           int delta = ((tm->tm_year - era->offset)
835                                        * era->absolute_direction);
836                           if (delta >= 0
837                               && delta < (((int64_t) era->stop_date[0]
838                                            - (int64_t) era->start_date[0])
839                                           * era->absolute_direction))
840                             {
841                               *decided = loc;
842                               break;
843                             }
844                         }
845                     }
846                   if (era_cnt != (int) num_eras)
847                     break;
848
849                   era_cnt = -1;
850                   if (*decided == loc)
851                     return NULL;
852
853                   *decided = raw;
854                 }
855
856               goto match_year_in_century;
857             case 'Y':
858               if (*decided != raw)
859                 {
860                   num_eras = _NL_CURRENT_WORD (LC_TIME,
861                                                _NL_TIME_ERA_NUM_ENTRIES);
862                   for (era_cnt = 0; era_cnt < (int) num_eras;
863                        ++era_cnt, rp = rp_backup)
864                     {
865                       era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
866                       if (era != NULL && recursive (era->era_format))
867                         break;
868                     }
869                   if (era_cnt == (int) num_eras)
870                     {
871                       era_cnt = -1;
872                       if (*decided == loc)
873                         return NULL;
874                       else
875                         rp = rp_backup;
876                     }
877                   else
878                     {
879                       *decided = loc;
880                       era_cnt = -1;
881                       break;
882                     }
883
884                   *decided = raw;
885                 }
886               get_number (0, 9999, 4);
887               tm->tm_year = val - 1900;
888               want_century = 0;
889               want_xday = 1;
890               break;
891             case 'x':
892               if (*decided != raw)
893                 {
894                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
895
896                   if (*fmt == '\0')
897                     fmt = _NL_CURRENT (LC_TIME, D_FMT);
898
899                   if (!recursive (fmt))
900                     {
901                       if (*decided == loc)
902                         return NULL;
903                       else
904                         rp = rp_backup;
905                     }
906                   else
907                     {
908                       if (strcmp (fmt, HERE_D_FMT))
909                         *decided = loc;
910                       break;
911                     }
912                   *decided = raw;
913                 }
914               if (!recursive (HERE_D_FMT))
915                 return NULL;
916               break;
917             case 'X':
918               if (*decided != raw)
919                 {
920                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
921
922                   if (*fmt == '\0')
923                     fmt = _NL_CURRENT (LC_TIME, T_FMT);
924
925                   if (!recursive (fmt))
926                     {
927                       if (*decided == loc)
928                         return NULL;
929                       else
930                         rp = rp_backup;
931                     }
932                   else
933                     {
934                       if (strcmp (fmt, HERE_T_FMT))
935                         *decided = loc;
936                       break;
937                     }
938                   *decided = raw;
939                 }
940               if (!recursive (HERE_T_FMT))
941                 return NULL;
942               break;
943             default:
944               return NULL;
945             }
946           break;
947 #else
948           /* We have no information about the era format.  Just use
949              the normal format.  */
950           if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
951               && *fmt != 'x' && *fmt != 'X')
952             /* This is an illegal format.  */
953             return NULL;
954
955           goto start_over;
956 #endif
957         case 'O':
958           switch (*fmt++)
959             {
960             case 'd':
961             case 'e':
962               /* Match day of month using alternate numeric symbols.  */
963               get_alt_number (1, 31, 2);
964               tm->tm_mday = val;
965               have_mday = 1;
966               want_xday = 1;
967               break;
968             case 'H':
969               /* Match hour in 24-hour clock using alternate numeric
970                  symbols.  */
971               get_alt_number (0, 23, 2);
972               tm->tm_hour = val;
973               have_I = 0;
974               break;
975             case 'I':
976               /* Match hour in 12-hour clock using alternate numeric
977                  symbols.  */
978               get_alt_number (1, 12, 2);
979               tm->tm_hour = val % 12;
980               have_I = 1;
981               break;
982             case 'm':
983               /* Match month using alternate numeric symbols.  */
984               get_alt_number (1, 12, 2);
985               tm->tm_mon = val - 1;
986               have_mon = 1;
987               want_xday = 1;
988               break;
989             case 'M':
990               /* Match minutes using alternate numeric symbols.  */
991               get_alt_number (0, 59, 2);
992               tm->tm_min = val;
993               break;
994             case 'q':
995               /* Match quarter using alternate numeric symbols.  */
996               get_alt_number (1, 4, 1);
997               tm->tm_mon = (val - 1) * 3;
998               tm->tm_mday = 1;
999               have_mon = 1;
1000               have_mday = 1;
1001               want_xday = 1;
1002               break;
1003             case 'S':
1004               /* Match seconds using alternate numeric symbols.  */
1005               get_alt_number (0, 61, 2);
1006               tm->tm_sec = val;
1007               break;
1008             case 'U':
1009               get_alt_number (0, 53, 2);
1010               week_no = val;
1011               have_uweek = 1;
1012               break;
1013             case 'W':
1014               get_alt_number (0, 53, 2);
1015               week_no = val;
1016               have_wweek = 1;
1017               break;
1018             case 'V':
1019               get_alt_number (0, 53, 2);
1020               /* XXX This cannot determine any field in TM without
1021                  further information.  */
1022               break;
1023             case 'w':
1024               /* Match number of weekday using alternate numeric symbols.  */
1025               get_alt_number (0, 6, 1);
1026               tm->tm_wday = val;
1027               have_wday = 1;
1028               break;
1029             case 'y':
1030               /* Match year within century using alternate numeric symbols.  */
1031               get_alt_number (0, 99, 2);
1032               tm->tm_year = val >= 69 ? val : val + 100;
1033               want_xday = 1;
1034               break;
1035             default:
1036               return NULL;
1037             }
1038           break;
1039         default:
1040           return NULL;
1041         }
1042     }
1043
1044   if (have_I && is_pm)
1045     tm->tm_hour += 12;
1046
1047   if (century != -1)
1048     {
1049       if (want_century)
1050         tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
1051       else
1052         /* Only the century, but not the year.  Strange, but so be it.  */
1053         tm->tm_year = (century - 19) * 100;
1054     }
1055
1056   if (era_cnt != -1)
1057     {
1058 #ifdef _NL_CURRENT
1059       era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1060       if (era == NULL)
1061         return NULL;
1062       if (want_era)
1063         tm->tm_year = (era->start_date[0]
1064                        + ((tm->tm_year - era->offset)
1065                           * era->absolute_direction));
1066       else
1067         /* Era start year assumed.  */
1068         tm->tm_year = era->start_date[0];
1069 #endif
1070     }
1071   else
1072     if (want_era)
1073       {
1074         /* No era found but we have seen an E modifier.  Rectify some
1075            values.  */
1076         if (want_century && century == -1 && tm->tm_year < 69)
1077           tm->tm_year += 100;
1078       }
1079
1080   if (want_xday && !have_wday)
1081     {
1082       if ( !(have_mon && have_mday) && have_yday)
1083         {
1084           /* We don't have tm_mon and/or tm_mday, compute them.  */
1085           int t_mon = 0;
1086           while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1087               t_mon++;
1088           if (!have_mon)
1089               tm->tm_mon = t_mon - 1;
1090           if (!have_mday)
1091               tm->tm_mday =
1092                 (tm->tm_yday
1093                  - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1094         }
1095       day_of_the_week (tm);
1096     }
1097
1098   if (want_xday && !have_yday)
1099     day_of_the_year (tm);
1100
1101   if ((have_uweek || have_wweek) && have_wday)
1102     {
1103       int save_wday = tm->tm_wday;
1104       int save_mday = tm->tm_mday;
1105       int save_mon = tm->tm_mon;
1106       int w_offset = have_uweek ? 0 : 1;
1107
1108       tm->tm_mday = 1;
1109       tm->tm_mon = 0;
1110       day_of_the_week (tm);
1111       if (have_mday)
1112         tm->tm_mday = save_mday;
1113       if (have_mon)
1114         tm->tm_mon = save_mon;
1115
1116       if (!have_yday)
1117         tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1118                        + (week_no - 1) *7
1119                        + save_wday - w_offset);
1120
1121       if (!have_mday || !have_mon)
1122         {
1123           int t_mon = 0;
1124           while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1125                  <= tm->tm_yday)
1126             t_mon++;
1127           if (!have_mon)
1128             tm->tm_mon = t_mon - 1;
1129           if (!have_mday)
1130               tm->tm_mday =
1131                 (tm->tm_yday
1132                  - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1133         }
1134
1135       tm->tm_wday = save_wday;
1136     }
1137
1138   return (char *) rp;
1139 }
1140
1141
1142 char *
1143 strptime (buf, format, tm LOCALE_PARAM)
1144      const char *restrict buf;
1145      const char *restrict format;
1146      struct tm *restrict tm;
1147      LOCALE_PARAM_DECL
1148 {
1149   enum ptime_locale_status decided;
1150
1151 #ifdef _NL_CURRENT
1152   decided = not;
1153 #else
1154   decided = raw;
1155 #endif
1156   return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG);
1157 }
1158
1159 #ifdef _LIBC
1160 weak_alias (__strptime_l, strptime_l)
1161 #endif