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