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