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