AArch64: Add optimized Q-register memcpy
[glibc.git] / time / strptime_l.c
blob9646157afbde57f3114707bcee1603220ed4e4a1
1 /* Copyright (C) 2002-2020 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
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.
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.
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/>. */
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
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>
30 #ifdef _LIBC
31 # define HAVE_LOCALTIME_R 0
32 # include "../locale/localeinfo.h"
33 #endif
36 #if ! HAVE_LOCALTIME_R && ! defined localtime_r
37 # ifdef _LIBC
38 # define localtime_r __localtime_r
39 # else
40 /* Approximate localtime_r as best we can in its absence. */
41 # define localtime_r my_localtime_r
42 static struct tm *localtime_r (const time_t *, struct tm *);
43 static struct tm *
44 localtime_r (const time_t *t, struct tm *tp)
46 struct tm *l = localtime (t);
47 if (! l)
48 return 0;
49 *tp = *l;
50 return tp;
52 # endif /* ! _LIBC */
53 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
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; \
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; \
101 else \
103 do_normal: \
104 get_number (from, to, n); \
106 0; \
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)
118 #ifdef _LIBC
119 /* This is defined in locale/C-time.c in the GNU libc. */
120 extern const struct __locale_data _nl_C_LC_TIME attribute_hidden;
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)
139 # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
140 #else
141 static char const weekday_name[][10] =
143 "Sunday", "Monday", "Tuesday", "Wednesday",
144 "Thursday", "Friday", "Saturday"
146 static char const ab_weekday_name[][4] =
148 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
150 static char const month_name[][10] =
152 "January", "February", "March", "April", "May", "June",
153 "July", "August", "September", "October", "November", "December"
155 static char const ab_month_name[][4] =
157 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
158 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
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"
167 static const unsigned short int __mon_yday[2][13] =
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 }
174 #endif
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
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
209 /* Compute the day of the week. */
210 static void
211 day_of_the_week (struct tm *tm)
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;
227 /* Compute the day of the year. */
228 static void
229 day_of_the_year (struct tm *tm)
231 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
232 + (tm->tm_mday - 1));
236 #ifdef _LIBC
237 char *
238 #else
239 static char *
240 #endif
241 __strptime_internal (const char *rp, const char *fmt, struct tm *tmp,
242 void *statep LOCALE_PARAM)
244 #ifdef _LIBC
245 struct __locale_data *const current = locale->__locales[LC_TIME];
246 #endif
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
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;
277 if (statep == NULL)
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;
289 else
291 s = *(struct __strptime_state *) statep;
292 tmb = *tmp;
293 tm = &tmb;
296 while (*fmt != '\0')
298 /* A white space in the format string matches 0 more or white
299 space in the input string. */
300 if (ISSPACE (*fmt))
302 while (ISSPACE (*rp))
303 ++rp;
304 ++fmt;
305 continue;
308 /* Any character but `%' must be matched by the same character
309 in the input string. */
310 if (*fmt != '%')
312 match_char (*fmt++, *rp++);
313 continue;
316 ++fmt;
317 /* We discard strftime modifiers. */
318 while (*fmt == '-' || *fmt == '_' || *fmt == '0'
319 || *fmt == '^' || *fmt == '#')
320 ++fmt;
322 /* And field width. */
323 while (*fmt >= '0' && *fmt <= '9')
324 ++fmt;
326 /* In some cases, modifiers are handled by adjusting state and
327 then restarting the switch statement below. */
328 start_over:
330 /* Make back up of current processing pointer. */
331 rp_backup = rp;
333 switch (*fmt++)
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)
347 const char *trp;
348 #ifdef _NL_CURRENT
349 if (s.decided !=raw)
351 trp = rp;
352 if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), trp)
353 && trp > rp_longest)
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;
362 trp = rp;
363 if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), trp)
364 && trp > rp_longest)
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;
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)))
381 rp_longest = trp;
382 cnt_longest = cnt;
383 decided_longest = raw;
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)
403 const char *trp;
404 #ifdef _NL_CURRENT
405 if (s.decided !=raw)
407 trp = rp;
408 if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), trp)
409 && trp > rp_longest)
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;
418 trp = rp;
419 if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), trp)
420 && trp > rp_longest)
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;
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)
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;
442 trp = rp;
443 if (match_string (_NL_CURRENT (LC_TIME, _NL_ABALTMON_1 + cnt),
444 trp)
445 && trp > rp_longest)
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;
454 #endif
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
470 rp_longest = trp;
471 cnt_longest = cnt;
472 decided_longest = raw;
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)
489 if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
491 if (s.decided == loc)
492 return NULL;
493 else
494 rp = rp_backup;
496 else
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;
504 s.decided = raw;
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)
535 if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
537 if (s.decided == loc)
538 return NULL;
539 else
540 rp = rp_backup;
542 else
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;
550 s.decided = raw;
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)
604 if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
606 if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
607 s.decided = loc;
608 s.is_pm = 0;
609 break;
611 if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
613 if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
614 s.decided = loc;
615 s.is_pm = 1;
616 break;
618 s.decided = raw;
620 #endif
621 if (!match_string (HERE_AM_STR, rp))
623 if (match_string (HERE_PM_STR, rp))
624 s.is_pm = 1;
625 else
626 return NULL;
628 else
629 s.is_pm = 0;
630 break;
631 case 'r':
632 #ifdef _NL_CURRENT
633 if (s.decided != raw)
635 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
637 if (s.decided == loc)
638 return NULL;
639 else
640 rp = rp_backup;
642 else
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;
650 s.decided = raw;
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':
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;
673 secs *= 10;
674 secs += *rp++ - '0';
676 while (*rp >= '0' && *rp <= '9');
678 if (localtime_r (&secs, tm) == NULL)
679 /* Error in function. */
680 return NULL;
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)
691 if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
693 if (s.decided == loc)
694 return NULL;
695 else
696 rp = rp_backup;
698 else
700 if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
701 s.decided = loc;
702 break;
704 s.decided = raw;
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. */
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. */
783 val = 0;
784 while (ISSPACE (*rp))
785 ++rp;
786 if (*rp == 'Z')
788 ++rp;
789 tm->tm_gmtoff = 0;
790 break;
792 if (*rp != '+' && *rp != '-')
793 return NULL;
794 bool neg = *rp++ == '-';
795 int n = 0;
796 while (n < 4 && *rp >= '0' && *rp <= '9')
798 val = val * 10 + *rp++ - '0';
799 ++n;
800 if (*rp == ':' && n == 2 && isdigit (*(rp + 1)))
801 ++rp;
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;
815 break;
816 case 'E':
817 #ifdef _NL_CURRENT
818 switch (*fmt++)
820 case 'c':
821 /* Match locale's alternate date and time format. */
822 if (s.decided != raw)
824 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
826 if (*fmt == '\0')
827 fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
829 if (!recursive (fmt))
831 if (s.decided == loc)
832 return NULL;
833 else
834 rp = rp_backup;
836 else
838 if (strcmp (fmt, HERE_D_T_FMT))
839 s.decided = loc;
840 s.want_xday = 1;
841 break;
843 s.decided = raw;
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)
854 if (s.era_cnt >= 0)
856 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
857 if (era != NULL && match_string (era->era_name, rp))
859 s.decided = loc;
860 break;
862 else
863 return NULL;
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)
871 era = _nl_select_era_entry (s.era_cnt
872 HELPER_LOCALE_ARG);
873 if (era != NULL && match_string (era->era_name, rp))
875 s.decided = loc;
876 break;
879 if (s.era_cnt != (int) num_eras)
880 break;
882 s.era_cnt = -1;
883 if (s.decided == loc)
884 return NULL;
886 s.decided = raw;
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)
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;
900 if (s.era_cnt >= 0)
902 assert (s.decided == loc);
904 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
905 bool match = false;
906 if (era != NULL)
908 int delta = ((tm->tm_year - era->offset)
909 * era->absolute_direction);
910 /* The difference between two sets of years
911 does not include the final year itself,
912 therefore add 1 to the difference to
913 account for that final year. */
914 match = (delta >= 0
915 && delta < (((int64_t) era->stop_date[0]
916 - (int64_t) era->start_date[0])
917 * era->absolute_direction
918 + 1));
920 if (! match)
921 return NULL;
923 break;
926 num_eras = _NL_CURRENT_WORD (LC_TIME,
927 _NL_TIME_ERA_NUM_ENTRIES);
928 for (s.era_cnt = 0; s.era_cnt < (int) num_eras; ++s.era_cnt)
930 era = _nl_select_era_entry (s.era_cnt
931 HELPER_LOCALE_ARG);
932 if (era != NULL)
934 int delta = ((tm->tm_year - era->offset)
935 * era->absolute_direction);
936 /* See comment above about year difference + 1. */
937 if (delta >= 0
938 && delta < (((int64_t) era->stop_date[0]
939 - (int64_t) era->start_date[0])
940 * era->absolute_direction
941 + 1))
943 s.decided = loc;
944 break;
948 if (s.era_cnt != (int) num_eras)
949 break;
951 s.era_cnt = -1;
952 if (s.decided == loc)
953 return NULL;
955 s.decided = raw;
958 goto match_year_in_century;
959 case 'Y':
960 if (s.decided != raw)
962 num_eras = _NL_CURRENT_WORD (LC_TIME,
963 _NL_TIME_ERA_NUM_ENTRIES);
964 for (s.era_cnt = 0; s.era_cnt < (int) num_eras;
965 ++s.era_cnt, rp = rp_backup)
967 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
968 if (era != NULL && recursive (era->era_format))
969 break;
971 if (s.era_cnt == (int) num_eras)
973 s.era_cnt = -1;
974 if (s.decided == loc)
975 return NULL;
976 else
977 rp = rp_backup;
979 else
981 s.decided = loc;
982 break;
985 s.decided = raw;
987 get_number (0, 9999, 4);
988 tm->tm_year = val - 1900;
989 s.want_century = 0;
990 s.want_xday = 1;
991 break;
992 case 'x':
993 if (s.decided != raw)
995 const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
997 if (*fmt == '\0')
998 fmt = _NL_CURRENT (LC_TIME, D_FMT);
1000 if (!recursive (fmt))
1002 if (s.decided == loc)
1003 return NULL;
1004 else
1005 rp = rp_backup;
1007 else
1009 if (strcmp (fmt, HERE_D_FMT))
1010 s.decided = loc;
1011 break;
1013 s.decided = raw;
1015 if (!recursive (HERE_D_FMT))
1016 return NULL;
1017 break;
1018 case 'X':
1019 if (s.decided != raw)
1021 const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
1023 if (*fmt == '\0')
1024 fmt = _NL_CURRENT (LC_TIME, T_FMT);
1026 if (!recursive (fmt))
1028 if (s.decided == loc)
1029 return NULL;
1030 else
1031 rp = rp_backup;
1033 else
1035 if (strcmp (fmt, HERE_T_FMT))
1036 s.decided = loc;
1037 break;
1039 s.decided = raw;
1041 if (!recursive (HERE_T_FMT))
1042 return NULL;
1043 break;
1044 default:
1045 return NULL;
1047 break;
1048 #else
1049 /* We have no information about the era format. Just use
1050 the normal format. */
1051 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
1052 && *fmt != 'x' && *fmt != 'X')
1053 /* This is an illegal format. */
1054 return NULL;
1056 goto start_over;
1057 #endif
1058 case 'O':
1059 switch (*fmt++)
1061 case 'b':
1062 case 'B':
1063 case 'h':
1064 /* Match month name. Reprocess as plain 'B'. */
1065 fmt--;
1066 goto start_over;
1067 case 'd':
1068 case 'e':
1069 /* Match day of month using alternate numeric symbols. */
1070 get_alt_number (1, 31, 2);
1071 tm->tm_mday = val;
1072 s.have_mday = 1;
1073 s.want_xday = 1;
1074 break;
1075 case 'H':
1076 /* Match hour in 24-hour clock using alternate numeric
1077 symbols. */
1078 get_alt_number (0, 23, 2);
1079 tm->tm_hour = val;
1080 s.have_I = 0;
1081 break;
1082 case 'I':
1083 /* Match hour in 12-hour clock using alternate numeric
1084 symbols. */
1085 get_alt_number (1, 12, 2);
1086 tm->tm_hour = val % 12;
1087 s.have_I = 1;
1088 break;
1089 case 'm':
1090 /* Match month using alternate numeric symbols. */
1091 get_alt_number (1, 12, 2);
1092 tm->tm_mon = val - 1;
1093 s.have_mon = 1;
1094 s.want_xday = 1;
1095 break;
1096 case 'M':
1097 /* Match minutes using alternate numeric symbols. */
1098 get_alt_number (0, 59, 2);
1099 tm->tm_min = val;
1100 break;
1101 case 'S':
1102 /* Match seconds using alternate numeric symbols. */
1103 get_alt_number (0, 61, 2);
1104 tm->tm_sec = val;
1105 break;
1106 case 'U':
1107 get_alt_number (0, 53, 2);
1108 s.week_no = val;
1109 s.have_uweek = 1;
1110 break;
1111 case 'W':
1112 get_alt_number (0, 53, 2);
1113 s.week_no = val;
1114 s.have_wweek = 1;
1115 break;
1116 case 'V':
1117 get_alt_number (0, 53, 2);
1118 /* XXX This cannot determine any field in TM without
1119 further information. */
1120 break;
1121 case 'w':
1122 /* Match number of weekday using alternate numeric symbols. */
1123 get_alt_number (0, 6, 1);
1124 tm->tm_wday = val;
1125 s.have_wday = 1;
1126 break;
1127 case 'y':
1128 /* Match year within century using alternate numeric symbols. */
1129 get_alt_number (0, 99, 2);
1130 tm->tm_year = val >= 69 ? val : val + 100;
1131 s.want_xday = 1;
1132 break;
1133 default:
1134 return NULL;
1136 break;
1137 default:
1138 return NULL;
1142 if (statep != NULL)
1144 /* Recursive invocation, returning success, so
1145 update parent's struct tm and state. */
1146 *(struct __strptime_state *) statep = s;
1147 *tmp = tmb;
1148 return (char *) rp;
1151 if (s.have_I && s.is_pm)
1152 tm->tm_hour += 12;
1154 if (s.century != -1)
1156 if (s.want_century)
1157 tm->tm_year = tm->tm_year % 100 + (s.century - 19) * 100;
1158 else
1159 /* Only the century, but not the year. Strange, but so be it. */
1160 tm->tm_year = (s.century - 19) * 100;
1163 if (s.era_cnt != -1)
1165 era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG);
1166 if (era == NULL)
1167 return NULL;
1168 if (s.want_era)
1169 tm->tm_year = (era->start_date[0]
1170 + ((tm->tm_year - era->offset)
1171 * era->absolute_direction));
1172 else
1173 /* Era start year assumed. */
1174 tm->tm_year = era->start_date[0];
1176 else
1177 if (s.want_era)
1179 /* No era found but we have seen an E modifier. Rectify some
1180 values. */
1181 if (s.want_century && s.century == -1 && tm->tm_year < 69)
1182 tm->tm_year += 100;
1185 if (s.want_xday && !s.have_wday)
1187 if ( !(s.have_mon && s.have_mday) && s.have_yday)
1189 /* We don't have tm_mon and/or tm_mday, compute them. */
1190 int t_mon = 0;
1191 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
1192 t_mon++;
1193 if (!s.have_mon)
1194 tm->tm_mon = t_mon - 1;
1195 if (!s.have_mday)
1196 tm->tm_mday =
1197 (tm->tm_yday
1198 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1199 s.have_mon = 1;
1200 s.have_mday = 1;
1202 /* Don't crash in day_of_the_week if tm_mon is uninitialized. */
1203 if (s.have_mon || (unsigned) tm->tm_mon <= 11)
1204 day_of_the_week (tm);
1207 if (s.want_xday && !s.have_yday && (s.have_mon || (unsigned) tm->tm_mon <= 11))
1208 day_of_the_year (tm);
1210 if ((s.have_uweek || s.have_wweek) && s.have_wday)
1212 int save_wday = tm->tm_wday;
1213 int save_mday = tm->tm_mday;
1214 int save_mon = tm->tm_mon;
1215 int w_offset = s.have_uweek ? 0 : 1;
1217 tm->tm_mday = 1;
1218 tm->tm_mon = 0;
1219 day_of_the_week (tm);
1220 if (s.have_mday)
1221 tm->tm_mday = save_mday;
1222 if (s.have_mon)
1223 tm->tm_mon = save_mon;
1225 if (!s.have_yday)
1226 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
1227 + (s.week_no - 1) * 7
1228 + (save_wday - w_offset + 7) % 7);
1230 if (!s.have_mday || !s.have_mon)
1232 int t_mon = 0;
1233 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
1234 <= tm->tm_yday)
1235 t_mon++;
1236 if (!s.have_mon)
1237 tm->tm_mon = t_mon - 1;
1238 if (!s.have_mday)
1239 tm->tm_mday =
1240 (tm->tm_yday
1241 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
1244 tm->tm_wday = save_wday;
1247 return (char *) rp;
1251 char *
1252 strptime (const char *buf, const char *format, struct tm *tm LOCALE_PARAM)
1254 return __strptime_internal (buf, format, tm, NULL LOCALE_ARG);
1257 #ifdef _LIBC
1258 weak_alias (__strptime_l, strptime_l)
1259 #endif