suricata
util-strptime.c
Go to the documentation of this file.
1/* $NetBSD: strptime.c,v 1.36 2012/03/13 21:13:48 christos Exp $ */
2
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 * Heavily optimised by David Laight
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31/*
32#include <sys/cdefs.h>
33#if defined(LIBC_SCCS) && !defined(lint)
34__RCSID("$NetBSD: strptime.c,v 1.36 2012/03/13 21:13:48 christos Exp $");
35#endif
36
37#include "namespace.h"
38#include <sys/localedef.h>
39*/
40#include "suricata-common.h"
41#ifndef HAVE_STRPTIME
42#include <ctype.h>
43#include <locale.h>
44#include <string.h>
45#include <time.h>
46#include <stdint.h>
47/*
48#include <tzfile.h>
49#include "private.h"
50
51#ifdef __weak_alias
52__weak_alias(strptime,_strptime)
53#endif
54*/
55
56#define _ctloc(x) (_CurrentTimeLocale->x)
57
58/*
59 * We do not implement alternate representations. However, we always
60 * check whether a given modifier is allowed for a certain conversion.
61 */
62#define ALT_E 0x01
63#define ALT_O 0x02
64#define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; }
65
66static int TM_YEAR_BASE = 1900;
67static char gmt[] = { "GMT" };
68static char utc[] = { "UTC" };
69/* RFC-822/RFC-2822 */
70static const char * const nast[5] = {
71 "EST", "CST", "MST", "PST", "\0\0\0"
72};
73static const char * const nadt[5] = {
74 "EDT", "CDT", "MDT", "PDT", "\0\0\0"
75};
76static const char * const am_pm[2] = {
77 "am", "pm"
78};
79static const char * const day[7] = {
80 "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"
81};
82static const char * const abday[7] = {
83 "sun", "mon", "tue", "wed", "thu", "fri", "sat"
84};
85static const char * const mon[12] = {
86 "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"
87};
88static const char * const abmon[12] = {
89 "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"
90};
91
92static const u_char *conv_num(const unsigned char *, int *, unsigned int, unsigned int);
93static const u_char *find_string(const u_char *, int *, const char * const *,
94 const char * const *, int);
95
96char *
97strptime(const char *buf, const char *fmt, struct tm *tm)
98{
99 unsigned char c;
100 const unsigned char *bp, *ep;
101 int alt_format, i, split_year = 0, neg = 0, offs;
102 const char *new_fmt;
103
104 bp = (const u_char *)buf;
105
106 while (bp != NULL && (c = *fmt++) != '\0') {
107 /* Clear `alternate' modifier prior to new conversion. */
108 alt_format = 0;
109 i = 0;
110
111 /* Eat up white-space. */
112 if (isspace(c)) {
113 while (isspace(*bp))
114 bp++;
115 continue;
116 }
117
118 if (c != '%')
119 goto literal;
120
121
122again: switch (c = *fmt++) {
123 case '%': /* "%%" is converted to "%". */
124literal:
125 if (c != *bp++)
126 return NULL;
127 LEGAL_ALT(0);
128 continue;
129
130 /*
131 * "Alternative" modifiers. Just set the appropriate flag
132 * and start over again.
133 */
134 case 'E': /* "%E?" alternative conversion modifier. */
135 LEGAL_ALT(0);
136 alt_format |= ALT_E;
137 goto again;
138
139 case 'O': /* "%O?" alternative conversion modifier. */
140 LEGAL_ALT(0);
141 alt_format |= ALT_O;
142 goto again;
143
144 /*
145 * "Complex" conversion rules, implemented through recursion.
146 */
147 /* we do not need 'c'
148 case 'c': Date and time, using the locale's format.
149 new_fmt = _ctloc(d_t_fmt);
150 goto recurse;
151 */
152
153 case 'D': /* The date as "%m/%d/%y". */
154 new_fmt = "%m/%d/%y";
155 LEGAL_ALT(0);
156 goto recurse;
157
158 case 'F': /* The date as "%Y-%m-%d". */
159 new_fmt = "%Y-%m-%d";
160 LEGAL_ALT(0);
161 goto recurse;
162
163 case 'R': /* The time as "%H:%M". */
164 new_fmt = "%H:%M";
165 LEGAL_ALT(0);
166 goto recurse;
167
168 case 'r': /* The time in 12-hour clock representation. */
169 new_fmt = "%I:%M:S %p";//_ctloc(t_fmt_ampm);
170 LEGAL_ALT(0);
171 goto recurse;
172
173 case 'T': /* The time as "%H:%M:%S". */
174 new_fmt = "%H:%M:%S";
175 LEGAL_ALT(0);
176 goto recurse;
177
178 /* we don't use 'X'
179 case 'X': The time, using the locale's format.
180 new_fmt =_ctloc(t_fmt);
181 goto recurse;
182 */
183
184 /* we do not need 'x'
185 case 'x': The date, using the locale's format.
186 new_fmt =_ctloc(d_fmt);*/
187recurse:
188 bp = (const u_char *)strptime((const char *)bp,
189 new_fmt, tm);
191 continue;
192
193 /*
194 * "Elementary" conversion rules.
195 */
196 case 'A': /* The day of week, using the locale's form. */
197 case 'a':
198 bp = find_string(bp, &tm->tm_wday, day, abday, 7);
199 LEGAL_ALT(0);
200 continue;
201
202 case 'B': /* The month, using the locale's form. */
203 case 'b':
204 case 'h':
205 bp = find_string(bp, &tm->tm_mon, mon, abmon, 12);
206 LEGAL_ALT(0);
207 continue;
208
209 case 'C': /* The century number. */
210 i = 20;
211 bp = conv_num(bp, &i, 0, 99);
212
213 i = i * 100 - TM_YEAR_BASE;
214 if (split_year)
215 i += tm->tm_year % 100;
216 split_year = 1;
217 tm->tm_year = i;
219 continue;
220
221 case 'd': /* The day of month. */
222 case 'e':
223 bp = conv_num(bp, &tm->tm_mday, 1, 31);
225 continue;
226
227 case 'k': /* The hour (24-hour clock representation). */
228 LEGAL_ALT(0);
229 /* FALLTHROUGH */
230 case 'H':
231 bp = conv_num(bp, &tm->tm_hour, 0, 23);
233 continue;
234
235 case 'l': /* The hour (12-hour clock representation). */
236 LEGAL_ALT(0);
237 /* FALLTHROUGH */
238 case 'I':
239 bp = conv_num(bp, &tm->tm_hour, 1, 12);
240 if (tm->tm_hour == 12)
241 tm->tm_hour = 0;
243 continue;
244
245 case 'j': /* The day of year. */
246 i = 1;
247 bp = conv_num(bp, &i, 1, 366);
248 tm->tm_yday = i - 1;
249 LEGAL_ALT(0);
250 continue;
251
252 case 'M': /* The minute. */
253 bp = conv_num(bp, &tm->tm_min, 0, 59);
255 continue;
256
257 case 'm': /* The month. */
258 i = 1;
259 bp = conv_num(bp, &i, 1, 12);
260 tm->tm_mon = i - 1;
262 continue;
263
264 case 'p': /* The locale's equivalent of AM/PM. */
265 bp = find_string(bp, &i, am_pm, NULL, 2);
266 if (tm->tm_hour > 11)
267 return NULL;
268 tm->tm_hour += i * 12;
269 LEGAL_ALT(0);
270 continue;
271
272 case 'S': /* The seconds. */
273 bp = conv_num(bp, &tm->tm_sec, 0, 61);
275 continue;
276
277#ifndef TIME_MAX
278#define TIME_MAX INT64_MAX
279#endif
280 case 's': /* seconds since the epoch */
281 {
282 time_t sse = 0;
283 uint64_t rulim = TIME_MAX;
284
285 if (*bp < '0' || *bp > '9') {
286 bp = NULL;
287 continue;
288 }
289
290 do {
291 sse *= 10;
292 sse += *bp++ - '0';
293 rulim /= 10;
294 } while (((uint64_t)sse * 10 <= TIME_MAX) &&
295 rulim && *bp >= '0' && *bp <= '9');
296
297 if (sse < 0 || (uint64_t)sse > TIME_MAX) {
298 bp = NULL;
299 continue;
300 }
301
302 tm = localtime(&sse);
303 if (tm == NULL)
304 bp = NULL;
305 }
306 continue;
307
308 case 'U': /* The week of year, beginning on sunday. */
309 case 'W': /* The week of year, beginning on monday. */
310 /*
311 * XXX This is bogus, as we can not assume any valid
312 * information present in the tm structure at this
313 * point to calculate a real value, so just check the
314 * range for now.
315 */
316 bp = conv_num(bp, &i, 0, 53);
318 continue;
319
320 case 'w': /* The day of week, beginning on sunday. */
321 bp = conv_num(bp, &tm->tm_wday, 0, 6);
323 continue;
324
325 case 'u': /* The day of week, monday = 1. */
326 bp = conv_num(bp, &i, 1, 7);
327 tm->tm_wday = i % 7;
329 continue;
330
331 case 'g': /* The year corresponding to the ISO week
332 * number but without the century.
333 */
334 bp = conv_num(bp, &i, 0, 99);
335 continue;
336
337 case 'G': /* The year corresponding to the ISO week
338 * number with century.
339 */
340 do
341 bp++;
342 while (isdigit(*bp));
343 continue;
344
345 case 'V': /* The ISO 8601:1988 week number as decimal */
346 bp = conv_num(bp, &i, 0, 53);
347 continue;
348
349 case 'Y': /* The year. */
350 i = TM_YEAR_BASE; /* just for data sanity... */
351 bp = conv_num(bp, &i, 0, 9999);
352 tm->tm_year = i - TM_YEAR_BASE;
354 continue;
355
356 case 'y': /* The year within 100 years of the epoch. */
357 /* LEGAL_ALT(ALT_E | ALT_O); */
358 bp = conv_num(bp, &i, 0, 99);
359
360 if (split_year)
361 /* preserve century */
362 i += (tm->tm_year / 100) * 100;
363 else {
364 split_year = 1;
365 if (i <= 68)
366 i = i + 2000 - TM_YEAR_BASE;
367 else
368 i = i + 1900 - TM_YEAR_BASE;
369 }
370 tm->tm_year = i;
371 continue;
372
373 case 'Z':
374 tzset();
375 if (strncasecmp((const char *)bp, gmt, 3) == 0
376 || strncasecmp((const char *)bp, utc, 3) == 0) {
377 tm->tm_isdst = 0;
378#ifdef TM_GMTOFF
379 tm->TM_GMTOFF = 0;
380#endif
381#ifdef TM_ZONE
382 tm->TM_ZONE = gmt;
383#endif
384 bp += 3;
385 } else {
386 ep = find_string(bp, &i,
387#ifdef _WIN32
388 (const char *const *)_tzname,
389#else
390 (const char *const *)tzname,
391#endif
392 NULL, 2);
393 if (ep != NULL) {
394 tm->tm_isdst = i;
395#ifdef TM_GMTOFF
396 tm->TM_GMTOFF = -(timezone);
397#endif
398#ifdef TM_ZONE
399 tm->TM_ZONE = tzname[i];
400#endif
401 }
402 bp = ep;
403 }
404 continue;
405
406 case 'z':
407 /*
408 * We recognize all ISO 8601 formats:
409 * Z = Zulu time/UTC
410 * [+-]hhmm
411 * [+-]hh:mm
412 * [+-]hh
413 * We recognize all RFC-822/RFC-2822 formats:
414 * UT|GMT
415 * North American : UTC offsets
416 * E[DS]T = Eastern : -4 | -5
417 * C[DS]T = Central : -5 | -6
418 * M[DS]T = Mountain: -6 | -7
419 * P[DS]T = Pacific : -7 | -8
420 * Military
421 * [A-IL-M] = -1 ... -9 (J not used)
422 * [N-Y] = +1 ... +12
423 */
424 while (isspace(*bp))
425 bp++;
426
427 switch (*bp++) {
428 case 'G':
429 if (*bp++ != 'M')
430 return NULL;
431 /*FALLTHROUGH*/
432 case 'U':
433 if (*bp++ != 'T')
434 return NULL;
435 /*FALLTHROUGH*/
436 case 'Z':
437 tm->tm_isdst = 0;
438#ifdef TM_GMTOFF
439 tm->TM_GMTOFF = 0;
440#endif
441#ifdef TM_ZONE
442 tm->TM_ZONE = utc;
443#endif
444 continue;
445 case '+':
446 neg = 0;
447 break;
448 case '-':
449 neg = 1;
450 break;
451 default:
452 --bp;
453 ep = find_string(bp, &i, nast, NULL, 4);
454 if (ep != NULL) {
455#ifdef TM_GMTOFF
456 tm->TM_GMTOFF = -5 - i;
457#endif
458#ifdef TM_ZONE
459 tm->TM_ZONE = __UNCONST(nast[i]);
460#endif
461 bp = ep;
462 continue;
463 }
464 ep = find_string(bp, &i, nadt, NULL, 4);
465 if (ep != NULL) {
466 tm->tm_isdst = 1;
467#ifdef TM_GMTOFF
468 tm->TM_GMTOFF = -4 - i;
469#endif
470#ifdef TM_ZONE
471 tm->TM_ZONE = __UNCONST(nadt[i]);
472#endif
473 bp = ep;
474 continue;
475 }
476
477 if ((*bp >= 'A' && *bp <= 'I') ||
478 (*bp >= 'L' && *bp <= 'Y')) {
479#ifdef TM_GMTOFF
480 /* Argh! No 'J'! */
481 if (*bp >= 'A' && *bp <= 'I')
482 tm->TM_GMTOFF =
483 ('A' - 1) - (int)*bp;
484 else if (*bp >= 'L' && *bp <= 'M')
485 tm->TM_GMTOFF = 'A' - (int)*bp;
486 else if (*bp >= 'N' && *bp <= 'Y')
487 tm->TM_GMTOFF = (int)*bp - 'M';
488#endif
489#ifdef TM_ZONE
490 tm->TM_ZONE = NULL; /* XXX */
491#endif
492 bp++;
493 continue;
494 }
495 return NULL;
496 }
497 offs = 0;
498 for (i = 0; i < 4;) {
499 if (isdigit(*bp)) {
500 offs = offs * 10 + (*bp++ - '0');
501 i++;
502 continue;
503 }
504 if (i == 2 && *bp == ':') {
505 bp++;
506 continue;
507 }
508 break;
509 }
510 switch (i) {
511 case 2:
512 offs *= 100;
513 break;
514 case 4:
515 i = offs % 100;
516 if (i >= 60)
517 return NULL;
518 /* Convert minutes into decimal */
519 offs = (offs / 100) * 100 + (i * 50) / 30;
520 break;
521 default:
522 return NULL;
523 }
524 if (neg)
525 offs = -offs;
526 tm->tm_isdst = 0; /* XXX */
527#ifdef TM_GMTOFF
528 tm->TM_GMTOFF = offs;
529#endif
530#ifdef TM_ZONE
531 tm->TM_ZONE = NULL; /* XXX */
532#endif
533 continue;
534
535 /*
536 * Miscellaneous conversions.
537 */
538 case 'n': /* Any kind of white-space. */
539 case 't':
540 while (isspace(*bp))
541 bp++;
542 LEGAL_ALT(0);
543 continue;
544
545
546 default: /* Unknown/unsupported conversion. */
547 return NULL;
548 }
549 }
550
551 return (char *)(bp);
552}
553
554
555static const u_char *
556conv_num(const unsigned char *buf, int *dest, unsigned int llim, unsigned int ulim)
557{
558 unsigned int result = 0;
559 unsigned char ch;
560
561 /* The limit also determines the number of valid digits. */
562 unsigned int rulim = ulim;
563
564 ch = *buf;
565 if (ch < '0' || ch > '9')
566 return NULL;
567
568 do {
569 result *= 10;
570 result += ch - '0';
571 rulim /= 10;
572 ch = *++buf;
573 } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9');
574
575 if (result < llim || result > ulim)
576 return NULL;
577
578 *dest = result;
579 return buf;
580}
581
582static const u_char *
583find_string(const u_char *bp, int *tgt, const char * const *n1,
584 const char * const *n2, int c)
585{
586 int i;
587 size_t len;
588
589 /* check full name - then abbreviated ones */
590 for (; n1 != NULL; n1 = n2, n2 = NULL) {
591 for (i = 0; i < c; i++, n1++) {
592 len = strlen(*n1);
593 if (strncasecmp(*n1, (const char *)bp, len) == 0) {
594 *tgt = i;
595 return bp + len;
596 }
597 }
598 }
599
600 /* Nothing matched */
601 return NULL;
602}
603#endif /* HAVE_STRPTIME */
uint8_t len
#define TIME_MAX
#define ALT_E
#define LEGAL_ALT(x)
char * strptime(const char *buf, const char *fmt, struct tm *tm)
#define ALT_O