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 /*
46 * public API
47 */
49 int
50 sdb_data_copy(sdb_data_t *dst, const sdb_data_t *src)
51 {
52 sdb_data_t tmp;
54 if ((! dst) || (! src))
55 return -1;
57 tmp = *src;
58 switch (src->type) {
59 case SDB_TYPE_STRING:
60 tmp.data.string = strdup(src->data.string);
61 if (! tmp.data.string)
62 return -1;
63 break;
64 case SDB_TYPE_BINARY:
65 tmp.data.binary.datum = malloc(src->data.binary.length);
66 if (! tmp.data.binary.datum)
67 return -1;
68 memcpy(tmp.data.binary.datum, src->data.binary.datum,
69 src->data.binary.length);
70 break;
71 }
73 sdb_data_free_datum(dst);
74 *dst = tmp;
75 return 0;
76 } /* sdb_data_copy */
78 void
79 sdb_data_free_datum(sdb_data_t *datum)
80 {
81 if (! datum)
82 return;
84 switch (datum->type) {
85 case SDB_TYPE_STRING:
86 if (datum->data.string)
87 free(datum->data.string);
88 datum->data.string = NULL;
89 break;
90 case SDB_TYPE_BINARY:
91 if (datum->data.binary.datum)
92 free(datum->data.binary.datum);
93 datum->data.binary.datum = NULL;
94 datum->data.binary.length = 0;
95 break;
96 }
97 } /* sdb_data_free_datum */
99 int
100 sdb_data_cmp(const sdb_data_t *d1, const sdb_data_t *d2)
101 {
102 #define CMP_NULL(a, b) \
103 do { \
104 if (!(a) && !(b)) return 0; \
105 if (!(a)) return -1; \
106 if (!(b)) return 1; \
107 } while (0)
109 CMP_NULL(d1, d2);
111 if (d1->type != d2->type)
112 return -1;
114 switch (d1->type) {
115 case SDB_TYPE_INTEGER:
116 return SDB_CMP(d1->data.integer, d2->data.integer);
117 case SDB_TYPE_DECIMAL:
118 return SDB_CMP(d1->data.decimal, d2->data.decimal);
119 case SDB_TYPE_STRING:
120 CMP_NULL(d1->data.string, d2->data.string);
121 return strcasecmp(d1->data.string, d2->data.string);
122 case SDB_TYPE_DATETIME:
123 return SDB_CMP(d1->data.datetime, d2->data.datetime);
124 case SDB_TYPE_BINARY:
125 {
126 int diff;
128 CMP_NULL(d1->data.binary.datum, d2->data.binary.datum);
130 /* on a common prefix, the shorter datum sorts less */
131 if (d1->data.binary.length < d2->data.binary.length) {
132 diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
133 d1->data.binary.length);
134 diff = diff ? diff : -1;
135 }
136 else if (d1->data.binary.length > d2->data.binary.length) {
137 diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
138 d2->data.binary.length);
139 diff = diff ? diff : 1;
140 }
141 else
142 diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
143 d1->data.binary.length);
145 return diff;
146 }
147 default:
148 return -1;
149 }
150 #undef CMP_NULL
151 } /* sdb_data_cmp */
153 size_t
154 sdb_data_strlen(const sdb_data_t *datum)
155 {
156 if (! datum)
157 return 0;
159 switch (datum->type) {
160 case SDB_TYPE_INTEGER:
161 /* log(64) */
162 return 20;
163 case SDB_TYPE_DECIMAL:
164 /* XXX: -0xN.NNNNNNp+NNN */
165 return 42;
166 case SDB_TYPE_STRING:
167 if (! datum->data.string)
168 return 6; /* "NULL" */
169 /* in the worst case, each character needs to be escaped */
170 return 2 * strlen(datum->data.string) + 2;
171 case SDB_TYPE_DATETIME:
172 /* "YYYY-MM-DD HH:MM:SS +zzzz" */
173 return 27;
174 case SDB_TYPE_BINARY:
175 /* "\xNN" */
176 return 4 * datum->data.binary.length + 2;
177 }
178 return 0;
179 } /* sdb_data_strlen */
181 int
182 sdb_data_format(const sdb_data_t *datum, char *buf, size_t buflen, int quoted)
183 {
184 char tmp[sdb_data_strlen(datum) + 1];
185 char *data = NULL;
186 int ret = -1;
188 size_t i, pos;
190 if ((! datum) || (! buf))
191 return -1;
193 switch (datum->type) {
194 case SDB_TYPE_INTEGER:
195 ret = snprintf(buf, buflen, "%"PRIi64, datum->data.integer);
196 break;
197 case SDB_TYPE_DECIMAL:
198 ret = snprintf(buf, buflen, "%a", datum->data.decimal);
199 break;
200 case SDB_TYPE_STRING:
201 if (! datum->data.string)
202 data = "NULL";
203 else {
204 pos = 0;
205 for (i = 0; i < strlen(datum->data.string); ++i) {
206 char byte = datum->data.string[i];
208 if ((byte == '\\') || (byte == '"')) {
209 tmp[pos] = '\\';
210 ++pos;
211 }
212 tmp[pos] = byte;
213 ++pos;
214 }
215 tmp[pos] = '\0';
216 data = tmp;
217 }
218 break;
219 case SDB_TYPE_DATETIME:
220 if (! sdb_strftime(tmp, sizeof(tmp), "%F %T %z",
221 datum->data.datetime))
222 return -1;
223 tmp[sizeof(tmp) - 1] = '\0';
224 data = tmp;
225 break;
226 case SDB_TYPE_BINARY:
227 pos = 0;
228 for (i = 0; i < datum->data.binary.length; ++i) {
229 int byte = (int)datum->data.binary.datum[i];
230 char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
231 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
233 tmp[pos] = '\\';
234 tmp[pos + 1] = 'x';
235 pos += 2;
237 if (byte > 0xf) {
238 tmp[pos] = hex[byte >> 4];
239 ++pos;
240 }
241 tmp[pos] = hex[byte & 0xf];
242 ++pos;
243 }
244 tmp[pos] = '\0';
245 data = tmp;
246 break;
247 }
249 if (data) {
250 if (quoted == SDB_UNQUOTED)
251 ret = snprintf(buf, buflen, "%s", data);
252 else if (quoted == SDB_SINGLE_QUOTED)
253 ret = snprintf(buf, buflen, "'%s'", data);
254 else
255 ret = snprintf(buf, buflen, "\"%s\"", data);
256 }
257 buf[buflen - 1] = '\0';
258 return ret;
259 } /* sdb_data_format */
261 int
262 sdb_data_parse(char *str, int type, sdb_data_t *data)
263 {
264 sdb_data_t tmp;
266 char *endptr = NULL;
268 errno = 0;
269 switch (type) {
270 case SDB_TYPE_INTEGER:
271 tmp.data.integer = strtoll(str, &endptr, 0);
272 break;
273 case SDB_TYPE_DECIMAL:
274 tmp.data.decimal = strtod(str, &endptr);
275 break;
276 case SDB_TYPE_STRING:
277 tmp.data.string = str;
278 break;
279 case SDB_TYPE_DATETIME:
280 {
281 double datetime = strtod(str, &endptr);
282 tmp.data.datetime = DOUBLE_TO_SDB_TIME(datetime);
283 }
284 break;
285 case SDB_TYPE_BINARY:
286 /* we don't support any binary information containing 0-bytes */
287 tmp.data.binary.length = strlen(str);
288 tmp.data.binary.datum = (unsigned char *)str;
289 break;
290 default:
291 errno = EINVAL;
292 return -1;
293 }
295 if ((type == SDB_TYPE_INTEGER) || (type == SDB_TYPE_DECIMAL)
296 || (type == SDB_TYPE_DATETIME)) {
297 if (errno || (str == endptr)) {
298 char errbuf[1024];
299 sdb_log(SDB_LOG_ERR, "core: Failed to parse string "
300 "'%s' as numeric value (type %i): %s", str, type,
301 sdb_strerror(errno, errbuf, sizeof(errbuf)));
302 return -1;
303 }
304 else if (endptr && (*endptr != '\0'))
305 sdb_log(SDB_LOG_WARNING, "core: Ignoring garbage after "
306 "number while parsing numeric value (type %i): %s.",
307 type, endptr);
308 }
310 if (data) {
311 *data = tmp;
312 data->type = type;
313 }
314 return 0;
315 } /* sdb_data_parse */
317 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */