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