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