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