dc0cc686fd45736b591ec45125f75c86e0889c61
[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 'z':
691           /* We recognize two formats: if two digits are given, these
692              specify hours.  If fours digits are used, minutes are
693              also specified.  */
694           {
695             val = 0;
696             while (*rp == ' ')
697               ++rp;
698             if (*rp != '+' && *rp != '-')
699               return NULL;
700             bool neg = *rp++ == '-';
701             int n = 0;
702             while (n < 4 && *rp >= '0' && *rp <= '9')
703               {
704                 val = val * 10 + *rp++ - '0';
705                 ++n;
706               }
707             if (n == 2)
708               val *= 100;
709             else if (n != 4)
710               /* Only two or four digits recognized.  */
711               return NULL;
712             else
713               {
714                 /* We have to convert the minutes into decimal.  */
715                 if (val % 100 >= 60)
716                   return NULL;
717                 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
718               }
719             if (val > 1200)
720               return NULL;
721             tm->tm_gmtoff = (val * 3600) / 100;
722             if (neg)
723               tm->tm_gmtoff = -tm->tm_gmtoff;
724           }
725           break;
726         case 'E':
727 #ifdef _NL_CURRENT
728           switch (*fmt++)
729             {
730             case 'c':
731               /* Match locale's alternate date and time format.  */
732               if (*decided != raw)
733                 {
734                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
735
736                   if (*fmt == '\0')
737                     fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
738
739                   if (!recursive (fmt))
740                     {
741                       if (*decided == loc)
742                         return NULL;
743                       else
744                         rp = rp_backup;
745                     }
746                   else
747                     {
748                       if (strcmp (fmt, HERE_D_T_FMT))
749                         *decided = loc;
750                       want_xday = 1;
751                       break;
752                     }
753                   *decided = raw;
754                 }
755               /* The C locale has no era information, so use the
756                  normal representation.  */
757               if (!recursive (HERE_D_T_FMT))
758                 return NULL;
759               want_xday = 1;
760               break;
761             case 'C':
762               if (*decided != raw)
763                 {
764                   if (era_cnt >= 0)
765                     {
766                       era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
767                       if (era != NULL && match_string (era->era_name, rp))
768                         {
769                           *decided = loc;
770                           break;
771                         }
772                       else
773                         return NULL;
774                     }
775
776                   num_eras = _NL_CURRENT_WORD (LC_TIME,
777                                                _NL_TIME_ERA_NUM_ENTRIES);
778                   for (era_cnt = 0; era_cnt < (int) num_eras;
779                        ++era_cnt, rp = rp_backup)
780                     {
781                       era = _nl_select_era_entry (era_cnt
782                                                   HELPER_LOCALE_ARG);
783                       if (era != NULL && match_string (era->era_name, rp))
784                         {
785                           *decided = loc;
786                           break;
787                         }
788                     }
789                   if (era_cnt != (int) num_eras)
790                     break;
791
792                   era_cnt = -1;
793                   if (*decided == loc)
794                     return NULL;
795
796                   *decided = raw;
797                 }
798               /* The C locale has no era information, so use the
799                  normal representation.  */
800               goto match_century;
801             case 'y':
802               if (*decided != raw)
803                 {
804                   get_number(0, 9999, 4);
805                   tm->tm_year = val;
806                   want_era = 1;
807                   want_xday = 1;
808                   want_century = 1;
809
810                   if (era_cnt >= 0)
811                     {
812                       assert (*decided == loc);
813
814                       era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
815                       bool match = false;
816                       if (era != NULL)
817                         {
818                           int delta = ((tm->tm_year - era->offset)
819                                        * era->absolute_direction);
820                           match = (delta >= 0
821                                    && delta < (((int64_t) era->stop_date[0]
822                                                 - (int64_t) era->start_date[0])
823                                                * era->absolute_direction));
824                         }
825                       if (! match)
826                         return NULL;
827
828                       break;
829                     }
830
831                   num_eras = _NL_CURRENT_WORD (LC_TIME,
832                                                _NL_TIME_ERA_NUM_ENTRIES);
833                   for (era_cnt = 0; era_cnt < (int) num_eras; ++era_cnt)
834                     {
835                       era = _nl_select_era_entry (era_cnt
836                                                   HELPER_LOCALE_ARG);
837                       if (era != NULL)
838                         {
839                           int delta = ((tm->tm_year - era->offset)
840                                        * era->absolute_direction);
841                           if (delta >= 0
842                               && delta < (((int64_t) era->stop_date[0]
843                                            - (int64_t) era->start_date[0])
844                                           * era->absolute_direction))
845                             {
846                               *decided = loc;
847                               break;
848                             }
849                         }
850                     }
851                   if (era_cnt != (int) num_eras)
852                     break;
853
854                   era_cnt = -1;
855                   if (*decided == loc)
856                     return NULL;
857
858                   *decided = raw;
859                 }
860
861               goto match_year_in_century;
862             case 'Y':
863               if (*decided != raw)
864                 {
865                   num_eras = _NL_CURRENT_WORD (LC_TIME,
866                                                _NL_TIME_ERA_NUM_ENTRIES);
867                   for (era_cnt = 0; era_cnt < (int) num_eras;
868                        ++era_cnt, rp = rp_backup)
869                     {
870                       era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
871                       if (era != NULL && recursive (era->era_format))
872                         break;
873                     }
874                   if (era_cnt == (int) num_eras)
875                     {
876                       era_cnt = -1;
877                       if (*decided == loc)
878                         return NULL;
879                       else
880                         rp = rp_backup;
881                     }
882                   else
883                     {
884                       *decided = loc;
885                       era_cnt = -1;
886                       break;
887                     }
888
889                   *decided = raw;
890                 }
891               get_number (0, 9999, 4);
892               tm->tm_year = val - 1900;
893               want_century = 0;
894               want_xday = 1;
895               break;
896             case 'x':
897               if (*decided != raw)
898                 {
899                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
900
901                   if (*fmt == '\0')
902                     fmt = _NL_CURRENT (LC_TIME, D_FMT);
903
904                   if (!recursive (fmt))
905                     {
906                       if (*decided == loc)
907                         return NULL;
908                       else
909                         rp = rp_backup;
910                     }
911                   else
912                     {
913                       if (strcmp (fmt, HERE_D_FMT))
914                         *decided = loc;
915                       break;
916                     }
917                   *decided = raw;
918                 }
919               if (!recursive (HERE_D_FMT))
920                 return NULL;
921               break;
922             case 'X':
923               if (*decided != raw)
924                 {
925                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
926
927                   if (*fmt == '\0')
928                     fmt = _NL_CURRENT (LC_TIME, T_FMT);
929
930                   if (!recursive (fmt))
931                     {
932                       if (*decided == loc)
933                         return NULL;
934                       else
935                         rp = rp_backup;
936                     }
937                   else
938                     {
939                       if (strcmp (fmt, HERE_T_FMT))
940                         *decided = loc;
941                       break;
942                     }
943                   *decided = raw;
944                 }
945               if (!recursive (HERE_T_FMT))
946                 return NULL;
947               break;
948             default:
949               return NULL;
950             }
951           break;
952 #else
953           /* We have no information about the era format.  Just use
954              the normal format.  */
955           if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
956               && *fmt != 'x' && *fmt != 'X')
957             /* This is an illegal format.  */
958             return NULL;
959
960           goto start_over;
961 #endif
962         case 'O':
963           switch (*fmt++)
964             {
965             case 'd':
966             case 'e':
967               /* Match day of month using alternate numeric symbols.  */
968               get_alt_number (1, 31, 2);
969               tm->tm_mday = val;
970               have_mday = 1;
971               want_xday = 1;
972               break;
973             case 'H':
974               /* Match hour in 24-hour clock using alternate numeric
975                  symbols.  */
976               get_alt_number (0, 23, 2);
977               tm->tm_hour = val;
978               have_I = 0;
979               break;
980             case 'I':
981               /* Match hour in 12-hour clock using alternate numeric
982                  symbols.  */
983               get_alt_number (1, 12, 2);
984               tm->tm_hour = val % 12;
985               have_I = 1;
986               break;
987             case 'm':
988               /* Match month using alternate numeric symbols.  */
989               get_alt_number (1, 12, 2);
990               tm->tm_mon = val - 1;
991               have_mon = 1;
992               want_xday = 1;
993               break;
994             case 'M':
995               /* Match minutes using alternate numeric symbols.  */
996               get_alt_number (0, 59, 2);
997               tm->tm_min = val;
998               break;
999             case 'S':
1000               /* Match seconds using alternate numeric symbols.  */
1001               get_alt_number (0, 61, 2);
1002               tm->tm_sec = val;
1003               break;
1004             case 'U':
1005               get_alt_number (0, 53, 2);
1006               week_no = val;
1007               have_uweek = 1;
1008               break;
1009             case 'W':
1010               get_alt_number (0, 53, 2);
1011               week_no = val;
1012               have_wweek = 1;
1013               break;
1014             case 'V':
1015               get_alt_number (0, 53, 2);
1016               /* XXX This cannot determine any field in TM without
1017                  further information.  */
1018               break;
1019             case 'w':
1020               /* Match number of weekday using alternate numeric symbols.  */
1021               get_alt_number (0, 6, 1);
1022               tm->tm_wday = val;
1023               have_wday = 1;
1024               break;
1025             case 'y':
1026               /* Match year within century using alternate numeric symbols.  */
1027               get_alt_number (0, 99, 2);
1028               tm->tm_year = val >= 69 ? val : val + 100;
1029               want_xday = 1;
1030               break;
1031             default:
1032               return NULL;
1033             }
1034           break;
1035         default:
1036           return NULL;
1037         }
1038     }
1039
1040   if (have_I && is_pm)
1041     tm->tm_hour += 12;
1042
1043   if (century != -1)
1044     {
1045       if (want_century)
1046         tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
1047       else
1048         /* Only the century, but not the year.  Strange, but so be it.  */
1049         tm->tm_year = (century - 19) * 100;
1050     }
1051
1052   if (era_cnt != -1)
1053     {
1054       era = _nl_select_era_entry (era_cnt HELPER_LOCALE_ARG);
1055       if (era == NULL)
1056         return NULL;
1057       if (want_era)
1058         tm->tm_year = (era->start_date[0]
1059                        + ((tm->tm_year - era->offset)
1060                           * era->absolute_direction));
1061       else
1062         /* Era start year assumed.  */
1063         tm->tm_year = era->start_date[0];
1064     }
1065   else
1066     if (want_era)
1067       {
1068         /* No era found but we have seen an E modifier.  Rectify some
1069            values.  */
1070         if (want_century && century == -1 && tm->tm_year < 69)
1071           tm->tm_year += 100;
1072       }
1073
1074   if (want_xday && !have_wday)
1075     {
1076       if ( !(have_mon && have_mday) && have_yday)
1077         {
1078           /* We don't have tm_mon and/or tm_mday, compute them.  */
1079           int t_mon = 0;
1080           while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1081               t_mon++;
1082           if (!have_mon)
1083               tm->tm_mon = t_mon - 1;
1084           if (!have_mday)
1085               tm->tm_mday =
1086                 (tm->tm_yday
1087                  - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1088         }
1089       day_of_the_week (tm);
1090     }
1091
1092   if (want_xday && !have_yday)
1093     day_of_the_year (tm);
1094
1095   if ((have_uweek || have_wweek) && have_wday)
1096     {
1097       int save_wday = tm->tm_wday;
1098       int save_mday = tm->tm_mday;
1099       int save_mon = tm->tm_mon;
1100       int w_offset = have_uweek ? 0 : 1;
1101
1102       tm->tm_mday = 1;
1103       tm->tm_mon = 0;
1104       day_of_the_week (tm);
1105       if (have_mday)
1106         tm->tm_mday = save_mday;
1107       if (have_mon)
1108         tm->tm_mon = save_mon;
1109
1110       if (!have_yday)
1111         tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1112                        + (week_no - 1) *7
1113                        + save_wday - w_offset);
1114
1115       if (!have_mday || !have_mon)
1116         {
1117           int t_mon = 0;
1118           while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1119                  <= tm->tm_yday)
1120             t_mon++;
1121           if (!have_mon)
1122             tm->tm_mon = t_mon - 1;
1123           if (!have_mday)
1124               tm->tm_mday =
1125                 (tm->tm_yday
1126                  - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1127         }
1128
1129       tm->tm_wday = save_wday;
1130     }
1131
1132   return (char *) rp;
1133 }
1134
1135
1136 char *
1137 strptime (buf, format, tm LOCALE_PARAM)
1138      const char *buf;
1139      const char *format;
1140      struct tm *tm;
1141      LOCALE_PARAM_DECL
1142 {
1143   enum ptime_locale_status decided;
1144
1145 #ifdef _NL_CURRENT
1146   decided = not;
1147 #else
1148   decided = raw;
1149 #endif
1150   return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG);
1151 }
1152
1153 #ifdef _LIBC
1154 weak_alias (__strptime_l, strptime_l)
1155 #endif