]> git.saurik.com Git - apple/libc.git/blob - stdtime/FreeBSD/strptime.c.patch
5bce7e0b46fa36863638ba79ee955212a74fea3c
[apple/libc.git] / stdtime / FreeBSD / strptime.c.patch
1 --- strptime.c.orig 2009-11-14 13:55:44.000000000 -0800
2 +++ strptime.c 2009-11-18 16:56:27.000000000 -0800
3 @@ -61,41 +61,55 @@ static char sccsid[] __unused = "@(#)str
4 #endif /* not lint */
5 __FBSDID("$FreeBSD: src/lib/libc/stdtime/strptime.c,v 1.37 2009/09/02 04:56:30 ache Exp $");
6
7 +#include "xlocale_private.h"
8 +
9 #include "namespace.h"
10 #include <time.h>
11 #include <ctype.h>
12 #include <errno.h>
13 +#include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <pthread.h>
17 +#include <stdint.h>
18 +#include <limits.h>
19 #include "un-namespace.h"
20 #include "libc_private.h"
21 #include "timelocal.h"
22
23 -static char * _strptime(const char *, const char *, struct tm *, int *);
24 +time_t _mktime(struct tm *, const char *);
25
26 #define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
27
28 +enum {CONVERT_NONE, CONVERT_GMT, CONVERT_ZONE};
29 +
30 +#define _strptime(b,f,t,c,l) _strptime0(b,f,t,c,l,-1,0,-1)
31 +
32 static char *
33 -_strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp)
34 +_strptime0(const char *buf, const char *fmt, struct tm *tm, int *convp, locale_t loc, int year, int yday, int wday)
35 {
36 char c;
37 const char *ptr;
38 int i,
39 len;
40 int Ealternative, Oalternative;
41 - struct lc_time_T *tptr = __get_current_time_locale();
42 + struct lc_time_T *tptr = __get_current_time_locale(loc);
43
44 ptr = fmt;
45 while (*ptr != 0) {
46 - if (*buf == 0)
47 - break;
48 + if (*buf == 0) {
49 + fmt = ptr;
50 + while (isspace_l((unsigned char)*ptr, loc)) {
51 + ptr++;
52 + }
53 + return ((*ptr)==0) ? (char *)fmt : 0; /* trailing whitespace is ok */
54 + }
55
56 c = *ptr++;
57
58 if (c != '%') {
59 - if (isspace((unsigned char)c))
60 - while (*buf != 0 && isspace((unsigned char)*buf))
61 + if (isspace_l((unsigned char)c, loc))
62 + while (*buf != 0 && isspace_l((unsigned char)*buf, loc))
63 buf++;
64 else if (c != *buf++)
65 return 0;
66 @@ -114,18 +128,18 @@ label:
67 break;
68
69 case '+':
70 - buf = _strptime(buf, tptr->date_fmt, tm, GMTp);
71 + buf = _strptime(buf, tptr->date_fmt, tm, convp, loc);
72 if (buf == 0)
73 return 0;
74 break;
75
76 case 'C':
77 - if (!isdigit((unsigned char)*buf))
78 + if (!isdigit_l((unsigned char)*buf, loc))
79 return 0;
80
81 /* XXX This will break for 3-digit centuries. */
82 len = 2;
83 - for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
84 + for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
85 i *= 10;
86 i += *buf - '0';
87 len--;
88 @@ -133,17 +147,21 @@ label:
89 if (i < 19)
90 return 0;
91
92 + if (year != -1)
93 + tm->tm_year = (year % 100) + i * 100 - 1900;
94 + else
95 tm->tm_year = i * 100 - 1900;
96 + year = tm->tm_year;
97 break;
98
99 case 'c':
100 - buf = _strptime(buf, tptr->c_fmt, tm, GMTp);
101 + buf = _strptime(buf, tptr->c_fmt, tm, convp, loc);
102 if (buf == 0)
103 return 0;
104 break;
105
106 case 'D':
107 - buf = _strptime(buf, "%m/%d/%y", tm, GMTp);
108 + buf = _strptime(buf, "%m/%d/%y", tm, convp, loc);
109 if (buf == 0)
110 return 0;
111 break;
112 @@ -161,47 +179,55 @@ label:
113 goto label;
114
115 case 'F':
116 - buf = _strptime(buf, "%Y-%m-%d", tm, GMTp);
117 + buf = _strptime(buf, "%Y-%m-%d", tm, convp, loc);
118 if (buf == 0)
119 return 0;
120 break;
121
122 case 'R':
123 - buf = _strptime(buf, "%H:%M", tm, GMTp);
124 + buf = _strptime(buf, "%H:%M", tm, convp, loc);
125 if (buf == 0)
126 return 0;
127 break;
128
129 case 'r':
130 - buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp);
131 + buf = _strptime(buf, tptr->ampm_fmt, tm, convp, loc);
132 if (buf == 0)
133 return 0;
134 break;
135
136 + case 'n':
137 + case 't':
138 + if (!isspace((unsigned char)*buf))
139 + return 0;
140 + while (isspace((unsigned char)*buf))
141 + buf++;
142 + break;
143 +
144 case 'T':
145 - buf = _strptime(buf, "%H:%M:%S", tm, GMTp);
146 + buf = _strptime(buf, "%H:%M:%S", tm, convp, loc);
147 if (buf == 0)
148 return 0;
149 break;
150
151 case 'X':
152 - buf = _strptime(buf, tptr->X_fmt, tm, GMTp);
153 + buf = _strptime(buf, tptr->X_fmt, tm, convp, loc);
154 if (buf == 0)
155 return 0;
156 break;
157
158 case 'x':
159 - buf = _strptime(buf, tptr->x_fmt, tm, GMTp);
160 + buf = _strptime(buf, tptr->x_fmt, tm, convp, loc);
161 if (buf == 0)
162 return 0;
163 break;
164
165 case 'j':
166 - if (!isdigit((unsigned char)*buf))
167 + if (!isdigit_l((unsigned char)*buf, loc))
168 return 0;
169
170 len = 3;
171 - for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
172 + for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
173 i *= 10;
174 i += *buf - '0';
175 len--;
176 @@ -209,19 +235,19 @@ label:
177 if (i < 1 || i > 366)
178 return 0;
179
180 - tm->tm_yday = i - 1;
181 + tm->tm_yday = yday = i - 1;
182 break;
183
184 case 'M':
185 case 'S':
186 - if (*buf == 0 || isspace((unsigned char)*buf))
187 + if (*buf == 0 || isspace_l((unsigned char)*buf, loc))
188 break;
189
190 - if (!isdigit((unsigned char)*buf))
191 + if (!isdigit_l((unsigned char)*buf, loc))
192 return 0;
193
194 len = 2;
195 - for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
196 + for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
197 i *= 10;
198 i += *buf - '0';
199 len--;
200 @@ -237,8 +263,8 @@ label:
201 tm->tm_sec = i;
202 }
203
204 - if (*buf != 0 && isspace((unsigned char)*buf))
205 - while (*ptr != 0 && !isspace((unsigned char)*ptr))
206 + if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
207 + while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
208 ptr++;
209 break;
210
211 @@ -254,11 +280,11 @@ label:
212 * XXX The %l specifier may gobble one too many
213 * digits if used incorrectly.
214 */
215 - if (!isdigit((unsigned char)*buf))
216 + if (!isdigit_l((unsigned char)*buf, loc))
217 return 0;
218
219 len = 2;
220 - for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
221 + for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
222 i *= 10;
223 i += *buf - '0';
224 len--;
225 @@ -271,8 +297,8 @@ label:
226
227 tm->tm_hour = i;
228
229 - if (*buf != 0 && isspace((unsigned char)*buf))
230 - while (*ptr != 0 && !isspace((unsigned char)*ptr))
231 + if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
232 + while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
233 ptr++;
234 break;
235
236 @@ -282,7 +308,7 @@ label:
237 * specifiers.
238 */
239 len = strlen(tptr->am);
240 - if (strncasecmp(buf, tptr->am, len) == 0) {
241 + if (strncasecmp_l(buf, tptr->am, len, loc) == 0) {
242 if (tm->tm_hour > 12)
243 return 0;
244 if (tm->tm_hour == 12)
245 @@ -292,7 +318,7 @@ label:
246 }
247
248 len = strlen(tptr->pm);
249 - if (strncasecmp(buf, tptr->pm, len) == 0) {
250 + if (strncasecmp_l(buf, tptr->pm, len, loc) == 0) {
251 if (tm->tm_hour > 12)
252 return 0;
253 if (tm->tm_hour != 12)
254 @@ -307,34 +333,28 @@ label:
255 case 'a':
256 for (i = 0; i < asizeof(tptr->weekday); i++) {
257 len = strlen(tptr->weekday[i]);
258 - if (strncasecmp(buf, tptr->weekday[i],
259 - len) == 0)
260 + if (strncasecmp_l(buf, tptr->weekday[i],
261 + len, loc) == 0)
262 break;
263 len = strlen(tptr->wday[i]);
264 - if (strncasecmp(buf, tptr->wday[i],
265 - len) == 0)
266 + if (strncasecmp_l(buf, tptr->wday[i],
267 + len, loc) == 0)
268 break;
269 }
270 if (i == asizeof(tptr->weekday))
271 return 0;
272
273 - tm->tm_wday = i;
274 + tm->tm_wday = wday = i;
275 buf += len;
276 break;
277
278 - case 'U':
279 - case 'W':
280 - /*
281 - * XXX This is bogus, as we can not assume any valid
282 - * information present in the tm structure at this
283 - * point to calculate a real value, so just check the
284 - * range for now.
285 - */
286 - if (!isdigit((unsigned char)*buf))
287 + case 'U': /* Sunday week */
288 + case 'W': /* Monday week */
289 + if (!isdigit_l((unsigned char)*buf, loc))
290 return 0;
291
292 len = 2;
293 - for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
294 + for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
295 i *= 10;
296 i += *buf - '0';
297 len--;
298 @@ -342,23 +362,46 @@ label:
299 if (i > 53)
300 return 0;
301
302 - if (*buf != 0 && isspace((unsigned char)*buf))
303 - while (*ptr != 0 && !isspace((unsigned char)*ptr))
304 + /* Calculate yday if we have enough data */
305 + if ((year != -1) && (wday != -1)) {
306 + struct tm mktm;
307 + mktm.tm_year = year;
308 + mktm.tm_mon = 0;
309 + mktm.tm_mday = 1;
310 + mktm.tm_sec = 1;
311 + mktm.tm_min = mktm.tm_hour = 0;
312 + mktm.tm_isdst = 0;
313 + mktm.tm_gmtoff = 0;
314 + if (mktime(&mktm) != -1) {
315 + /* yday0 == Jan 1 == mktm.tm_wday */
316 + int delta = wday - mktm.tm_wday;
317 + if (!wday && c =='W')
318 + i++; /* Sunday is part of the following week */
319 + yday = 7 * i + delta;
320 + if (yday < 0)
321 + yday += 7;
322 + tm->tm_yday = yday;
323 + }
324 + }
325 + if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
326 + while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
327 ptr++;
328 break;
329
330 - case 'w':
331 - if (!isdigit((unsigned char)*buf))
332 + case 'u': /* [1,7] */
333 + case 'w': /* [0,6] */
334 + if (!isdigit_l((unsigned char)*buf, loc))
335 return 0;
336
337 i = *buf - '0';
338 - if (i > 6)
339 + if (i > 6 + (c == 'u'))
340 return 0;
341 -
342 - tm->tm_wday = i;
343 -
344 - if (*buf != 0 && isspace((unsigned char)*buf))
345 - while (*ptr != 0 && !isspace((unsigned char)*ptr))
346 + if (i == 7)
347 + i = 0;
348 + tm->tm_wday = wday = i;
349 + buf++;
350 + if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
351 + while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
352 ptr++;
353 break;
354
355 @@ -372,11 +415,18 @@ label:
356 * XXX The %e specifier may gobble one too many
357 * digits if used incorrectly.
358 */
359 - if (!isdigit((unsigned char)*buf))
360 + /* Leading space is ok if date is single digit */
361 + len = 2;
362 + if (isspace_l((unsigned char)buf[0], loc) &&
363 + isdigit_l((unsigned char)buf[1], loc) &&
364 + !isdigit_l((unsigned char)buf[2], loc)) {
365 + len = 1;
366 + buf++;
367 + }
368 + if (!isdigit_l((unsigned char)*buf, loc))
369 return 0;
370
371 - len = 2;
372 - for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
373 + for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
374 i *= 10;
375 i += *buf - '0';
376 len--;
377 @@ -386,8 +436,8 @@ label:
378
379 tm->tm_mday = i;
380
381 - if (*buf != 0 && isspace((unsigned char)*buf))
382 - while (*ptr != 0 && !isspace((unsigned char)*ptr))
383 + if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
384 + while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
385 ptr++;
386 break;
387
388 @@ -398,19 +448,19 @@ label:
389 if (Oalternative) {
390 if (c == 'B') {
391 len = strlen(tptr->alt_month[i]);
392 - if (strncasecmp(buf,
393 + if (strncasecmp_l(buf,
394 tptr->alt_month[i],
395 - len) == 0)
396 + len, loc) == 0)
397 break;
398 }
399 } else {
400 len = strlen(tptr->month[i]);
401 - if (strncasecmp(buf, tptr->month[i],
402 - len) == 0)
403 + if (strncasecmp_l(buf, tptr->month[i],
404 + len, loc) == 0)
405 break;
406 len = strlen(tptr->mon[i]);
407 - if (strncasecmp(buf, tptr->mon[i],
408 - len) == 0)
409 + if (strncasecmp_l(buf, tptr->mon[i],
410 + len, loc) == 0)
411 break;
412 }
413 }
414 @@ -422,11 +472,11 @@ label:
415 break;
416
417 case 'm':
418 - if (!isdigit((unsigned char)*buf))
419 + if (!isdigit_l((unsigned char)*buf, loc))
420 return 0;
421
422 len = 2;
423 - for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
424 + for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
425 i *= 10;
426 i += *buf - '0';
427 len--;
428 @@ -436,8 +486,8 @@ label:
429
430 tm->tm_mon = i - 1;
431
432 - if (*buf != 0 && isspace((unsigned char)*buf))
433 - while (*ptr != 0 && !isspace((unsigned char)*ptr))
434 + if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
435 + while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
436 ptr++;
437 break;
438
439 @@ -450,7 +500,7 @@ label:
440
441 sverrno = errno;
442 errno = 0;
443 - n = strtol(buf, &cp, 10);
444 + n = strtol_l(buf, &cp, 10, loc);
445 if (errno == ERANGE || (long)(t = n) != n) {
446 errno = sverrno;
447 return 0;
448 @@ -458,24 +508,82 @@ label:
449 errno = sverrno;
450 buf = cp;
451 gmtime_r(&t, tm);
452 - *GMTp = 1;
453 + *convp = CONVERT_GMT;
454 }
455 break;
456
457 case 'Y':
458 case 'y':
459 - if (*buf == 0 || isspace((unsigned char)*buf))
460 + if (*buf == 0 || isspace_l((unsigned char)*buf, loc))
461 break;
462
463 - if (!isdigit((unsigned char)*buf))
464 + if (!isdigit_l((unsigned char)*buf, loc))
465 return 0;
466
467 +#if __DARWIN_UNIX03
468 + if (c == 'Y') {
469 + int savei = 0;
470 + const char *savebuf = buf;
471 + int64_t i64 = 0;
472 + int overflow = 0;
473 +
474 + for (len = 0; *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
475 + i64 *= 10;
476 + i64 += *buf - '0';
477 + if (++len <= 4) {
478 + savei = i64;
479 + savebuf = buf + 1;
480 + }
481 + if (i64 > INT_MAX) {
482 + overflow++;
483 + break;
484 + }
485 + }
486 + /*
487 + * Conformance requires %Y to be more then 4
488 + * digits. However, there are several cases
489 + * where %Y is immediately followed by other
490 + * digits values. So we do the conformance
491 + * case first (as many digits as possible),
492 + * and if we fail, we backup and try just 4
493 + * digits for %Y.
494 + */
495 + if (len > 4 && !overflow) {
496 + struct tm savetm = *tm;
497 + int saveconv = *convp;
498 + const char *saveptr = ptr;
499 + char *ret;
500 +
501 + if (i64 < 1900)
502 + return 0;
503 +
504 + tm->tm_year = i64 - 1900;
505 +
506 + if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
507 + while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
508 + ptr++;
509 + ret = _strptime0(buf, ptr, tm, convp, loc, tm->tm_year, yday, wday);
510 + if (ret) return ret;
511 + /* Failed, so try 4-digit year */
512 + *tm = savetm;
513 + *convp = saveconv;
514 + ptr = saveptr;
515 + }
516 + buf = savebuf;
517 + i = savei;
518 + } else {
519 + len = 2;
520 +#else /* !__DARWIN_UNIX03 */
521 len = (c == 'Y') ? 4 : 2;
522 - for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
523 +#endif /* __DARWIN_UNIX03 */
524 + for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
525 i *= 10;
526 i += *buf - '0';
527 len--;
528 }
529 +#if __DARWIN_UNIX03
530 + }
531 +#endif /* __DARWIN_UNIX03 */
532 if (c == 'Y')
533 i -= 1900;
534 if (c == 'y' && i < 69)
535 @@ -483,37 +591,40 @@ label:
536 if (i < 0)
537 return 0;
538
539 - tm->tm_year = i;
540 + tm->tm_year = year = i;
541
542 - if (*buf != 0 && isspace((unsigned char)*buf))
543 - while (*ptr != 0 && !isspace((unsigned char)*ptr))
544 + if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
545 + while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
546 ptr++;
547 break;
548
549 case 'Z':
550 {
551 const char *cp;
552 - char *zonestr;
553 + size_t tzlen, len;
554
555 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/}
556 - if (cp - buf) {
557 - zonestr = alloca(cp - buf + 1);
558 - strncpy(zonestr, buf, cp - buf);
559 - zonestr[cp - buf] = '\0';
560 - tzset();
561 - if (0 == strcmp(zonestr, "GMT")) {
562 - *GMTp = 1;
563 - } else if (0 == strcmp(zonestr, tzname[0])) {
564 - tm->tm_isdst = 0;
565 - } else if (0 == strcmp(zonestr, tzname[1])) {
566 - tm->tm_isdst = 1;
567 - } else {
568 - return 0;
569 - }
570 - buf += cp - buf;
571 + len = cp - buf;
572 + if (len == 3 && strncmp(buf, "GMT", 3) == 0) {
573 + *convp = CONVERT_GMT;
574 + buf += len;
575 + break;
576 }
577 + tzset();
578 + tzlen = strlen(tzname[0]);
579 + if (len == tzlen && strncmp(buf, tzname[0], tzlen) == 0) {
580 + tm->tm_isdst = 0;
581 + buf += len;
582 + break;
583 + }
584 + tzlen = strlen(tzname[1]);
585 + if (len == tzlen && strncmp(buf, tzname[1], tzlen) == 0) {
586 + tm->tm_isdst = 1;
587 + buf += len;
588 + break;
589 + }
590 + return 0;
591 }
592 - break;
593
594 case 'z':
595 {
596 @@ -529,7 +640,7 @@ label:
597 buf++;
598 i = 0;
599 for (len = 4; len > 0; len--) {
600 - if (isdigit((unsigned char)*buf)) {
601 + if (isdigit_l((unsigned char)*buf, loc)) {
602 i *= 10;
603 i += *buf - '0';
604 buf++;
605 @@ -539,7 +650,7 @@ label:
606
607 tm->tm_hour -= sign * (i / 100);
608 tm->tm_min -= sign * (i % 100);
609 - *GMTp = 1;
610 + *convp = CONVERT_GMT;
611 }
612 break;
613 }
614 @@ -552,14 +663,39 @@ char *
615 strptime(const char * __restrict buf, const char * __restrict fmt,
616 struct tm * __restrict tm)
617 {
618 + return strptime_l(buf, fmt, tm, __current_locale());
619 +}
620 +
621 +extern time_t timeoff(struct tm *, long);
622 +
623 +char *
624 +strptime_l(const char * __restrict buf, const char * __restrict fmt,
625 + struct tm * __restrict tm, locale_t loc)
626 +{
627 char *ret;
628 - int gmt;
629 + int conv;
630
631 - gmt = 0;
632 - ret = _strptime(buf, fmt, tm, &gmt);
633 - if (ret && gmt) {
634 - time_t t = timegm(tm);
635 - localtime_r(&t, tm);
636 + NORMALIZE_LOCALE(loc);
637 + conv = CONVERT_NONE;
638 + tm->tm_zone = NULL;
639 + ret = _strptime(buf, fmt, tm, &conv, loc);
640 + if (ret) {
641 + time_t t;
642 +
643 + switch(conv) {
644 + case CONVERT_GMT:
645 + t = timegm(tm);
646 + localtime_r(&t, tm);
647 + break;
648 + case CONVERT_ZONE:
649 + {
650 + long offset = tm->tm_gmtoff;
651 + tm->tm_gmtoff = 0;
652 + t = timeoff(tm, offset);
653 + localtime_r(&t, tm);
654 + break;
655 + }
656 + }
657 }
658
659 return (ret);