]>
Commit | Line | Data |
---|---|---|
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.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 | ||
80 | time_t _mktime(struct tm *, const char *); | |
81 | ||
82 | #define asizeof(a) (sizeof (a) / sizeof ((a)[0])) | |
83 | ||
84 | enum {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 | ||
88 | static 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; | |
121 | label: | |
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 | ||
662 | char * | |
663 | strptime(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 | ||
669 | extern time_t timeoff(struct tm *, long); | |
670 | ||
671 | char * | |
672 | strptime_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 | } |