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