]>
Commit | Line | Data |
---|---|---|
5290baf0 UD |
1 | /* Convert a string representation of time to a time value. |
2 | Copyright (C) 1996, 1997 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) | |
145 | # define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string) | |
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) | |
151 | #else | |
152 | static char const weekday_name[][10] = | |
153 | { | |
154 | "Sunday", "Monday", "Tuesday", "Wednesday", | |
155 | "Thursday", "Friday", "Saturday" | |
156 | }; | |
157 | static char const ab_weekday_name[][4] = | |
158 | { | |
159 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" | |
160 | }; | |
161 | static char const month_name[][10] = | |
162 | { | |
163 | "January", "February", "March", "April", "May", "June", | |
164 | "July", "August", "September", "October", "November", "December" | |
165 | }; | |
166 | static char const ab_month_name[][4] = | |
167 | { | |
168 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", | |
169 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" | |
170 | }; | |
171 | # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y" | |
172 | # define HERE_D_FMT "%m/%d/%y" | |
173 | # define HERE_AM_STR "AM" | |
174 | # define HERE_PM_STR "PM" | |
175 | # define HERE_T_FMT_AMPM "%I:%M:%S %p" | |
176 | # define HERE_T_FMT "%H:%M:%S" | |
177 | #endif | |
178 | ||
179 | /* Status of lookup: do we use the locale data or the raw data? */ | |
180 | enum locale_status { not, loc, raw }; | |
181 | ||
0c5ecdc4 | 182 | static char * |
dfd2257a UD |
183 | #ifdef _LIBC |
184 | internal_function | |
185 | #endif | |
0c5ecdc4 UD |
186 | strptime_internal __P ((const char *buf, const char *format, struct tm *tm, |
187 | enum locale_status *decided)); | |
5290baf0 UD |
188 | |
189 | static char * | |
dfd2257a UD |
190 | #ifdef _LIBC |
191 | internal_function | |
192 | #endif | |
5290baf0 UD |
193 | strptime_internal (buf, format, tm, decided) |
194 | const char *buf; | |
195 | const char *format; | |
196 | struct tm *tm; | |
197 | enum locale_status *decided; | |
f8adc70c RM |
198 | { |
199 | const char *rp; | |
200 | const char *fmt; | |
201 | int cnt; | |
202 | size_t val; | |
203 | int have_I, is_pm; | |
204 | ||
205 | rp = buf; | |
206 | fmt = format; | |
207 | have_I = is_pm = 0; | |
208 | ||
209 | while (*fmt != '\0') | |
210 | { | |
211 | /* A white space in the format string matches 0 more or white | |
212 | space in the input string. */ | |
213 | if (isspace (*fmt)) | |
214 | { | |
215 | while (isspace (*rp)) | |
216 | ++rp; | |
217 | ++fmt; | |
218 | continue; | |
219 | } | |
220 | ||
221 | /* Any character but `%' must be matched by the same character | |
222 | in the iput string. */ | |
223 | if (*fmt != '%') | |
224 | { | |
225 | match_char (*fmt++, *rp++); | |
226 | continue; | |
227 | } | |
228 | ||
229 | ++fmt; | |
5290baf0 UD |
230 | #ifndef _NL_CURRENT |
231 | /* We need this for handling the `E' modifier. */ | |
232 | start_over: | |
233 | #endif | |
f8adc70c RM |
234 | switch (*fmt++) |
235 | { | |
236 | case '%': | |
237 | /* Match the `%' character itself. */ | |
238 | match_char ('%', *rp++); | |
239 | break; | |
240 | case 'a': | |
241 | case 'A': | |
242 | /* Match day of week. */ | |
243 | for (cnt = 0; cnt < 7; ++cnt) | |
244 | { | |
5290baf0 UD |
245 | #ifdef _NL_CURRENT |
246 | if (*decided !=raw) | |
247 | { | |
0d8733c4 | 248 | if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp)) |
5290baf0 UD |
249 | { |
250 | if (*decided == not | |
0d8733c4 UD |
251 | && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt), |
252 | weekday_name[cnt])) | |
5290baf0 UD |
253 | *decided = loc; |
254 | break; | |
255 | } | |
0d8733c4 | 256 | if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp)) |
5290baf0 UD |
257 | { |
258 | if (*decided == not | |
0d8733c4 UD |
259 | && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), |
260 | ab_weekday_name[cnt])) | |
5290baf0 UD |
261 | *decided = loc; |
262 | break; | |
263 | } | |
264 | } | |
265 | #endif | |
266 | if (*decided != loc | |
0d8733c4 UD |
267 | && (match_string (weekday_name[cnt], rp) |
268 | || match_string (ab_weekday_name[cnt], rp))) | |
5290baf0 UD |
269 | { |
270 | *decided = raw; | |
271 | break; | |
272 | } | |
f8adc70c RM |
273 | } |
274 | if (cnt == 7) | |
275 | /* Does not match a weekday name. */ | |
276 | return NULL; | |
277 | tm->tm_wday = cnt; | |
278 | break; | |
279 | case 'b': | |
280 | case 'B': | |
281 | case 'h': | |
282 | /* Match month name. */ | |
283 | for (cnt = 0; cnt < 12; ++cnt) | |
284 | { | |
5290baf0 UD |
285 | #ifdef _NL_CURRENT |
286 | if (*decided !=raw) | |
287 | { | |
0d8733c4 | 288 | if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp)) |
5290baf0 UD |
289 | { |
290 | if (*decided == not | |
0d8733c4 UD |
291 | && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt), |
292 | month_name[cnt])) | |
5290baf0 UD |
293 | *decided = loc; |
294 | break; | |
295 | } | |
0d8733c4 | 296 | if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp)) |
5290baf0 UD |
297 | { |
298 | if (*decided == not | |
0d8733c4 UD |
299 | && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), |
300 | ab_month_name[cnt])) | |
5290baf0 UD |
301 | *decided = loc; |
302 | break; | |
303 | } | |
304 | } | |
305 | #endif | |
0d8733c4 UD |
306 | if (match_string (month_name[cnt], rp) |
307 | || match_string (ab_month_name[cnt], rp)) | |
5290baf0 UD |
308 | { |
309 | *decided = raw; | |
310 | break; | |
311 | } | |
f8adc70c RM |
312 | } |
313 | if (cnt == 12) | |
314 | /* Does not match a month name. */ | |
315 | return NULL; | |
316 | tm->tm_mon = cnt; | |
317 | break; | |
318 | case 'c': | |
319 | /* Match locale's date and time format. */ | |
5290baf0 UD |
320 | #ifdef _NL_CURRENT |
321 | if (*decided != raw) | |
322 | { | |
323 | if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT))) | |
324 | { | |
325 | if (*decided == loc) | |
326 | return NULL; | |
327 | } | |
328 | else | |
329 | { | |
330 | if (*decided == not && | |
331 | strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT)) | |
332 | *decided = loc; | |
333 | break; | |
334 | } | |
335 | *decided = raw; | |
336 | } | |
337 | #endif | |
338 | if (!recursive (HERE_D_T_FMT)) | |
339 | return NULL; | |
f8adc70c RM |
340 | break; |
341 | case 'C': | |
342 | /* Match century number. */ | |
343 | get_number (0, 99); | |
344 | /* We don't need the number. */ | |
345 | break; | |
346 | case 'd': | |
347 | case 'e': | |
348 | /* Match day of month. */ | |
349 | get_number (1, 31); | |
350 | tm->tm_mday = val; | |
351 | break; | |
5290baf0 UD |
352 | case 'x': |
353 | #ifdef _NL_CURRENT | |
354 | if (*decided != raw) | |
355 | { | |
356 | if (!recursive (_NL_CURRENT (LC_TIME, D_FMT))) | |
357 | { | |
358 | if (*decided == loc) | |
359 | return NULL; | |
360 | } | |
361 | else | |
362 | { | |
363 | if (decided == not | |
364 | && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT)) | |
365 | *decided = loc; | |
366 | break; | |
367 | } | |
368 | *decided = raw; | |
369 | } | |
370 | #endif | |
371 | /* Fall through. */ | |
f8adc70c RM |
372 | case 'D': |
373 | /* Match standard day format. */ | |
5290baf0 UD |
374 | if (!recursive (HERE_D_FMT)) |
375 | return NULL; | |
f8adc70c RM |
376 | break; |
377 | case 'H': | |
378 | /* Match hour in 24-hour clock. */ | |
379 | get_number (0, 23); | |
380 | tm->tm_hour = val; | |
381 | have_I = 0; | |
382 | break; | |
383 | case 'I': | |
384 | /* Match hour in 12-hour clock. */ | |
385 | get_number (1, 12); | |
779ae82e | 386 | tm->tm_hour = val % 12; |
f8adc70c RM |
387 | have_I = 1; |
388 | break; | |
389 | case 'j': | |
390 | /* Match day number of year. */ | |
391 | get_number (1, 366); | |
392 | tm->tm_yday = val - 1; | |
393 | break; | |
394 | case 'm': | |
395 | /* Match number of month. */ | |
396 | get_number (1, 12); | |
397 | tm->tm_mon = val - 1; | |
398 | break; | |
399 | case 'M': | |
400 | /* Match minute. */ | |
401 | get_number (0, 59); | |
402 | tm->tm_min = val; | |
403 | break; | |
404 | case 'n': | |
405 | case 't': | |
406 | /* Match any white space. */ | |
407 | while (isspace (*rp)) | |
408 | ++rp; | |
409 | break; | |
410 | case 'p': | |
411 | /* Match locale's equivalent of AM/PM. */ | |
5290baf0 UD |
412 | #ifdef _NL_CURRENT |
413 | if (*decided != raw) | |
f8adc70c | 414 | { |
5290baf0 UD |
415 | if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp)) |
416 | { | |
417 | if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR)) | |
418 | *decided = loc; | |
419 | break; | |
420 | } | |
421 | if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp)) | |
422 | { | |
423 | if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR)) | |
424 | *decided = loc; | |
425 | is_pm = 1; | |
426 | break; | |
427 | } | |
428 | *decided = raw; | |
f8adc70c | 429 | } |
5290baf0 UD |
430 | #endif |
431 | if (!match_string (HERE_AM_STR, rp)) | |
432 | if (match_string (HERE_PM_STR, rp)) | |
433 | is_pm = 1; | |
434 | else | |
435 | return NULL; | |
436 | break; | |
f8adc70c | 437 | case 'r': |
5290baf0 UD |
438 | #ifdef _NL_CURRENT |
439 | if (*decided != raw) | |
440 | { | |
441 | if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM))) | |
442 | { | |
443 | if (*decided == loc) | |
444 | return NULL; | |
445 | } | |
446 | else | |
447 | { | |
448 | if (*decided == not && | |
449 | strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM), | |
450 | HERE_T_FMT_AMPM)) | |
451 | *decided = loc; | |
452 | break; | |
453 | } | |
454 | *decided = raw; | |
455 | } | |
456 | #endif | |
457 | if (!recursive (HERE_T_FMT_AMPM)) | |
458 | return NULL; | |
f8adc70c RM |
459 | break; |
460 | case 'R': | |
5290baf0 UD |
461 | if (!recursive ("%H:%M")) |
462 | return NULL; | |
f8adc70c | 463 | break; |
d41c6f61 UD |
464 | case 's': |
465 | { | |
466 | /* The number of seconds may be very high so we cannot use | |
467 | the `get_number' macro. Instead read the number | |
468 | character for character and construct the result while | |
469 | doing this. */ | |
470 | time_t secs; | |
471 | if (*rp < '0' || *rp > '9') | |
472 | /* We need at least one digit. */ | |
473 | return NULL; | |
474 | ||
475 | do | |
476 | { | |
477 | secs *= 10; | |
478 | secs += *rp++ - '0'; | |
479 | } | |
480 | while (*rp >= '0' && *rp <= '9'); | |
481 | ||
5290baf0 | 482 | if (localtime_r (&secs, tm) == NULL) |
d41c6f61 UD |
483 | /* Error in function. */ |
484 | return NULL; | |
485 | } | |
486 | break; | |
f8adc70c RM |
487 | case 'S': |
488 | get_number (0, 61); | |
489 | tm->tm_sec = val; | |
490 | break; | |
5290baf0 UD |
491 | case 'X': |
492 | #ifdef _NL_CURRENT | |
493 | if (*decided != raw) | |
494 | { | |
495 | if (!recursive (_NL_CURRENT (LC_TIME, T_FMT))) | |
496 | { | |
497 | if (*decided == loc) | |
498 | return NULL; | |
499 | } | |
500 | else | |
501 | { | |
502 | if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT)) | |
503 | *decided = loc; | |
504 | break; | |
505 | } | |
506 | *decided = raw; | |
507 | } | |
508 | #endif | |
509 | /* Fall through. */ | |
f8adc70c | 510 | case 'T': |
5290baf0 UD |
511 | if (!recursive (HERE_T_FMT)) |
512 | return NULL; | |
f8adc70c | 513 | break; |
d41c6f61 UD |
514 | case 'u': |
515 | get_number (1, 7); | |
516 | tm->tm_wday = val % 7; | |
517 | break; | |
518 | case 'g': | |
519 | get_number (0, 99); | |
520 | /* XXX This cannot determine any field in TM. */ | |
521 | break; | |
522 | case 'G': | |
523 | if (*rp < '0' || *rp > '9') | |
524 | return NULL; | |
525 | /* XXX Ignore the number since we would need some more | |
526 | information to compute a real date. */ | |
527 | do | |
528 | ++rp; | |
529 | while (*rp >= '0' && *rp <= '9'); | |
530 | break; | |
f8adc70c RM |
531 | case 'U': |
532 | case 'V': | |
533 | case 'W': | |
534 | get_number (0, 53); | |
5290baf0 UD |
535 | /* XXX This cannot determine any field in TM without some |
536 | information. */ | |
f8adc70c RM |
537 | break; |
538 | case 'w': | |
539 | /* Match number of weekday. */ | |
540 | get_number (0, 6); | |
541 | tm->tm_wday = val; | |
542 | break; | |
f8adc70c RM |
543 | case 'y': |
544 | /* Match year within century. */ | |
545 | get_number (0, 99); | |
1cab5444 UD |
546 | /* The "Year 2000 :The Millennium Rollover" paper suggests that |
547 | values in the range 69-99 refer to the twentieth century. */ | |
548 | tm->tm_year = val >= 69 ? val : val + 100; | |
f8adc70c RM |
549 | break; |
550 | case 'Y': | |
551 | /* Match year including century number. */ | |
1618c590 | 552 | get_number (0, 9999); |
0d8733c4 | 553 | tm->tm_year = val - 1900; |
f8adc70c RM |
554 | break; |
555 | case 'Z': | |
556 | /* XXX How to handle this? */ | |
557 | break; | |
558 | case 'E': | |
5290baf0 | 559 | #ifdef _NL_CURRENT |
f8adc70c RM |
560 | switch (*fmt++) |
561 | { | |
562 | case 'c': | |
563 | /* Match locale's alternate date and time format. */ | |
5290baf0 UD |
564 | if (*decided != raw) |
565 | { | |
566 | const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT); | |
567 | ||
568 | if (*fmt == '\0') | |
569 | fmt = _NL_CURRENT (LC_TIME, D_T_FMT); | |
570 | ||
571 | if (!recursive (fmt)) | |
572 | { | |
573 | if (*decided == loc) | |
574 | return NULL; | |
575 | } | |
576 | else | |
577 | { | |
578 | if (strcmp (fmt, HERE_D_T_FMT)) | |
579 | *decided = loc; | |
580 | break; | |
581 | } | |
582 | *decided = raw; | |
583 | } | |
584 | /* The C locale has no era information, so use the | |
585 | normal representation. */ | |
586 | if (!recursive (HERE_D_T_FMT)) | |
587 | return NULL; | |
f8adc70c RM |
588 | break; |
589 | case 'C': | |
590 | case 'y': | |
591 | case 'Y': | |
592 | /* Match name of base year in locale's alternate | |
593 | representation. */ | |
594 | /* XXX This is currently not implemented. It should | |
5290baf0 | 595 | use the value _NL_CURRENT (LC_TIME, ERA). */ |
f8adc70c RM |
596 | break; |
597 | case 'x': | |
5290baf0 UD |
598 | if (*decided != raw) |
599 | { | |
600 | const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT); | |
601 | ||
602 | if (*fmt == '\0') | |
603 | fmt = _NL_CURRENT (LC_TIME, D_FMT); | |
604 | ||
605 | if (!recursive (fmt)) | |
606 | { | |
607 | if (*decided == loc) | |
608 | return NULL; | |
609 | } | |
610 | else | |
611 | { | |
612 | if (strcmp (fmt, HERE_D_FMT)) | |
613 | *decided = loc; | |
614 | break; | |
615 | } | |
616 | *decided = raw; | |
617 | } | |
618 | if (!recursive (HERE_D_FMT)) | |
619 | return NULL; | |
f8adc70c RM |
620 | break; |
621 | case 'X': | |
5290baf0 UD |
622 | if (*decided != raw) |
623 | { | |
624 | const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT); | |
625 | ||
626 | if (*fmt == '\0') | |
627 | fmt = _NL_CURRENT (LC_TIME, T_FMT); | |
628 | ||
629 | if (!recursive (fmt)) | |
630 | { | |
631 | if (*decided == loc) | |
632 | return NULL; | |
633 | } | |
634 | else | |
635 | { | |
636 | if (strcmp (fmt, HERE_T_FMT)) | |
637 | *decided = loc; | |
638 | break; | |
639 | } | |
640 | *decided = raw; | |
641 | } | |
642 | if (!recursive (HERE_T_FMT)) | |
643 | return NULL; | |
f8adc70c RM |
644 | break; |
645 | default: | |
646 | return NULL; | |
647 | } | |
648 | break; | |
5290baf0 UD |
649 | #else |
650 | /* We have no information about the era format. Just use | |
651 | the normal format. */ | |
652 | if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y' | |
653 | && *fmt != 'x' && *fmt != 'X') | |
654 | /* This is an illegal format. */ | |
655 | return NULL; | |
656 | ||
657 | goto start_over; | |
658 | #endif | |
f8adc70c RM |
659 | case 'O': |
660 | switch (*fmt++) | |
661 | { | |
662 | case 'd': | |
663 | case 'e': | |
664 | /* Match day of month using alternate numeric symbols. */ | |
665 | get_alt_number (1, 31); | |
666 | tm->tm_mday = val; | |
667 | break; | |
668 | case 'H': | |
669 | /* Match hour in 24-hour clock using alternate numeric | |
670 | symbols. */ | |
671 | get_alt_number (0, 23); | |
672 | tm->tm_hour = val; | |
673 | have_I = 0; | |
674 | break; | |
675 | case 'I': | |
676 | /* Match hour in 12-hour clock using alternate numeric | |
677 | symbols. */ | |
678 | get_alt_number (1, 12); | |
679 | tm->tm_hour = val - 1; | |
680 | have_I = 1; | |
681 | break; | |
682 | case 'm': | |
683 | /* Match month using alternate numeric symbols. */ | |
684 | get_alt_number (1, 12); | |
685 | tm->tm_mon = val - 1; | |
686 | break; | |
687 | case 'M': | |
688 | /* Match minutes using alternate numeric symbols. */ | |
689 | get_alt_number (0, 59); | |
690 | tm->tm_min = val; | |
691 | break; | |
692 | case 'S': | |
693 | /* Match seconds using alternate numeric symbols. */ | |
694 | get_alt_number (0, 61); | |
695 | tm->tm_sec = val; | |
696 | break; | |
697 | case 'U': | |
698 | case 'V': | |
699 | case 'W': | |
700 | get_alt_number (0, 53); | |
5290baf0 UD |
701 | /* XXX This cannot determine any field in TM without |
702 | further information. */ | |
f8adc70c RM |
703 | break; |
704 | case 'w': | |
705 | /* Match number of weekday using alternate numeric symbols. */ | |
706 | get_alt_number (0, 6); | |
707 | tm->tm_wday = val; | |
708 | break; | |
709 | case 'y': | |
710 | /* Match year within century using alternate numeric symbols. */ | |
711 | get_alt_number (0, 99); | |
1618c590 | 712 | tm->tm_year = val >= 69 ? val : val + 100; |
f8adc70c RM |
713 | break; |
714 | default: | |
715 | return NULL; | |
716 | } | |
717 | break; | |
718 | default: | |
719 | return NULL; | |
720 | } | |
721 | } | |
722 | ||
723 | if (have_I && is_pm) | |
724 | tm->tm_hour += 12; | |
d41c6f61 | 725 | |
f8adc70c RM |
726 | return (char *) rp; |
727 | } | |
5290baf0 UD |
728 | |
729 | ||
730 | char * | |
731 | strptime (buf, format, tm) | |
732 | const char *buf; | |
733 | const char *format; | |
734 | struct tm *tm; | |
735 | { | |
736 | enum locale_status decided; | |
737 | #ifdef _NL_CURRENT | |
738 | decided = not; | |
739 | #else | |
740 | decided = raw; | |
741 | #endif | |
742 | return strptime_internal (buf, format, tm, &decided); | |
743 | } |