]> git.saurik.com Git - apple/libc.git/blob - stdtime/strptime-fbsd.c
Libc-594.9.1.tar.gz
[apple/libc.git] / stdtime / strptime-fbsd.c
1 /*
2 * Powerdog Industries kindly requests feedback from anyone modifying
3 * this function:
4 *
5 * Date: Thu, 05 Jun 1997 23:17:17 -0400
6 * From: Kevin Ruddy <kevin.ruddy@powerdog.com>
7 * To: James FitzGibbon <james@nexis.net>
8 * Subject: Re: Use of your strptime(3) code (fwd)
9 *
10 * The reason for the "no mod" clause was so that modifications would
11 * come back and we could integrate them and reissue so that a wider
12 * audience could use it (thereby spreading the wealth). This has
13 * made it possible to get strptime to work on many operating systems.
14 * I'm not sure why that's "plain unacceptable" to the FreeBSD team.
15 *
16 * Anyway, you can change it to "with or without modification" as
17 * you see fit. Enjoy.
18 *
19 * Kevin Ruddy
20 * Powerdog Industries, Inc.
21 */
22 /*
23 * Copyright (c) 1994 Powerdog Industries. All rights reserved.
24 *
25 * Redistribution and use in source and binary forms, with or without
26 * modification, are permitted provided that the following conditions
27 * are met:
28 * 1. Redistributions of source code must retain the above copyright
29 * notice, this list of conditions and the following disclaimer.
30 * 2. Redistributions in binary form must reproduce the above copyright
31 * notice, this list of conditions and the following disclaimer
32 * in the documentation and/or other materials provided with the
33 * distribution.
34 * 3. All advertising materials mentioning features or use of this
35 * software must display the following acknowledgement:
36 * This product includes software developed by Powerdog Industries.
37 * 4. The name of Powerdog Industries may not be used to endorse or
38 * promote products derived from this software without specific prior
39 * written permission.
40 *
41 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
42 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
44 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
45 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
46 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
47 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
48 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
49 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
50 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
51 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 */
53
54 #include <sys/cdefs.h>
55 #ifndef lint
56 #ifndef NOID
57 static char copyright[] __unused =
58 "@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved.";
59 static char sccsid[] __unused = "@(#)strptime.c 0.1 (Powerdog) 94/03/27";
60 #endif /* !defined NOID */
61 #endif /* not lint */
62 __FBSDID("$FreeBSD: src/lib/libc/stdtime/strptime.c,v 1.35 2003/11/17 04:19:15 nectar Exp $");
63
64 #include "xlocale_private.h"
65
66 #include "namespace.h"
67 #include <time.h>
68 #include <ctype.h>
69 #include <errno.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <pthread.h>
74 #include <stdint.h>
75 #include <limits.h>
76 #include "un-namespace.h"
77 #include "libc_private.h"
78 #include "timelocal.h"
79
80 static char * _strptime(const char *, const char *, struct tm *, int *, locale_t) __DARWIN_ALIAS(_strptime);
81 time_t _mktime(struct tm *, const char *);
82
83 #define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
84
85 enum {CONVERT_NONE, CONVERT_GMT, CONVERT_ZONE};
86
87 #define _strptime(b,f,t,c,l) _strptime0(b,f,t,c,l,-1,0,-1)
88
89 static char *
90 _strptime0(const char *buf, const char *fmt, struct tm *tm, int *convp, locale_t loc, int year, int yday, int wday)
91 {
92 char c;
93 const char *ptr;
94 int i,
95 len;
96 int Ealternative, Oalternative;
97 struct lc_time_T *tptr = __get_current_time_locale(loc);
98
99 ptr = fmt;
100 while (*ptr != 0) {
101 if (*buf == 0) {
102 fmt = ptr;
103 while (isspace_l((unsigned char)*ptr, loc)) {
104 ptr++;
105 }
106 return ((*ptr)==0) ? fmt : 0; /* trailing whitespace is ok */
107 }
108
109 c = *ptr++;
110
111 if (c != '%') {
112 if (isspace_l((unsigned char)c, loc))
113 while (*buf != 0 && isspace_l((unsigned char)*buf, loc))
114 buf++;
115 else if (c != *buf++)
116 return 0;
117 continue;
118 }
119
120 Ealternative = 0;
121 Oalternative = 0;
122 label:
123 c = *ptr++;
124 switch (c) {
125 case 0:
126 case '%':
127 if (*buf++ != '%')
128 return 0;
129 break;
130
131 case '+':
132 buf = _strptime(buf, tptr->date_fmt, tm, convp, loc);
133 if (buf == 0)
134 return 0;
135 break;
136
137 case 'C':
138 if (!isdigit_l((unsigned char)*buf, loc))
139 return 0;
140
141 /* XXX This will break for 3-digit centuries. */
142 len = 2;
143 for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
144 i *= 10;
145 i += *buf - '0';
146 len--;
147 }
148 if (i < 19)
149 return 0;
150
151 if (year != -1)
152 tm->tm_year = (year % 100) + i * 100 - 1900;
153 else
154 tm->tm_year = i * 100 - 1900;
155 year = tm->tm_year;
156 break;
157
158 case 'c':
159 buf = _strptime(buf, tptr->c_fmt, tm, convp, loc);
160 if (buf == 0)
161 return 0;
162 break;
163
164 case 'D':
165 buf = _strptime(buf, "%m/%d/%y", tm, convp, loc);
166 if (buf == 0)
167 return 0;
168 break;
169
170 case 'E':
171 if (Ealternative || Oalternative)
172 break;
173 Ealternative++;
174 goto label;
175
176 case 'O':
177 if (Ealternative || Oalternative)
178 break;
179 Oalternative++;
180 goto label;
181
182 case 'F':
183 buf = _strptime(buf, "%Y-%m-%d", tm, convp, loc);
184 if (buf == 0)
185 return 0;
186 break;
187
188 case 'R':
189 buf = _strptime(buf, "%H:%M", tm, convp, loc);
190 if (buf == 0)
191 return 0;
192 break;
193
194 case 'r':
195 buf = _strptime(buf, tptr->ampm_fmt, tm, convp, loc);
196 if (buf == 0)
197 return 0;
198 break;
199
200 case 'n':
201 case 't':
202 if (!isspace((unsigned char)*buf))
203 return 0;
204 while (isspace((unsigned char)*buf))
205 buf++;
206 break;
207
208 case 'T':
209 buf = _strptime(buf, "%H:%M:%S", tm, convp, loc);
210 if (buf == 0)
211 return 0;
212 break;
213
214 case 'X':
215 buf = _strptime(buf, tptr->X_fmt, tm, convp, loc);
216 if (buf == 0)
217 return 0;
218 break;
219
220 case 'x':
221 buf = _strptime(buf, tptr->x_fmt, tm, convp, loc);
222 if (buf == 0)
223 return 0;
224 break;
225
226 case 'j':
227 if (!isdigit_l((unsigned char)*buf, loc))
228 return 0;
229
230 len = 3;
231 for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
232 i *= 10;
233 i += *buf - '0';
234 len--;
235 }
236 if (i < 1 || i > 366)
237 return 0;
238
239 tm->tm_yday = yday = i - 1;
240 break;
241
242 case 'M':
243 case 'S':
244 if (*buf == 0 || isspace_l((unsigned char)*buf, loc))
245 break;
246
247 if (!isdigit_l((unsigned char)*buf, loc))
248 return 0;
249
250 len = 2;
251 for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
252 i *= 10;
253 i += *buf - '0';
254 len--;
255 }
256
257 if (c == 'M') {
258 if (i > 59)
259 return 0;
260 tm->tm_min = i;
261 } else {
262 if (i > 60)
263 return 0;
264 tm->tm_sec = i;
265 }
266
267 if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
268 while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
269 ptr++;
270 break;
271
272 case 'H':
273 case 'I':
274 case 'k':
275 case 'l':
276 /*
277 * Of these, %l is the only specifier explicitly
278 * documented as not being zero-padded. However,
279 * there is no harm in allowing zero-padding.
280 *
281 * XXX The %l specifier may gobble one too many
282 * digits if used incorrectly.
283 */
284 if (!isdigit_l((unsigned char)*buf, loc))
285 return 0;
286
287 len = 2;
288 for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
289 i *= 10;
290 i += *buf - '0';
291 len--;
292 }
293 if (c == 'H' || c == 'k') {
294 if (i > 23)
295 return 0;
296 } else if (i > 12)
297 return 0;
298
299 tm->tm_hour = i;
300
301 if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
302 while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
303 ptr++;
304 break;
305
306 case 'p':
307 /*
308 * XXX This is bogus if parsed before hour-related
309 * specifiers.
310 */
311 len = strlen(tptr->am);
312 if (strncasecmp_l(buf, tptr->am, len, loc) == 0) {
313 if (tm->tm_hour > 12)
314 return 0;
315 if (tm->tm_hour == 12)
316 tm->tm_hour = 0;
317 buf += len;
318 break;
319 }
320
321 len = strlen(tptr->pm);
322 if (strncasecmp_l(buf, tptr->pm, len, loc) == 0) {
323 if (tm->tm_hour > 12)
324 return 0;
325 if (tm->tm_hour != 12)
326 tm->tm_hour += 12;
327 buf += len;
328 break;
329 }
330
331 return 0;
332
333 case 'A':
334 case 'a':
335 for (i = 0; i < asizeof(tptr->weekday); i++) {
336 len = strlen(tptr->weekday[i]);
337 if (strncasecmp_l(buf, tptr->weekday[i],
338 len, loc) == 0)
339 break;
340 len = strlen(tptr->wday[i]);
341 if (strncasecmp_l(buf, tptr->wday[i],
342 len, loc) == 0)
343 break;
344 }
345 if (i == asizeof(tptr->weekday))
346 return 0;
347
348 tm->tm_wday = wday = i;
349 buf += len;
350 break;
351
352 case 'U': /* Sunday week */
353 case 'W': /* Monday week */
354 if (!isdigit_l((unsigned char)*buf, loc))
355 return 0;
356
357 len = 2;
358 for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
359 i *= 10;
360 i += *buf - '0';
361 len--;
362 }
363 if (i > 53)
364 return 0;
365
366 /* Calculate yday if we have enough data */
367 if ((year != -1) && (wday != -1)) {
368 struct tm mktm;
369 mktm.tm_year = year;
370 mktm.tm_mon = 0;
371 mktm.tm_mday = 1;
372 mktm.tm_sec = 1;
373 mktm.tm_min = mktm.tm_hour = 0;
374 mktm.tm_isdst = 0;
375 mktm.tm_gmtoff = 0;
376 if (mktime(&mktm) != -1) {
377 /* yday0 == Jan 1 == mktm.tm_wday */
378 int delta = wday - mktm.tm_wday;
379 if (!wday && c =='W')
380 i++; /* Sunday is part of the following week */
381 yday = 7 * i + delta;
382 if (yday < 0)
383 yday += 7;
384 tm->tm_yday = yday;
385 }
386 }
387 if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
388 while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
389 ptr++;
390 break;
391
392 case 'u': /* [1,7] */
393 case 'w': /* [0,6] */
394 if (!isdigit_l((unsigned char)*buf, loc))
395 return 0;
396
397 i = *buf - '0';
398 if (i > 6 + (c == 'u'))
399 return 0;
400 if (i == 7)
401 i = 0;
402 tm->tm_wday = wday = i;
403 buf++;
404 if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
405 while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
406 ptr++;
407 break;
408
409 case 'd':
410 case 'e':
411 /*
412 * The %e specifier is explicitly documented as not
413 * being zero-padded but there is no harm in allowing
414 * such padding.
415 *
416 * XXX The %e specifier may gobble one too many
417 * digits if used incorrectly.
418 */
419 /* Leading space is ok if date is single digit */
420 len = 2;
421 if (isspace_l((unsigned char)buf[0], loc) &&
422 isdigit_l((unsigned char)buf[1], loc) &&
423 !isdigit_l((unsigned char)buf[2], loc)) {
424 len = 1;
425 buf++;
426 }
427 if (!isdigit_l((unsigned char)*buf, loc))
428 return 0;
429
430 for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
431 i *= 10;
432 i += *buf - '0';
433 len--;
434 }
435 if (i > 31)
436 return 0;
437
438 tm->tm_mday = i;
439
440 if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
441 while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
442 ptr++;
443 break;
444
445 case 'B':
446 case 'b':
447 case 'h':
448 for (i = 0; i < asizeof(tptr->month); i++) {
449 if (Oalternative) {
450 if (c == 'B') {
451 len = strlen(tptr->alt_month[i]);
452 if (strncasecmp_l(buf,
453 tptr->alt_month[i],
454 len, loc) == 0)
455 break;
456 }
457 } else {
458 len = strlen(tptr->month[i]);
459 if (strncasecmp_l(buf, tptr->month[i],
460 len, loc) == 0)
461 break;
462 len = strlen(tptr->mon[i]);
463 if (strncasecmp_l(buf, tptr->mon[i],
464 len, loc) == 0)
465 break;
466 }
467 }
468 if (i == asizeof(tptr->month))
469 return 0;
470
471 tm->tm_mon = i;
472 buf += len;
473 break;
474
475 case 'm':
476 if (!isdigit_l((unsigned char)*buf, loc))
477 return 0;
478
479 len = 2;
480 for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
481 i *= 10;
482 i += *buf - '0';
483 len--;
484 }
485 if (i < 1 || i > 12)
486 return 0;
487
488 tm->tm_mon = i - 1;
489
490 if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
491 while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
492 ptr++;
493 break;
494
495 case 's':
496 {
497 char *cp;
498 int sverrno;
499 long n;
500 time_t t;
501
502 sverrno = errno;
503 errno = 0;
504 n = strtol_l(buf, &cp, 10, loc);
505 if (errno == ERANGE || (long)(t = n) != n) {
506 errno = sverrno;
507 return 0;
508 }
509 errno = sverrno;
510 buf = cp;
511 gmtime_r(&t, tm);
512 *convp = CONVERT_GMT;
513 }
514 break;
515
516 case 'Y':
517 case 'y':
518 if (*buf == 0 || isspace_l((unsigned char)*buf, loc))
519 break;
520
521 if (!isdigit_l((unsigned char)*buf, loc))
522 return 0;
523
524 #if __DARWIN_UNIX03
525 if (c == 'Y') {
526 int savei = 0;
527 const char *savebuf = buf;
528 int64_t i64 = 0;
529 int overflow = 0;
530
531 for (len = 0; *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
532 i64 *= 10;
533 i64 += *buf - '0';
534 if (++len <= 4) {
535 savei = i64;
536 savebuf = buf + 1;
537 }
538 if (i64 > INT_MAX) {
539 overflow++;
540 break;
541 }
542 }
543 /*
544 * Conformance requires %Y to be more then 4
545 * digits. However, there are several cases
546 * where %Y is immediately followed by other
547 * digits values. So we do the conformance
548 * case first (as many digits as possible),
549 * and if we fail, we backup and try just 4
550 * digits for %Y.
551 */
552 if (len > 4 && !overflow) {
553 struct tm savetm = *tm;
554 int saveconv = *convp;
555 const char *saveptr = ptr;
556 char *ret;
557
558 if (i64 < 1900)
559 return 0;
560
561 tm->tm_year = i64 - 1900;
562
563 if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
564 while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
565 ptr++;
566 ret = _strptime0(buf, ptr, tm, convp, loc, tm->tm_year, yday, wday);
567 if (ret) return ret;
568 /* Failed, so try 4-digit year */
569 *tm = savetm;
570 *convp = saveconv;
571 ptr = saveptr;
572 }
573 buf = savebuf;
574 i = savei;
575 } else {
576 len = 2;
577 #else /* !__DARWIN_UNIX03 */
578 len = (c == 'Y') ? 4 : 2;
579 #endif /* __DARWIN_UNIX03 */
580 for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
581 i *= 10;
582 i += *buf - '0';
583 len--;
584 }
585 #if __DARWIN_UNIX03
586 }
587 #endif /* __DARWIN_UNIX03 */
588 if (c == 'Y')
589 i -= 1900;
590 if (c == 'y' && i < 69)
591 i += 100;
592 if (i < 0)
593 return 0;
594
595 tm->tm_year = year = i;
596
597 if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
598 while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
599 ptr++;
600 break;
601
602 case 'Z':
603 {
604 const char *cp;
605 size_t tzlen, len;
606
607 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/}
608 len = cp - buf;
609 if (len == 3 && strncmp(buf, "GMT", 3) == 0) {
610 *convp = CONVERT_GMT;
611 buf += len;
612 break;
613 }
614 tzset();
615 tzlen = strlen(tzname[0]);
616 if (len == tzlen && strncmp(buf, tzname[0], tzlen) == 0) {
617 tm->tm_isdst = 0;
618 buf += len;
619 break;
620 }
621 tzlen = strlen(tzname[1]);
622 if (len == tzlen && strncmp(buf, tzname[1], tzlen) == 0) {
623 tm->tm_isdst = 1;
624 buf += len;
625 break;
626 }
627 return 0;
628 }
629
630 case 'z':
631 {
632 char sign;
633 int hr, min;
634
635 if ((buf[0] != '+' && buf[0] != '-')
636 || !isdigit_l((unsigned char)buf[1], loc)
637 || !isdigit_l((unsigned char)buf[2], loc)
638 || !isdigit_l((unsigned char)buf[3], loc)
639 || !isdigit_l((unsigned char)buf[4], loc))
640 return 0;
641 sscanf(buf, "%c%2d%2d", &sign, &hr, &min);
642 *convp = CONVERT_ZONE;
643 tm->tm_gmtoff = 60 * (60 * hr + min);
644 if (sign == '-')
645 tm->tm_gmtoff = -tm->tm_gmtoff;
646 buf += 5;
647 }
648 break;
649 }
650 }
651 return (char *)buf;
652 }
653
654
655 char *
656 strptime(const char * __restrict buf, const char * __restrict fmt,
657 struct tm * __restrict tm)
658 {
659 return strptime_l(buf, fmt, tm, __current_locale());
660 }
661
662 extern time_t timeoff(struct tm *, long);
663
664 char *
665 strptime_l(const char * __restrict buf, const char * __restrict fmt,
666 struct tm * __restrict tm, locale_t loc)
667 {
668 char *ret;
669 int conv;
670
671 NORMALIZE_LOCALE(loc);
672 conv = CONVERT_NONE;
673 tm->tm_zone = NULL;
674 ret = _strptime(buf, fmt, tm, &conv, loc);
675 if (ret) {
676 time_t t;
677
678 switch(conv) {
679 case CONVERT_GMT:
680 t = timegm(tm);
681 localtime_r(&t, tm);
682 break;
683 case CONVERT_ZONE:
684 {
685 long offset = tm->tm_gmtoff;
686 tm->tm_gmtoff = 0;
687 t = timeoff(tm, offset);
688 localtime_r(&t, tm);
689 break;
690 }
691 }
692 }
693
694 return (ret);
695 }