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