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