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