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