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