]> git.saurik.com Git - apple/libc.git/blob - stdtime/FreeBSD/strptime.c
Libc-320.tar.gz
[apple/libc.git] / stdtime / FreeBSD / strptime.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.34 2003/04/30 10:25:57 mtm Exp $");
63
64 #include "namespace.h"
65 #include <time.h>
66 #include <ctype.h>
67 #include <limits.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <pthread.h>
71 #include "un-namespace.h"
72 #include "libc_private.h"
73 #include "timelocal.h"
74
75 static char * _strptime(const char *, const char *, struct tm *, int *);
76
77 #define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
78
79 static char *
80 _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp)
81 {
82 char c;
83 const char *ptr;
84 int i,
85 len;
86 int Ealternative, Oalternative;
87 struct lc_time_T *tptr = __get_current_time_locale();
88
89 ptr = fmt;
90 while (*ptr != 0) {
91 if (*buf == 0)
92 break;
93
94 c = *ptr++;
95
96 if (c != '%') {
97 if (isspace((unsigned char)c))
98 while (*buf != 0 && isspace((unsigned char)*buf))
99 buf++;
100 else if (c != *buf++)
101 return 0;
102 continue;
103 }
104
105 Ealternative = 0;
106 Oalternative = 0;
107 label:
108 c = *ptr++;
109 switch (c) {
110 case 0:
111 case '%':
112 if (*buf++ != '%')
113 return 0;
114 break;
115
116 case '+':
117 buf = _strptime(buf, tptr->date_fmt, tm, GMTp);
118 if (buf == 0)
119 return 0;
120 break;
121
122 case 'C':
123 if (!isdigit((unsigned char)*buf))
124 return 0;
125
126 /* XXX This will break for 3-digit centuries. */
127 len = 2;
128 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
129 i *= 10;
130 i += *buf - '0';
131 len--;
132 }
133 if (i < 19)
134 return 0;
135
136 tm->tm_year = i * 100 - 1900;
137 break;
138
139 case 'c':
140 buf = _strptime(buf, tptr->c_fmt, tm, GMTp);
141 if (buf == 0)
142 return 0;
143 break;
144
145 case 'D':
146 buf = _strptime(buf, "%m/%d/%y", tm, GMTp);
147 if (buf == 0)
148 return 0;
149 break;
150
151 case 'E':
152 if (Ealternative || Oalternative)
153 break;
154 Ealternative++;
155 goto label;
156
157 case 'O':
158 if (Ealternative || Oalternative)
159 break;
160 Oalternative++;
161 goto label;
162
163 case 'F':
164 buf = _strptime(buf, "%Y-%m-%d", tm, GMTp);
165 if (buf == 0)
166 return 0;
167 break;
168
169 case 'R':
170 buf = _strptime(buf, "%H:%M", tm, GMTp);
171 if (buf == 0)
172 return 0;
173 break;
174
175 case 'r':
176 buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp);
177 if (buf == 0)
178 return 0;
179 break;
180
181 case 'T':
182 buf = _strptime(buf, "%H:%M:%S", tm, GMTp);
183 if (buf == 0)
184 return 0;
185 break;
186
187 case 'X':
188 buf = _strptime(buf, tptr->X_fmt, tm, GMTp);
189 if (buf == 0)
190 return 0;
191 break;
192
193 case 'x':
194 buf = _strptime(buf, tptr->x_fmt, tm, GMTp);
195 if (buf == 0)
196 return 0;
197 break;
198
199 case 'j':
200 if (!isdigit((unsigned char)*buf))
201 return 0;
202
203 len = 3;
204 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
205 i *= 10;
206 i += *buf - '0';
207 len--;
208 }
209 if (i < 1 || i > 366)
210 return 0;
211
212 tm->tm_yday = i - 1;
213 break;
214
215 case 'M':
216 case 'S':
217 if (*buf == 0 || isspace((unsigned char)*buf))
218 break;
219
220 if (!isdigit((unsigned char)*buf))
221 return 0;
222
223 len = 2;
224 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
225 i *= 10;
226 i += *buf - '0';
227 len--;
228 }
229
230 if (c == 'M') {
231 if (i > 59)
232 return 0;
233 tm->tm_min = i;
234 } else {
235 if (i > 60)
236 return 0;
237 tm->tm_sec = i;
238 }
239
240 if (*buf != 0 && isspace((unsigned char)*buf))
241 while (*ptr != 0 && !isspace((unsigned char)*ptr))
242 ptr++;
243 break;
244
245 case 'H':
246 case 'I':
247 case 'k':
248 case 'l':
249 /*
250 * Of these, %l is the only specifier explicitly
251 * documented as not being zero-padded. However,
252 * there is no harm in allowing zero-padding.
253 *
254 * XXX The %l specifier may gobble one too many
255 * digits if used incorrectly.
256 */
257 if (!isdigit((unsigned char)*buf))
258 return 0;
259
260 len = 2;
261 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
262 i *= 10;
263 i += *buf - '0';
264 len--;
265 }
266 if (c == 'H' || c == 'k') {
267 if (i > 23)
268 return 0;
269 } else if (i > 12)
270 return 0;
271
272 tm->tm_hour = i;
273
274 if (*buf != 0 && isspace((unsigned char)*buf))
275 while (*ptr != 0 && !isspace((unsigned char)*ptr))
276 ptr++;
277 break;
278
279 case 'p':
280 /*
281 * XXX This is bogus if parsed before hour-related
282 * specifiers.
283 */
284 len = strlen(tptr->am);
285 if (strncasecmp(buf, tptr->am, len) == 0) {
286 if (tm->tm_hour > 12)
287 return 0;
288 if (tm->tm_hour == 12)
289 tm->tm_hour = 0;
290 buf += len;
291 break;
292 }
293
294 len = strlen(tptr->pm);
295 if (strncasecmp(buf, tptr->pm, len) == 0) {
296 if (tm->tm_hour > 12)
297 return 0;
298 if (tm->tm_hour != 12)
299 tm->tm_hour += 12;
300 buf += len;
301 break;
302 }
303
304 return 0;
305
306 case 'A':
307 case 'a':
308 for (i = 0; i < asizeof(tptr->weekday); i++) {
309 len = strlen(tptr->weekday[i]);
310 if (strncasecmp(buf, tptr->weekday[i],
311 len) == 0)
312 break;
313 len = strlen(tptr->wday[i]);
314 if (strncasecmp(buf, tptr->wday[i],
315 len) == 0)
316 break;
317 }
318 if (i == asizeof(tptr->weekday))
319 return 0;
320
321 tm->tm_wday = i;
322 buf += len;
323 break;
324
325 case 'U':
326 case 'W':
327 /*
328 * XXX This is bogus, as we can not assume any valid
329 * information present in the tm structure at this
330 * point to calculate a real value, so just check the
331 * range for now.
332 */
333 if (!isdigit((unsigned char)*buf))
334 return 0;
335
336 len = 2;
337 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
338 i *= 10;
339 i += *buf - '0';
340 len--;
341 }
342 if (i > 53)
343 return 0;
344
345 if (*buf != 0 && isspace((unsigned char)*buf))
346 while (*ptr != 0 && !isspace((unsigned char)*ptr))
347 ptr++;
348 break;
349
350 case 'w':
351 if (!isdigit((unsigned char)*buf))
352 return 0;
353
354 i = *buf - '0';
355 if (i > 6)
356 return 0;
357
358 tm->tm_wday = i;
359
360 if (*buf != 0 && isspace((unsigned char)*buf))
361 while (*ptr != 0 && !isspace((unsigned char)*ptr))
362 ptr++;
363 break;
364
365 case 'd':
366 case 'e':
367 /*
368 * The %e specifier is explicitly documented as not
369 * being zero-padded but there is no harm in allowing
370 * such padding.
371 *
372 * XXX The %e specifier may gobble one too many
373 * digits if used incorrectly.
374 */
375 if (!isdigit((unsigned char)*buf))
376 return 0;
377
378 len = 2;
379 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
380 i *= 10;
381 i += *buf - '0';
382 len--;
383 }
384 if (i > 31)
385 return 0;
386
387 tm->tm_mday = i;
388
389 if (*buf != 0 && isspace((unsigned char)*buf))
390 while (*ptr != 0 && !isspace((unsigned char)*ptr))
391 ptr++;
392 break;
393
394 case 'B':
395 case 'b':
396 case 'h':
397 for (i = 0; i < asizeof(tptr->month); i++) {
398 if (Oalternative) {
399 if (c == 'B') {
400 len = strlen(tptr->alt_month[i]);
401 if (strncasecmp(buf,
402 tptr->alt_month[i],
403 len) == 0)
404 break;
405 }
406 } else {
407 len = strlen(tptr->month[i]);
408 if (strncasecmp(buf, tptr->month[i],
409 len) == 0)
410 break;
411 len = strlen(tptr->mon[i]);
412 if (strncasecmp(buf, tptr->mon[i],
413 len) == 0)
414 break;
415 }
416 }
417 if (i == asizeof(tptr->month))
418 return 0;
419
420 tm->tm_mon = i;
421 buf += len;
422 break;
423
424 case 'm':
425 if (!isdigit((unsigned char)*buf))
426 return 0;
427
428 len = 2;
429 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
430 i *= 10;
431 i += *buf - '0';
432 len--;
433 }
434 if (i < 1 || i > 12)
435 return 0;
436
437 tm->tm_mon = i - 1;
438
439 if (*buf != 0 && isspace((unsigned char)*buf))
440 while (*ptr != 0 && !isspace((unsigned char)*ptr))
441 ptr++;
442 break;
443
444 case 's':
445 {
446 char *cp;
447 time_t t;
448
449 t = strtol(buf, &cp, 10);
450 if (t == LONG_MAX)
451 return 0;
452 buf = cp;
453 gmtime_r(&t, tm);
454 *GMTp = 1;
455 }
456 break;
457
458 case 'Y':
459 case 'y':
460 if (*buf == 0 || isspace((unsigned char)*buf))
461 break;
462
463 if (!isdigit((unsigned char)*buf))
464 return 0;
465
466 len = (c == 'Y') ? 4 : 2;
467 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
468 i *= 10;
469 i += *buf - '0';
470 len--;
471 }
472 if (c == 'Y')
473 i -= 1900;
474 if (c == 'y' && i < 69)
475 i += 100;
476 if (i < 0)
477 return 0;
478
479 tm->tm_year = i;
480
481 if (*buf != 0 && isspace((unsigned char)*buf))
482 while (*ptr != 0 && !isspace((unsigned char)*ptr))
483 ptr++;
484 break;
485
486 case 'Z':
487 {
488 const char *cp;
489 char *zonestr;
490
491 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/}
492 if (cp - buf) {
493 zonestr = alloca(cp - buf + 1);
494 strncpy(zonestr, buf, cp - buf);
495 zonestr[cp - buf] = '\0';
496 tzset();
497 if (0 == strcmp(zonestr, "GMT")) {
498 *GMTp = 1;
499 } else if (0 == strcmp(zonestr, tzname[0])) {
500 tm->tm_isdst = 0;
501 } else if (0 == strcmp(zonestr, tzname[1])) {
502 tm->tm_isdst = 1;
503 } else {
504 return 0;
505 }
506 buf += cp - buf;
507 }
508 }
509 break;
510 }
511 }
512 return (char *)buf;
513 }
514
515
516 char *
517 strptime(const char * __restrict buf, const char * __restrict fmt,
518 struct tm * __restrict tm)
519 {
520 char *ret;
521 int gmt;
522
523 gmt = 0;
524 ret = _strptime(buf, fmt, tm, &gmt);
525 if (ret && gmt) {
526 time_t t = timegm(tm);
527 localtime_r(&t, tm);
528 }
529
530 return (ret);
531 }