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