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