cdafac30f284701482b583bd2f14f85e508b6526
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->type != d2->type)
212 return -1;
214 switch (op) {
215 case SDB_DATA_CONCAT:
216 return data_concat(d1, d2, res);
217 case SDB_DATA_ADD:
218 switch (d1->type) {
219 case SDB_TYPE_INTEGER:
220 res->data.integer = d1->data.integer + d2->data.integer;
221 break;
222 case SDB_TYPE_DECIMAL:
223 res->data.decimal = d1->data.decimal + d2->data.decimal;
224 break;
225 case SDB_TYPE_DATETIME:
226 res->data.datetime = d1->data.datetime + d2->data.datetime;
227 break;
228 default:
229 return -1;
230 }
231 break;
232 case SDB_DATA_SUB:
233 switch (d1->type) {
234 case SDB_TYPE_INTEGER:
235 res->data.integer = d1->data.integer - d2->data.integer;
236 break;
237 case SDB_TYPE_DECIMAL:
238 res->data.decimal = d1->data.decimal - d2->data.decimal;
239 break;
240 case SDB_TYPE_DATETIME:
241 res->data.datetime = d1->data.datetime - d2->data.datetime;
242 break;
243 default:
244 return -1;
245 }
246 break;
247 case SDB_DATA_MUL:
248 switch (d1->type) {
249 case SDB_TYPE_INTEGER:
250 res->data.integer = d1->data.integer * d2->data.integer;
251 break;
252 case SDB_TYPE_DECIMAL:
253 res->data.decimal = d1->data.decimal * d2->data.decimal;
254 break;
255 case SDB_TYPE_DATETIME:
256 res->data.datetime = d1->data.datetime * d2->data.datetime;
257 break;
258 default:
259 return -1;
260 }
261 break;
262 case SDB_DATA_DIV:
263 switch (d1->type) {
264 case SDB_TYPE_INTEGER:
265 res->data.integer = d1->data.integer / d2->data.integer;
266 break;
267 case SDB_TYPE_DECIMAL:
268 res->data.decimal = d1->data.decimal / d2->data.decimal;
269 break;
270 case SDB_TYPE_DATETIME:
271 res->data.datetime = d1->data.datetime / d2->data.datetime;
272 break;
273 default:
274 return -1;
275 }
276 break;
277 case SDB_DATA_MOD:
278 switch (d1->type) {
279 case SDB_TYPE_INTEGER:
280 res->data.integer = d1->data.integer % d2->data.integer;
281 break;
282 case SDB_TYPE_DECIMAL:
283 res->data.decimal = fmod(d1->data.decimal, d2->data.decimal);
284 break;
285 case SDB_TYPE_DATETIME:
286 res->data.datetime = d1->data.datetime % d2->data.datetime;
287 break;
288 default:
289 return -1;
290 }
291 break;
292 default:
293 return -1;
294 }
296 res->type = d1->type;
297 return 0;
298 } /* sdb_data_expr_eval */
300 size_t
301 sdb_data_strlen(const sdb_data_t *datum)
302 {
303 if (! datum)
304 return 0;
306 switch (datum->type) {
307 case SDB_TYPE_INTEGER:
308 /* log(64) */
309 return 20;
310 case SDB_TYPE_DECIMAL:
311 /* XXX: -0xN.NNNNNNp+NNN */
312 return 42;
313 case SDB_TYPE_STRING:
314 if (! datum->data.string)
315 return 6; /* "NULL" */
316 /* in the worst case, each character needs to be escaped */
317 return 2 * strlen(datum->data.string) + 2;
318 case SDB_TYPE_DATETIME:
319 /* "YYYY-MM-DD HH:MM:SS +zzzz" */
320 return 27;
321 case SDB_TYPE_BINARY:
322 /* "\xNN" */
323 return 4 * datum->data.binary.length + 2;
324 }
325 return 0;
326 } /* sdb_data_strlen */
328 int
329 sdb_data_format(const sdb_data_t *datum, char *buf, size_t buflen, int quoted)
330 {
331 char tmp[sdb_data_strlen(datum) + 1];
332 char *data = NULL;
333 int ret = -1;
335 size_t i, pos;
337 if ((! datum) || (! buf))
338 return -1;
340 switch (datum->type) {
341 case SDB_TYPE_INTEGER:
342 ret = snprintf(buf, buflen, "%"PRIi64, datum->data.integer);
343 break;
344 case SDB_TYPE_DECIMAL:
345 ret = snprintf(buf, buflen, "%a", datum->data.decimal);
346 break;
347 case SDB_TYPE_STRING:
348 if (! datum->data.string)
349 data = "NULL";
350 else {
351 pos = 0;
352 for (i = 0; i < strlen(datum->data.string); ++i) {
353 char byte = datum->data.string[i];
355 if ((byte == '\\') || (byte == '"')) {
356 tmp[pos] = '\\';
357 ++pos;
358 }
359 tmp[pos] = byte;
360 ++pos;
361 }
362 tmp[pos] = '\0';
363 data = tmp;
364 }
365 break;
366 case SDB_TYPE_DATETIME:
367 if (! sdb_strftime(tmp, sizeof(tmp), "%F %T %z",
368 datum->data.datetime))
369 return -1;
370 tmp[sizeof(tmp) - 1] = '\0';
371 data = tmp;
372 break;
373 case SDB_TYPE_BINARY:
374 pos = 0;
375 for (i = 0; i < datum->data.binary.length; ++i) {
376 int byte = (int)datum->data.binary.datum[i];
377 char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
378 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
380 tmp[pos] = '\\';
381 tmp[pos + 1] = 'x';
382 pos += 2;
384 if (byte > 0xf) {
385 tmp[pos] = hex[byte >> 4];
386 ++pos;
387 }
388 tmp[pos] = hex[byte & 0xf];
389 ++pos;
390 }
391 tmp[pos] = '\0';
392 data = tmp;
393 break;
394 }
396 if (data) {
397 if (quoted == SDB_UNQUOTED)
398 ret = snprintf(buf, buflen, "%s", data);
399 else if (quoted == SDB_SINGLE_QUOTED)
400 ret = snprintf(buf, buflen, "'%s'", data);
401 else
402 ret = snprintf(buf, buflen, "\"%s\"", data);
403 }
404 buf[buflen - 1] = '\0';
405 return ret;
406 } /* sdb_data_format */
408 int
409 sdb_data_parse(char *str, int type, sdb_data_t *data)
410 {
411 sdb_data_t tmp;
413 char *endptr = NULL;
415 errno = 0;
416 switch (type) {
417 case SDB_TYPE_INTEGER:
418 tmp.data.integer = strtoll(str, &endptr, 0);
419 break;
420 case SDB_TYPE_DECIMAL:
421 tmp.data.decimal = strtod(str, &endptr);
422 break;
423 case SDB_TYPE_STRING:
424 tmp.data.string = str;
425 break;
426 case SDB_TYPE_DATETIME:
427 {
428 double datetime = strtod(str, &endptr);
429 tmp.data.datetime = DOUBLE_TO_SDB_TIME(datetime);
430 }
431 break;
432 case SDB_TYPE_BINARY:
433 /* we don't support any binary information containing 0-bytes */
434 tmp.data.binary.length = strlen(str);
435 tmp.data.binary.datum = (unsigned char *)str;
436 break;
437 default:
438 errno = EINVAL;
439 return -1;
440 }
442 if ((type == SDB_TYPE_INTEGER) || (type == SDB_TYPE_DECIMAL)
443 || (type == SDB_TYPE_DATETIME)) {
444 if (errno || (str == endptr)) {
445 char errbuf[1024];
446 sdb_log(SDB_LOG_ERR, "core: Failed to parse string "
447 "'%s' as numeric value (type %i): %s", str, type,
448 sdb_strerror(errno, errbuf, sizeof(errbuf)));
449 return -1;
450 }
451 else if (endptr && (*endptr != '\0'))
452 sdb_log(SDB_LOG_WARNING, "core: Ignoring garbage after "
453 "number while parsing numeric value (type %i): %s.",
454 type, endptr);
455 }
457 if (data) {
458 *data = tmp;
459 data->type = type;
460 }
461 return 0;
462 } /* sdb_data_parse */
464 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */