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