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