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