]>
Commit | Line | Data |
---|---|---|
5290baf0 | 1 | /* Convert a string representation of time to a time value. |
50304ef0 | 2 | Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. |
8a4b65b4 UD |
3 | This file is part of the GNU C Library. |
4 | Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. | |
f8adc70c | 5 | |
8a4b65b4 UD |
6 | The GNU C Library is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU Library General Public License as | |
8 | published by the Free Software Foundation; either version 2 of the | |
9 | License, or (at your option) any later version. | |
f8adc70c | 10 | |
8a4b65b4 UD |
11 | The GNU C Library is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | Library General Public License for more details. | |
f8adc70c | 15 | |
8a4b65b4 UD |
16 | You should have received a copy of the GNU Library General Public |
17 | License along with the GNU C Library; see the file COPYING.LIB. If not, | |
18 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 | Boston, MA 02111-1307, USA. */ | |
f8adc70c | 20 | |
5290baf0 UD |
21 | /* XXX This version of the implementation is not really complete. |
22 | Some of the fields cannot add information alone. But if seeing | |
23 | some of them in the same format (such as year, week and weekday) | |
24 | this is enough information for determining the date. */ | |
25 | ||
26 | #ifdef HAVE_CONFIG_H | |
27 | # include <config.h> | |
28 | #endif | |
29 | ||
f8adc70c RM |
30 | #include <ctype.h> |
31 | #include <langinfo.h> | |
32 | #include <limits.h> | |
33 | #include <string.h> | |
34 | #include <time.h> | |
35 | ||
5290baf0 UD |
36 | #ifdef _LIBC |
37 | # include "../locale/localeinfo.h" | |
38 | #endif | |
39 | ||
40 | ||
41 | #ifndef __P | |
42 | # if defined (__GNUC__) || (defined (__STDC__) && __STDC__) | |
43 | # define __P(args) args | |
44 | # else | |
45 | # define __P(args) () | |
46 | # endif /* GCC. */ | |
47 | #endif /* Not __P. */ | |
48 | ||
49 | #if ! HAVE_LOCALTIME_R && ! defined (localtime_r) | |
50 | #ifdef _LIBC | |
51 | #define localtime_r __localtime_r | |
52 | #else | |
53 | /* Approximate localtime_r as best we can in its absence. */ | |
54 | #define localtime_r my_localtime_r | |
55 | static struct tm *localtime_r __P ((const time_t *, struct tm *)); | |
56 | static struct tm * | |
57 | localtime_r (t, tp) | |
58 | const time_t *t; | |
59 | struct tm *tp; | |
60 | { | |
61 | struct tm *l = localtime (t); | |
62 | if (! l) | |
63 | return 0; | |
64 | *tp = *l; | |
65 | return tp; | |
66 | } | |
67 | #endif /* ! _LIBC */ | |
68 | #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */ | |
f8adc70c RM |
69 | |
70 | ||
71 | #define match_char(ch1, ch2) if (ch1 != ch2) return NULL | |
5290baf0 UD |
72 | #if defined __GNUC__ && __GNUC__ >= 2 |
73 | # define match_string(cs1, s2) \ | |
f8adc70c | 74 | ({ size_t len = strlen (cs1); \ |
5290baf0 UD |
75 | int result = strncasecmp ((cs1), (s2), len) == 0; \ |
76 | if (result) (s2) += len; \ | |
f8adc70c | 77 | result; }) |
5290baf0 UD |
78 | #else |
79 | /* Oh come on. Get a reasonable compiler. */ | |
80 | # define match_string(cs1, s2) \ | |
81 | (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1)) | |
82 | #endif | |
f8adc70c RM |
83 | /* We intentionally do not use isdigit() for testing because this will |
84 | lead to problems with the wide character version. */ | |
5290baf0 | 85 | #define get_number(from, to) \ |
f8adc70c RM |
86 | do { \ |
87 | val = 0; \ | |
88 | if (*rp < '0' || *rp > '9') \ | |
89 | return NULL; \ | |
90 | do { \ | |
91 | val *= 10; \ | |
92 | val += *rp++ - '0'; \ | |
93 | } while (val * 10 <= to && *rp >= '0' && *rp <= '9'); \ | |
94 | if (val < from || val > to) \ | |
95 | return NULL; \ | |
96 | } while (0) | |
5290baf0 UD |
97 | #ifdef _NL_CURRENT |
98 | # define get_alt_number(from, to) \ | |
f8adc70c | 99 | do { \ |
5290baf0 | 100 | if (*decided != raw) \ |
f8adc70c | 101 | { \ |
5290baf0 UD |
102 | const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS); \ |
103 | val = 0; \ | |
104 | while (*alts != '\0') \ | |
105 | { \ | |
106 | size_t len = strlen (alts); \ | |
107 | if (strncasecmp (alts, rp, len) == 0) \ | |
108 | break; \ | |
109 | alts = strchr (alts, '\0') + 1; \ | |
110 | ++val; \ | |
111 | } \ | |
112 | if (*alts == '\0') \ | |
113 | { \ | |
114 | if (*decided == loc && val != 0) \ | |
115 | return NULL; \ | |
116 | } \ | |
117 | else \ | |
118 | { \ | |
119 | *decided = loc; \ | |
120 | break; \ | |
121 | } \ | |
f8adc70c | 122 | } \ |
5290baf0 | 123 | get_number (from, to); \ |
f8adc70c | 124 | } while (0) |
5290baf0 UD |
125 | #else |
126 | # define get_alt_number(from, to) \ | |
127 | /* We don't have the alternate representation. */ \ | |
128 | get_number(from, to) | |
129 | #endif | |
130 | #define recursive(new_fmt) \ | |
131 | (*(new_fmt) != '\0' \ | |
0d8733c4 | 132 | && (rp = strptime_internal (rp, (new_fmt), tm, decided)) != NULL) |
d41c6f61 | 133 | |
f8adc70c | 134 | |
5290baf0 UD |
135 | #ifdef _LIBC |
136 | /* This is defined in locale/C-time.c in the GNU libc. */ | |
137 | extern const struct locale_data _nl_C_LC_TIME; | |
138 | ||
139 | # define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string) | |
140 | # define ab_weekday_name \ | |
141 | (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string) | |
142 | # define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string) | |
143 | # define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string) | |
144 | # define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string) | |
655b26bb | 145 | # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string) |
5290baf0 UD |
146 | # define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string) |
147 | # define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string) | |
148 | # define HERE_T_FMT_AMPM \ | |
149 | (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string) | |
150 | # define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string) | |
50304ef0 UD |
151 | |
152 | # define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n) | |
5290baf0 UD |
153 | #else |
154 | static char const weekday_name[][10] = | |
155 | { | |
156 | "Sunday", "Monday", "Tuesday", "Wednesday", | |
157 | "Thursday", "Friday", "Saturday" | |
158 | }; | |
159 | static char const ab_weekday_name[][4] = | |
160 | { | |
161 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" | |
162 | }; | |
163 | static char const month_name[][10] = | |
164 | { | |
165 | "January", "February", "March", "April", "May", "June", | |
166 | "July", "August", "September", "October", "November", "December" | |
167 | }; | |
168 | static char const ab_month_name[][4] = | |
169 | { | |
170 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", | |
171 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" | |
172 | }; | |
173 | # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y" | |
174 | # define HERE_D_FMT "%m/%d/%y" | |
175 | # define HERE_AM_STR "AM" | |
176 | # define HERE_PM_STR "PM" | |
177 | # define HERE_T_FMT_AMPM "%I:%M:%S %p" | |
178 | # define HERE_T_FMT "%H:%M:%S" | |
179 | #endif | |
180 | ||
181 | /* Status of lookup: do we use the locale data or the raw data? */ | |
182 | enum locale_status { not, loc, raw }; | |
183 | ||
0c5ecdc4 | 184 | static char * |
dfd2257a UD |
185 | #ifdef _LIBC |
186 | internal_function | |
187 | #endif | |
0c5ecdc4 UD |
188 | strptime_internal __P ((const char *buf, const char *format, struct tm *tm, |
189 | enum locale_status *decided)); | |
5290baf0 UD |
190 | |
191 | static char * | |
dfd2257a UD |
192 | #ifdef _LIBC |
193 | internal_function | |
194 | #endif | |
5290baf0 UD |
195 | strptime_internal (buf, format, tm, decided) |
196 | const char *buf; | |
197 | const char *format; | |
198 | struct tm *tm; | |
199 | enum locale_status *decided; | |
f8adc70c RM |
200 | { |
201 | const char *rp; | |
202 | const char *fmt; | |
203 | int cnt; | |
204 | size_t val; | |
205 | int have_I, is_pm; | |
a6ff34d7 | 206 | int century, want_century; |
f8adc70c RM |
207 | |
208 | rp = buf; | |
209 | fmt = format; | |
210 | have_I = is_pm = 0; | |
a6ff34d7 UD |
211 | century = -1; |
212 | want_century = 0; | |
f8adc70c RM |
213 | |
214 | while (*fmt != '\0') | |
215 | { | |
216 | /* A white space in the format string matches 0 more or white | |
217 | space in the input string. */ | |
218 | if (isspace (*fmt)) | |
219 | { | |
220 | while (isspace (*rp)) | |
221 | ++rp; | |
222 | ++fmt; | |
223 | continue; | |
224 | } | |
225 | ||
226 | /* Any character but `%' must be matched by the same character | |
227 | in the iput string. */ | |
228 | if (*fmt != '%') | |
229 | { | |
230 | match_char (*fmt++, *rp++); | |
231 | continue; | |
232 | } | |
233 | ||
234 | ++fmt; | |
5290baf0 UD |
235 | #ifndef _NL_CURRENT |
236 | /* We need this for handling the `E' modifier. */ | |
237 | start_over: | |
238 | #endif | |
f8adc70c RM |
239 | switch (*fmt++) |
240 | { | |
241 | case '%': | |
242 | /* Match the `%' character itself. */ | |
243 | match_char ('%', *rp++); | |
244 | break; | |
245 | case 'a': | |
246 | case 'A': | |
247 | /* Match day of week. */ | |
248 | for (cnt = 0; cnt < 7; ++cnt) | |
249 | { | |
5290baf0 UD |
250 | #ifdef _NL_CURRENT |
251 | if (*decided !=raw) | |
252 | { | |
0d8733c4 | 253 | if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp)) |
5290baf0 UD |
254 | { |
255 | if (*decided == not | |
0d8733c4 UD |
256 | && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt), |
257 | weekday_name[cnt])) | |
5290baf0 UD |
258 | *decided = loc; |
259 | break; | |
260 | } | |
0d8733c4 | 261 | if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp)) |
5290baf0 UD |
262 | { |
263 | if (*decided == not | |
0d8733c4 UD |
264 | && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), |
265 | ab_weekday_name[cnt])) | |
5290baf0 UD |
266 | *decided = loc; |
267 | break; | |
268 | } | |
269 | } | |
270 | #endif | |
271 | if (*decided != loc | |
0d8733c4 UD |
272 | && (match_string (weekday_name[cnt], rp) |
273 | || match_string (ab_weekday_name[cnt], rp))) | |
5290baf0 UD |
274 | { |
275 | *decided = raw; | |
276 | break; | |
277 | } | |
f8adc70c RM |
278 | } |
279 | if (cnt == 7) | |
280 | /* Does not match a weekday name. */ | |
281 | return NULL; | |
282 | tm->tm_wday = cnt; | |
283 | break; | |
284 | case 'b': | |
285 | case 'B': | |
286 | case 'h': | |
287 | /* Match month name. */ | |
288 | for (cnt = 0; cnt < 12; ++cnt) | |
289 | { | |
5290baf0 UD |
290 | #ifdef _NL_CURRENT |
291 | if (*decided !=raw) | |
292 | { | |
0d8733c4 | 293 | if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp)) |
5290baf0 UD |
294 | { |
295 | if (*decided == not | |
0d8733c4 UD |
296 | && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt), |
297 | month_name[cnt])) | |
5290baf0 UD |
298 | *decided = loc; |
299 | break; | |
300 | } | |
0d8733c4 | 301 | if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp)) |
5290baf0 UD |
302 | { |
303 | if (*decided == not | |
0d8733c4 UD |
304 | && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), |
305 | ab_month_name[cnt])) | |
5290baf0 UD |
306 | *decided = loc; |
307 | break; | |
308 | } | |
309 | } | |
310 | #endif | |
0d8733c4 UD |
311 | if (match_string (month_name[cnt], rp) |
312 | || match_string (ab_month_name[cnt], rp)) | |
5290baf0 UD |
313 | { |
314 | *decided = raw; | |
315 | break; | |
316 | } | |
f8adc70c RM |
317 | } |
318 | if (cnt == 12) | |
319 | /* Does not match a month name. */ | |
320 | return NULL; | |
321 | tm->tm_mon = cnt; | |
322 | break; | |
323 | case 'c': | |
324 | /* Match locale's date and time format. */ | |
5290baf0 UD |
325 | #ifdef _NL_CURRENT |
326 | if (*decided != raw) | |
327 | { | |
328 | if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT))) | |
329 | { | |
330 | if (*decided == loc) | |
331 | return NULL; | |
332 | } | |
333 | else | |
334 | { | |
335 | if (*decided == not && | |
336 | strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT)) | |
337 | *decided = loc; | |
338 | break; | |
339 | } | |
340 | *decided = raw; | |
341 | } | |
342 | #endif | |
343 | if (!recursive (HERE_D_T_FMT)) | |
344 | return NULL; | |
f8adc70c RM |
345 | break; |
346 | case 'C': | |
347 | /* Match century number. */ | |
348 | get_number (0, 99); | |
a6ff34d7 | 349 | century = val; |
f8adc70c RM |
350 | break; |
351 | case 'd': | |
352 | case 'e': | |
353 | /* Match day of month. */ | |
354 | get_number (1, 31); | |
355 | tm->tm_mday = val; | |
356 | break; | |
5290baf0 UD |
357 | case 'x': |
358 | #ifdef _NL_CURRENT | |
359 | if (*decided != raw) | |
360 | { | |
361 | if (!recursive (_NL_CURRENT (LC_TIME, D_FMT))) | |
362 | { | |
363 | if (*decided == loc) | |
364 | return NULL; | |
365 | } | |
366 | else | |
367 | { | |
368 | if (decided == not | |
369 | && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT)) | |
370 | *decided = loc; | |
371 | break; | |
372 | } | |
373 | *decided = raw; | |
374 | } | |
375 | #endif | |
376 | /* Fall through. */ | |
f8adc70c RM |
377 | case 'D': |
378 | /* Match standard day format. */ | |
5290baf0 UD |
379 | if (!recursive (HERE_D_FMT)) |
380 | return NULL; | |
f8adc70c RM |
381 | break; |
382 | case 'H': | |
383 | /* Match hour in 24-hour clock. */ | |
384 | get_number (0, 23); | |
385 | tm->tm_hour = val; | |
386 | have_I = 0; | |
387 | break; | |
388 | case 'I': | |
389 | /* Match hour in 12-hour clock. */ | |
390 | get_number (1, 12); | |
779ae82e | 391 | tm->tm_hour = val % 12; |
f8adc70c RM |
392 | have_I = 1; |
393 | break; | |
394 | case 'j': | |
395 | /* Match day number of year. */ | |
396 | get_number (1, 366); | |
397 | tm->tm_yday = val - 1; | |
398 | break; | |
399 | case 'm': | |
400 | /* Match number of month. */ | |
401 | get_number (1, 12); | |
402 | tm->tm_mon = val - 1; | |
403 | break; | |
404 | case 'M': | |
405 | /* Match minute. */ | |
406 | get_number (0, 59); | |
407 | tm->tm_min = val; | |
408 | break; | |
409 | case 'n': | |
410 | case 't': | |
411 | /* Match any white space. */ | |
412 | while (isspace (*rp)) | |
413 | ++rp; | |
414 | break; | |
415 | case 'p': | |
416 | /* Match locale's equivalent of AM/PM. */ | |
5290baf0 UD |
417 | #ifdef _NL_CURRENT |
418 | if (*decided != raw) | |
f8adc70c | 419 | { |
5290baf0 UD |
420 | if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp)) |
421 | { | |
422 | if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR)) | |
423 | *decided = loc; | |
424 | break; | |
425 | } | |
426 | if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp)) | |
427 | { | |
428 | if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR)) | |
429 | *decided = loc; | |
430 | is_pm = 1; | |
431 | break; | |
432 | } | |
433 | *decided = raw; | |
f8adc70c | 434 | } |
5290baf0 UD |
435 | #endif |
436 | if (!match_string (HERE_AM_STR, rp)) | |
437 | if (match_string (HERE_PM_STR, rp)) | |
438 | is_pm = 1; | |
439 | else | |
440 | return NULL; | |
441 | break; | |
f8adc70c | 442 | case 'r': |
5290baf0 UD |
443 | #ifdef _NL_CURRENT |
444 | if (*decided != raw) | |
445 | { | |
446 | if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM))) | |
447 | { | |
448 | if (*decided == loc) | |
449 | return NULL; | |
450 | } | |
451 | else | |
452 | { | |
453 | if (*decided == not && | |
454 | strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM), | |
455 | HERE_T_FMT_AMPM)) | |
456 | *decided = loc; | |
457 | break; | |
458 | } | |
459 | *decided = raw; | |
460 | } | |
461 | #endif | |
462 | if (!recursive (HERE_T_FMT_AMPM)) | |
463 | return NULL; | |
f8adc70c RM |
464 | break; |
465 | case 'R': | |
5290baf0 UD |
466 | if (!recursive ("%H:%M")) |
467 | return NULL; | |
f8adc70c | 468 | break; |
d41c6f61 UD |
469 | case 's': |
470 | { | |
471 | /* The number of seconds may be very high so we cannot use | |
472 | the `get_number' macro. Instead read the number | |
473 | character for character and construct the result while | |
474 | doing this. */ | |
475 | time_t secs; | |
476 | if (*rp < '0' || *rp > '9') | |
477 | /* We need at least one digit. */ | |
478 | return NULL; | |
479 | ||
480 | do | |
481 | { | |
482 | secs *= 10; | |
483 | secs += *rp++ - '0'; | |
484 | } | |
485 | while (*rp >= '0' && *rp <= '9'); | |
486 | ||
5290baf0 | 487 | if (localtime_r (&secs, tm) == NULL) |
d41c6f61 UD |
488 | /* Error in function. */ |
489 | return NULL; | |
490 | } | |
491 | break; | |
f8adc70c RM |
492 | case 'S': |
493 | get_number (0, 61); | |
494 | tm->tm_sec = val; | |
495 | break; | |
5290baf0 UD |
496 | case 'X': |
497 | #ifdef _NL_CURRENT | |
498 | if (*decided != raw) | |
499 | { | |
500 | if (!recursive (_NL_CURRENT (LC_TIME, T_FMT))) | |
501 | { | |
502 | if (*decided == loc) | |
503 | return NULL; | |
504 | } | |
505 | else | |
506 | { | |
507 | if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT)) | |
508 | *decided = loc; | |
509 | break; | |
510 | } | |
511 | *decided = raw; | |
512 | } | |
513 | #endif | |
514 | /* Fall through. */ | |
f8adc70c | 515 | case 'T': |
5290baf0 UD |
516 | if (!recursive (HERE_T_FMT)) |
517 | return NULL; | |
f8adc70c | 518 | break; |
d41c6f61 UD |
519 | case 'u': |
520 | get_number (1, 7); | |
521 | tm->tm_wday = val % 7; | |
522 | break; | |
523 | case 'g': | |
524 | get_number (0, 99); | |
525 | /* XXX This cannot determine any field in TM. */ | |
526 | break; | |
527 | case 'G': | |
528 | if (*rp < '0' || *rp > '9') | |
529 | return NULL; | |
530 | /* XXX Ignore the number since we would need some more | |
531 | information to compute a real date. */ | |
532 | do | |
533 | ++rp; | |
534 | while (*rp >= '0' && *rp <= '9'); | |
535 | break; | |
f8adc70c RM |
536 | case 'U': |
537 | case 'V': | |
538 | case 'W': | |
539 | get_number (0, 53); | |
5290baf0 UD |
540 | /* XXX This cannot determine any field in TM without some |
541 | information. */ | |
f8adc70c RM |
542 | break; |
543 | case 'w': | |
544 | /* Match number of weekday. */ | |
545 | get_number (0, 6); | |
546 | tm->tm_wday = val; | |
547 | break; | |
f8adc70c RM |
548 | case 'y': |
549 | /* Match year within century. */ | |
550 | get_number (0, 99); | |
1cab5444 UD |
551 | /* The "Year 2000 :The Millennium Rollover" paper suggests that |
552 | values in the range 69-99 refer to the twentieth century. */ | |
553 | tm->tm_year = val >= 69 ? val : val + 100; | |
a6ff34d7 UD |
554 | /* Indicate that we want to use the century, if specified |
555 | want_century = 1; | |
f8adc70c RM |
556 | break; |
557 | case 'Y': | |
558 | /* Match year including century number. */ | |
1618c590 | 559 | get_number (0, 9999); |
0d8733c4 | 560 | tm->tm_year = val - 1900; |
a6ff34d7 | 561 | want_century = 0; |
f8adc70c RM |
562 | break; |
563 | case 'Z': | |
564 | /* XXX How to handle this? */ | |
565 | break; | |
566 | case 'E': | |
5290baf0 | 567 | #ifdef _NL_CURRENT |
f8adc70c RM |
568 | switch (*fmt++) |
569 | { | |
570 | case 'c': | |
571 | /* Match locale's alternate date and time format. */ | |
5290baf0 UD |
572 | if (*decided != raw) |
573 | { | |
574 | const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT); | |
575 | ||
576 | if (*fmt == '\0') | |
577 | fmt = _NL_CURRENT (LC_TIME, D_T_FMT); | |
578 | ||
579 | if (!recursive (fmt)) | |
580 | { | |
581 | if (*decided == loc) | |
582 | return NULL; | |
583 | } | |
584 | else | |
585 | { | |
586 | if (strcmp (fmt, HERE_D_T_FMT)) | |
587 | *decided = loc; | |
588 | break; | |
589 | } | |
590 | *decided = raw; | |
591 | } | |
592 | /* The C locale has no era information, so use the | |
593 | normal representation. */ | |
594 | if (!recursive (HERE_D_T_FMT)) | |
595 | return NULL; | |
f8adc70c RM |
596 | break; |
597 | case 'C': | |
598 | case 'y': | |
599 | case 'Y': | |
600 | /* Match name of base year in locale's alternate | |
601 | representation. */ | |
602 | /* XXX This is currently not implemented. It should | |
5290baf0 | 603 | use the value _NL_CURRENT (LC_TIME, ERA). */ |
f8adc70c RM |
604 | break; |
605 | case 'x': | |
5290baf0 UD |
606 | if (*decided != raw) |
607 | { | |
608 | const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT); | |
609 | ||
610 | if (*fmt == '\0') | |
611 | fmt = _NL_CURRENT (LC_TIME, D_FMT); | |
612 | ||
613 | if (!recursive (fmt)) | |
614 | { | |
615 | if (*decided == loc) | |
616 | return NULL; | |
617 | } | |
618 | else | |
619 | { | |
620 | if (strcmp (fmt, HERE_D_FMT)) | |
621 | *decided = loc; | |
622 | break; | |
623 | } | |
624 | *decided = raw; | |
625 | } | |
626 | if (!recursive (HERE_D_FMT)) | |
627 | return NULL; | |
f8adc70c RM |
628 | break; |
629 | case 'X': | |
5290baf0 UD |
630 | if (*decided != raw) |
631 | { | |
632 | const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT); | |
633 | ||
634 | if (*fmt == '\0') | |
635 | fmt = _NL_CURRENT (LC_TIME, T_FMT); | |
636 | ||
637 | if (!recursive (fmt)) | |
638 | { | |
639 | if (*decided == loc) | |
640 | return NULL; | |
641 | } | |
642 | else | |
643 | { | |
644 | if (strcmp (fmt, HERE_T_FMT)) | |
645 | *decided = loc; | |
646 | break; | |
647 | } | |
648 | *decided = raw; | |
649 | } | |
650 | if (!recursive (HERE_T_FMT)) | |
651 | return NULL; | |
f8adc70c RM |
652 | break; |
653 | default: | |
654 | return NULL; | |
655 | } | |
656 | break; | |
5290baf0 UD |
657 | #else |
658 | /* We have no information about the era format. Just use | |
659 | the normal format. */ | |
660 | if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y' | |
661 | && *fmt != 'x' && *fmt != 'X') | |
662 | /* This is an illegal format. */ | |
663 | return NULL; | |
664 | ||
665 | goto start_over; | |
666 | #endif | |
f8adc70c RM |
667 | case 'O': |
668 | switch (*fmt++) | |
669 | { | |
670 | case 'd': | |
671 | case 'e': | |
672 | /* Match day of month using alternate numeric symbols. */ | |
673 | get_alt_number (1, 31); | |
674 | tm->tm_mday = val; | |
675 | break; | |
676 | case 'H': | |
677 | /* Match hour in 24-hour clock using alternate numeric | |
678 | symbols. */ | |
679 | get_alt_number (0, 23); | |
680 | tm->tm_hour = val; | |
681 | have_I = 0; | |
682 | break; | |
683 | case 'I': | |
684 | /* Match hour in 12-hour clock using alternate numeric | |
685 | symbols. */ | |
686 | get_alt_number (1, 12); | |
687 | tm->tm_hour = val - 1; | |
688 | have_I = 1; | |
689 | break; | |
690 | case 'm': | |
691 | /* Match month using alternate numeric symbols. */ | |
692 | get_alt_number (1, 12); | |
693 | tm->tm_mon = val - 1; | |
694 | break; | |
695 | case 'M': | |
696 | /* Match minutes using alternate numeric symbols. */ | |
697 | get_alt_number (0, 59); | |
698 | tm->tm_min = val; | |
699 | break; | |
700 | case 'S': | |
701 | /* Match seconds using alternate numeric symbols. */ | |
702 | get_alt_number (0, 61); | |
703 | tm->tm_sec = val; | |
704 | break; | |
705 | case 'U': | |
706 | case 'V': | |
707 | case 'W': | |
708 | get_alt_number (0, 53); | |
5290baf0 UD |
709 | /* XXX This cannot determine any field in TM without |
710 | further information. */ | |
f8adc70c RM |
711 | break; |
712 | case 'w': | |
713 | /* Match number of weekday using alternate numeric symbols. */ | |
714 | get_alt_number (0, 6); | |
715 | tm->tm_wday = val; | |
716 | break; | |
717 | case 'y': | |
718 | /* Match year within century using alternate numeric symbols. */ | |
719 | get_alt_number (0, 99); | |
1618c590 | 720 | tm->tm_year = val >= 69 ? val : val + 100; |
f8adc70c RM |
721 | break; |
722 | default: | |
723 | return NULL; | |
724 | } | |
725 | break; | |
726 | default: | |
727 | return NULL; | |
728 | } | |
729 | } | |
730 | ||
731 | if (have_I && is_pm) | |
732 | tm->tm_hour += 12; | |
d41c6f61 | 733 | |
a6ff34d7 UD |
734 | if (want_century && century != -1) |
735 | tm->tm_year = tm->tm_year % 100 + (century - 19) * 100; | |
736 | ||
f8adc70c RM |
737 | return (char *) rp; |
738 | } | |
5290baf0 UD |
739 | |
740 | ||
741 | char * | |
742 | strptime (buf, format, tm) | |
743 | const char *buf; | |
744 | const char *format; | |
745 | struct tm *tm; | |
746 | { | |
747 | enum locale_status decided; | |
748 | #ifdef _NL_CURRENT | |
749 | decided = not; | |
750 | #else | |
751 | decided = raw; | |
752 | #endif | |
753 | return strptime_internal (buf, format, tm, &decided); | |
754 | } |