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