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