Code

sysdb.h: Added helpers to compare two values.
[sysdb.git] / src / core / data.c
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)
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)
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)
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)
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 : */