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