Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
tracker-date-time.c:316:2 | clang-analyzer | Function call argument is an uninitialized value |
1 /*
2 * Copyright (C) 2006, Jamie McCracken <jamiemcc@gnome.org>
3 * Copyright (C) 2008-2010, Nokia <ivan.frade@nokia.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include "config.h"
22
23 /* For timegm usage on __GLIBC__ */
24 #ifndef _GNU_SOURCE
25 #define _GNU_SOURCE
26 #endif
27
28 #include <strings.h>
29 #include <string.h>
30 #include <math.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <time.h>
34
35 #include <glib.h>
36
37 #include "tracker-date-time.h"
38 #include "tracker-type-utils.h"
39
40 GQuark tracker_date_error_quark (void) {
41 return g_quark_from_static_string ("tracker_date_error-quark");
42 }
43
44 gdouble
45 tracker_string_to_date (const gchar *date_string,
46 gint *offset_p,
47 GError **error)
48 {
49 /* TODO Add more checks and use GError to report invalid input
50 * as this is potential user input.
51 */
52
53 static GRegex *regex = NULL;
54
55 GMatchInfo *match_info;
56 gchar *match;
57 struct tm tm;
58 gdouble t;
59 gint offset;
60 gboolean timezoned;
61
62 g_return_val_if_fail (date_string, -1);
63
64 /* We should have a valid iso 8601 date in format
65 * YYYY-MM-DDThh:mm:ss with optional TZ
66 */
67
68 if (!regex) {
69 GError *e = NULL;
70 regex = g_regex_new ("^(-?[0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])T([0-9][0-9]):([0-9][0-9]):([0-9][0-9])(\\.[0-9]+)?(Z|(\\+|-)([0-9][0-9]):?([0-9][0-9]))?$", 0, 0, &e);
71 if (e) {
72 g_error ("%s", e->message);
73 }
74 }
75
76 if (!g_regex_match (regex, date_string, 0, &match_info)) {
77 g_match_info_free (match_info);
78 g_set_error (error, TRACKER_DATE_ERROR, TRACKER_DATE_ERROR_INVALID_ISO8601,
79 "Not a ISO 8601 date string. Allowed form is [-]CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]");
80 return -1;
81 }
82
83 memset (&tm, 0, sizeof (struct tm));
84
85 /* year */
86 match = g_match_info_fetch (match_info, 1);
87 tm.tm_year = atoi (match) - 1900;
88 g_free (match);
89
90 /* month */
91 match = g_match_info_fetch (match_info, 2);
92 tm.tm_mon = atoi (match) - 1;
93 g_free (match);
94
95 /* day of month */
96 match = g_match_info_fetch (match_info, 3);
97 tm.tm_mday = atoi (match);
98 g_free (match);
99
100 /* hour */
101 match = g_match_info_fetch (match_info, 4);
102 tm.tm_hour = atoi (match);
103 g_free (match);
104
105 /* minute */
106 match = g_match_info_fetch (match_info, 5);
107 tm.tm_min = atoi (match);
108 g_free (match);
109
110 /* second */
111 match = g_match_info_fetch (match_info, 6);
112 tm.tm_sec = atoi (match);
113 g_free (match);
114
115 match = g_match_info_fetch (match_info, 8);
116 timezoned = (match && strlen (match) > 0);
117 g_free (match);
118
119 if (timezoned) {
120 /* timezoned */
121
122 /* mktime() always assumes that "tm" is in locale time but we
123 * want to keep control on time, so we go to UTC
124 */
125 #if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__GLIBC__))
126 t = mktime (&tm);
127 t -= timezone;
128 #else
129 t = timegm (&tm);
130 #endif
131
132 offset = 0;
133
134 match = g_match_info_fetch (match_info, 9);
135 if (match && strlen (match) > 0) {
136 /* non-UTC timezone */
137
138 gboolean positive_offset;
139
140 positive_offset = (match[0] == '+');
141 g_free (match);
142
143 match = g_match_info_fetch (match_info, 10);
144 offset = atoi (match) * 3600;
145 g_free (match);
146
147 match = g_match_info_fetch (match_info, 11);
148 offset += atoi (match) * 60;
149 g_free (match);
150
151 if (!positive_offset) {
152 offset = -offset;
153 }
154
155 if (offset < -14 * 3600 || offset > 14 * 3600) {
156 g_set_error (error, TRACKER_DATE_ERROR, TRACKER_DATE_ERROR_OFFSET,
157 "UTC offset too large: %d seconds", offset);
158 g_match_info_free (match_info);
159 return -1;
160 }
161
162 t -= offset;
163 }
164 } else {
165 time_t t2;
166
167 /* local time */
168 tm.tm_isdst = -1;
169
170 t = mktime (&tm);
171
172 /* calculate UTC offset, requires timegm for correct result
173 with past times when timezone had different UTC offset */
174 #if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__GLIBC__))
175 offset = -timezone + (tm.tm_isdst > 0 ? 3600 : 0);
176 #else
177 t2 = timegm (&tm);
178 offset = t2 - (time_t) t;
179 #endif
180 }
181
182 match = g_match_info_fetch (match_info, 7);
183 if (match && strlen (match) > 0) {
184 char milliseconds[4] = "000\0";
185 /* first character of match is decimal point
186 we're interested in a maximum of 3 decimal places (milliseconds) */
187 memcpy (milliseconds, match + 1, MIN (3, strlen (match + 1)));
188 t += (gdouble) atoi (milliseconds) / 1000;
189 }
190 g_free (match);
191
192 g_match_info_free (match_info);
193
194 if (offset_p) {
195 *offset_p = offset;
196 }
197
198 return t;
199 }
200
201 gchar *
202 tracker_date_to_string (gdouble date_time)
203 {
204 gchar buffer[30];
205 time_t seconds;
206 gint64 total_milliseconds;
207 gint milliseconds;
208 struct tm utc_time;
209 size_t count;
210
211 memset (buffer, '\0', sizeof (buffer));
212 memset (&utc_time, 0, sizeof (struct tm));
213
214 total_milliseconds = (gint64) round (date_time * 1000);
215 milliseconds = total_milliseconds % 1000;
216 if (milliseconds < 0) {
217 milliseconds += 1000;
218 }
219 seconds = (time_t) ((total_milliseconds - milliseconds) / 1000);
220 gmtime_r (&seconds, &utc_time);
221
222 /* Output is ISO 8601 format : "YYYY-MM-DDThh:mm:ss" */
223 count = strftime (buffer, sizeof (buffer), "%FT%T", &utc_time);
224
225 /* Append milliseconds (if non-zero) and time zone */
226 if (milliseconds > 0) {
227 snprintf (buffer + count, sizeof (buffer) - count, ".%03dZ", milliseconds);
228 } else {
229 buffer[count] = 'Z';
230 }
231
232 return count > 0 ? g_strdup (buffer) : NULL;
233 }
234
235 static void
236 date_time_value_init (GValue *value)
237 {
238 value->data[0].v_double = 0;
239 value->data[1].v_int = 0;
240 }
241
242 static void
243 date_time_value_copy (const GValue *src_value,
244 GValue *dest_value)
245 {
246 dest_value->data[0].v_double = src_value->data[0].v_double;
247 dest_value->data[1].v_int = src_value->data[1].v_int;
248 }
249
250 GType
251 tracker_date_time_get_type (void)
252 {
253 static GType tracker_date_time_type_id = 0;
254 if (G_UNLIKELY (tracker_date_time_type_id == 0)) {
255 static const GTypeValueTable value_table = {
256 date_time_value_init,
257 NULL,
258 date_time_value_copy
259 };
260 static const GTypeInfo type_info = {
261 0,
262 NULL,
263 NULL,
264 NULL,
265 NULL,
266 NULL,
267 0,
268 0,
269 NULL,
270 &value_table
271 };
272 static const GTypeFundamentalInfo fundamental_info = {
273 0
274 };
275 tracker_date_time_type_id = g_type_register_fundamental (
276 g_type_fundamental_next (),
277 "TrackerDateTime",
278 &type_info,
279 &fundamental_info,
280 0);
281 }
282 return tracker_date_time_type_id;
283 }
284
285 void
286 tracker_date_time_set (GValue *value,
287 gdouble time,
288 gint offset)
289 {
290 g_return_if_fail (G_VALUE_HOLDS (value, TRACKER_TYPE_DATE_TIME));
291 g_return_if_fail (offset >= -14 * 3600 && offset <= 14 * 3600);
292
293 value->data[0].v_double = time;
294 value->data[1].v_int = offset;
295 }
296
297 void
298 tracker_date_time_set_from_string (GValue *value,
299 const gchar *date_time_string,
300 GError **error)
301 {
302 gdouble time;
303 gint offset;
304 GError *new_error = NULL;
305
306 g_return_if_fail (G_VALUE_HOLDS (value, TRACKER_TYPE_DATE_TIME));
307 g_return_if_fail (date_time_string != NULL);
308
309 time = tracker_string_to_date (date_time_string, &offset, &new_error);
310
311 if (new_error != NULL) {
312 g_propagate_error (error, new_error);
313 return;
314 }
315
316 tracker_date_time_set (value, time, offset);
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
317 }
318
319 gdouble
320 tracker_date_time_get_time (const GValue *value)
321 {
322 g_return_val_if_fail (G_VALUE_HOLDS (value, TRACKER_TYPE_DATE_TIME), 0);
323
324 /* UTC timestamp */
325 return value->data[0].v_double;
326 }
327
328 gint
329 tracker_date_time_get_offset (const GValue *value)
330 {
331 g_return_val_if_fail (G_VALUE_HOLDS (value, TRACKER_TYPE_DATE_TIME), 0);
332
333 /* UTC offset */
334 return value->data[1].v_int;
335 }
336
337 gint
338 tracker_date_time_get_local_date (const GValue *value)
339 {
340 gdouble local_timestamp;
341
342 g_return_val_if_fail (G_VALUE_HOLDS (value, TRACKER_TYPE_DATE_TIME), 0);
343
344 /* return number of days since epoch */
345 local_timestamp = tracker_date_time_get_time (value) + tracker_date_time_get_offset (value);
346 return (gint) (local_timestamp / 3600 / 24);
347 }
348
349 gint
350 tracker_date_time_get_local_time (const GValue *value)
351 {
352 gdouble local_timestamp;
353
354 g_return_val_if_fail (G_VALUE_HOLDS (value, TRACKER_TYPE_DATE_TIME), 0);
355
356 /* return local time of day */
357 local_timestamp = tracker_date_time_get_time (value) + tracker_date_time_get_offset (value);
358 return (int) local_timestamp % (24 * 3600);
359 }