/src/fluent-bit/src/flb_strptime.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* $OpenBSD: strptime.c,v 1.30 2019/05/12 12:49:52 schwarze Exp $ */ |
2 | | /* $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $ */ |
3 | | /*- |
4 | | * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. |
5 | | * All rights reserved. |
6 | | * |
7 | | * This code was contributed to The NetBSD Foundation by Klaus Klein. |
8 | | * |
9 | | * Redistribution and use in source and binary forms, with or without |
10 | | * modification, are permitted provided that the following conditions |
11 | | * are met: |
12 | | * 1. Redistributions of source code must retain the above copyright |
13 | | * notice, this list of conditions and the following disclaimer. |
14 | | * 2. Redistributions in binary form must reproduce the above copyright |
15 | | * notice, this list of conditions and the following disclaimer in the |
16 | | * documentation and/or other materials provided with the distribution. |
17 | | * |
18 | | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
19 | | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
20 | | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
21 | | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
22 | | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
23 | | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
24 | | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
25 | | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
26 | | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
27 | | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
28 | | * POSSIBILITY OF SUCH DAMAGE. |
29 | | */ |
30 | | |
31 | | /* |
32 | | * This file provides a portable implementation of strptime(2), based |
33 | | * on the work of OpenSBD project. Since various platforms implement |
34 | | * strptime differently, this one should work as a fallback. |
35 | | */ |
36 | | |
37 | | #include <ctype.h> |
38 | | #include <locale.h> |
39 | | #include <stdint.h> |
40 | | #include <string.h> |
41 | | |
42 | | #include <fluent-bit/flb_compat.h> |
43 | | #include <fluent-bit/flb_langinfo.h> |
44 | | #include <fluent-bit/flb_time.h> |
45 | | |
46 | 135k | #define _ctloc(x) (nl_langinfo(x)) |
47 | | |
48 | | /* |
49 | | * We do not implement alternate representations. However, we always |
50 | | * check whether a given modifier is allowed for a certain conversion. |
51 | | */ |
52 | 0 | #define _ALT_E 0x01 |
53 | 0 | #define _ALT_O 0x02 |
54 | 44.1k | #define _LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); } |
55 | | |
56 | | /* |
57 | | * Copied from libc/time/private.h and libc/time/tzfile.h |
58 | | */ |
59 | 34.9k | #define TM_YEAR_BASE 1900 |
60 | 467 | #define DAYSPERNYEAR 365 |
61 | | #define DAYSPERLYEAR 366 |
62 | 1.75k | #define DAYSPERWEEK 7 |
63 | 0 | #define MONSPERYEAR 12 |
64 | 934 | #define EPOCH_YEAR 1970 |
65 | 467 | #define EPOCH_WDAY 4 /* Thursday */ |
66 | 68 | #define SECSPERHOUR 3600 |
67 | 31 | #define SECSPERMIN 60 |
68 | | |
69 | 3.46k | #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) |
70 | | |
71 | | /* |
72 | | * We keep track of some of the fields we set in order to compute missing ones. |
73 | | */ |
74 | 6.54k | #define FIELD_TM_MON (1 << 0) |
75 | 2.78k | #define FIELD_TM_MDAY (1 << 1) |
76 | 675 | #define FIELD_TM_WDAY (1 << 2) |
77 | 7.39k | #define FIELD_TM_YDAY (1 << 3) |
78 | 13.1k | #define FIELD_TM_YEAR (1 << 4) |
79 | | |
80 | | static char gmt[] = { "GMT" }; |
81 | | static char utc[] = { "UTC" }; |
82 | | /* RFC-822/RFC-2822 */ |
83 | | static const char * const nast[5] = { |
84 | | "EST", "CST", "MST", "PST", "\0\0\0" |
85 | | }; |
86 | | static const char * const nadt[5] = { |
87 | | "EDT", "CDT", "MDT", "PDT", "\0\0\0" |
88 | | }; |
89 | | |
90 | | static const int mon_lengths[2][MONSPERYEAR] = { |
91 | | { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, |
92 | | { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } |
93 | | }; |
94 | | |
95 | | static nl_item day[] = { |
96 | | DAY_1, DAY_2, DAY_3, DAY_4, DAY_5, DAY_6, DAY_7 |
97 | | }; |
98 | | |
99 | | static nl_item mon[] = { |
100 | | MON_1, MON_2, MON_3, MON_4, MON_5, MON_6, MON_7, MON_8, MON_9, |
101 | | MON_10, MON_11, MON_12 |
102 | | }; |
103 | | |
104 | | static nl_item abday[] = { |
105 | | ABDAY_1, ABDAY_2, ABDAY_3, ABDAY_4, ABDAY_5, ABDAY_6, ABDAY_7 |
106 | | }; |
107 | | |
108 | | static nl_item abmon[] = { |
109 | | ABMON_1, ABMON_2, ABMON_3, ABMON_4, ABMON_5, ABMON_6, ABMON_7, |
110 | | ABMON_8, ABMON_9, ABMON_10, ABMON_11, ABMON_12 |
111 | | }; |
112 | | |
113 | | static int _conv_num64(const unsigned char **, int64_t *, int64_t, int64_t); |
114 | | static int _conv_num(const unsigned char **, int *, int, int); |
115 | | static int leaps_thru_end_of(const int y); |
116 | | static char *_flb_strptime(const char *, const char *, struct flb_tm *, int); |
117 | | static const u_char *_find_string(const u_char *, int *, const char * const *, |
118 | | const char * const *, int); |
119 | | |
120 | | /* |
121 | | * FreeBSD does not support `timezone` in time.h. |
122 | | * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=24590 |
123 | | */ |
124 | | #ifdef __FreeBSD__ |
125 | | int flb_timezone(void) |
126 | | { |
127 | | struct tm tm; |
128 | | time_t t = 0; |
129 | | tzset(); |
130 | | localtime_r(&t, &tm); |
131 | | return -(tm.tm_gmtoff); |
132 | | } |
133 | | #define timezone (flb_timezone()) |
134 | | #endif |
135 | | |
136 | | char * |
137 | | flb_strptime(const char *buf, const char *fmt, struct flb_tm *tm) |
138 | 21.8k | { |
139 | 21.8k | return(_flb_strptime(buf, fmt, tm, 1)); |
140 | 21.8k | } |
141 | | |
142 | | static char * |
143 | | _flb_strptime(const char *buf, const char *fmt, struct flb_tm *tm, int initialize) |
144 | 21.8k | { |
145 | 21.8k | unsigned char c; |
146 | 21.8k | const unsigned char *bp, *ep; |
147 | 21.8k | size_t len = 0; |
148 | 21.8k | int alt_format, i, offs; |
149 | 21.8k | int neg = 0; |
150 | 21.8k | static int century, relyear, fields; |
151 | | |
152 | 21.8k | if (initialize) { |
153 | 21.8k | century = TM_YEAR_BASE; |
154 | 21.8k | relyear = -1; |
155 | 21.8k | fields = 0; |
156 | 21.8k | } |
157 | | |
158 | 21.8k | bp = (const unsigned char *)buf; |
159 | 83.1k | while ((c = *fmt) != '\0') { |
160 | | /* Clear `alternate' modifier prior to new conversion. */ |
161 | 79.5k | alt_format = 0; |
162 | | |
163 | | /* Eat up white-space. */ |
164 | 79.5k | if (isspace(c)) { |
165 | 8.72k | while (isspace(*bp)) |
166 | 4.66k | bp++; |
167 | | |
168 | 8.72k | fmt++; |
169 | 8.72k | continue; |
170 | 8.72k | } |
171 | | |
172 | | /* |
173 | | * Having increased bp we need to ensure we are not |
174 | | * moving beyond bounds. |
175 | | */ |
176 | 70.8k | if (*bp == '\0') |
177 | 5.20k | return (NULL); |
178 | | |
179 | 65.6k | if ((c = *fmt++) != '%') |
180 | 21.2k | goto literal; |
181 | | |
182 | | |
183 | 44.4k | again: switch (c = *fmt++) { |
184 | 0 | case '%': /* "%%" is converted to "%". */ |
185 | 21.2k | literal: |
186 | 21.2k | if (c != *bp++) |
187 | 2.33k | return (NULL); |
188 | | |
189 | 18.9k | break; |
190 | | |
191 | | /* |
192 | | * "Alternative" modifiers. Just set the appropriate flag |
193 | | * and start over again. |
194 | | */ |
195 | 18.9k | case 'E': /* "%E?" alternative conversion modifier. */ |
196 | 0 | _LEGAL_ALT(0); |
197 | 0 | alt_format |= _ALT_E; |
198 | 0 | goto again; |
199 | | |
200 | 0 | case 'O': /* "%O?" alternative conversion modifier. */ |
201 | 0 | _LEGAL_ALT(0); |
202 | 0 | alt_format |= _ALT_O; |
203 | 0 | goto again; |
204 | | |
205 | | /* |
206 | | * "Complex" conversion rules, implemented through recursion. |
207 | | */ |
208 | 0 | case 'c': /* Date and time, using the locale's format. */ |
209 | 0 | _LEGAL_ALT(_ALT_E); |
210 | 0 | if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, _ctloc(D_T_FMT), tm, 0))) |
211 | 0 | return (NULL); |
212 | 0 | break; |
213 | | |
214 | 0 | case 'D': /* The date as "%m/%d/%y". */ |
215 | 0 | _LEGAL_ALT(0); |
216 | 0 | if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, "%m/%d/%y", tm, 0))) |
217 | 0 | return (NULL); |
218 | 0 | break; |
219 | | |
220 | 0 | case 'F': /* The date as "%Y-%m-%d". */ |
221 | 0 | _LEGAL_ALT(0); |
222 | 0 | if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, "%Y-%m-%d", tm, 0))) |
223 | 0 | return (NULL); |
224 | 0 | continue; |
225 | | |
226 | 0 | case 'R': /* The time as "%H:%M". */ |
227 | 0 | _LEGAL_ALT(0); |
228 | 0 | if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, "%H:%M", tm, 0))) |
229 | 0 | return (NULL); |
230 | 0 | break; |
231 | | |
232 | 0 | case 'r': /* The time as "%I:%M:%S %p". */ |
233 | 0 | _LEGAL_ALT(0); |
234 | 0 | if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, "%I:%M:%S %p", tm, 0))) |
235 | 0 | return (NULL); |
236 | 0 | break; |
237 | | |
238 | 0 | case 'T': /* The time as "%H:%M:%S". */ |
239 | 0 | _LEGAL_ALT(0); |
240 | 0 | if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, "%H:%M:%S", tm, 0))) |
241 | 0 | return (NULL); |
242 | 0 | break; |
243 | | |
244 | 0 | case 'X': /* The time, using the locale's format. */ |
245 | 0 | _LEGAL_ALT(_ALT_E); |
246 | 0 | if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, _ctloc(T_FMT), tm, 0))) |
247 | 0 | return (NULL); |
248 | 0 | break; |
249 | | |
250 | 0 | case 'x': /* The date, using the locale's format. */ |
251 | 0 | _LEGAL_ALT(_ALT_E); |
252 | 0 | if (!(bp = (const unsigned char *)_flb_strptime((const char *)bp, _ctloc(D_FMT), tm, 0))) |
253 | 0 | return (NULL); |
254 | 0 | break; |
255 | | |
256 | | /* |
257 | | * "Elementary" conversion rules. |
258 | | */ |
259 | 0 | case 'A': /* The day of week, using the locale's form. */ |
260 | 1.58k | case 'a': |
261 | 1.58k | _LEGAL_ALT(0); |
262 | 12.1k | for (i = 0; i < 7; i++) { |
263 | | /* Full name. */ |
264 | 10.7k | len = strlen(_ctloc(day[i])); |
265 | 10.7k | if (strncasecmp(_ctloc(day[i]), (const char *)bp, len) == 0) |
266 | 12 | break; |
267 | | |
268 | | /* Abbreviated name. */ |
269 | 10.7k | len = strlen(_ctloc(abday[i])); |
270 | 10.7k | if (strncasecmp(_ctloc(abday[i]), (const char *)bp, len) == 0) |
271 | 196 | break; |
272 | 10.7k | } |
273 | | |
274 | | /* Nothing matched. */ |
275 | 1.58k | if (i == 7) |
276 | 1.37k | return (NULL); |
277 | | |
278 | 208 | tm->tm.tm_wday = i; |
279 | 208 | bp += len; |
280 | 208 | fields |= FIELD_TM_WDAY; |
281 | 208 | break; |
282 | | |
283 | 0 | case 'B': /* The month, using the locale's form. */ |
284 | 2.06k | case 'b': |
285 | 2.06k | case 'h': |
286 | 2.06k | _LEGAL_ALT(0); |
287 | 24.8k | for (i = 0; i < 12; i++) { |
288 | | /* Full name. */ |
289 | 23.1k | len = strlen(_ctloc(mon[i])); |
290 | 23.1k | if (strncasecmp(_ctloc(mon[i]), (const char *)bp, len) == 0) |
291 | 31 | break; |
292 | | |
293 | | /* Abbreviated name. */ |
294 | 23.0k | len = strlen(_ctloc(abmon[i])); |
295 | 23.0k | if (strncasecmp(_ctloc(abmon[i]), (const char *)bp, len) == 0) |
296 | 304 | break; |
297 | 23.0k | } |
298 | | |
299 | | /* Nothing matched. */ |
300 | 2.06k | if (i == 12) |
301 | 1.72k | return (NULL); |
302 | | |
303 | 335 | tm->tm.tm_mon = i; |
304 | 335 | bp += len; |
305 | 335 | fields |= FIELD_TM_MON; |
306 | 335 | break; |
307 | | |
308 | 0 | case 'C': /* The century number. */ |
309 | 0 | _LEGAL_ALT(_ALT_E); |
310 | 0 | if (!(_conv_num(&bp, &i, 0, 99))) |
311 | 0 | return (NULL); |
312 | | |
313 | 0 | century = i * 100; |
314 | 0 | break; |
315 | | |
316 | 0 | case 'e': /* The day of month. */ |
317 | 0 | if (isspace(*bp)) |
318 | 0 | bp++; |
319 | | /* FALLTHROUGH */ |
320 | 2.13k | case 'd': |
321 | 2.13k | _LEGAL_ALT(_ALT_O); |
322 | 2.13k | if (!(_conv_num(&bp, &tm->tm.tm_mday, 1, 31))) |
323 | 284 | return (NULL); |
324 | 1.85k | fields |= FIELD_TM_MDAY; |
325 | 1.85k | break; |
326 | | |
327 | 0 | case 'k': /* The hour (24-hour clock representation). */ |
328 | 0 | _LEGAL_ALT(0); |
329 | | /* FALLTHROUGH */ |
330 | 4.74k | case 'H': |
331 | 4.74k | _LEGAL_ALT(_ALT_O); |
332 | 4.74k | if (!(_conv_num(&bp, &tm->tm.tm_hour, 0, 23))) |
333 | 210 | return (NULL); |
334 | 4.53k | break; |
335 | | |
336 | 4.53k | case 'l': /* The hour (12-hour clock representation). */ |
337 | 0 | _LEGAL_ALT(0); |
338 | | /* FALLTHROUGH */ |
339 | 0 | case 'I': |
340 | 0 | _LEGAL_ALT(_ALT_O); |
341 | 0 | if (!(_conv_num(&bp, &tm->tm.tm_hour, 1, 12))) |
342 | 0 | return (NULL); |
343 | 0 | break; |
344 | | |
345 | 0 | case 'j': /* The day of year. */ |
346 | 0 | _LEGAL_ALT(0); |
347 | 0 | if (!(_conv_num(&bp, &tm->tm.tm_yday, 1, 366))) |
348 | 0 | return (NULL); |
349 | 0 | tm->tm.tm_yday--; |
350 | 0 | fields |= FIELD_TM_YDAY; |
351 | 0 | break; |
352 | | |
353 | 7.71k | case 'M': /* The minute. */ |
354 | 7.71k | _LEGAL_ALT(_ALT_O); |
355 | 7.71k | if (!(_conv_num(&bp, &tm->tm.tm_min, 0, 59))) |
356 | 126 | return (NULL); |
357 | 7.59k | break; |
358 | | |
359 | 7.59k | case 'm': /* The month. */ |
360 | 4.51k | _LEGAL_ALT(_ALT_O); |
361 | 4.51k | if (!(_conv_num(&bp, &tm->tm.tm_mon, 1, 12))) |
362 | 2.23k | return (NULL); |
363 | 2.27k | tm->tm.tm_mon--; |
364 | 2.27k | fields |= FIELD_TM_MON; |
365 | 2.27k | break; |
366 | | |
367 | 0 | case 'p': /* The locale's equivalent of AM/PM. */ |
368 | 0 | _LEGAL_ALT(0); |
369 | | /* AM? */ |
370 | 0 | len = strlen(_ctloc(AM_STR)); |
371 | 0 | if (strncasecmp(_ctloc(AM_STR), (const char *)bp, len) == 0) { |
372 | 0 | if (tm->tm.tm_hour > 12) /* i.e., 13:00 AM ?! */ |
373 | 0 | return (NULL); |
374 | 0 | else if (tm->tm.tm_hour == 12) |
375 | 0 | tm->tm.tm_hour = 0; |
376 | | |
377 | 0 | bp += len; |
378 | 0 | break; |
379 | 0 | } |
380 | | /* PM? */ |
381 | 0 | len = strlen(_ctloc(PM_STR)); |
382 | 0 | if (strncasecmp(_ctloc(PM_STR), (const char *)bp, len) == 0) { |
383 | 0 | if (tm->tm.tm_hour > 12) /* i.e., 13:00 PM ?! */ |
384 | 0 | return (NULL); |
385 | 0 | else if (tm->tm.tm_hour < 12) |
386 | 0 | tm->tm.tm_hour += 12; |
387 | | |
388 | 0 | bp += len; |
389 | 0 | break; |
390 | 0 | } |
391 | | |
392 | | /* Nothing matched. */ |
393 | 0 | return (NULL); |
394 | | |
395 | 7.38k | case 'S': /* The seconds. */ |
396 | 7.38k | _LEGAL_ALT(_ALT_O); |
397 | 7.38k | if (!(_conv_num(&bp, &tm->tm.tm_sec, 0, 60))) |
398 | 197 | return (NULL); |
399 | 7.19k | break; |
400 | 7.19k | case 's': /* Seconds since epoch */ |
401 | 0 | { |
402 | 0 | int64_t i64; |
403 | 0 | if (!(_conv_num64(&bp, &i64, 0, INT64_MAX))) |
404 | 0 | return (NULL); |
405 | 0 | if (!gmtime_r(&i64, &tm->tm)) |
406 | 0 | return (NULL); |
407 | 0 | fields = 0xffff; /* everything */ |
408 | 0 | } |
409 | 0 | break; |
410 | 0 | case 'U': /* The week of year, beginning on sunday. */ |
411 | 0 | case 'W': /* The week of year, beginning on monday. */ |
412 | 0 | _LEGAL_ALT(_ALT_O); |
413 | | /* |
414 | | * XXX This is bogus, as we can not assume any valid |
415 | | * information present in the tm structure at this |
416 | | * point to calculate a real value, so just check the |
417 | | * range for now. |
418 | | */ |
419 | 0 | if (!(_conv_num(&bp, &i, 0, 53))) |
420 | 0 | return (NULL); |
421 | 0 | break; |
422 | | |
423 | 0 | case 'w': /* The day of week, beginning on sunday. */ |
424 | 0 | _LEGAL_ALT(_ALT_O); |
425 | 0 | if (!(_conv_num(&bp, &tm->tm.tm_wday, 0, 6))) |
426 | 0 | return (NULL); |
427 | 0 | fields |= FIELD_TM_WDAY; |
428 | 0 | break; |
429 | | |
430 | 0 | case 'u': /* The day of week, monday = 1. */ |
431 | 0 | _LEGAL_ALT(_ALT_O); |
432 | 0 | if (!(_conv_num(&bp, &i, 1, 7))) |
433 | 0 | return (NULL); |
434 | 0 | tm->tm.tm_wday = i % 7; |
435 | 0 | fields |= FIELD_TM_WDAY; |
436 | 0 | continue; |
437 | | |
438 | 0 | case 'g': /* The year corresponding to the ISO week |
439 | | * number but without the century. |
440 | | */ |
441 | 0 | if (!(_conv_num(&bp, &i, 0, 99))) |
442 | 0 | return (NULL); |
443 | 0 | continue; |
444 | | |
445 | 0 | case 'G': /* The year corresponding to the ISO week |
446 | | * number with century. |
447 | | */ |
448 | 0 | do |
449 | 0 | bp++; |
450 | 0 | while (isdigit(*bp)); |
451 | 0 | continue; |
452 | | |
453 | 0 | case 'V': /* The ISO 8601:1988 week number as decimal */ |
454 | 0 | if (!(_conv_num(&bp, &i, 0, 53))) |
455 | 0 | return (NULL); |
456 | 0 | continue; |
457 | | |
458 | 14.0k | case 'Y': /* The year. */ |
459 | 14.0k | _LEGAL_ALT(_ALT_E); |
460 | 14.0k | if (!(_conv_num(&bp, &i, 0, 9999))) |
461 | 4.47k | return (NULL); |
462 | | |
463 | 9.58k | relyear = -1; |
464 | 9.58k | tm->tm.tm_year = i - TM_YEAR_BASE; |
465 | 9.58k | fields |= FIELD_TM_YEAR; |
466 | 9.58k | break; |
467 | | |
468 | 0 | case 'y': /* The year within the century (2 digits). */ |
469 | 0 | _LEGAL_ALT(_ALT_E | _ALT_O); |
470 | 0 | if (!(_conv_num(&bp, &relyear, 0, 99))) |
471 | 0 | return (NULL); |
472 | 0 | break; |
473 | | |
474 | 0 | case 'Z': |
475 | 0 | tzset(); |
476 | 0 | if (strncmp((const char *)bp, gmt, 3) == 0) { |
477 | 0 | tm->tm.tm_isdst = 0; |
478 | 0 | flb_tm_gmtoff(tm) = 0; |
479 | | #ifdef FLB_HAVE_ZONE |
480 | | tm->tm.tm_zone = gmt; |
481 | | #endif |
482 | 0 | bp += 3; |
483 | 0 | } else if (strncmp((const char *)bp, utc, 3) == 0) { |
484 | 0 | tm->tm.tm_isdst = 0; |
485 | 0 | flb_tm_gmtoff(tm) = 0; |
486 | | #ifdef FLB_HAVE_ZONE |
487 | | tm->tm.tm_zone = utc; |
488 | | #endif |
489 | 0 | bp += 3; |
490 | 0 | } else { |
491 | 0 | ep = _find_string(bp, &i, |
492 | 0 | (const char * const *)tzname, |
493 | 0 | NULL, 2); |
494 | 0 | if (ep == NULL) |
495 | 0 | return (NULL); |
496 | | |
497 | 0 | tm->tm.tm_isdst = i; |
498 | 0 | flb_tm_gmtoff(tm) = -(timezone); |
499 | | #ifdef FLB_HAVE_ZONE |
500 | | tm->tm.tm_zone = tzname[i]; |
501 | | #endif |
502 | 0 | bp = ep; |
503 | 0 | } |
504 | 0 | continue; |
505 | | |
506 | 235 | case 'z': |
507 | | /* |
508 | | * We recognize all ISO 8601 formats: |
509 | | * Z = Zulu time/UTC |
510 | | * [+-]hhmm |
511 | | * [+-]hh:mm |
512 | | * [+-]hh |
513 | | * We recognize all RFC-822/RFC-2822 formats: |
514 | | * UT|GMT |
515 | | * North American : UTC offsets |
516 | | * E[DS]T = Eastern : -4 | -5 |
517 | | * C[DS]T = Central : -5 | -6 |
518 | | * M[DS]T = Mountain: -6 | -7 |
519 | | * P[DS]T = Pacific : -7 | -8 |
520 | | */ |
521 | 235 | while (isspace(*bp)) |
522 | 0 | bp++; |
523 | | |
524 | 235 | switch (*bp++) { |
525 | 16 | case 'G': |
526 | 16 | if (*bp++ != 'M') |
527 | 11 | return NULL; |
528 | | /*FALLTHROUGH*/ |
529 | 25 | case 'U': |
530 | 25 | if (*bp++ != 'T') |
531 | 19 | return NULL; |
532 | | /*FALLTHROUGH*/ |
533 | 33 | case 'Z': |
534 | 33 | tm->tm.tm_isdst = 0; |
535 | 33 | flb_tm_gmtoff(tm) = 0; |
536 | | #ifdef FLB_HAVE_ZONE |
537 | | tm->tm.tm_zone = utc; |
538 | | #endif |
539 | 33 | continue; |
540 | 8 | case '+': |
541 | 8 | neg = 0; |
542 | 8 | break; |
543 | 67 | case '-': |
544 | 67 | neg = 1; |
545 | 67 | break; |
546 | 97 | default: |
547 | 97 | --bp; |
548 | 97 | ep = _find_string(bp, &i, nast, NULL, 4); |
549 | 97 | if (ep != NULL) { |
550 | 5 | flb_tm_gmtoff(tm) = (-5 - i) * SECSPERHOUR; |
551 | | #ifdef FLB_HAVE_ZONE |
552 | | tm->tm.tm_zone = (char *)nast[i]; |
553 | | #endif |
554 | 5 | bp = ep; |
555 | 5 | continue; |
556 | 5 | } |
557 | 92 | ep = _find_string(bp, &i, nadt, NULL, 4); |
558 | 92 | if (ep != NULL) { |
559 | 11 | tm->tm.tm_isdst = 1; |
560 | 11 | flb_tm_gmtoff(tm) = (-4 - i) * SECSPERHOUR; |
561 | | #ifdef FLB_HAVE_ZONE |
562 | | tm->tm.tm_zone = (char *)nadt[i]; |
563 | | #endif |
564 | 11 | bp = ep; |
565 | 11 | continue; |
566 | 11 | } |
567 | 81 | return NULL; |
568 | 235 | } |
569 | 75 | if (!isdigit(bp[0]) || !isdigit(bp[1])) |
570 | 23 | return NULL; |
571 | 52 | offs = ((bp[0]-'0') * 10 + (bp[1]-'0')) * SECSPERHOUR; |
572 | 52 | bp += 2; |
573 | 52 | if (*bp == ':') |
574 | 12 | bp++; |
575 | 52 | if (isdigit(*bp)) { |
576 | 21 | offs += (*bp++ - '0') * 10 * SECSPERMIN; |
577 | 21 | if (!isdigit(*bp)) |
578 | 11 | return NULL; |
579 | 10 | offs += (*bp++ - '0') * SECSPERMIN; |
580 | 10 | } |
581 | 41 | if (neg) |
582 | 38 | offs = -offs; |
583 | 41 | tm->tm.tm_isdst = 0; /* XXX */ |
584 | 41 | flb_tm_gmtoff(tm) = offs; |
585 | | #ifdef FLB_HAVE_ZONE |
586 | | tm->tm.tm_zone = NULL; /* XXX */ |
587 | | #endif |
588 | 41 | continue; |
589 | | |
590 | | /* |
591 | | * Miscellaneous conversions. |
592 | | */ |
593 | 0 | case 'n': /* Any kind of white-space. */ |
594 | 0 | case 't': |
595 | 0 | _LEGAL_ALT(0); |
596 | 0 | while (isspace(*bp)) |
597 | 0 | bp++; |
598 | 0 | break; |
599 | | |
600 | | |
601 | 0 | default: /* Unknown/unsupported conversion. */ |
602 | 0 | return (NULL); |
603 | 44.4k | } |
604 | | |
605 | | |
606 | 44.4k | } |
607 | | |
608 | | /* |
609 | | * We need to evaluate the two digit year spec (%y) |
610 | | * last as we can get a century spec (%C) at any time. |
611 | | */ |
612 | 3.56k | if (relyear != -1) { |
613 | 0 | if (century == TM_YEAR_BASE) { |
614 | 0 | if (relyear <= 68) |
615 | 0 | tm->tm.tm_year = relyear + 2000 - TM_YEAR_BASE; |
616 | 0 | else |
617 | 0 | tm->tm.tm_year = relyear + 1900 - TM_YEAR_BASE; |
618 | 0 | } else { |
619 | 0 | tm->tm.tm_year = relyear + century - TM_YEAR_BASE; |
620 | 0 | } |
621 | 0 | fields |= FIELD_TM_YEAR; |
622 | 0 | } |
623 | | |
624 | | /* Compute some missing values when possible. */ |
625 | 3.56k | if (fields & FIELD_TM_YEAR) { |
626 | 3.46k | const int year = (unsigned int)tm->tm.tm_year + (unsigned int)TM_YEAR_BASE; |
627 | 3.46k | const int *mon_lens = mon_lengths[isleap(year)]; |
628 | 3.46k | if (!(fields & FIELD_TM_YDAY) && |
629 | 3.46k | (fields & FIELD_TM_MON) && (fields & FIELD_TM_MDAY)) { |
630 | 467 | tm->tm.tm_yday = tm->tm.tm_mday - 1; |
631 | 1.81k | for (i = 0; i < tm->tm.tm_mon; i++) |
632 | 1.35k | tm->tm.tm_yday += mon_lens[i]; |
633 | 467 | fields |= FIELD_TM_YDAY; |
634 | 467 | } |
635 | 3.46k | if (fields & FIELD_TM_YDAY) { |
636 | 467 | int days = tm->tm.tm_yday; |
637 | 467 | if (!(fields & FIELD_TM_WDAY)) { |
638 | 467 | tm->tm.tm_wday = EPOCH_WDAY + |
639 | 467 | ((year - EPOCH_YEAR) % DAYSPERWEEK) * |
640 | 467 | (DAYSPERNYEAR % DAYSPERWEEK) + |
641 | 467 | leaps_thru_end_of(year - 1) - |
642 | 467 | leaps_thru_end_of(EPOCH_YEAR - 1) + |
643 | 467 | tm->tm.tm_yday; |
644 | 467 | tm->tm.tm_wday %= DAYSPERWEEK; |
645 | 467 | if (tm->tm.tm_wday < 0) |
646 | 356 | tm->tm.tm_wday += DAYSPERWEEK; |
647 | 467 | } |
648 | 467 | if (!(fields & FIELD_TM_MON)) { |
649 | 0 | tm->tm.tm_mon = 0; |
650 | 0 | while (tm->tm.tm_mon < MONSPERYEAR && days >= mon_lens[tm->tm.tm_mon]) |
651 | 0 | days -= mon_lens[tm->tm.tm_mon++]; |
652 | 0 | } |
653 | 467 | if (!(fields & FIELD_TM_MDAY)) |
654 | 0 | tm->tm.tm_mday = days + 1; |
655 | 467 | } |
656 | 3.46k | } |
657 | | |
658 | 3.56k | return ((char *)bp); |
659 | 21.8k | } |
660 | | |
661 | | |
662 | | static int |
663 | | _conv_num(const unsigned char **buf, int *dest, int llim, int ulim) |
664 | 40.5k | { |
665 | 40.5k | int result = 0; |
666 | 40.5k | int rulim = ulim; |
667 | | |
668 | 40.5k | if (**buf < '0' || **buf > '9') |
669 | 6.96k | return (0); |
670 | | |
671 | | /* we use rulim to break out of the loop when we run out of digits */ |
672 | 47.9k | do { |
673 | 47.9k | result *= 10; |
674 | 47.9k | result += *(*buf)++ - '0'; |
675 | 47.9k | rulim /= 10; |
676 | 47.9k | } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9'); |
677 | | |
678 | 33.5k | if (result < llim || result > ulim) |
679 | 558 | return (0); |
680 | | |
681 | 33.0k | *dest = result; |
682 | 33.0k | return (1); |
683 | 33.5k | } |
684 | | |
685 | | static int |
686 | | _conv_num64(const unsigned char **buf, int64_t *dest, int64_t llim, int64_t ulim) |
687 | 0 | { |
688 | 0 | int64_t result = 0; |
689 | 0 | int64_t rulim = ulim; |
690 | |
|
691 | 0 | if (**buf < '0' || **buf > '9') |
692 | 0 | return (0); |
693 | | |
694 | | /* we use rulim to break out of the loop when we run out of digits */ |
695 | 0 | do { |
696 | | /* Avoid overflow: result > ((2**64)/2.0) / 10.0 */ |
697 | 0 | if (result > 922337203685477580) { |
698 | 0 | return (0); |
699 | 0 | } |
700 | 0 | result *= 10; |
701 | | |
702 | | /* Avoid overflow: result > ((2**64)/2.0) - 48 */ |
703 | 0 | if (result > 9223372036854775760) { |
704 | 0 | return (0); |
705 | 0 | } |
706 | 0 | result += *(*buf)++ - '0'; |
707 | 0 | rulim /= 10; |
708 | | /* watch out for overflows. If value gets above |
709 | | * ((2**64)/2.0)/10.0 then we will overflow. So instead |
710 | | * we return 0 */ |
711 | 0 | if (result >= 922337203685477580) { |
712 | 0 | return (0); |
713 | 0 | } |
714 | 0 | } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9'); |
715 | | |
716 | 0 | if (result < llim || result > ulim) |
717 | 0 | return (0); |
718 | | |
719 | 0 | *dest = result; |
720 | 0 | return (1); |
721 | 0 | } |
722 | | |
723 | | static const u_char * |
724 | | _find_string(const u_char *bp, int *tgt, const char * const *n1, |
725 | | const char * const *n2, int c) |
726 | 189 | { |
727 | 189 | int i; |
728 | 189 | unsigned int len; |
729 | | |
730 | | /* check full name - then abbreviated ones */ |
731 | 362 | for (; n1 != NULL; n1 = n2, n2 = NULL) { |
732 | 903 | for (i = 0; i < c; i++, n1++) { |
733 | 730 | len = strlen(*n1); |
734 | 730 | if (strncasecmp(*n1, (const char *)bp, len) == 0) { |
735 | 16 | *tgt = i; |
736 | 16 | return bp + len; |
737 | 16 | } |
738 | 730 | } |
739 | 189 | } |
740 | | |
741 | | /* Nothing matched */ |
742 | 173 | return NULL; |
743 | 189 | } |
744 | | |
745 | | static int |
746 | | leaps_thru_end_of(const int y) |
747 | 1.12k | { |
748 | 1.12k | return (y >= 0) ? (y / 4 - y / 100 + y / 400) : |
749 | 1.12k | -(leaps_thru_end_of(-(y + 1)) + 1); |
750 | 1.12k | } |