Remove _nl_C_LC_TIME declaration.
[kopensolaris-gnu/glibc.git] / time / strptime.c
1 /* Convert a string representation of time to a time value.
2    Copyright (C) 1996, 1997, 1998, 1999 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 <langinfo.h>
32 #include <limits.h>
33 #include <string.h>
34 #include <time.h>
35
36 #ifdef _LIBC
37 # include "../locale/localeinfo.h"
38 #endif
39
40
41 #ifndef __P
42 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
43 #  define __P(args) args
44 # else
45 #  define __P(args) ()
46 # endif  /* GCC.  */
47 #endif  /* Not __P.  */
48
49
50 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
51 # ifdef _LIBC
52 #  define localtime_r __localtime_r
53 # else
54 /* Approximate localtime_r as best we can in its absence.  */
55 #  define localtime_r my_localtime_r
56 static struct tm *localtime_r __P ((const time_t *, struct tm *));
57 static struct tm *
58 localtime_r (t, tp)
59      const time_t *t;
60      struct tm *tp;
61 {
62   struct tm *l = localtime (t);
63   if (! l)
64     return 0;
65   *tp = *l;
66   return tp;
67 }
68 # endif /* ! _LIBC */
69 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
70
71
72 #define match_char(ch1, ch2) if (ch1 != ch2) return NULL
73 #if defined __GNUC__ && __GNUC__ >= 2
74 # define match_string(cs1, s2) \
75   ({ size_t len = strlen (cs1);                                               \
76      int result = strncasecmp ((cs1), (s2), len) == 0;                        \
77      if (result) (s2) += len;                                                 \
78      result; })
79 #else
80 /* Oh come on.  Get a reasonable compiler.  */
81 # define match_string(cs1, s2) \
82   (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
83 #endif
84 /* We intentionally do not use isdigit() for testing because this will
85    lead to problems with the wide character version.  */
86 #define get_number(from, to, n) \
87   do {                                                                        \
88     int __n = n;                                                              \
89     val = 0;                                                                  \
90     while (*rp == ' ')                                                        \
91       ++rp;                                                                   \
92     if (*rp < '0' || *rp > '9')                                               \
93       return NULL;                                                            \
94     do {                                                                      \
95       val *= 10;                                                              \
96       val += *rp++ - '0';                                                     \
97     } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9');        \
98     if (val < from || val > to)                                               \
99       return NULL;                                                            \
100   } while (0)
101 #ifdef _NL_CURRENT
102 # define get_alt_number(from, to, n) \
103   ({                                                                          \
104     __label__ do_normal;                                                      \
105     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)) != NULL)
155
156
157 #ifdef _LIBC
158 /* This is defined in locale/C-time.c in the GNU libc.  */
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;
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 *buf, const char *format, struct tm *tm,
253                         enum locale_status *decided));
254
255 static char *
256 #ifdef _LIBC
257 internal_function
258 #endif
259 strptime_internal (buf, format, tm, decided)
260      const char *buf;
261      const char *format;
262      struct tm *tm;
263      enum locale_status *decided;
264 {
265   const char *rp;
266   const char *fmt;
267   int cnt;
268   size_t val;
269   int have_I, is_pm;
270   int century, want_century;
271   int have_wday, want_xday;
272   int have_yday;
273   int have_mon, have_mday;
274
275   rp = buf;
276   fmt = format;
277   have_I = is_pm = 0;
278   century = -1;
279   want_century = 0;
280   have_wday = want_xday = have_yday = have_mon = have_mday = 0;
281
282   while (*fmt != '\0')
283     {
284       /* A white space in the format string matches 0 more or white
285          space in the input string.  */
286       if (isspace (*fmt))
287         {
288           while (isspace (*rp))
289             ++rp;
290           ++fmt;
291           continue;
292         }
293
294       /* Any character but `%' must be matched by the same character
295          in the iput string.  */
296       if (*fmt != '%')
297         {
298           match_char (*fmt++, *rp++);
299           continue;
300         }
301
302       ++fmt;
303 #ifndef _NL_CURRENT
304       /* We need this for handling the `E' modifier.  */
305     start_over:
306 #endif
307       switch (*fmt++)
308         {
309         case '%':
310           /* Match the `%' character itself.  */
311           match_char ('%', *rp++);
312           break;
313         case 'a':
314         case 'A':
315           /* Match day of week.  */
316           for (cnt = 0; cnt < 7; ++cnt)
317             {
318 #ifdef _NL_CURRENT
319               if (*decided !=raw)
320                 {
321                   if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
322                     {
323                       if (*decided == not
324                           && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
325                                      weekday_name[cnt]))
326                         *decided = loc;
327                       break;
328                     }
329                   if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
330                     {
331                       if (*decided == not
332                           && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
333                                      ab_weekday_name[cnt]))
334                         *decided = loc;
335                       break;
336                     }
337                 }
338 #endif
339               if (*decided != loc
340                   && (match_string (weekday_name[cnt], rp)
341                       || match_string (ab_weekday_name[cnt], rp)))
342                 {
343                   *decided = raw;
344                   break;
345                 }
346             }
347           if (cnt == 7)
348             /* Does not match a weekday name.  */
349             return NULL;
350           tm->tm_wday = cnt;
351           have_wday = 1;
352           break;
353         case 'b':
354         case 'B':
355         case 'h':
356           /* Match month name.  */
357           for (cnt = 0; cnt < 12; ++cnt)
358             {
359 #ifdef _NL_CURRENT
360               if (*decided !=raw)
361                 {
362                   if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
363                     {
364                       if (*decided == not
365                           && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
366                                      month_name[cnt]))
367                         *decided = loc;
368                       break;
369                     }
370                   if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
371                     {
372                       if (*decided == not
373                           && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
374                                      ab_month_name[cnt]))
375                         *decided = loc;
376                       break;
377                     }
378                 }
379 #endif
380               if (match_string (month_name[cnt], rp)
381                   || match_string (ab_month_name[cnt], rp))
382                 {
383                   *decided = raw;
384                   break;
385                 }
386             }
387           if (cnt == 12)
388             /* Does not match a month name.  */
389             return NULL;
390           tm->tm_mon = cnt;
391           want_xday = 1;
392           break;
393         case 'c':
394           /* Match locale's date and time format.  */
395 #ifdef _NL_CURRENT
396           if (*decided != raw)
397             {
398               if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
399                 {
400                   if (*decided == loc)
401                     return NULL;
402                 }
403               else
404                 {
405                   if (*decided == not &&
406                       strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
407                     *decided = loc;
408                   want_xday = 1;
409                   break;
410                 }
411               *decided = raw;
412             }
413 #endif
414           if (!recursive (HERE_D_T_FMT))
415             return NULL;
416           want_xday = 1;
417           break;
418         case 'C':
419           /* Match century number.  */
420           get_number (0, 99, 2);
421           century = val;
422           want_xday = 1;
423           break;
424         case 'd':
425         case 'e':
426           /* Match day of month.  */
427           get_number (1, 31, 2);
428           tm->tm_mday = val;
429           have_mday = 1;
430           want_xday = 1;
431           break;
432         case 'F':
433           if (!recursive ("%Y-%m-%d"))
434             return NULL;
435           want_xday = 1;
436           break;
437         case 'x':
438 #ifdef _NL_CURRENT
439           if (*decided != raw)
440             {
441               if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
442                 {
443                   if (*decided == loc)
444                     return NULL;
445                 }
446               else
447                 {
448                   if (decided == not
449                       && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
450                     *decided = loc;
451                   want_xday = 1;
452                   break;
453                 }
454               *decided = raw;
455             }
456 #endif
457           /* Fall through.  */
458         case 'D':
459           /* Match standard day format.  */
460           if (!recursive (HERE_D_FMT))
461             return NULL;
462           want_xday = 1;
463           break;
464         case 'k':
465         case 'H':
466           /* Match hour in 24-hour clock.  */
467           get_number (0, 23, 2);
468           tm->tm_hour = val;
469           have_I = 0;
470           break;
471         case 'I':
472           /* Match hour in 12-hour clock.  */
473           get_number (1, 12, 2);
474           tm->tm_hour = val % 12;
475           have_I = 1;
476           break;
477         case 'j':
478           /* Match day number of year.  */
479           get_number (1, 366, 3);
480           tm->tm_yday = val - 1;
481           have_yday = 1;
482           break;
483         case 'm':
484           /* Match number of month.  */
485           get_number (1, 12, 2);
486           tm->tm_mon = val - 1;
487           have_mon = 1;
488           want_xday = 1;
489           break;
490         case 'M':
491           /* Match minute.  */
492           get_number (0, 59, 2);
493           tm->tm_min = val;
494           break;
495         case 'n':
496         case 't':
497           /* Match any white space.  */
498           while (isspace (*rp))
499             ++rp;
500           break;
501         case 'p':
502           /* Match locale's equivalent of AM/PM.  */
503 #ifdef _NL_CURRENT
504           if (*decided != raw)
505             {
506               if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
507                 {
508                   if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
509                     *decided = loc;
510                   break;
511                 }
512               if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
513                 {
514                   if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
515                     *decided = loc;
516                   is_pm = 1;
517                   break;
518                 }
519               *decided = raw;
520             }
521 #endif
522           if (!match_string (HERE_AM_STR, rp))
523             if (match_string (HERE_PM_STR, rp))
524               is_pm = 1;
525             else
526               return NULL;
527           break;
528         case 'r':
529 #ifdef _NL_CURRENT
530           if (*decided != raw)
531             {
532               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
533                 {
534                   if (*decided == loc)
535                     return NULL;
536                 }
537               else
538                 {
539                   if (*decided == not &&
540                       strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
541                               HERE_T_FMT_AMPM))
542                     *decided = loc;
543                   break;
544                 }
545               *decided = raw;
546             }
547 #endif
548           if (!recursive (HERE_T_FMT_AMPM))
549             return NULL;
550           break;
551         case 'R':
552           if (!recursive ("%H:%M"))
553             return NULL;
554           break;
555         case 's':
556           {
557             /* The number of seconds may be very high so we cannot use
558                the `get_number' macro.  Instead read the number
559                character for character and construct the result while
560                doing this.  */
561             time_t secs = 0;
562             if (*rp < '0' || *rp > '9')
563               /* We need at least one digit.  */
564               return NULL;
565
566             do
567               {
568                 secs *= 10;
569                 secs += *rp++ - '0';
570               }
571             while (*rp >= '0' && *rp <= '9');
572
573             if (localtime_r (&secs, tm) == NULL)
574               /* Error in function.  */
575               return NULL;
576           }
577           break;
578         case 'S':
579           get_number (0, 61, 2);
580           tm->tm_sec = val;
581           break;
582         case 'X':
583 #ifdef _NL_CURRENT
584           if (*decided != raw)
585             {
586               if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
587                 {
588                   if (*decided == loc)
589                     return NULL;
590                 }
591               else
592                 {
593                   if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
594                     *decided = loc;
595                   break;
596                 }
597               *decided = raw;
598             }
599 #endif
600           /* Fall through.  */
601         case 'T':
602           if (!recursive (HERE_T_FMT))
603             return NULL;
604           break;
605         case 'u':
606           get_number (1, 7, 1);
607           tm->tm_wday = val % 7;
608           have_wday = 1;
609           break;
610         case 'g':
611           get_number (0, 99, 2);
612           /* XXX This cannot determine any field in TM.  */
613           break;
614         case 'G':
615           if (*rp < '0' || *rp > '9')
616             return NULL;
617           /* XXX Ignore the number since we would need some more
618              information to compute a real date.  */
619           do
620             ++rp;
621           while (*rp >= '0' && *rp <= '9');
622           break;
623         case 'U':
624         case 'V':
625         case 'W':
626           get_number (0, 53, 2);
627           /* XXX This cannot determine any field in TM without some
628              information.  */
629           break;
630         case 'w':
631           /* Match number of weekday.  */
632           get_number (0, 6, 1);
633           tm->tm_wday = val;
634           have_wday = 1;
635           break;
636         case 'y':
637           /* Match year within century.  */
638           get_number (0, 99, 2);
639           /* The "Year 2000: The Millennium Rollover" paper suggests that
640              values in the range 69-99 refer to the twentieth century.  */
641           tm->tm_year = val >= 69 ? val : val + 100;
642           /* Indicate that we want to use the century, if specified.  */
643           want_century = 1;
644           want_xday = 1;
645           break;
646         case 'Y':
647           /* Match year including century number.  */
648           get_number (0, 9999, 4);
649           tm->tm_year = val - 1900;
650           want_century = 0;
651           want_xday = 1;
652           break;
653         case 'Z':
654           /* XXX How to handle this?  */
655           break;
656         case 'E':
657 #ifdef _NL_CURRENT
658           switch (*fmt++)
659             {
660             case 'c':
661               /* Match locale's alternate date and time format.  */
662               if (*decided != raw)
663                 {
664                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
665
666                   if (*fmt == '\0')
667                     fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
668
669                   if (!recursive (fmt))
670                     {
671                       if (*decided == loc)
672                         return NULL;
673                     }
674                   else
675                     {
676                       if (strcmp (fmt, HERE_D_T_FMT))
677                         *decided = loc;
678                       want_xday = 1;
679                       break;
680                     }
681                   *decided = raw;
682                 }
683               /* The C locale has no era information, so use the
684                  normal representation.  */
685               if (!recursive (HERE_D_T_FMT))
686                 return NULL;
687               want_xday = 1;
688               break;
689             case 'C':
690             case 'y':
691             case 'Y':
692               /* Match name of base year in locale's alternate
693                  representation.  */
694               /* XXX This is currently not implemented.  It should
695                  use the value _NL_CURRENT (LC_TIME, ERA).  */
696               break;
697             case 'x':
698               if (*decided != raw)
699                 {
700                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
701
702                   if (*fmt == '\0')
703                     fmt = _NL_CURRENT (LC_TIME, D_FMT);
704
705                   if (!recursive (fmt))
706                     {
707                       if (*decided == loc)
708                         return NULL;
709                     }
710                   else
711                     {
712                       if (strcmp (fmt, HERE_D_FMT))
713                         *decided = loc;
714                       break;
715                     }
716                   *decided = raw;
717                 }
718               if (!recursive (HERE_D_FMT))
719                 return NULL;
720               break;
721             case 'X':
722               if (*decided != raw)
723                 {
724                   const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
725
726                   if (*fmt == '\0')
727                     fmt = _NL_CURRENT (LC_TIME, T_FMT);
728
729                   if (!recursive (fmt))
730                     {
731                       if (*decided == loc)
732                         return NULL;
733                     }
734                   else
735                     {
736                       if (strcmp (fmt, HERE_T_FMT))
737                         *decided = loc;
738                       break;
739                     }
740                   *decided = raw;
741                 }
742               if (!recursive (HERE_T_FMT))
743                 return NULL;
744               break;
745             default:
746               return NULL;
747             }
748           break;
749 #else
750           /* We have no information about the era format.  Just use
751              the normal format.  */
752           if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
753               && *fmt != 'x' && *fmt != 'X')
754             /* This is an illegal format.  */
755             return NULL;
756
757           goto start_over;
758 #endif
759         case 'O':
760           switch (*fmt++)
761             {
762             case 'd':
763             case 'e':
764               /* Match day of month using alternate numeric symbols.  */
765               get_alt_number (1, 31, 2);
766               tm->tm_mday = val;
767               have_mday = 1;
768               want_xday = 1;
769               break;
770             case 'H':
771               /* Match hour in 24-hour clock using alternate numeric
772                  symbols.  */
773               get_alt_number (0, 23, 2);
774               tm->tm_hour = val;
775               have_I = 0;
776               break;
777             case 'I':
778               /* Match hour in 12-hour clock using alternate numeric
779                  symbols.  */
780               get_alt_number (1, 12, 2);
781               tm->tm_hour = val - 1;
782               have_I = 1;
783               break;
784             case 'm':
785               /* Match month using alternate numeric symbols.  */
786               get_alt_number (1, 12, 2);
787               tm->tm_mon = val - 1;
788               have_mon = 1;
789               want_xday = 1;
790               break;
791             case 'M':
792               /* Match minutes using alternate numeric symbols.  */
793               get_alt_number (0, 59, 2);
794               tm->tm_min = val;
795               break;
796             case 'S':
797               /* Match seconds using alternate numeric symbols.  */
798               get_alt_number (0, 61, 2);
799               tm->tm_sec = val;
800               break;
801             case 'U':
802             case 'V':
803             case 'W':
804               get_alt_number (0, 53, 2);
805               /* XXX This cannot determine any field in TM without
806                  further information.  */
807               break;
808             case 'w':
809               /* Match number of weekday using alternate numeric symbols.  */
810               get_alt_number (0, 6, 1);
811               tm->tm_wday = val;
812               have_wday = 1;
813               break;
814             case 'y':
815               /* Match year within century using alternate numeric symbols.  */
816               get_alt_number (0, 99, 2);
817               tm->tm_year = val >= 69 ? val : val + 100;
818               want_xday = 1;
819               break;
820             default:
821               return NULL;
822             }
823           break;
824         default:
825           return NULL;
826         }
827     }
828
829   if (have_I && is_pm)
830     tm->tm_hour += 12;
831
832   if (want_century && century != -1)
833     tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
834
835   if (want_xday && !have_wday) {
836       if ( !(have_mon && have_mday) && have_yday)  {
837           /* we don't have tm_mon and/or tm_mday, compute them */
838           int t_mon = 0;
839           while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
840               t_mon++;
841           if (!have_mon)
842               tm->tm_mon = t_mon - 1;
843           if (!have_mday)
844               tm->tm_mday = tm->tm_yday - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1;
845       }
846       day_of_the_week (tm);
847   }
848   if (want_xday && !have_yday)
849     day_of_the_year (tm);
850
851   return (char *) rp;
852 }
853
854
855 char *
856 strptime (buf, format, tm)
857      const char *buf;
858      const char *format;
859      struct tm *tm;
860 {
861   enum locale_status decided;
862 #ifdef _NL_CURRENT
863   decided = not;
864 #else
865   decided = raw;
866 #endif
867   return strptime_internal (buf, format, tm, &decided);
868 }