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