(gc): Fix test for stack overuse.
[kopensolaris-gnu/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       if (statep != NULL)
334         {
335           /* In recursive calls silently discard strftime modifiers.  */
336           while (*fmt == '-' || *fmt == '_' || *fmt == '0'
337                  || *fmt == '^' || *fmt == '#')
338             ++fmt;
339
340           /* And field width.  */
341           while (*fmt >= '0' && *fmt <= '9')
342             ++fmt;
343         }
344
345 #ifndef _NL_CURRENT
346       /* We need this for handling the `E' modifier.  */
347     start_over:
348 #endif
349
350       /* Make back up of current processing pointer.  */
351       rp_backup = rp;
352
353       switch (*fmt++)
354         {
355         case '%':
356           /* Match the `%' character itself.  */
357           match_char ('%', *rp++);
358           break;
359         case 'a':
360         case 'A':
361           /* Match day of week.  */
362           rp_longest = NULL;
363           decided_longest = s.decided;
364           cnt_longest = -1;
365           for (cnt = 0; cnt < 7; ++cnt)
366             {
367               const char *trp;
368 #ifdef _NL_CURRENT
369               if (s.decided !=raw)
370                 {
371                   trp = rp;
372                   if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), trp)
373                       && trp > rp_longest)
374                     {
375                       rp_longest = trp;
376                       cnt_longest = cnt;
377                       if (s.decided == not
378                           && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
379                                      weekday_name[cnt]))
380                         decided_longest = loc;
381                     }
382                   trp = rp;
383                   if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), trp)
384                       && trp > rp_longest)
385                     {
386                       rp_longest = trp;
387                       cnt_longest = cnt;
388                       if (s.decided == not
389                           && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
390                                      ab_weekday_name[cnt]))
391                         decided_longest = loc;
392                     }
393                 }
394 #endif
395               if (s.decided != loc
396                   && (((trp = rp, match_string (weekday_name[cnt], trp))
397                        && trp > rp_longest)
398                       || ((trp = rp, match_string (ab_weekday_name[cnt], rp))
399                           && trp > rp_longest)))
400                 {
401                   rp_longest = trp;
402                   cnt_longest = cnt;
403                   decided_longest = raw;
404                 }
405             }
406           if (rp_longest == NULL)
407             /* Does not match a weekday name.  */
408             return NULL;
409           rp = rp_longest;
410           s.decided = decided_longest;
411           tm->tm_wday = cnt_longest;
412           s.have_wday = 1;
413           break;
414         case 'b':
415         case 'B':
416         case 'h':
417           /* Match month name.  */
418           rp_longest = NULL;
419           decided_longest = s.decided;
420           cnt_longest = -1;
421           for (cnt = 0; cnt < 12; ++cnt)
422             {
423               const char *trp;
424 #ifdef _NL_CURRENT
425               if (s.decided !=raw)
426                 {
427                   trp = rp;
428                   if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), trp)
429                       && trp > rp_longest)
430                     {
431                       rp_longest = trp;
432                       cnt_longest = cnt;
433                       if (s.decided == not
434                           && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
435                                      month_name[cnt]))
436                         decided_longest = loc;
437                     }
438                   trp = rp;
439                   if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), trp)
440                       && trp > rp_longest)
441                     {
442                       rp_longest = trp;
443                       cnt_longest = cnt;
444                       if (s.decided == not
445                           && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
446                                      ab_month_name[cnt]))
447                         decided_longest = loc;
448                     }
449                 }
450 #endif
451               if (s.decided != loc
452                   && (((trp = rp, match_string (month_name[cnt], trp))
453                        && trp > rp_longest)
454                       || ((trp = rp, match_string (ab_month_name[cnt], trp))
455                           && trp > rp_longest)))
456                 {
457                   rp_longest = trp;
458                   cnt_longest = cnt;
459                   decided_longest = raw;
460                 }
461             }
462           if (rp_longest == NULL)
463             /* Does not match a month name.  */
464             return NULL;
465           rp = rp_longest;
466           s.decided = decided_longest;
467           tm->tm_mon = cnt_longest;
468           s.have_mon = 1;
469           s.want_xday = 1;
470           break;
471         case 'c':
472           /* Match locale's date and time format.  */
473 #ifdef _NL_CURRENT
474           if (s.decided != raw)
475             {
476               if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
477                 {
478                   if (s.decided == loc)
479                     return NULL;
480                   else
481                     rp = rp_backup;
482                 }
483               else
484                 {
485                   if (s.decided == not &&
486                       strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
487                     s.decided = loc;
488                   s.want_xday = 1;
489                   break;
490                 }
491               s.decided = raw;
492             }
493 #endif
494           if (!recursive (HERE_D_T_FMT))
495             return NULL;
496           s.want_xday = 1;
497           break;
498         case 'C':
499           /* Match century number.  */
500         match_century:
501           get_number (0, 99, 2);
502           s.century = val;
503           s.want_xday = 1;
504           break;
505         case 'd':
506         case 'e':
507           /* Match day of month.  */
508           get_number (1, 31, 2);
509           tm->tm_mday = val;
510           s.have_mday = 1;
511           s.want_xday = 1;
512           break;
513         case 'F':
514           if (!recursive ("%Y-%m-%d"))
515             return NULL;
516           s.want_xday = 1;
517           break;
518         case 'x':
519 #ifdef _NL_CURRENT
520           if (s.decided != raw)
521             {
522               if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
523                 {
524                   if (s.decided == loc)
525                     return NULL;
526                   else
527                     rp = rp_backup;
528                 }
529               else
530                 {
531                   if (s.decided == not
532                       && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
533                     s.decided = loc;
534                   s.want_xday = 1;
535                   break;
536                 }
537               s.decided = raw;
538             }
539 #endif
540           /* Fall through.  */
541         case 'D':
542           /* Match standard day format.  */
543           if (!recursive (HERE_D_FMT))
544             return NULL;
545           s.want_xday = 1;
546           break;
547         case 'k':
548         case 'H':
549           /* Match hour in 24-hour clock.  */
550           get_number (0, 23, 2);
551           tm->tm_hour = val;
552           s.have_I = 0;
553           break;
554         case 'l':
555           /* Match hour in 12-hour clock.  GNU extension.  */
556         case 'I':
557           /* Match hour in 12-hour clock.  */
558           get_number (1, 12, 2);
559           tm->tm_hour = val % 12;
560           s.have_I = 1;
561           break;
562         case 'j':
563           /* Match day number of year.  */
564           get_number (1, 366, 3);
565           tm->tm_yday = val - 1;
566           s.have_yday = 1;
567           break;
568         case 'm':
569           /* Match number of month.  */
570           get_number (1, 12, 2);
571           tm->tm_mon = val - 1;
572           s.have_mon = 1;
573           s.want_xday = 1;
574           break;
575         case 'M':
576           /* Match minute.  */
577           get_number (0, 59, 2);
578           tm->tm_min = val;
579           break;
580         case 'n':
581         case 't':
582           /* Match any white space.  */
583           while (ISSPACE (*rp))
584             ++rp;
585           break;
586         case 'p':
587           /* Match locale's equivalent of AM/PM.  */
588 #ifdef _NL_CURRENT
589           if (s.decided != raw)
590             {
591               if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
592                 {
593                   if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
594                     s.decided = loc;
595                   s.is_pm = 0;
596                   break;
597                 }
598               if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
599                 {
600                   if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
601                     s.decided = loc;
602                   s.is_pm = 1;
603                   break;
604                 }
605               s.decided = raw;
606             }
607 #endif
608           if (!match_string (HERE_AM_STR, rp))
609             {
610               if (match_string (HERE_PM_STR, rp))
611                 s.is_pm = 1;
612               else
613                 return NULL;
614             }
615           else
616             s.is_pm = 0;
617           break;
618         case 'r':
619 #ifdef _NL_CURRENT
620           if (s.decided != raw)
621             {
622               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
623                 {
624                   if (s.decided == loc)
625                     return NULL;
626                   else
627                     rp = rp_backup;
628                 }
629               else
630                 {
631                   if (s.decided == not &&
632                       strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
633                               HERE_T_FMT_AMPM))
634                     s.decided = loc;
635                   break;
636                 }
637               s.decided = raw;
638             }
639 #endif
640           if (!recursive (HERE_T_FMT_AMPM))
641             return NULL;
642           break;
643         case 'R':
644           if (!recursive ("%H:%M"))
645             return NULL;
646           break;
647         case 's':
648           {
649             /* The number of seconds may be very high so we cannot use
650                the `get_number' macro.  Instead read the number
651                character for character and construct the result while
652                doing this.  */
653             time_t secs = 0;
654             if (*rp < '0' || *rp > '9')
655               /* We need at least one digit.  */
656               return NULL;
657
658             do
659               {
660                 secs *= 10;
661                 secs += *rp++ - '0';
662               }
663             while (*rp >= '0' && *rp <= '9');
664
665             if (localtime_r (&secs, tm) == NULL)
666               /* Error in function.  */
667               return NULL;
668           }
669           break;
670         case 'S':
671           get_number (0, 61, 2);
672           tm->tm_sec = val;
673           break;
674         case 'X':
675 #ifdef _NL_CURRENT
676           if (s.decided != raw)
677             {
678               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
679                 {
680                   if (s.decided == loc)
681                     return NULL;
682                   else
683                     rp = rp_backup;
684                 }
685               else
686                 {
687                   if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
688                     s.decided = loc;
689                   break;
690                 }
691               s.decided = raw;
692             }
693 #endif
694           /* Fall through.  */
695         case 'T':
696           if (!recursive (HERE_T_FMT))
697             return NULL;
698           break;
699         case 'u':
700           get_number (1, 7, 1);
701           tm->tm_wday = val % 7;
702           s.have_wday = 1;
703           break;
704         case 'g':
705           get_number (0, 99, 2);
706           /* XXX This cannot determine any field in TM.  */
707           break;
708         case 'G':
709           if (*rp < '0' || *rp > '9')
710             return NULL;
711           /* XXX Ignore the number since we would need some more
712              information to compute a real date.  */
713           do
714             ++rp;
715           while (*rp >= '0' && *rp <= '9');
716           break;
717         case 'U':
718           get_number (0, 53, 2);
719           s.week_no = val;
720           s.have_uweek = 1;
721           break;
722         case 'W':
723           get_number (0, 53, 2);
724           s.week_no = val;
725           s.have_wweek = 1;
726           break;
727         case 'V':
728           get_number (0, 53, 2);
729           /* XXX This cannot determine any field in TM without some
730              information.  */
731           break;
732         case 'w':
733           /* Match number of weekday.  */
734           get_number (0, 6, 1);
735           tm->tm_wday = val;
736           s.have_wday = 1;
737           break;
738         case 'y':
739         match_year_in_century:
740           /* Match year within century.  */
741           get_number (0, 99, 2);
742           /* The "Year 2000: The Millennium Rollover" paper suggests that
743              values in the range 69-99 refer to the twentieth century.  */
744           tm->tm_year = val >= 69 ? val : val + 100;
745           /* Indicate that we want to use the century, if specified.  */
746           s.want_century = 1;
747           s.want_xday = 1;
748           break;
749         case 'Y':
750           /* Match year including century number.  */
751           get_number (0, 9999, 4);
752           tm->tm_year = val - 1900;
753           s.want_century = 0;
754           s.want_xday = 1;
755           break;
756         case 'Z':
757           /* XXX How to handle this?  */
758           break;
759         case 'z':
760           /* We recognize two formats: if two digits are given, these
761              specify hours.  If fours digits are used, minutes are
762              also specified.  */
763           {
764             val = 0;
765             while (*rp == ' ')
766               ++rp;
767             if (*rp != '+' && *rp != '-')
768               return NULL;
769             bool neg = *rp++ == '-';
770             int n = 0;
771             while (n < 4 && *rp >= '0' && *rp <= '9')
772               {
773                 val = val * 10 + *rp++ - '0';
774                 ++n;
775               }
776             if (n == 2)
777               val *= 100;
778             else if (n != 4)
779               /* Only two or four digits recognized.  */
780               return NULL;
781             else
782               {
783                 /* We have to convert the minutes into decimal.  */
784                 if (val % 100 >= 60)
785                   return NULL;
786                 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
787               }
788             if (val > 1200)
789               return NULL;
790             tm->tm_gmtoff = (val * 3600) / 100;
791             if (neg)
792               tm->tm_gmtoff = -tm->tm_gmtoff;
793           }
794           break;
795         case 'E':
796 #ifdef _NL_CURRENT
797           switch (*fmt++)
798             {
799             case 'c':
800               /* Match locale's alternate date and time format.  */
801               if (s.decided != raw)
802                 {
803                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
804
805                   if (*fmt == '\0')
806                     fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
807
808                   if (!recursive (fmt))
809                     {
810                       if (s.decided == loc)
811                         return NULL;
812                       else
813                         rp = rp_backup;
814                     }
815                   else
816                     {
817                       if (strcmp (fmt, HERE_D_T_FMT))
818                         s.decided = loc;
819                       s.want_xday = 1;
820                       break;
821                     }
822                   s.decided = raw;
823                 }
824               /* The C locale has no era information, so use the
825                  normal representation.  */
826               if (!recursive (HERE_D_T_FMT))
827                 return NULL;
828               s.want_xday = 1;
829               break;
830             case 'C':
831               if (s.decided != raw)
832                 {
833                   if (s.era_cnt >= 0)
834                     {
835                       era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
836                       if (era != NULL && match_string (era->era_name, rp))
837                         {
838                           s.decided = loc;
839                           break;
840                         }
841                       else
842                         return NULL;
843                     }
844
845                   num_eras = _NL_CURRENT_WORD (LC_TIME,
846                                                _NL_TIME_ERA_NUM_ENTRIES);
847                   for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
848                        ++s.era_cnt, rp = rp_backup)
849                     {
850                       era = _nl_select_era_entry (s.era_cnt
851                                                   HELPER_LOCALE_ARG);
852                       if (era != NULL && match_string (era->era_name, rp))
853                         {
854                           s.decided = loc;
855                           break;
856                         }
857                     }
858                   if (s.era_cnt != (int) num_eras)
859                     break;
860
861                   s.era_cnt = -1;
862                   if (s.decided == loc)
863                     return NULL;
864
865                   s.decided = raw;
866                 }
867               /* The C locale has no era information, so use the
868                  normal representation.  */
869               goto match_century;
870             case 'y':
871               if (s.decided != raw)
872                 {
873                   get_number(0, 9999, 4);
874                   tm->tm_year = val;
875                   s.want_era = 1;
876                   s.want_xday = 1;
877                   s.want_century = 1;
878
879                   if (s.era_cnt >= 0)
880                     {
881                       assert (s.decided == loc);
882
883                       era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
884                       bool match = false;
885                       if (era != NULL)
886                         {
887                           int delta = ((tm->tm_year - era->offset)
888                                        * era->absolute_direction);
889                           match = (delta >= 0
890                                    && delta < (((int64_t) era->stop_date[0]
891                                                 - (int64_t) era->start_date[0])
892                                                * era->absolute_direction));
893                         }
894                       if (! match)
895                         return NULL;
896
897                       break;
898                     }
899
900                   num_eras = _NL_CURRENT_WORD (LC_TIME,
901                                                _NL_TIME_ERA_NUM_ENTRIES);
902                   for (s.era_cnt = 0; s.era_cnt < (int) num_eras; ++s.era_cnt)
903                     {
904                       era = _nl_select_era_entry (s.era_cnt
905                                                   HELPER_LOCALE_ARG);
906                       if (era != NULL)
907                         {
908                           int delta = ((tm->tm_year - era->offset)
909                                        * era->absolute_direction);
910                           if (delta >= 0
911                               && delta < (((int64_t) era->stop_date[0]
912                                            - (int64_t) era->start_date[0])
913                                           * era->absolute_direction))
914                             {
915                               s.decided = loc;
916                               break;
917                             }
918                         }
919                     }
920                   if (s.era_cnt != (int) num_eras)
921                     break;
922
923                   s.era_cnt = -1;
924                   if (s.decided == loc)
925                     return NULL;
926
927                   s.decided = raw;
928                 }
929
930               goto match_year_in_century;
931             case 'Y':
932               if (s.decided != raw)
933                 {
934                   num_eras = _NL_CURRENT_WORD (LC_TIME,
935                                                _NL_TIME_ERA_NUM_ENTRIES);
936                   for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
937                        ++s.era_cnt, rp = rp_backup)
938                     {
939                       era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
940                       if (era != NULL && recursive (era->era_format))
941                         break;
942                     }
943                   if (s.era_cnt == (int) num_eras)
944                     {
945                       s.era_cnt = -1;
946                       if (s.decided == loc)
947                         return NULL;
948                       else
949                         rp = rp_backup;
950                     }
951                   else
952                     {
953                       s.decided = loc;
954                       s.era_cnt = -1;
955                       break;
956                     }
957
958                   s.decided = raw;
959                 }
960               get_number (0, 9999, 4);
961               tm->tm_year = val - 1900;
962               s.want_century = 0;
963               s.want_xday = 1;
964               break;
965             case 'x':
966               if (s.decided != raw)
967                 {
968                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
969
970                   if (*fmt == '\0')
971                     fmt = _NL_CURRENT (LC_TIME, D_FMT);
972
973                   if (!recursive (fmt))
974                     {
975                       if (s.decided == loc)
976                         return NULL;
977                       else
978                         rp = rp_backup;
979                     }
980                   else
981                     {
982                       if (strcmp (fmt, HERE_D_FMT))
983                         s.decided = loc;
984                       break;
985                     }
986                   s.decided = raw;
987                 }
988               if (!recursive (HERE_D_FMT))
989                 return NULL;
990               break;
991             case 'X':
992               if (s.decided != raw)
993                 {
994                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
995
996                   if (*fmt == '\0')
997                     fmt = _NL_CURRENT (LC_TIME, T_FMT);
998
999                   if (!recursive (fmt))
1000                     {
1001                       if (s.decided == loc)
1002                         return NULL;
1003                       else
1004                         rp = rp_backup;
1005                     }
1006                   else
1007                     {
1008                       if (strcmp (fmt, HERE_T_FMT))
1009                         s.decided = loc;
1010                       break;
1011                     }
1012                   s.decided = raw;
1013                 }
1014               if (!recursive (HERE_T_FMT))
1015                 return NULL;
1016               break;
1017             default:
1018               return NULL;
1019             }
1020           break;
1021 #else
1022           /* We have no information about the era format.  Just use
1023              the normal format.  */
1024           if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
1025               && *fmt != 'x' && *fmt != 'X')
1026             /* This is an illegal format.  */
1027             return NULL;
1028
1029           goto start_over;
1030 #endif
1031         case 'O':
1032           switch (*fmt++)
1033             {
1034             case 'd':
1035             case 'e':
1036               /* Match day of month using alternate numeric symbols.  */
1037               get_alt_number (1, 31, 2);
1038               tm->tm_mday = val;
1039               s.have_mday = 1;
1040               s.want_xday = 1;
1041               break;
1042             case 'H':
1043               /* Match hour in 24-hour clock using alternate numeric
1044                  symbols.  */
1045               get_alt_number (0, 23, 2);
1046               tm->tm_hour = val;
1047               s.have_I = 0;
1048               break;
1049             case 'I':
1050               /* Match hour in 12-hour clock using alternate numeric
1051                  symbols.  */
1052               get_alt_number (1, 12, 2);
1053               tm->tm_hour = val % 12;
1054               s.have_I = 1;
1055               break;
1056             case 'm':
1057               /* Match month using alternate numeric symbols.  */
1058               get_alt_number (1, 12, 2);
1059               tm->tm_mon = val - 1;
1060               s.have_mon = 1;
1061               s.want_xday = 1;
1062               break;
1063             case 'M':
1064               /* Match minutes using alternate numeric symbols.  */
1065               get_alt_number (0, 59, 2);
1066               tm->tm_min = val;
1067               break;
1068             case 'S':
1069               /* Match seconds using alternate numeric symbols.  */
1070               get_alt_number (0, 61, 2);
1071               tm->tm_sec = val;
1072               break;
1073             case 'U':
1074               get_alt_number (0, 53, 2);
1075               s.week_no = val;
1076               s.have_uweek = 1;
1077               break;
1078             case 'W':
1079               get_alt_number (0, 53, 2);
1080               s.week_no = val;
1081               s.have_wweek = 1;
1082               break;
1083             case 'V':
1084               get_alt_number (0, 53, 2);
1085               /* XXX This cannot determine any field in TM without
1086                  further information.  */
1087               break;
1088             case 'w':
1089               /* Match number of weekday using alternate numeric symbols.  */
1090               get_alt_number (0, 6, 1);
1091               tm->tm_wday = val;
1092               s.have_wday = 1;
1093               break;
1094             case 'y':
1095               /* Match year within century using alternate numeric symbols.  */
1096               get_alt_number (0, 99, 2);
1097               tm->tm_year = val >= 69 ? val : val + 100;
1098               s.want_xday = 1;
1099               break;
1100             default:
1101               return NULL;
1102             }
1103           break;
1104         default:
1105           return NULL;
1106         }
1107     }
1108
1109   if (statep != NULL)
1110     {
1111       /* Recursive invocation, returning success, so
1112          update parent's struct tm and state.  */
1113       *(struct __strptime_state *) statep = s;
1114       *tmp = tmb;
1115       return (char *) rp;
1116     }
1117
1118   if (s.have_I && s.is_pm)
1119     tm->tm_hour += 12;
1120
1121   if (s.century != -1)
1122     {
1123       if (s.want_century)
1124         tm->tm_year = tm->tm_year % 100 + (s.century - 19) * 100;
1125       else
1126         /* Only the century, but not the year.  Strange, but so be it.  */
1127         tm->tm_year = (s.century - 19) * 100;
1128     }
1129
1130   if (s.era_cnt != -1)
1131     {
1132       era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
1133       if (era == NULL)
1134         return NULL;
1135       if (s.want_era)
1136         tm->tm_year = (era->start_date[0]
1137                        + ((tm->tm_year - era->offset)
1138                           * era->absolute_direction));
1139       else
1140         /* Era start year assumed.  */
1141         tm->tm_year = era->start_date[0];
1142     }
1143   else
1144     if (s.want_era)
1145       {
1146         /* No era found but we have seen an E modifier.  Rectify some
1147            values.  */
1148         if (s.want_century && s.century == -1 && tm->tm_year < 69)
1149           tm->tm_year += 100;
1150       }
1151
1152   if (s.want_xday && !s.have_wday)
1153     {
1154       if ( !(s.have_mon && s.have_mday) && s.have_yday)
1155         {
1156           /* We don't have tm_mon and/or tm_mday, compute them.  */
1157           int t_mon = 0;
1158           while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1159               t_mon++;
1160           if (!s.have_mon)
1161               tm->tm_mon = t_mon - 1;
1162           if (!s.have_mday)
1163               tm->tm_mday =
1164                 (tm->tm_yday
1165                  - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1166           s.have_mon = 1;
1167           s.have_mday = 1;
1168         }
1169       /* Don't crash in day_of_the_week if tm_mon is uninitialized.  */
1170       if (s.have_mon || (unsigned) tm->tm_mon <= 11)
1171         day_of_the_week (tm);
1172     }
1173
1174   if (s.want_xday && !s.have_yday && (s.have_mon || (unsigned) tm->tm_mon <= 11))
1175     day_of_the_year (tm);
1176
1177   if ((s.have_uweek || s.have_wweek) && s.have_wday)
1178     {
1179       int save_wday = tm->tm_wday;
1180       int save_mday = tm->tm_mday;
1181       int save_mon = tm->tm_mon;
1182       int w_offset = s.have_uweek ? 0 : 1;
1183
1184       tm->tm_mday = 1;
1185       tm->tm_mon = 0;
1186       day_of_the_week (tm);
1187       if (s.have_mday)
1188         tm->tm_mday = save_mday;
1189       if (s.have_mon)
1190         tm->tm_mon = save_mon;
1191
1192       if (!s.have_yday)
1193         tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1194                        + (s.week_no - 1) *7
1195                        + save_wday - w_offset);
1196
1197       if (!s.have_mday || !s.have_mon)
1198         {
1199           int t_mon = 0;
1200           while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1201                  <= tm->tm_yday)
1202             t_mon++;
1203           if (!s.have_mon)
1204             tm->tm_mon = t_mon - 1;
1205           if (!s.have_mday)
1206               tm->tm_mday =
1207                 (tm->tm_yday
1208                  - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1209         }
1210
1211       tm->tm_wday = save_wday;
1212     }
1213
1214   return (char *) rp;
1215 }
1216
1217
1218 char *
1219 strptime (buf, format, tm LOCALE_PARAM)
1220      const char *buf;
1221      const char *format;
1222      struct tm *tm;
1223      LOCALE_PARAM_DECL
1224 {
1225   return __strptime_internal (buf, format, tm, NULL LOCALE_ARG);
1226 }
1227
1228 #ifdef _LIBC
1229 weak_alias (__strptime_l, strptime_l)
1230 #endif