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 if ((! d1) || (! d2) || (! res))
212 return -1;
213 switch (op) {
214 case SDB_DATA_CONCAT:
215 return data_concat(d1, d2, res);
216 case SDB_DATA_ADD:
217 if (d1->type != d2->type)
218 return -1;
219 switch (d1->type) {
220 case SDB_TYPE_INTEGER:
221 res->data.integer = d1->data.integer + d2->data.integer;
222 break;
223 case SDB_TYPE_DECIMAL:
224 res->data.decimal = d1->data.decimal + d2->data.decimal;
225 break;
226 case SDB_TYPE_DATETIME:
227 res->data.datetime = d1->data.datetime + d2->data.datetime;
228 break;
229 default:
230 return -1;
231 }
232 break;
233 case SDB_DATA_SUB:
234 if (d1->type != d2->type)
235 return -1;
236 switch (d1->type) {
237 case SDB_TYPE_INTEGER:
238 res->data.integer = d1->data.integer - d2->data.integer;
239 break;
240 case SDB_TYPE_DECIMAL:
241 res->data.decimal = d1->data.decimal - d2->data.decimal;
242 break;
243 case SDB_TYPE_DATETIME:
244 res->data.datetime = d1->data.datetime - d2->data.datetime;
245 break;
246 default:
247 return -1;
248 }
249 break;
250 case SDB_DATA_MUL:
251 switch (d1->type) {
252 case SDB_TYPE_INTEGER:
253 if (d2->type == SDB_TYPE_INTEGER)
254 res->data.integer = d1->data.integer
255 * d2->data.integer;
256 else if (d2->type == SDB_TYPE_DATETIME) {
257 res->data.datetime = (sdb_time_t)d1->data.integer
258 * d2->data.datetime;
259 res->type = SDB_TYPE_DATETIME;
260 return 0;
261 }
262 else
263 return -1;
264 break;
265 case SDB_TYPE_DECIMAL:
266 if (d2->type == SDB_TYPE_DECIMAL)
267 res->data.decimal = d1->data.decimal
268 * d2->data.decimal;
269 else if (d2->type == SDB_TYPE_DATETIME) {
270 double tmp = d1->data.decimal
271 * (double)d2->data.datetime;
272 res->data.datetime = (sdb_time_t)tmp;
273 res->type = SDB_TYPE_DATETIME;
274 return 0;
275 }
276 else
277 return -1;
278 break;
279 case SDB_TYPE_DATETIME:
280 if (d2->type == SDB_TYPE_DATETIME)
281 res->data.datetime = d1->data.datetime
282 * d2->data.datetime;
283 else if (d2->type == SDB_TYPE_INTEGER) {
284 res->data.datetime = d1->data.datetime
285 * (sdb_time_t)d2->data.integer;
286 res->type = SDB_TYPE_DATETIME;
287 return 0;
288 }
289 else if (d2->type == SDB_TYPE_DECIMAL) {
290 double tmp = (double)d1->data.datetime
291 * d2->data.decimal;
292 res->data.datetime = (sdb_time_t)tmp;
293 res->type = SDB_TYPE_DATETIME;
294 return 0;
295 }
296 else
297 return -1;
298 break;
299 default:
300 return -1;
301 }
302 break;
303 case SDB_DATA_DIV:
304 switch (d1->type) {
305 case SDB_TYPE_INTEGER:
306 if (d2->type != SDB_TYPE_INTEGER)
307 return -1;
308 res->data.integer = d1->data.integer / d2->data.integer;
309 break;
310 case SDB_TYPE_DECIMAL:
311 if (d2->type != SDB_TYPE_DECIMAL)
312 return -1;
313 res->data.decimal = d1->data.decimal / d2->data.decimal;
314 break;
315 case SDB_TYPE_DATETIME:
316 if (d2->type == SDB_TYPE_DATETIME)
317 res->data.datetime = d1->data.datetime
318 / d2->data.datetime;
319 else if (d2->type == SDB_TYPE_INTEGER) {
320 res->data.datetime = d1->data.datetime
321 / (sdb_time_t)d2->data.integer;
322 res->type = SDB_TYPE_DATETIME;
323 return 0;
324 }
325 else if (d2->type == SDB_TYPE_DECIMAL) {
326 double tmp = (double)d1->data.datetime
327 / d2->data.decimal;
328 res->data.datetime = (sdb_time_t)tmp;
329 res->type = SDB_TYPE_DATETIME;
330 return 0;
331 }
332 else
333 return -1;
334 break;
335 default:
336 return -1;
337 }
338 break;
339 case SDB_DATA_MOD:
340 switch (d1->type) {
341 case SDB_TYPE_INTEGER:
342 if (d2->type != SDB_TYPE_INTEGER)
343 return -1;
344 res->data.integer = d1->data.integer % d2->data.integer;
345 break;
346 case SDB_TYPE_DECIMAL:
347 if (d2->type != SDB_TYPE_DECIMAL)
348 return -1;
349 res->data.decimal = fmod(d1->data.decimal, d2->data.decimal);
350 break;
351 case SDB_TYPE_DATETIME:
352 if (d2->type == SDB_TYPE_DATETIME)
353 res->data.datetime = d1->data.datetime
354 % d2->data.datetime;
355 else if (d2->type == SDB_TYPE_INTEGER) {
356 res->data.datetime = d1->data.datetime
357 % (sdb_time_t)d2->data.integer;
358 res->type = SDB_TYPE_DATETIME;
359 return 0;
360 }
361 else if (d2->type == SDB_TYPE_DECIMAL) {
362 double tmp = fmod((double)d1->data.datetime,
363 d2->data.decimal);
364 res->data.datetime = (sdb_time_t)tmp;
365 res->type = SDB_TYPE_DATETIME;
366 return 0;
367 }
368 else
369 return -1;
370 break;
371 default:
372 return -1;
373 }
374 break;
375 default:
376 return -1;
377 }
379 res->type = d1->type;
380 return 0;
381 } /* sdb_data_expr_eval */
383 size_t
384 sdb_data_strlen(const sdb_data_t *datum)
385 {
386 if (! datum)
387 return 0;
389 switch (datum->type) {
390 case SDB_TYPE_INTEGER:
391 /* log(64) */
392 return 20;
393 case SDB_TYPE_DECIMAL:
394 /* XXX: -0xN.NNNNNNp+NNN */
395 return 42;
396 case SDB_TYPE_STRING:
397 if (! datum->data.string)
398 return 6; /* "NULL" */
399 /* in the worst case, each character needs to be escaped */
400 return 2 * strlen(datum->data.string) + 2;
401 case SDB_TYPE_DATETIME:
402 /* "YYYY-MM-DD HH:MM:SS +zzzz" */
403 return 27;
404 case SDB_TYPE_BINARY:
405 /* "\xNN" */
406 return 4 * datum->data.binary.length + 2;
407 }
408 return 0;
409 } /* sdb_data_strlen */
411 int
412 sdb_data_format(const sdb_data_t *datum, char *buf, size_t buflen, int quoted)
413 {
414 char tmp[sdb_data_strlen(datum) + 1];
415 char *data = NULL;
416 int ret = -1;
418 size_t i, pos;
420 if ((! datum) || (! buf))
421 return -1;
423 switch (datum->type) {
424 case SDB_TYPE_INTEGER:
425 ret = snprintf(buf, buflen, "%"PRIi64, datum->data.integer);
426 break;
427 case SDB_TYPE_DECIMAL:
428 ret = snprintf(buf, buflen, "%a", datum->data.decimal);
429 break;
430 case SDB_TYPE_STRING:
431 if (! datum->data.string)
432 data = "NULL";
433 else {
434 pos = 0;
435 for (i = 0; i < strlen(datum->data.string); ++i) {
436 char byte = datum->data.string[i];
438 if ((byte == '\\') || (byte == '"')) {
439 tmp[pos] = '\\';
440 ++pos;
441 }
442 tmp[pos] = byte;
443 ++pos;
444 }
445 tmp[pos] = '\0';
446 data = tmp;
447 }
448 break;
449 case SDB_TYPE_DATETIME:
450 if (! sdb_strftime(tmp, sizeof(tmp), "%F %T %z",
451 datum->data.datetime))
452 return -1;
453 tmp[sizeof(tmp) - 1] = '\0';
454 data = tmp;
455 break;
456 case SDB_TYPE_BINARY:
457 pos = 0;
458 for (i = 0; i < datum->data.binary.length; ++i) {
459 int byte = (int)datum->data.binary.datum[i];
460 char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
461 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
463 tmp[pos] = '\\';
464 tmp[pos + 1] = 'x';
465 pos += 2;
467 if (byte > 0xf) {
468 tmp[pos] = hex[byte >> 4];
469 ++pos;
470 }
471 tmp[pos] = hex[byte & 0xf];
472 ++pos;
473 }
474 tmp[pos] = '\0';
475 data = tmp;
476 break;
477 }
479 if (data) {
480 if (quoted == SDB_UNQUOTED)
481 ret = snprintf(buf, buflen, "%s", data);
482 else if (quoted == SDB_SINGLE_QUOTED)
483 ret = snprintf(buf, buflen, "'%s'", data);
484 else
485 ret = snprintf(buf, buflen, "\"%s\"", data);
486 }
487 buf[buflen - 1] = '\0';
488 return ret;
489 } /* sdb_data_format */
491 int
492 sdb_data_parse(char *str, int type, sdb_data_t *data)
493 {
494 sdb_data_t tmp;
496 char *endptr = NULL;
498 errno = 0;
499 switch (type) {
500 case SDB_TYPE_INTEGER:
501 tmp.data.integer = strtoll(str, &endptr, 0);
502 break;
503 case SDB_TYPE_DECIMAL:
504 tmp.data.decimal = strtod(str, &endptr);
505 break;
506 case SDB_TYPE_STRING:
507 tmp.data.string = str;
508 break;
509 case SDB_TYPE_DATETIME:
510 {
511 double datetime = strtod(str, &endptr);
512 tmp.data.datetime = DOUBLE_TO_SDB_TIME(datetime);
513 }
514 break;
515 case SDB_TYPE_BINARY:
516 /* we don't support any binary information containing 0-bytes */
517 tmp.data.binary.length = strlen(str);
518 tmp.data.binary.datum = (unsigned char *)str;
519 break;
520 default:
521 errno = EINVAL;
522 return -1;
523 }
525 if ((type == SDB_TYPE_INTEGER) || (type == SDB_TYPE_DECIMAL)
526 || (type == SDB_TYPE_DATETIME)) {
527 if (errno || (str == endptr)) {
528 char errbuf[1024];
529 sdb_log(SDB_LOG_ERR, "core: Failed to parse string "
530 "'%s' as numeric value (type %i): %s", str, type,
531 sdb_strerror(errno, errbuf, sizeof(errbuf)));
532 return -1;
533 }
534 else if (endptr && (*endptr != '\0'))
535 sdb_log(SDB_LOG_WARNING, "core: Ignoring garbage after "
536 "number while parsing numeric value (type %i): %s.",
537 type, endptr);
538 }
540 if (data) {
541 *data = tmp;
542 data->type = type;
543 }
544 return 0;
545 } /* sdb_data_parse */
547 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */