Code

unixsock, data: Moved sdb_unixsock_parse_cell() to sdb_data_parse().
[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 "core/data.h"
33 #include "utils/error.h"
35 #include <errno.h>
37 #include <inttypes.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
43 /*
44  * public API
45  */
47 int
48 sdb_data_copy(sdb_data_t *dst, const sdb_data_t *src)
49 {
50         sdb_data_t tmp;
52         if ((! dst) || (! src))
53                 return -1;
55         tmp = *src;
56         switch (src->type) {
57                 case SDB_TYPE_STRING:
58                         tmp.data.string = strdup(src->data.string);
59                         if (! tmp.data.string)
60                                 return -1;
61                         break;
62                 case SDB_TYPE_BINARY:
63                         tmp.data.binary.datum = malloc(src->data.binary.length);
64                         if (! tmp.data.binary.datum)
65                                 return -1;
66                         memcpy(tmp.data.binary.datum, src->data.binary.datum,
67                                         src->data.binary.length);
68                         break;
69         }
71         sdb_data_free_datum(dst);
72         *dst = tmp;
73         return 0;
74 } /* sdb_data_copy */
76 void
77 sdb_data_free_datum(sdb_data_t *datum)
78 {
79         if (! datum)
80                 return;
82         switch (datum->type) {
83                 case SDB_TYPE_STRING:
84                         if (datum->data.string)
85                                 free(datum->data.string);
86                         datum->data.string = NULL;
87                         break;
88                 case SDB_TYPE_BINARY:
89                         if (datum->data.binary.datum)
90                                 free(datum->data.binary.datum);
91                         datum->data.binary.datum = NULL;
92                         datum->data.binary.length = 0;
93                         break;
94         }
95 } /* sdb_data_free_datum */
97 int
98 sdb_data_cmp(const sdb_data_t *d1, const sdb_data_t *d2)
99 {
100 #define CMP_NULL(a, b) \
101         do { \
102                 if (!(a) && !(b)) return 0; \
103                 if (!(a)) return -1; \
104                 if (!(b)) return 1; \
105         } while (0)
107         CMP_NULL(d1, d2);
109         if (d1->type != d2->type)
110                 return -1;
112 #define CMP(a, b) ((a) < (b) ? -1 : (a) > (b) ? 1 : 0)
113         switch (d1->type) {
114                 case SDB_TYPE_INTEGER:
115                         return CMP(d1->data.integer, d2->data.integer);
116                 case SDB_TYPE_DECIMAL:
117                         return CMP(d1->data.decimal, d2->data.decimal);
118                 case SDB_TYPE_STRING:
119                         CMP_NULL(d1->data.string, d2->data.string);
120                         return strcasecmp(d1->data.string, d2->data.string);
121                 case SDB_TYPE_DATETIME:
122                         return CMP(d1->data.datetime, d2->data.datetime);
123                 case SDB_TYPE_BINARY:
124                 {
125                         int diff;
127                         CMP_NULL(d1->data.binary.datum, d2->data.binary.datum);
129                         /* on a common prefix, the shorter datum sorts less */
130                         if (d1->data.binary.length < d2->data.binary.length) {
131                                 diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
132                                                 d1->data.binary.length);
133                                 diff = diff ? diff : -1;
134                         }
135                         else if (d1->data.binary.length > d2->data.binary.length) {
136                                 diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
137                                                 d2->data.binary.length);
138                                 diff = diff ? diff : 1;
139                         }
140                         else
141                                 diff = memcmp(d1->data.binary.datum, d2->data.binary.datum,
142                                                 d1->data.binary.length);
144                         return diff;
145                 }
146                 default:
147                         return -1;
148         }
149 #undef CMP
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 : */