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