]> git.saurik.com Git - apple/libc.git/blame - stdtime/strptime-fbsd.c
Libc-498.1.7.tar.gz
[apple/libc.git] / stdtime / strptime-fbsd.c
CommitLineData
224c7076
A
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.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 "un-namespace.h"
75#include "libc_private.h"
76#include "timelocal.h"
77
78static char * _strptime(const char *, const char *, struct tm *, int *, locale_t) __DARWIN_ALIAS(_strptime);
79time_t _mktime(struct tm *, const char *);
80
81#define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
82
83enum {CONVERT_NONE, CONVERT_GMT, CONVERT_ZONE};
84
85static char *
86_strptime(const char *buf, const char *fmt, struct tm *tm, int *convp, locale_t loc)
87{
88 char c;
89 const char *ptr;
90 int i,
91 year = -1,
92 yday = 0,
93 wday = -1,
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) ? 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 for (i = 0; *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
526 i *= 10;
527 i += *buf - '0';
528 }
529 } else {
530 len = 2;
531#else /* !__DARWIN_UNIX03 */
532 len = (c == 'Y') ? 4 : 2;
533#endif /* __DARWIN_UNIX03 */
534 for (i = 0; len && *buf != 0 && isdigit_l((unsigned char)*buf, loc); buf++) {
535 i *= 10;
536 i += *buf - '0';
537 len--;
538 }
539#if __DARWIN_UNIX03
540 }
541#endif /* __DARWIN_UNIX03 */
542 if (c == 'Y')
543 i -= 1900;
544 if (c == 'y' && i < 69)
545 i += 100;
546 if (i < 0)
547 return 0;
548
549 tm->tm_year = year = i;
550
551 if (*buf != 0 && isspace_l((unsigned char)*buf, loc))
552 while (*ptr != 0 && !isspace_l((unsigned char)*ptr, loc) && *ptr != '%')
553 ptr++;
554 break;
555
556 case 'Z':
557 {
558 const char *cp;
b5d655f7 559 size_t tzlen, len;
224c7076
A
560
561 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/}
b5d655f7
A
562 len = cp - buf;
563 if (len == 3 && strncmp(buf, "GMT", 3) == 0) {
564 *convp = CONVERT_GMT;
565 buf += len;
566 break;
567 }
568 tzset();
569 tzlen = strlen(tzname[0]);
570 if (len == tzlen && strncmp(buf, tzname[0], tzlen) == 0) {
571 tm->tm_isdst = 0;
572 buf += len;
573 break;
574 }
575 tzlen = strlen(tzname[1]);
576 if (len == tzlen && strncmp(buf, tzname[1], tzlen) == 0) {
577 tm->tm_isdst = 1;
578 buf += len;
579 break;
224c7076 580 }
b5d655f7 581 return 0;
224c7076 582 }
224c7076
A
583
584 case 'z':
585 {
586 char sign;
587 int hr, min;
588
589 if ((buf[0] != '+' && buf[0] != '-')
590 || !isdigit_l((unsigned char)buf[1], loc)
591 || !isdigit_l((unsigned char)buf[2], loc)
592 || !isdigit_l((unsigned char)buf[3], loc)
593 || !isdigit_l((unsigned char)buf[4], loc))
594 return 0;
595 sscanf(buf, "%c%2d%2d", &sign, &hr, &min);
596 *convp = CONVERT_ZONE;
597 tm->tm_gmtoff = 60 * (60 * hr + min);
598 if (sign == '-')
599 tm->tm_gmtoff = -tm->tm_gmtoff;
600 buf += 5;
601 }
602 break;
603 }
604 }
605 return (char *)buf;
606}
607
608
609char *
610strptime(const char * __restrict buf, const char * __restrict fmt,
611 struct tm * __restrict tm)
612{
613 return strptime_l(buf, fmt, tm, __current_locale());
614}
615
616extern time_t timeoff(struct tm *, long);
617
618char *
619strptime_l(const char * __restrict buf, const char * __restrict fmt,
620 struct tm * __restrict tm, locale_t loc)
621{
622 char *ret;
623 int conv;
624
625 NORMALIZE_LOCALE(loc);
626 conv = CONVERT_NONE;
627 tm->tm_zone = NULL;
628 ret = _strptime(buf, fmt, tm, &conv, loc);
629 if (ret) {
630 time_t t;
631
632 switch(conv) {
633 case CONVERT_GMT:
634 t = timegm(tm);
635 localtime_r(&t, tm);
636 break;
637 case CONVERT_ZONE:
638 {
639 long offset = tm->tm_gmtoff;
640 tm->tm_gmtoff = 0;
641 t = timeoff(tm, offset);
642 localtime_r(&t, tm);
643 break;
644 }
645 }
646 }
647
648 return (ret);
649}