epan: use SPDX indentifiers.
[metze/wireshark/wip.git] / epan / ftypes / ftype-time.c
1 /*
2  * Wireshark - Network traffic analyzer
3  * By Gerald Combs <gerald@wireshark.org>
4  * Copyright 2001 Gerald Combs
5  *
6  * SPDX-License-Identifier: GPL-2.0-or-later
7  */
8
9 #include "config.h"
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 /*
16  * Just make sure we include the prototype for strptime as well
17  * (needed for glibc 2.2) but make sure we do this only if not
18  * yet defined.
19  */
20 #ifndef __USE_XOPEN
21 #  define __USE_XOPEN
22 #endif
23
24 #include <time.h>
25
26 #include <ftypes-int.h>
27 #include <epan/to_str.h>
28
29 #ifndef HAVE_STRPTIME
30 #include "wsutil/strptime.h"
31 #endif
32
33 static gboolean
34 cmp_eq(const fvalue_t *a, const fvalue_t *b)
35 {
36         return ((a->value.time.secs) ==(b->value.time.secs))
37              &&((a->value.time.nsecs)==(b->value.time.nsecs));
38 }
39 static gboolean
40 cmp_ne(const fvalue_t *a, const fvalue_t *b)
41 {
42         return (a->value.time.secs !=b->value.time.secs)
43              ||(a->value.time.nsecs!=b->value.time.nsecs);
44 }
45 static gboolean
46 cmp_gt(const fvalue_t *a, const fvalue_t *b)
47 {
48         if (a->value.time.secs > b->value.time.secs) {
49                 return TRUE;
50         }
51         if (a->value.time.secs < b->value.time.secs) {
52                 return FALSE;
53         }
54
55         return a->value.time.nsecs > b->value.time.nsecs;
56 }
57 static gboolean
58 cmp_ge(const fvalue_t *a, const fvalue_t *b)
59 {
60         if (a->value.time.secs > b->value.time.secs) {
61                 return TRUE;
62         }
63         if (a->value.time.secs < b->value.time.secs) {
64                 return FALSE;
65         }
66
67         return a->value.time.nsecs >= b->value.time.nsecs;
68 }
69 static gboolean
70 cmp_lt(const fvalue_t *a, const fvalue_t *b)
71 {
72         if (a->value.time.secs < b->value.time.secs) {
73                 return TRUE;
74         }
75         if (a->value.time.secs > b->value.time.secs) {
76                 return FALSE;
77         }
78
79         return a->value.time.nsecs < b->value.time.nsecs;
80 }
81 static gboolean
82 cmp_le(const fvalue_t *a, const fvalue_t *b)
83 {
84         if (a->value.time.secs < b->value.time.secs) {
85                 return TRUE;
86         }
87         if (a->value.time.secs > b->value.time.secs) {
88                 return FALSE;
89         }
90
91         return a->value.time.nsecs <= b->value.time.nsecs;
92 }
93
94
95 /*
96  * Get a nanoseconds value, starting at "p".
97  *
98  * Returns true on success, false on failure.
99  */
100 static gboolean
101 get_nsecs(const char *startp, int *nsecs)
102 {
103         int ndigits;
104         int scale;
105         const char *p;
106         int val;
107         int digit;
108         int i;
109
110         /*
111          * How many characters are in the string?
112          */
113         ndigits = (int)strlen(startp);
114
115         /*
116          * If there are N characters in the string, the last of the
117          * characters would be the digit corresponding to 10^(9-N)
118          * nanoseconds.
119          */
120         scale = 9 - ndigits;
121
122         /*
123          * Start at the last character, and work backwards.
124          */
125         p = startp + ndigits;
126         val = 0;
127         while (p != startp) {
128                 p--;
129
130                 if (!g_ascii_isdigit(*p)) {
131                         /*
132                          * Not a digit - error.
133                          */
134                         return FALSE;
135                 }
136                 digit = *p - '0';
137                 if (digit != 0) {
138                         /*
139                          * Non-zero digit corresponding to that number
140                          * of (10^scale) units.
141                          *
142                          * If scale is less than zero, this digit corresponds
143                          * to a value less than a nanosecond, so this number
144                          * isn't valid.
145                          */
146                         if (scale < 0)
147                                 return FALSE;
148                         for (i = 0; i < scale; i++)
149                                 digit *= 10;
150                         val += digit;
151                 }
152                 scale++;
153         }
154         *nsecs = val;
155         return TRUE;
156 }
157
158 static gboolean
159 relative_val_from_unparsed(fvalue_t *fv, const char *s, gboolean allow_partial_value _U_, gchar **err_msg)
160 {
161         const char    *curptr;
162         char *endptr;
163         gboolean negative = FALSE;
164
165         curptr = s;
166
167         if(*curptr == '-') {
168             negative = TRUE;
169             curptr++;
170         }
171
172         /*
173          * If it doesn't begin with ".", it should contain a seconds
174          * value.
175          */
176         if (*curptr != '.') {
177                 /*
178                  * Get the seconds value.
179                  */
180                 fv->value.time.secs = strtoul(curptr, &endptr, 10);
181                 if (endptr == curptr || (*endptr != '\0' && *endptr != '.'))
182                         goto fail;
183                 curptr = endptr;
184                 if (*curptr == '.')
185                         curptr++;       /* skip the decimal point */
186         } else {
187                 /*
188                  * No seconds value - it's 0.
189                  */
190                 fv->value.time.secs = 0;
191                 curptr++;               /* skip the decimal point */
192         }
193
194         /*
195          * If there's more stuff left in the string, it should be the
196          * nanoseconds value.
197          */
198         if (*curptr != '\0') {
199                 /*
200                  * Get the nanoseconds value.
201                  */
202                 if (!get_nsecs(curptr, &fv->value.time.nsecs))
203                         goto fail;
204         } else {
205                 /*
206                  * No nanoseconds value - it's 0.
207                  */
208                 fv->value.time.nsecs = 0;
209         }
210
211         if(negative) {
212             fv->value.time.secs = -fv->value.time.secs;
213             fv->value.time.nsecs = -fv->value.time.nsecs;
214         }
215         return TRUE;
216
217 fail:
218         if (err_msg != NULL)
219                 *err_msg = g_strdup_printf("\"%s\" is not a valid time.", s);
220         return FALSE;
221 }
222
223
224 static gboolean
225 absolute_val_from_string(fvalue_t *fv, const char *s, gchar **err_msg)
226 {
227         struct tm tm;
228         char    *curptr;
229
230         memset(&tm, 0, sizeof(tm));
231         curptr = strptime(s,"%b %d, %Y %H:%M:%S", &tm);
232         if (curptr == NULL)
233                 curptr = strptime(s,"%Y-%m-%dT%H:%M:%S", &tm);
234         if (curptr == NULL)
235                 curptr = strptime(s,"%Y-%m-%d %H:%M:%S", &tm);
236         if (curptr == NULL)
237                 curptr = strptime(s,"%Y-%m-%d %H:%M", &tm);
238         if (curptr == NULL)
239                 curptr = strptime(s,"%Y-%m-%d %H", &tm);
240         if (curptr == NULL)
241                 curptr = strptime(s,"%Y-%m-%d", &tm);
242         if (curptr == NULL)
243                 goto fail;
244         tm.tm_isdst = -1;       /* let the computer figure out if it's DST */
245         fv->value.time.secs = mktime(&tm);
246         if (*curptr != '\0') {
247                 /*
248                  * Something came after the seconds field; it must be
249                  * a nanoseconds field.
250                  */
251                 if (*curptr != '.')
252                         goto fail;      /* it's not */
253                 curptr++;       /* skip the "." */
254                 if (!g_ascii_isdigit((unsigned char)*curptr))
255                         goto fail;      /* not a digit, so not valid */
256                 if (!get_nsecs(curptr, &fv->value.time.nsecs))
257                         goto fail;
258         } else {
259                 /*
260                  * No nanoseconds value - it's 0.
261                  */
262                 fv->value.time.nsecs = 0;
263         }
264
265         if (fv->value.time.secs == -1) {
266                 /*
267                  * XXX - should we supply an error message that mentions
268                  * that the time specified might be syntactically valid
269                  * but might not actually have occurred, e.g. a time in
270                  * the non-existent time range after the clocks are
271                  * set forward during daylight savings time (or possibly
272                  * that it's in the time range after the clocks are set
273                  * backward, so that there are two different times that
274                  * it could be)?
275                  */
276                 goto fail;
277         }
278
279         return TRUE;
280
281 fail:
282         if (err_msg != NULL)
283                 *err_msg = g_strdup_printf("\"%s\" is not a valid absolute time. Example: \"Nov 12, 1999 08:55:44.123\" or \"2011-07-04 12:34:56\"",
284                     s);
285         return FALSE;
286 }
287
288 static gboolean
289 absolute_val_from_unparsed(fvalue_t *fv, const char *s, gboolean allow_partial_value _U_, gchar **err_msg)
290 {
291         return absolute_val_from_string(fv, s, err_msg);
292 }
293
294 static void
295 time_fvalue_new(fvalue_t *fv)
296 {
297         fv->value.time.secs = 0;
298         fv->value.time.nsecs = 0;
299 }
300
301 static void
302 time_fvalue_set(fvalue_t *fv, const nstime_t *value)
303 {
304         fv->value.time = *value;
305 }
306
307 static gpointer
308 value_get(fvalue_t *fv)
309 {
310         return &(fv->value.time);
311 }
312
313 static int
314 absolute_val_repr_len(fvalue_t *fv, ftrepr_t rtype, int field_display _U_)
315 {
316         gchar *rep;
317         int ret;
318
319         rep = abs_time_to_str(NULL, &fv->value.time, ABSOLUTE_TIME_LOCAL,
320                 rtype == FTREPR_DISPLAY);
321
322         ret = (int)strlen(rep) + ((rtype == FTREPR_DFILTER) ? 2 : 0);   /* 2 for opening and closing quotes */
323
324         wmem_free(NULL, rep);
325
326         return ret;
327 }
328
329 static void
330 absolute_val_to_repr(fvalue_t *fv, ftrepr_t rtype, int field_display _U_, char *buf, unsigned int size)
331 {
332         gchar *rep = abs_time_to_str(NULL, &fv->value.time, ABSOLUTE_TIME_LOCAL,
333                 rtype == FTREPR_DISPLAY);
334         if (rtype == FTREPR_DFILTER) {
335                 *buf++ = '\"';
336         }
337
338         g_strlcpy(buf, rep, size);
339
340         if (rtype == FTREPR_DFILTER) {
341                 buf += strlen(rep);
342                 *buf++ = '\"';
343                 *buf++ = '\0';
344         }
345         wmem_free(NULL, rep);
346 }
347
348 static int
349 relative_val_repr_len(fvalue_t *fv, ftrepr_t rtype _U_, int field_display _U_)
350 {
351         gchar *rep;
352         int ret;
353
354         rep = rel_time_to_secs_str(NULL, &fv->value.time);
355         ret = (int)strlen(rep);
356         wmem_free(NULL, rep);
357
358         return ret;
359 }
360
361 static void
362 relative_val_to_repr(fvalue_t *fv, ftrepr_t rtype _U_, int field_display _U_, char *buf, unsigned int size)
363 {
364         gchar *rep;
365         rep = rel_time_to_secs_str(NULL, &fv->value.time);
366         g_strlcpy(buf, rep, size);
367         wmem_free(NULL, rep);
368 }
369
370 void
371 ftype_register_time(void)
372 {
373
374         static ftype_t abstime_type = {
375                 FT_ABSOLUTE_TIME,               /* ftype */
376                 "FT_ABSOLUTE_TIME",             /* name */
377                 "Date and time",                /* pretty_name */
378                 0,                              /* wire_size */
379                 time_fvalue_new,                /* new_value */
380                 NULL,                           /* free_value */
381                 absolute_val_from_unparsed,     /* val_from_unparsed */
382                 absolute_val_from_string,       /* val_from_string */
383                 absolute_val_to_repr,           /* val_to_string_repr */
384                 absolute_val_repr_len,          /* len_string_repr */
385
386                 { .set_value_time = time_fvalue_set },  /* union set_value */
387                 { .get_value_ptr = value_get },         /* union get_value */
388
389                 cmp_eq,
390                 cmp_ne,
391                 cmp_gt,
392                 cmp_ge,
393                 cmp_lt,
394                 cmp_le,
395                 NULL,                           /* cmp_bitwise_and */
396                 NULL,                           /* cmp_contains */
397                 NULL,                           /* cmp_matches */
398
399                 NULL,
400                 NULL
401         };
402         static ftype_t reltime_type = {
403                 FT_RELATIVE_TIME,               /* ftype */
404                 "FT_RELATIVE_TIME",             /* name */
405                 "Time offset",                  /* pretty_name */
406                 0,                              /* wire_size */
407                 time_fvalue_new,                /* new_value */
408                 NULL,                           /* free_value */
409                 relative_val_from_unparsed,     /* val_from_unparsed */
410                 NULL,                           /* val_from_string */
411                 relative_val_to_repr,           /* val_to_string_repr */
412                 relative_val_repr_len,          /* len_string_repr */
413
414                 { .set_value_time = time_fvalue_set },  /* union set_value */
415                 { .get_value_ptr = value_get },         /* union get_value */
416
417                 cmp_eq,
418                 cmp_ne,
419                 cmp_gt,
420                 cmp_ge,
421                 cmp_lt,
422                 cmp_le,
423                 NULL,                           /* cmp_bitwise_and */
424                 NULL,                           /* cmp_contains */
425                 NULL,                           /* cmp_matches */
426
427                 NULL,
428                 NULL
429         };
430
431         ftype_register(FT_ABSOLUTE_TIME, &abstime_type);
432         ftype_register(FT_RELATIVE_TIME, &reltime_type);
433 }
434
435 /*
436  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
437  *
438  * Local variables:
439  * c-basic-offset: 8
440  * tab-width: 8
441  * indent-tabs-mode: t
442  * End:
443  *
444  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
445  * :indentSize=8:tabSize=8:noTabs=false:
446  */