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