]>
Commit | Line | Data |
---|---|---|
974e3884 A |
1 | /*- |
2 | * Copyright (c) 2014 Gary Mills | |
3 | * Copyright 2011, Nexenta Systems, Inc. All rights reserved. | |
5b2abdfb A |
4 | * Copyright (c) 1994 Powerdog Industries. All rights reserved. |
5 | * | |
974e3884 A |
6 | * Copyright (c) 2011 The FreeBSD Foundation |
7 | * All rights reserved. | |
8 | * Portions of this software were developed by David Chisnall | |
9 | * under sponsorship from the FreeBSD Foundation. | |
10 | * | |
5b2abdfb A |
11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions | |
13 | * are met: | |
14 | * 1. Redistributions of source code must retain the above copyright | |
15 | * notice, this list of conditions and the following disclaimer. | |
16 | * 2. Redistributions in binary form must reproduce the above copyright | |
17 | * notice, this list of conditions and the following disclaimer | |
18 | * in the documentation and/or other materials provided with the | |
19 | * distribution. | |
5b2abdfb A |
20 | * |
21 | * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY | |
22 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE | |
25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | |
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
29 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | |
30 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
31 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
974e3884 A |
32 | * |
33 | * The views and conclusions contained in the software and documentation | |
34 | * are those of the authors and should not be interpreted as representing | |
35 | * official policies, either expressed or implied, of Powerdog Industries. | |
5b2abdfb A |
36 | */ |
37 | ||
9385eb3d | 38 | #include <sys/cdefs.h> |
5b2abdfb A |
39 | #ifndef lint |
40 | #ifndef NOID | |
9385eb3d | 41 | static char copyright[] __unused = |
5b2abdfb | 42 | "@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved."; |
9385eb3d | 43 | static char sccsid[] __unused = "@(#)strptime.c 0.1 (Powerdog) 94/03/27"; |
5b2abdfb A |
44 | #endif /* !defined NOID */ |
45 | #endif /* not lint */ | |
974e3884 | 46 | __FBSDID("$FreeBSD$"); |
5b2abdfb | 47 | |
ad3c9f2a A |
48 | #include "xlocale_private.h" |
49 | ||
9385eb3d | 50 | #include "namespace.h" |
5b2abdfb A |
51 | #include <time.h> |
52 | #include <ctype.h> | |
3d9156a7 | 53 | #include <errno.h> |
ad3c9f2a | 54 | #include <stdio.h> |
5b2abdfb A |
55 | #include <stdlib.h> |
56 | #include <string.h> | |
5b2abdfb | 57 | #include <pthread.h> |
9385eb3d A |
58 | #include "un-namespace.h" |
59 | #include "libc_private.h" | |
5b2abdfb | 60 | #include "timelocal.h" |
974e3884 | 61 | #include "tzfile.h" |
5b2abdfb | 62 | |
ad3c9f2a | 63 | time_t _mktime(struct tm *, const char *); |
5b2abdfb | 64 | |
974e3884 | 65 | #define asizeof(a) (sizeof(a) / sizeof((a)[0])) |
5b2abdfb | 66 | |
ad3c9f2a | 67 | enum {CONVERT_NONE, CONVERT_GMT, CONVERT_ZONE}; |
974e3884 A |
68 | enum week_kind { WEEK_U = 'U', WEEK_V = 'V', WEEK_W = 'W'}; |
69 | ||
70 | #define _strptime(b,f,t,c,l) _strptime0(b,f,t,c,l,FLAG_NONE,0,WEEK_U) | |
ad3c9f2a | 71 | |
974e3884 A |
72 | #define FLAG_NONE 0x01 |
73 | #define FLAG_YEAR 0x02 | |
74 | #define FLAG_MONTH 0x04 | |
75 | #define FLAG_YDAY 0x08 | |
76 | #define FLAG_MDAY 0x10 | |
77 | #define FLAG_WDAY 0x20 | |
78 | #define FLAG_WEEK 0x40 | |
79 | #define FLAG_CENTURY 0x100 | |
80 | #define FLAG_YEAR_IN_CENTURY 0x200 | |
6465356a | 81 | |
974e3884 A |
82 | /* |
83 | * Calculate the week day of the first day of a year. Valid for | |
84 | * the Gregorian calendar, which began Sept 14, 1752 in the UK | |
85 | * and its colonies. Ref: | |
86 | * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week | |
87 | */ | |
6465356a A |
88 | |
89 | static int | |
974e3884 | 90 | first_wday_of(int year) |
6465356a | 91 | { |
974e3884 A |
92 | return (((2 * (3 - (year / 100) % 4)) + (year % 100) + |
93 | ((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7); | |
6465356a | 94 | } |
ad3c9f2a | 95 | |
5b2abdfb | 96 | static char * |
974e3884 | 97 | _strptime0(const char *buf, const char *fmt, struct tm *tm, int *convp, locale_t locale, int flags, int week_number, enum week_kind week_kind) |
5b2abdfb A |
98 | { |
99 | char c; | |
100 | const char *ptr; | |
974e3884 A |
101 | int wday_offset; |
102 | int i, len; | |
5b2abdfb | 103 | int Ealternative, Oalternative; |
974e3884 A |
104 | const struct lc_time_T *tptr = __get_current_time_locale(locale); |
105 | static int start_of_month[2][13] = { | |
106 | {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, | |
107 | {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} | |
108 | }; | |
5b2abdfb A |
109 | |
110 | ptr = fmt; | |
111 | while (*ptr != 0) { | |
5b2abdfb A |
112 | c = *ptr++; |
113 | ||
114 | if (c != '%') { | |
974e3884 A |
115 | if (isspace_l((unsigned char)c, locale)) |
116 | while (*buf != 0 && | |
117 | isspace_l((unsigned char)*buf, locale)) | |
5b2abdfb A |
118 | buf++; |
119 | else if (c != *buf++) | |
974e3884 | 120 | return (NULL); |
5b2abdfb A |
121 | continue; |
122 | } | |
123 | ||
124 | Ealternative = 0; | |
125 | Oalternative = 0; | |
126 | label: | |
127 | c = *ptr++; | |
128 | switch (c) { | |
5b2abdfb A |
129 | case '%': |
130 | if (*buf++ != '%') | |
974e3884 | 131 | return (NULL); |
5b2abdfb A |
132 | break; |
133 | ||
134 | case '+': | |
974e3884 A |
135 | buf = _strptime(buf, tptr->date_fmt, tm, convp, locale); |
136 | if (buf == NULL) | |
137 | return (NULL); | |
138 | flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; | |
5b2abdfb A |
139 | break; |
140 | ||
141 | case 'C': | |
974e3884 A |
142 | if (!isdigit_l((unsigned char)*buf, locale)) |
143 | return (NULL); | |
5b2abdfb A |
144 | |
145 | /* XXX This will break for 3-digit centuries. */ | |
146 | len = 2; | |
974e3884 A |
147 | for (i = 0; len && *buf != 0 && |
148 | isdigit_l((unsigned char)*buf, locale); buf++) { | |
5b2abdfb A |
149 | i *= 10; |
150 | i += *buf - '0'; | |
151 | len--; | |
152 | } | |
153 | if (i < 19) | |
974e3884 A |
154 | return (NULL); |
155 | ||
156 | if (flags & FLAG_YEAR_IN_CENTURY) { | |
157 | tm->tm_year = i * 100 + (tm->tm_year % 100) - TM_YEAR_BASE; | |
158 | flags &= ~FLAG_YEAR_IN_CENTURY; | |
159 | } else { | |
160 | tm->tm_year = i * 100 - TM_YEAR_BASE; | |
161 | flags |= FLAG_YEAR; | |
162 | flags |= FLAG_CENTURY; | |
163 | } | |
5b2abdfb | 164 | |
5b2abdfb A |
165 | break; |
166 | ||
167 | case 'c': | |
974e3884 A |
168 | buf = _strptime(buf, tptr->c_fmt, tm, convp, locale); |
169 | if (buf == NULL) | |
170 | return (NULL); | |
171 | flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; | |
172 | flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY); | |
5b2abdfb A |
173 | break; |
174 | ||
175 | case 'D': | |
974e3884 A |
176 | buf = _strptime(buf, "%m/%d/%y", tm, convp, locale); |
177 | if (buf == NULL) | |
178 | return (NULL); | |
179 | flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; | |
180 | flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY); | |
5b2abdfb A |
181 | break; |
182 | ||
183 | case 'E': | |
184 | if (Ealternative || Oalternative) | |
185 | break; | |
186 | Ealternative++; | |
974e3884 | 187 | if (*ptr == '%') return (NULL); |
5b2abdfb A |
188 | goto label; |
189 | ||
190 | case 'O': | |
191 | if (Ealternative || Oalternative) | |
192 | break; | |
193 | Oalternative++; | |
974e3884 | 194 | if (*ptr == '%') return (NULL); |
5b2abdfb A |
195 | goto label; |
196 | ||
197 | case 'F': | |
974e3884 A |
198 | buf = _strptime(buf, "%Y-%m-%d", tm, convp, locale); |
199 | if (buf == NULL) | |
200 | return (NULL); | |
201 | flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; | |
202 | flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY); | |
5b2abdfb A |
203 | break; |
204 | ||
205 | case 'R': | |
974e3884 A |
206 | buf = _strptime(buf, "%H:%M", tm, convp, locale); |
207 | if (buf == NULL) | |
208 | return (NULL); | |
5b2abdfb A |
209 | break; |
210 | ||
211 | case 'r': | |
974e3884 A |
212 | buf = _strptime(buf, tptr->ampm_fmt, tm, convp, locale); |
213 | if (buf == NULL) | |
214 | return (NULL); | |
ad3c9f2a A |
215 | break; |
216 | ||
5b2abdfb | 217 | case 'T': |
974e3884 A |
218 | buf = _strptime(buf, "%H:%M:%S", tm, convp, locale); |
219 | if (buf == NULL) | |
220 | return (NULL); | |
5b2abdfb A |
221 | break; |
222 | ||
223 | case 'X': | |
974e3884 A |
224 | buf = _strptime(buf, tptr->X_fmt, tm, convp, locale); |
225 | if (buf == NULL) | |
226 | return (NULL); | |
5b2abdfb A |
227 | break; |
228 | ||
229 | case 'x': | |
974e3884 A |
230 | buf = _strptime(buf, tptr->x_fmt, tm, convp, locale); |
231 | if (buf == NULL) | |
232 | return (NULL); | |
233 | flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; | |
234 | flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY); | |
5b2abdfb A |
235 | break; |
236 | ||
237 | case 'j': | |
974e3884 A |
238 | if (!isdigit_l((unsigned char)*buf, locale)) |
239 | return (NULL); | |
5b2abdfb A |
240 | |
241 | len = 3; | |
974e3884 A |
242 | for (i = 0; len && *buf != 0 && |
243 | isdigit_l((unsigned char)*buf, locale); buf++){ | |
5b2abdfb A |
244 | i *= 10; |
245 | i += *buf - '0'; | |
246 | len--; | |
247 | } | |
248 | if (i < 1 || i > 366) | |
974e3884 A |
249 | return (NULL); |
250 | ||
251 | tm->tm_yday = i - 1; | |
252 | flags |= FLAG_YDAY; | |
5b2abdfb | 253 | |
5b2abdfb A |
254 | break; |
255 | ||
256 | case 'M': | |
257 | case 'S': | |
974e3884 A |
258 | if (*buf == 0 || |
259 | isspace_l((unsigned char)*buf, locale)) | |
5b2abdfb A |
260 | break; |
261 | ||
974e3884 A |
262 | if (!isdigit_l((unsigned char)*buf, locale)) |
263 | return (NULL); | |
5b2abdfb A |
264 | |
265 | len = 2; | |
974e3884 A |
266 | for (i = 0; len && *buf != 0 && |
267 | isdigit_l((unsigned char)*buf, locale); buf++){ | |
5b2abdfb A |
268 | i *= 10; |
269 | i += *buf - '0'; | |
270 | len--; | |
271 | } | |
272 | ||
273 | if (c == 'M') { | |
274 | if (i > 59) | |
974e3884 | 275 | return (NULL); |
5b2abdfb A |
276 | tm->tm_min = i; |
277 | } else { | |
278 | if (i > 60) | |
974e3884 | 279 | return (NULL); |
5b2abdfb A |
280 | tm->tm_sec = i; |
281 | } | |
282 | ||
5b2abdfb A |
283 | break; |
284 | ||
285 | case 'H': | |
286 | case 'I': | |
287 | case 'k': | |
288 | case 'l': | |
289 | /* | |
290 | * Of these, %l is the only specifier explicitly | |
291 | * documented as not being zero-padded. However, | |
292 | * there is no harm in allowing zero-padding. | |
293 | * | |
294 | * XXX The %l specifier may gobble one too many | |
295 | * digits if used incorrectly. | |
296 | */ | |
974e3884 A |
297 | if (!isdigit_l((unsigned char)*buf, locale)) |
298 | return (NULL); | |
5b2abdfb A |
299 | |
300 | len = 2; | |
974e3884 A |
301 | for (i = 0; len && *buf != 0 && |
302 | isdigit_l((unsigned char)*buf, locale); buf++) { | |
5b2abdfb A |
303 | i *= 10; |
304 | i += *buf - '0'; | |
305 | len--; | |
306 | } | |
307 | if (c == 'H' || c == 'k') { | |
308 | if (i > 23) | |
974e3884 | 309 | return (NULL); |
5b2abdfb | 310 | } else if (i > 12) |
974e3884 | 311 | return (NULL); |
5b2abdfb A |
312 | |
313 | tm->tm_hour = i; | |
314 | ||
5b2abdfb A |
315 | break; |
316 | ||
317 | case 'p': | |
318 | /* | |
319 | * XXX This is bogus if parsed before hour-related | |
320 | * specifiers. | |
321 | */ | |
9385eb3d | 322 | len = strlen(tptr->am); |
974e3884 | 323 | if (strncasecmp_l(buf, tptr->am, len, locale) == 0) { |
5b2abdfb | 324 | if (tm->tm_hour > 12) |
974e3884 | 325 | return (NULL); |
5b2abdfb A |
326 | if (tm->tm_hour == 12) |
327 | tm->tm_hour = 0; | |
328 | buf += len; | |
329 | break; | |
330 | } | |
331 | ||
9385eb3d | 332 | len = strlen(tptr->pm); |
974e3884 | 333 | if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) { |
5b2abdfb | 334 | if (tm->tm_hour > 12) |
974e3884 | 335 | return (NULL); |
5b2abdfb A |
336 | if (tm->tm_hour != 12) |
337 | tm->tm_hour += 12; | |
338 | buf += len; | |
339 | break; | |
340 | } | |
341 | ||
974e3884 | 342 | return (NULL); |
5b2abdfb A |
343 | |
344 | case 'A': | |
345 | case 'a': | |
9385eb3d A |
346 | for (i = 0; i < asizeof(tptr->weekday); i++) { |
347 | len = strlen(tptr->weekday[i]); | |
ad3c9f2a | 348 | if (strncasecmp_l(buf, tptr->weekday[i], |
974e3884 | 349 | len, locale) == 0) |
9385eb3d A |
350 | break; |
351 | len = strlen(tptr->wday[i]); | |
ad3c9f2a | 352 | if (strncasecmp_l(buf, tptr->wday[i], |
974e3884 | 353 | len, locale) == 0) |
9385eb3d | 354 | break; |
5b2abdfb | 355 | } |
9385eb3d | 356 | if (i == asizeof(tptr->weekday)) |
974e3884 | 357 | return (NULL); |
5b2abdfb | 358 | |
5b2abdfb | 359 | buf += len; |
974e3884 A |
360 | tm->tm_wday = i; |
361 | flags |= FLAG_WDAY; | |
5b2abdfb A |
362 | break; |
363 | ||
974e3884 A |
364 | case 'U': /* Sunday week */ |
365 | case 'V': /* ISO 8601 week */ | |
366 | case 'W': /* Monday week */ | |
367 | if (!isdigit_l((unsigned char)*buf, locale)) | |
368 | return (NULL); | |
5b2abdfb A |
369 | |
370 | len = 2; | |
974e3884 A |
371 | for (i = 0; len && *buf != 0 && |
372 | isdigit_l((unsigned char)*buf, locale); buf++) { | |
5b2abdfb A |
373 | i *= 10; |
374 | i += *buf - '0'; | |
375 | len--; | |
376 | } | |
377 | if (i > 53) | |
974e3884 A |
378 | return (NULL); |
379 | if (c == WEEK_V && i < 1) | |
380 | return (NULL); | |
6465356a | 381 | |
974e3884 A |
382 | week_number = i; |
383 | week_kind = c; | |
384 | flags |= FLAG_WEEK; | |
5b2abdfb | 385 | |
5b2abdfb A |
386 | break; |
387 | ||
974e3884 A |
388 | case 'u': /* [1,7] */ |
389 | case 'w': /* [0,6] */ | |
390 | if (!isdigit_l((unsigned char)*buf, locale)) | |
391 | return (NULL); | |
5b2abdfb A |
392 | |
393 | i = *buf - '0'; | |
ad3c9f2a | 394 | if (i > 6 + (c == 'u')) |
974e3884 | 395 | return (NULL); |
ad3c9f2a A |
396 | if (i == 7) |
397 | i = 0; | |
6465356a | 398 | |
974e3884 A |
399 | tm->tm_wday = i; |
400 | flags |= FLAG_WDAY; | |
ad3c9f2a | 401 | buf++; |
974e3884 | 402 | |
5b2abdfb A |
403 | break; |
404 | ||
5b2abdfb A |
405 | case 'e': |
406 | /* | |
974e3884 A |
407 | * With %e format, our strftime(3) adds a blank space |
408 | * before single digits. | |
409 | */ | |
410 | if (*buf != 0 && | |
411 | isspace_l((unsigned char)*buf, locale)) | |
412 | buf++; | |
413 | /* FALLTHROUGH */ | |
414 | case 'd': | |
415 | /* | |
416 | * The %e specifier was once explicitly documented as | |
417 | * not being zero-padded but was later changed to | |
418 | * equivalent to %d. There is no harm in allowing | |
5b2abdfb A |
419 | * such padding. |
420 | * | |
421 | * XXX The %e specifier may gobble one too many | |
422 | * digits if used incorrectly. | |
423 | */ | |
ad3c9f2a A |
424 | /* Leading space is ok if date is single digit */ |
425 | len = 2; | |
974e3884 A |
426 | if (isspace_l((unsigned char)buf[0], locale) && |
427 | isdigit_l((unsigned char)buf[1], locale) && | |
428 | !isdigit_l((unsigned char)buf[2], locale)) { | |
ad3c9f2a A |
429 | len = 1; |
430 | buf++; | |
431 | } | |
974e3884 A |
432 | if (!isdigit_l((unsigned char)*buf, locale)) |
433 | return (NULL); | |
5b2abdfb | 434 | |
974e3884 A |
435 | for (i = 0; len && *buf != 0 && |
436 | isdigit_l((unsigned char)*buf, locale); buf++) { | |
5b2abdfb A |
437 | i *= 10; |
438 | i += *buf - '0'; | |
439 | len--; | |
440 | } | |
441 | if (i > 31) | |
974e3884 | 442 | return (NULL); |
5b2abdfb A |
443 | |
444 | tm->tm_mday = i; | |
974e3884 | 445 | flags |= FLAG_MDAY; |
5b2abdfb | 446 | |
5b2abdfb A |
447 | break; |
448 | ||
449 | case 'B': | |
450 | case 'b': | |
451 | case 'h': | |
9385eb3d | 452 | for (i = 0; i < asizeof(tptr->month); i++) { |
5b2abdfb A |
453 | if (Oalternative) { |
454 | if (c == 'B') { | |
9385eb3d | 455 | len = strlen(tptr->alt_month[i]); |
ad3c9f2a | 456 | if (strncasecmp_l(buf, |
9385eb3d | 457 | tptr->alt_month[i], |
974e3884 | 458 | len, locale) == 0) |
5b2abdfb A |
459 | break; |
460 | } | |
461 | } else { | |
9385eb3d | 462 | len = strlen(tptr->month[i]); |
ad3c9f2a | 463 | if (strncasecmp_l(buf, tptr->month[i], |
974e3884 | 464 | len, locale) == 0) |
9385eb3d | 465 | break; |
974e3884 A |
466 | } |
467 | } | |
468 | /* | |
469 | * Try the abbreviated month name if the full name | |
470 | * wasn't found and Oalternative was not requested. | |
471 | */ | |
472 | if (i == asizeof(tptr->month) && !Oalternative) { | |
473 | for (i = 0; i < asizeof(tptr->month); i++) { | |
9385eb3d | 474 | len = strlen(tptr->mon[i]); |
ad3c9f2a | 475 | if (strncasecmp_l(buf, tptr->mon[i], |
974e3884 | 476 | len, locale) == 0) |
9385eb3d | 477 | break; |
5b2abdfb A |
478 | } |
479 | } | |
9385eb3d | 480 | if (i == asizeof(tptr->month)) |
974e3884 | 481 | return (NULL); |
5b2abdfb A |
482 | |
483 | tm->tm_mon = i; | |
484 | buf += len; | |
974e3884 A |
485 | flags |= FLAG_MONTH; |
486 | ||
5b2abdfb A |
487 | break; |
488 | ||
489 | case 'm': | |
974e3884 A |
490 | if (!isdigit_l((unsigned char)*buf, locale)) |
491 | return (NULL); | |
5b2abdfb A |
492 | |
493 | len = 2; | |
974e3884 A |
494 | for (i = 0; len && *buf != 0 && |
495 | isdigit_l((unsigned char)*buf, locale); buf++) { | |
5b2abdfb A |
496 | i *= 10; |
497 | i += *buf - '0'; | |
498 | len--; | |
499 | } | |
500 | if (i < 1 || i > 12) | |
974e3884 | 501 | return (NULL); |
5b2abdfb A |
502 | |
503 | tm->tm_mon = i - 1; | |
974e3884 | 504 | flags |= FLAG_MONTH; |
5b2abdfb | 505 | |
5b2abdfb A |
506 | break; |
507 | ||
508 | case 's': | |
509 | { | |
510 | char *cp; | |
3d9156a7 A |
511 | int sverrno; |
512 | long n; | |
5b2abdfb A |
513 | time_t t; |
514 | ||
3d9156a7 A |
515 | sverrno = errno; |
516 | errno = 0; | |
974e3884 | 517 | n = strtol_l(buf, &cp, 10, locale); |
3d9156a7 A |
518 | if (errno == ERANGE || (long)(t = n) != n) { |
519 | errno = sverrno; | |
974e3884 | 520 | return (NULL); |
3d9156a7 A |
521 | } |
522 | errno = sverrno; | |
5b2abdfb | 523 | buf = cp; |
974e3884 A |
524 | if (gmtime_r(&t, tm) == NULL) |
525 | return (NULL); | |
ad3c9f2a | 526 | *convp = CONVERT_GMT; |
974e3884 A |
527 | flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH | |
528 | FLAG_MDAY | FLAG_YEAR; | |
529 | flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY); | |
5b2abdfb A |
530 | } |
531 | break; | |
532 | ||
533 | case 'Y': | |
534 | case 'y': | |
974e3884 A |
535 | if (*buf == 0 || |
536 | isspace_l((unsigned char)*buf, locale)) | |
5b2abdfb A |
537 | break; |
538 | ||
974e3884 A |
539 | if (!isdigit_l((unsigned char)*buf, locale)) |
540 | return (NULL); | |
5b2abdfb | 541 | |
ad3c9f2a A |
542 | #if __DARWIN_UNIX03 |
543 | if (c == 'Y') { | |
544 | int savei = 0; | |
545 | const char *savebuf = buf; | |
546 | int64_t i64 = 0; | |
547 | int overflow = 0; | |
548 | ||
974e3884 | 549 | for (len = 0; *buf != 0 && isdigit_l((unsigned char)*buf, locale); buf++) { |
ad3c9f2a A |
550 | i64 *= 10; |
551 | i64 += *buf - '0'; | |
552 | if (++len <= 4) { | |
553 | savei = i64; | |
554 | savebuf = buf + 1; | |
555 | } | |
556 | if (i64 > INT_MAX) { | |
557 | overflow++; | |
558 | break; | |
559 | } | |
560 | } | |
561 | /* | |
562 | * Conformance requires %Y to be more then 4 | |
563 | * digits. However, there are several cases | |
564 | * where %Y is immediately followed by other | |
565 | * digits values. So we do the conformance | |
566 | * case first (as many digits as possible), | |
567 | * and if we fail, we backup and try just 4 | |
568 | * digits for %Y. | |
569 | */ | |
570 | if (len > 4 && !overflow) { | |
571 | struct tm savetm = *tm; | |
572 | int saveconv = *convp; | |
573 | const char *saveptr = ptr; | |
574 | char *ret; | |
575 | ||
576 | if (i64 < 1900) | |
577 | return 0; | |
578 | ||
579 | tm->tm_year = i64 - 1900; | |
580 | ||
974e3884 A |
581 | if (*buf != 0 && isspace_l((unsigned char)*buf, locale)) |
582 | while (*ptr != 0 && !isspace_l((unsigned char)*ptr, locale) && *ptr != '%') | |
ad3c9f2a | 583 | ptr++; |
974e3884 | 584 | ret = _strptime0(buf, ptr, tm, convp, locale, flags, week_number, week_kind); |
ad3c9f2a A |
585 | if (ret) return ret; |
586 | /* Failed, so try 4-digit year */ | |
587 | *tm = savetm; | |
588 | *convp = saveconv; | |
589 | ptr = saveptr; | |
590 | } | |
591 | buf = savebuf; | |
592 | i = savei; | |
593 | } else { | |
594 | len = 2; | |
595 | #else /* !__DARWIN_UNIX03 */ | |
974e3884 | 596 | len = (c == 'Y') ? 4 : 2; |
ad3c9f2a | 597 | #endif /* __DARWIN_UNIX03 */ |
974e3884 A |
598 | |
599 | for (i = 0; len && *buf != 0 && | |
600 | isdigit_l((unsigned char)*buf, locale); buf++) { | |
601 | i *= 10; | |
602 | i += *buf - '0'; | |
603 | len--; | |
604 | } | |
ad3c9f2a A |
605 | #if __DARWIN_UNIX03 |
606 | } | |
607 | #endif /* __DARWIN_UNIX03 */ | |
5b2abdfb | 608 | |
974e3884 A |
609 | if (i < 0) |
610 | return (NULL); | |
611 | ||
612 | if (c == 'Y'){ | |
613 | i -= TM_YEAR_BASE; | |
614 | } else if (c == 'y' && flags & FLAG_CENTURY) { | |
615 | i = tm->tm_year + (i % 100); | |
616 | flags &= ~FLAG_CENTURY; | |
617 | } else if (c == 'y'){ | |
618 | if (i < 69) i += 100; | |
619 | flags |= FLAG_YEAR_IN_CENTURY; | |
620 | } | |
5b2abdfb | 621 | |
974e3884 A |
622 | tm->tm_year = i; |
623 | flags |= FLAG_YEAR; | |
624 | if (c == 'Y'){ | |
625 | flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY); | |
6465356a A |
626 | } |
627 | ||
5b2abdfb A |
628 | break; |
629 | ||
630 | case 'Z': | |
631 | { | |
974e3884 A |
632 | const char *cp; |
633 | size_t tzlen, len; | |
5b2abdfb | 634 | |
974e3884 A |
635 | for (cp = buf; *cp && |
636 | isupper_l((unsigned char)*cp, locale); ++cp) { | |
637 | /*empty*/ | |
638 | } | |
639 | len = cp - buf; | |
640 | if (len == 3 && strncmp(buf, "GMT", 3) == 0) { | |
641 | *convp = CONVERT_GMT; | |
642 | buf += len; | |
643 | break; | |
644 | } | |
645 | ||
646 | tzset(); | |
647 | tzlen = strlen(tzname[0]); | |
648 | if (len == tzlen && strncmp(buf, tzname[0], tzlen) == 0) { | |
649 | tm->tm_isdst = 0; | |
650 | buf += len; | |
651 | break; | |
652 | } | |
653 | tzlen = strlen(tzname[1]); | |
654 | if (len == tzlen && strncmp(buf, tzname[1], tzlen) == 0) { | |
655 | tm->tm_isdst = 1; | |
656 | buf += len; | |
657 | break; | |
658 | } | |
659 | return (NULL); | |
5b2abdfb | 660 | } |
1f2f436a A |
661 | |
662 | case 'z': | |
663 | { | |
974e3884 A |
664 | char sign; |
665 | int hr, min; | |
666 | if ((buf[0] != '+' && buf[0] != '-') | |
667 | || !isdigit_l((unsigned char)buf[1], locale) | |
668 | || !isdigit_l((unsigned char)buf[2], locale) | |
669 | || !isdigit_l((unsigned char)buf[3], locale) | |
670 | || !isdigit_l((unsigned char)buf[4], locale)) | |
1f2f436a | 671 | return 0; |
974e3884 A |
672 | sscanf(buf, "%c%2d%2d", &sign, &hr, &min); |
673 | *convp = CONVERT_ZONE; | |
674 | tm->tm_gmtoff = 60 * (60 * hr + min); | |
675 | if (sign == '-') | |
676 | tm->tm_gmtoff = -tm->tm_gmtoff; | |
677 | buf += 5; | |
1f2f436a | 678 | } |
974e3884 | 679 | break; |
1f2f436a | 680 | |
974e3884 A |
681 | case 'n': |
682 | case 't': | |
683 | if (!isspace((unsigned char)*buf)) | |
684 | return 0; | |
685 | while (isspace_l((unsigned char)*buf, locale)) | |
686 | buf++; | |
687 | break; | |
688 | ||
689 | default: | |
690 | return (NULL); | |
691 | } | |
692 | } | |
693 | ||
694 | if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) { | |
695 | if ((flags & (FLAG_MONTH | FLAG_MDAY)) == | |
696 | (FLAG_MONTH | FLAG_MDAY)) { | |
697 | tm->tm_yday = start_of_month[isleap(tm->tm_year + | |
698 | TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1); | |
699 | flags |= FLAG_YDAY; | |
700 | } else if (flags & FLAG_WEEK){ | |
701 | if (!(flags & FLAG_WDAY)) { | |
702 | tm->tm_wday = week_kind == WEEK_U ? TM_SUNDAY : TM_MONDAY; | |
703 | flags |= FLAG_WDAY; | |
1f2f436a A |
704 | } |
705 | ||
974e3884 A |
706 | struct tm t = {0}; |
707 | t.tm_mday = week_kind == WEEK_V ? 4 : 1; | |
708 | t.tm_hour = 12; /* avoid any DST effects */ | |
709 | t.tm_year = tm->tm_year; | |
710 | if (timegm(&t) == (time_t)-1) return 0; | |
711 | ||
712 | int off = t.tm_wday; | |
713 | int wday = tm->tm_wday; | |
714 | ||
715 | if (week_kind != WEEK_U) { | |
716 | off = (off + 6) % 7; | |
717 | wday = (wday + 6) % 7; | |
1f2f436a | 718 | } |
974e3884 A |
719 | |
720 | if (week_kind == WEEK_V) { | |
721 | t.tm_mday = 7 * week_number + wday - off - 3; | |
722 | } else { | |
723 | if(off == 0) off = 7; | |
724 | t.tm_mday = 7 * week_number + wday - off + 1; | |
725 | } | |
726 | if (timegm(&t) == (time_t)-1) return 0; | |
727 | ||
728 | tm->tm_yday = t.tm_yday; | |
729 | ||
730 | flags |= FLAG_YDAY; | |
5b2abdfb A |
731 | } |
732 | } | |
974e3884 A |
733 | |
734 | if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) { | |
735 | if (!(flags & FLAG_MONTH)) { | |
736 | i = 0; | |
737 | while (tm->tm_yday >= | |
738 | start_of_month[isleap(tm->tm_year + | |
739 | TM_YEAR_BASE)][i]) | |
740 | i++; | |
741 | if (i > 12) { | |
742 | i = 1; | |
743 | tm->tm_yday -= | |
744 | start_of_month[isleap(tm->tm_year + | |
745 | TM_YEAR_BASE)][12]; | |
746 | tm->tm_year++; | |
747 | } | |
748 | tm->tm_mon = i - 1; | |
749 | flags |= FLAG_MONTH; | |
750 | } | |
751 | if (!(flags & FLAG_MDAY)) { | |
752 | tm->tm_mday = tm->tm_yday - | |
753 | start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)] | |
754 | [tm->tm_mon] + 1; | |
755 | flags |= FLAG_MDAY; | |
756 | } | |
757 | if (!(flags & FLAG_WDAY)) { | |
758 | i = 0; | |
759 | wday_offset = first_wday_of(tm->tm_year); | |
760 | while (i++ <= tm->tm_yday) { | |
761 | if (wday_offset++ >= 6) | |
762 | wday_offset = 0; | |
763 | } | |
764 | tm->tm_wday = wday_offset; | |
765 | flags |= FLAG_WDAY; | |
766 | } | |
767 | } | |
768 | ||
769 | return ((char *)buf); | |
5b2abdfb A |
770 | } |
771 | ||
772 | ||
773 | char * | |
9385eb3d | 774 | strptime(const char * __restrict buf, const char * __restrict fmt, |
974e3884 | 775 | struct tm * __restrict tm) |
5b2abdfb | 776 | { |
ad3c9f2a A |
777 | return strptime_l(buf, fmt, tm, __current_locale()); |
778 | } | |
779 | ||
780 | extern time_t timeoff(struct tm *, long); | |
5b2abdfb | 781 | |
ad3c9f2a A |
782 | char * |
783 | strptime_l(const char * __restrict buf, const char * __restrict fmt, | |
784 | struct tm * __restrict tm, locale_t loc) | |
785 | { | |
786 | char *ret; | |
787 | int conv; | |
788 | ||
789 | NORMALIZE_LOCALE(loc); | |
790 | conv = CONVERT_NONE; | |
791 | tm->tm_zone = NULL; | |
792 | ret = _strptime(buf, fmt, tm, &conv, loc); | |
793 | if (ret) { | |
794 | time_t t; | |
795 | ||
796 | switch(conv) { | |
797 | case CONVERT_GMT: | |
798 | t = timegm(tm); | |
799 | localtime_r(&t, tm); | |
800 | break; | |
801 | case CONVERT_ZONE: | |
802 | { | |
803 | long offset = tm->tm_gmtoff; | |
804 | tm->tm_gmtoff = 0; | |
805 | t = timeoff(tm, offset); | |
806 | localtime_r(&t, tm); | |
807 | break; | |
808 | } | |
809 | } | |
5b2abdfb A |
810 | } |
811 | ||
9385eb3d | 812 | return (ret); |
5b2abdfb | 813 | } |