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