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