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