1 /*
2 * SysDB - src/core/data.c
3 * Copyright (C) 2014 Sebastian 'tokkee' Harl <sh@tokkee.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
28 #if HAVE_CONFIG_H
29 # include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "sysdb.h"
34 #include "core/data.h"
35 #include "utils/error.h"
37 #include <errno.h>
39 #include <inttypes.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
45 #include <math.h>
47 /*
48 * private helper functions
49 */
51 static int
52 data_concat(const sdb_data_t *d1, const sdb_data_t *d2, sdb_data_t *res)
53 {
54 unsigned char *new;
55 unsigned char *s1, *s2;
56 size_t len1, len2;
58 if (d1->type == SDB_TYPE_STRING) {
59 s1 = (unsigned char *)d1->data.string;
60 s2 = (unsigned char *)d2->data.string;
61 len1 = s1 ? strlen((char *)s1) : 0;
62 len2 = s2 ? strlen((char *)s2) : 0;
63 }
64 else if (d1->type == SDB_TYPE_BINARY) {
65 s1 = d1->data.binary.datum;
66 s2 = d2->data.binary.datum;
67 len1 = d1->data.binary.length;
68 len2 = d2->data.binary.length;
69 }
70 else
71 return -1;
73 if (s1 || s2) {
74 new = malloc(len1 + len2 + 1);
75 if (! new)
76 return -1;
77 }
78 else
79 new = NULL;
81 if (len1)
82 memcpy(new, s1, len1);
83 if (len2)
84 memcpy(new + len1, s2, len2);
85 if (new)
86 new[len1 + len2] = '\0';
88 res->type = d1->type;
89 if (res->type == SDB_TYPE_STRING) {
90 res->data.string = (char *)new;
91 }
92 else {
93 res->data.binary.datum = new;
94 res->data.binary.length = len1 + len2;
95 }
96 return 0;
97 } /* data_concat */
99 /*
100 * public API
101 */
103 int
104 sdb_data_copy(sdb_data_t *dst, const sdb_data_t *src)
105 {
106 sdb_data_t tmp;
108 if ((! dst) || (! src))
109 return -1;
111 tmp = *src;
112 switch (src->type) {
113 case SDB_TYPE_STRING:
114 tmp.data.string = strdup(src->data.string);
115 if (! tmp.data.string)
116 return -1;
117 break;
118 case SDB_TYPE_BINARY:
119 tmp.data.binary.datum = malloc(src->data.binary.length);
120 if (! tmp.data.binary.datum)
121 return -1;
122 memcpy(tmp.data.binary.datum, src->data.binary.datum,
123 src->data.binary.length);
124 break;
125 }
127 sdb_data_free_datum(dst);
128 *dst = tmp;
129 return 0;
130 } /* sdb_data_copy */
132 void
133 sdb_data_free_datum(sdb_data_t *datum)
134 {
135 if (! datum)
136 return;
138 switch (datum->type) {
139 case SDB_TYPE_STRING:
140 if (datum->data.string)
141 free(datum->data.string);
142 datum->data.string = NULL;
143 break;
144 case SDB_TYPE_BINARY:
145 if (datum->data.binary.datum)
146 free(datum->data.binary.datum);
147 datum->data.binary.datum = NULL;
148 datum->data.binary.length = 0;
149 break;
150 }
151 } /* sdb_data_free_datum */
153 int
154 sdb_data_cmp(const sdb_data_t *d1, const sdb_data_t *d2)
155 {
156 #define CMP_NULL(a, b) \
157 do { \
158 if (!(a) && !(b)) return 0; \
159 if (!(a)) return -1; \
160 if (!(b)) return 1; \
161 } while (0)
163 CMP_NULL(d1, d2);
165 if (d1->type != d2->type)
166 return -1;
168 switch (d1->type) {
169 case SDB_TYPE_INTEGER:
170 return SDB_CMP(d1->data.integer, d2->data.integer);
171 case SDB_TYPE_DECIMAL:
172 return SDB_CMP(d1->data.decimal, d2->data.decimal);
173 case SDB_TYPE_STRING:
174 CMP_NULL(d1->data.string, d2->data.string);
175 return strcasecmp(d1->data.string, d2->data.string);
176 case SDB_TYPE_DATETIME:
177 return SDB_CMP(d1->data.datetime, d2->data.datetime);
178 case SDB_TYPE_BINARY:
179 {
180 int diff;
182 CMP_NULL(d1->data.binary.datum, d2->data.binary.datum);
184 /* on a common prefix, the shorter datum sorts less */
185 if (d1->data.binary.length < d2->data.binary.length) {
186 diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
187 d1->data.binary.length);
188 diff = diff ? diff : -1;
189 }
190 else if (d1->data.binary.length > d2->data.binary.length) {
191 diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
192 d2->data.binary.length);
193 diff = diff ? diff : 1;
194 }
195 else
196 diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
197 d1->data.binary.length);
199 return diff;
200 }
201 default:
202 return -1;
203 }
204 #undef CMP_NULL
205 } /* sdb_data_cmp */
207 int
208 sdb_data_expr_eval(int op, const sdb_data_t *d1, const sdb_data_t *d2,
209 sdb_data_t *res)
210 {
211 switch (op) {
212 case SDB_DATA_CONCAT:
213 return data_concat(d1, d2, res);
214 case SDB_DATA_ADD:
215 if (d1->type != d2->type)
216 return -1;
217 switch (d1->type) {
218 case SDB_TYPE_INTEGER:
219 res->data.integer = d1->data.integer + d2->data.integer;
220 break;
221 case SDB_TYPE_DECIMAL:
222 res->data.decimal = d1->data.decimal + d2->data.decimal;
223 break;
224 case SDB_TYPE_DATETIME:
225 res->data.datetime = d1->data.datetime + d2->data.datetime;
226 break;
227 default:
228 return -1;
229 }
230 break;
231 case SDB_DATA_SUB:
232 if (d1->type != d2->type)
233 return -1;
234 switch (d1->type) {
235 case SDB_TYPE_INTEGER:
236 res->data.integer = d1->data.integer - d2->data.integer;
237 break;
238 case SDB_TYPE_DECIMAL:
239 res->data.decimal = d1->data.decimal - d2->data.decimal;
240 break;
241 case SDB_TYPE_DATETIME:
242 res->data.datetime = d1->data.datetime - d2->data.datetime;
243 break;
244 default:
245 return -1;
246 }
247 break;
248 case SDB_DATA_MUL:
249 switch (d1->type) {
250 case SDB_TYPE_INTEGER:
251 if (d2->type == SDB_TYPE_INTEGER)
252 res->data.integer = d1->data.integer
253 * d2->data.integer;
254 else if (d2->type == SDB_TYPE_DATETIME) {
255 res->data.datetime = (sdb_time_t)d1->data.integer
256 * d2->data.datetime;
257 res->type = SDB_TYPE_DATETIME;
258 return 0;
259 }
260 else
261 return -1;
262 break;
263 case SDB_TYPE_DECIMAL:
264 if (d2->type == SDB_TYPE_DECIMAL)
265 res->data.decimal = d1->data.decimal
266 * d2->data.decimal;
267 else if (d2->type == SDB_TYPE_DATETIME) {
268 double tmp = d1->data.decimal
269 * (double)d2->data.datetime;
270 res->data.datetime = (sdb_time_t)tmp;
271 res->type = SDB_TYPE_DATETIME;
272 return 0;
273 }
274 else
275 return -1;
276 break;
277 case SDB_TYPE_DATETIME:
278 if (d2->type == SDB_TYPE_DATETIME)
279 res->data.datetime = d1->data.datetime
280 * d2->data.datetime;
281 else if (d2->type == SDB_TYPE_INTEGER) {
282 res->data.datetime = d1->data.datetime
283 * (sdb_time_t)d2->data.integer;
284 res->type = SDB_TYPE_DATETIME;
285 return 0;
286 }
287 else if (d2->type == SDB_TYPE_DECIMAL) {
288 double tmp = (double)d1->data.datetime
289 * d2->data.decimal;
290 res->data.datetime = (sdb_time_t)tmp;
291 res->type = SDB_TYPE_DATETIME;
292 return 0;
293 }
294 else
295 return -1;
296 break;
297 default:
298 return -1;
299 }
300 break;
301 case SDB_DATA_DIV:
302 switch (d1->type) {
303 case SDB_TYPE_INTEGER:
304 if (d2->type != SDB_TYPE_INTEGER)
305 return -1;
306 res->data.integer = d1->data.integer / d2->data.integer;
307 break;
308 case SDB_TYPE_DECIMAL:
309 if (d2->type != SDB_TYPE_DECIMAL)
310 return -1;
311 res->data.decimal = d1->data.decimal / d2->data.decimal;
312 break;
313 case SDB_TYPE_DATETIME:
314 if (d2->type == SDB_TYPE_DATETIME)
315 res->data.datetime = d1->data.datetime
316 / d2->data.datetime;
317 else if (d2->type == SDB_TYPE_INTEGER) {
318 res->data.datetime = d1->data.datetime
319 / (sdb_time_t)d2->data.integer;
320 res->type = SDB_TYPE_DATETIME;
321 return 0;
322 }
323 else if (d2->type == SDB_TYPE_DECIMAL) {
324 double tmp = (double)d1->data.datetime
325 / d2->data.decimal;
326 res->data.datetime = (sdb_time_t)tmp;
327 res->type = SDB_TYPE_DATETIME;
328 return 0;
329 }
330 else
331 return -1;
332 break;
333 default:
334 return -1;
335 }
336 break;
337 case SDB_DATA_MOD:
338 switch (d1->type) {
339 case SDB_TYPE_INTEGER:
340 if (d2->type != SDB_TYPE_INTEGER)
341 return -1;
342 res->data.integer = d1->data.integer % d2->data.integer;
343 break;
344 case SDB_TYPE_DECIMAL:
345 if (d2->type != SDB_TYPE_DECIMAL)
346 return -1;
347 res->data.decimal = fmod(d1->data.decimal, d2->data.decimal);
348 break;
349 case SDB_TYPE_DATETIME:
350 if (d2->type == SDB_TYPE_DATETIME)
351 res->data.datetime = d1->data.datetime
352 % d2->data.datetime;
353 else if (d2->type == SDB_TYPE_INTEGER) {
354 res->data.datetime = d1->data.datetime
355 % (sdb_time_t)d2->data.integer;
356 res->type = SDB_TYPE_DATETIME;
357 return 0;
358 }
359 else if (d2->type == SDB_TYPE_DECIMAL) {
360 double tmp = fmod((double)d1->data.datetime,
361 d2->data.decimal);
362 res->data.datetime = (sdb_time_t)tmp;
363 res->type = SDB_TYPE_DATETIME;
364 return 0;
365 }
366 else
367 return -1;
368 break;
369 default:
370 return -1;
371 }
372 break;
373 default:
374 return -1;
375 }
377 res->type = d1->type;
378 return 0;
379 } /* sdb_data_expr_eval */
381 size_t
382 sdb_data_strlen(const sdb_data_t *datum)
383 {
384 if (! datum)
385 return 0;
387 switch (datum->type) {
388 case SDB_TYPE_INTEGER:
389 /* log(64) */
390 return 20;
391 case SDB_TYPE_DECIMAL:
392 /* XXX: -0xN.NNNNNNp+NNN */
393 return 42;
394 case SDB_TYPE_STRING:
395 if (! datum->data.string)
396 return 6; /* "NULL" */
397 /* in the worst case, each character needs to be escaped */
398 return 2 * strlen(datum->data.string) + 2;
399 case SDB_TYPE_DATETIME:
400 /* "YYYY-MM-DD HH:MM:SS +zzzz" */
401 return 27;
402 case SDB_TYPE_BINARY:
403 /* "\xNN" */
404 return 4 * datum->data.binary.length + 2;
405 }
406 return 0;
407 } /* sdb_data_strlen */
409 int
410 sdb_data_format(const sdb_data_t *datum, char *buf, size_t buflen, int quoted)
411 {
412 char tmp[sdb_data_strlen(datum) + 1];
413 char *data = NULL;
414 int ret = -1;
416 size_t i, pos;
418 if ((! datum) || (! buf))
419 return -1;
421 switch (datum->type) {
422 case SDB_TYPE_INTEGER:
423 ret = snprintf(buf, buflen, "%"PRIi64, datum->data.integer);
424 break;
425 case SDB_TYPE_DECIMAL:
426 ret = snprintf(buf, buflen, "%a", datum->data.decimal);
427 break;
428 case SDB_TYPE_STRING:
429 if (! datum->data.string)
430 data = "NULL";
431 else {
432 pos = 0;
433 for (i = 0; i < strlen(datum->data.string); ++i) {
434 char byte = datum->data.string[i];
436 if ((byte == '\\') || (byte == '"')) {
437 tmp[pos] = '\\';
438 ++pos;
439 }
440 tmp[pos] = byte;
441 ++pos;
442 }
443 tmp[pos] = '\0';
444 data = tmp;
445 }
446 break;
447 case SDB_TYPE_DATETIME:
448 if (! sdb_strftime(tmp, sizeof(tmp), "%F %T %z",
449 datum->data.datetime))
450 return -1;
451 tmp[sizeof(tmp) - 1] = '\0';
452 data = tmp;
453 break;
454 case SDB_TYPE_BINARY:
455 pos = 0;
456 for (i = 0; i < datum->data.binary.length; ++i) {
457 int byte = (int)datum->data.binary.datum[i];
458 char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
459 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
461 tmp[pos] = '\\';
462 tmp[pos + 1] = 'x';
463 pos += 2;
465 if (byte > 0xf) {
466 tmp[pos] = hex[byte >> 4];
467 ++pos;
468 }
469 tmp[pos] = hex[byte & 0xf];
470 ++pos;
471 }
472 tmp[pos] = '\0';
473 data = tmp;
474 break;
475 }
477 if (data) {
478 if (quoted == SDB_UNQUOTED)
479 ret = snprintf(buf, buflen, "%s", data);
480 else if (quoted == SDB_SINGLE_QUOTED)
481 ret = snprintf(buf, buflen, "'%s'", data);
482 else
483 ret = snprintf(buf, buflen, "\"%s\"", data);
484 }
485 buf[buflen - 1] = '\0';
486 return ret;
487 } /* sdb_data_format */
489 int
490 sdb_data_parse(char *str, int type, sdb_data_t *data)
491 {
492 sdb_data_t tmp;
494 char *endptr = NULL;
496 errno = 0;
497 switch (type) {
498 case SDB_TYPE_INTEGER:
499 tmp.data.integer = strtoll(str, &endptr, 0);
500 break;
501 case SDB_TYPE_DECIMAL:
502 tmp.data.decimal = strtod(str, &endptr);
503 break;
504 case SDB_TYPE_STRING:
505 tmp.data.string = str;
506 break;
507 case SDB_TYPE_DATETIME:
508 {
509 double datetime = strtod(str, &endptr);
510 tmp.data.datetime = DOUBLE_TO_SDB_TIME(datetime);
511 }
512 break;
513 case SDB_TYPE_BINARY:
514 /* we don't support any binary information containing 0-bytes */
515 tmp.data.binary.length = strlen(str);
516 tmp.data.binary.datum = (unsigned char *)str;
517 break;
518 default:
519 errno = EINVAL;
520 return -1;
521 }
523 if ((type == SDB_TYPE_INTEGER) || (type == SDB_TYPE_DECIMAL)
524 || (type == SDB_TYPE_DATETIME)) {
525 if (errno || (str == endptr)) {
526 char errbuf[1024];
527 sdb_log(SDB_LOG_ERR, "core: Failed to parse string "
528 "'%s' as numeric value (type %i): %s", str, type,
529 sdb_strerror(errno, errbuf, sizeof(errbuf)));
530 return -1;
531 }
532 else if (endptr && (*endptr != '\0'))
533 sdb_log(SDB_LOG_WARNING, "core: Ignoring garbage after "
534 "number while parsing numeric value (type %i): %s.",
535 type, endptr);
536 }
538 if (data) {
539 *data = tmp;
540 data->type = type;
541 }
542 return 0;
543 } /* sdb_data_parse */
545 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */