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