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