Bugfixes
[rtl-433.git] / src / data.c
1 /*
2  * A general structure for extracting hierarchical data from the devices;
3  * typically key-value pairs, but allows for more rich data as well.
4  *
5  * Copyright (C) 2015 by Erkki Seppälä <flux@modeemi.fi>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <stdarg.h>
22 #include <assert.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdbool.h>
27
28 #include "data.h"
29
30 typedef void* (*array_elementwise_import_fn)(void*);
31 typedef void* (*array_element_release_fn)(void*);
32 typedef void* (*value_release_fn)(void*);
33
34 typedef struct {
35          /* what is the element size when put inside an array? */
36         int                         array_element_size;
37
38         /* is the element boxed (ie. behind a pointer) when inside an array?
39            if it's not boxed ("unboxed"), json dumping function needs to make
40            a copy of the value beforehand, because the dumping function only
41            deals with boxed values.
42          */
43         _Bool                       array_is_boxed;
44
45         /* function for importing arrays. strings are specially handled (as they
46            are copied deeply), whereas other arrays are just copied shallowly
47            (but copied nevertheles) */
48         array_elementwise_import_fn array_elementwise_import;
49
50         /* a function for releasing an element when put in an array; integers
51          * don't need to be released, while ie. strings and arrays do. */
52         array_element_release_fn    array_element_release;
53
54         /* a function for releasing a value. everything needs to be released. */
55         value_release_fn            value_release;
56 } data_meta_type_t;
57
58 struct data_printer_context;
59
60 typedef struct data_printer {
61         void (*print_data)(struct data_printer_context *printer_ctx, data_t *data, char *format, FILE *file);
62         void (*print_array)(struct data_printer_context *printer_ctx, data_array_t *data, char *format, FILE *file);
63         void (*print_string)(struct data_printer_context *printer_ctx, const char *data, char *format, FILE *file);
64         void (*print_double)(struct data_printer_context *printer_ctx, double data, char *format, FILE *file);
65         void (*print_int)(struct data_printer_context *printer_ctx, int data, char *format, FILE *file);
66 } data_printer_t;
67
68 typedef struct data_printer_context {
69         data_printer_t *printer;
70         void           *aux;
71 } data_printer_context_t;
72
73 static data_meta_type_t dmt[DATA_COUNT] = {
74         //  DATA_DATA
75         { .array_element_size       = sizeof(data_t*),
76           .array_is_boxed           = true,
77           .array_elementwise_import = NULL,
78           .array_element_release    = (array_element_release_fn) data_free,
79           .value_release            = (value_release_fn) data_free },
80
81         //  DATA_INT
82         { .array_element_size       = sizeof(int),
83           .array_is_boxed           = false,
84           .array_elementwise_import = NULL,
85           .array_element_release    = NULL,
86           .value_release            = (value_release_fn) free },
87
88         //  DATA_DOUBLE
89         { .array_element_size       = sizeof(double),
90           .array_is_boxed           = false,
91           .array_elementwise_import = NULL,
92           .array_element_release    = NULL,
93           .value_release            = (value_release_fn) free },
94
95         //  DATA_STRING
96         { .array_element_size       = sizeof(char*),
97           .array_is_boxed           = true,
98           .array_elementwise_import = (array_elementwise_import_fn) strdup,
99           .array_element_release    = (array_element_release_fn) free,
100           .value_release            = (value_release_fn) free },
101
102         //  DATA_ARRAY
103         { .array_element_size       = sizeof(data_array_t*),
104           .array_is_boxed           = true,
105           .array_elementwise_import = NULL,
106           .array_element_release    = (array_element_release_fn) data_array_free ,
107           .value_release            = (value_release_fn) data_array_free },
108 };
109
110 static void print_json_data(data_printer_context_t *printer_ctx, data_t *data, char *format, FILE *file);
111 static void print_json_array(data_printer_context_t *printer_ctx, data_array_t *data, char *format, FILE *file);
112 static void print_json_string(data_printer_context_t *printer_ctx, const char *data, char *format, FILE *file);
113 static void print_json_double(data_printer_context_t *printer_ctx, double data, char *format, FILE *file);
114 static void print_json_int(data_printer_context_t *printer_ctx, int data, char *format, FILE *file);
115
116 static void print_kv_data(data_printer_context_t *printer_ctx, data_t *data, char *format, FILE *file);
117 static void print_kv_string(data_printer_context_t *printer_ctx, const char *data, char *format, FILE *file);
118 static void print_kv_double(data_printer_context_t *printer_ctx, double data, char *format, FILE *file);
119 static void print_kv_int(data_printer_context_t *printer_ctx, int data, char *format, FILE *file);
120
121 typedef struct {
122         const char **fields;
123         int          data_recursion;
124         const char*  separator;
125 } data_csv_aux_t;
126
127 static void print_csv_data(data_printer_context_t *printer_ctx, data_t *data, char *format, FILE *file);
128 static void print_csv_string(data_printer_context_t *printer_ctx, const char *data, char *format, FILE *file);
129
130 data_printer_t data_json_printer = {
131         .print_data   = print_json_data,
132         .print_array  = print_json_array,
133         .print_string = print_json_string,
134         .print_double = print_json_double,
135         .print_int    = print_json_int
136 };
137
138 data_printer_t data_kv_printer = {
139         .print_data   = print_kv_data,
140         .print_array  = print_json_array,
141         .print_string = print_kv_string,
142         .print_double = print_kv_double,
143         .print_int    = print_kv_int
144 };
145
146 data_printer_t data_csv_printer = {
147         .print_data   = print_csv_data,
148         .print_array  = print_json_array,
149         .print_string = print_csv_string,
150         .print_double = print_json_double,
151         .print_int    = print_json_int
152 };
153
154 static _Bool import_values(void* dst, void* src, int num_values, data_type_t type) {
155         int element_size = dmt[type].array_element_size;
156         array_elementwise_import_fn import = dmt[type].array_elementwise_import;
157         if (import) {
158                 for (int i = 0; i < num_values; ++i) {
159                         void *copy = import(*(void**)(src + element_size * i));
160                         if (!copy) {
161                                 --i;
162                                 while (i >= 0) {
163                                         free(*(void**)(dst + element_size * i));
164                                         --i;
165                                 }
166                                 return false;
167                         } else {
168                                 *((char**) dst + i) = copy;
169                         }
170
171                 }
172         } else {
173                 memcpy(dst, src, element_size * num_values);
174         }
175         return true; // error is returned early
176 }
177
178 data_array_t *data_array(int num_values, data_type_t type, void *values) {
179         data_array_t *array = calloc(1, sizeof(data_array_t));
180         if (array) {
181                 int element_size = dmt[type].array_element_size;
182                 array->values = calloc(num_values, element_size);
183                 if (!array->values)
184                         goto alloc_error;
185                 if (!import_values(array->values, values, num_values, type))
186                         goto alloc_error;
187
188                 array->num_values = num_values;
189                 array->type = type;
190         }
191         return array;
192
193  alloc_error:
194         if (array)
195                 free(array->values);
196         free(array);
197         return NULL;
198 }
199
200 data_t *data_make(const char *key, const char *pretty_key, ...) {
201         va_list ap;
202         data_type_t type;
203         va_start(ap, pretty_key);
204
205         data_t *first = NULL;
206         data_t *prev = NULL;
207         char* format = false;
208         type = va_arg(ap, data_type_t);
209         do {
210                 data_t *current;
211                 void *value = NULL;
212
213                 switch (type) {
214                 case DATA_FORMAT : {
215                         format = strdup(va_arg(ap, char*));
216                         if (!format)
217                                 goto alloc_error;
218                         type = va_arg(ap, data_type_t);
219                         continue;
220                 } break;
221                 case DATA_COUNT  : {
222                         assert(0);
223                 } break;
224                 case DATA_DATA   : {
225                         value = va_arg(ap, data_t*);
226                 } break;
227                 case DATA_INT    : {
228                         value = malloc(sizeof(int));
229                         if (value)
230                                 *(int*) value = va_arg(ap, int);
231                 } break;
232                 case DATA_DOUBLE : {
233                         value = malloc(sizeof(double));
234                         if (value)
235                                 *(double*) value = va_arg(ap, double);
236                 } break;
237                 case DATA_STRING : {
238                         value = strdup(va_arg(ap, char*));
239                 } break;
240                 case DATA_ARRAY  : {
241                         value = va_arg(ap, data_t*);
242                 } break;
243                 }
244
245                 // also some null arguments are mapped to an alloc error;
246                 // that's ok, because they originate (typically..) from
247                 // an alloc error anyway
248                 if (!value)
249                         goto alloc_error;
250
251                 current = calloc(1, sizeof(*current));
252                 if (!current)
253                         goto alloc_error;
254                 if (prev)
255                         prev->next = current;
256
257                 current->key = strdup(key);
258                 if (!current->key)
259                         goto alloc_error;
260                 current->pretty_key = strdup(pretty_key ? pretty_key : key);
261                 if (!current->pretty_key)
262                         goto alloc_error;
263                 current->type = type;
264                 current->format = format;
265                 current->value = value;
266                 current->next = NULL;
267
268                 prev = current;
269                 if (!first)
270                         first = current;
271
272                 key = va_arg(ap, const char*);
273                 if (key) {
274                         pretty_key = va_arg(ap, const char*);
275                         type = va_arg(ap, data_type_t);
276                         format = NULL;
277                 }
278         } while (key);
279         va_end(ap);
280
281         return first;
282
283  alloc_error:
284         data_free(first);
285         va_end(ap);
286         return NULL;
287 }
288
289 void data_array_free(data_array_t *array) {
290         array_element_release_fn release = dmt[array->type].array_element_release;
291         if (release) {
292                 int element_size = dmt[array->type].array_element_size;
293                 for (int i = 0; i < array->num_values; ++i) 
294                         release(*(void**)(array->values + element_size * i));
295         }
296         free(array->values);
297         free(array);
298 }
299
300 void data_free(data_t *data) {
301         while (data) {
302                 data_t *prev_data = data;
303                 if (dmt[data->type].value_release)
304                         dmt[data->type].value_release(data->value);
305                 free(data->format);
306                 free(data->pretty_key);
307                 free(data->key);
308                 data = data->next;
309                 free(prev_data);
310         }
311 }
312
313 void data_print(data_t* data, FILE *file, data_printer_t *printer, void *aux)
314 {
315         data_printer_context_t ctx = {
316                 .printer = printer,
317                 .aux     = aux
318         };
319         ctx.printer->print_data(&ctx, data, NULL, file);
320         if (file) {
321                 fputc('\n', file);
322                 fflush(file);
323         }
324 }
325
326 static void print_value(data_printer_context_t *printer_ctx, FILE *file, data_type_t type, void *value, char *format) {
327         switch (type) {
328         case DATA_FORMAT :
329         case DATA_COUNT : {
330                 assert(0);
331         } break;
332         case DATA_DATA : {
333                 printer_ctx->printer->print_data(printer_ctx, value, format, file);
334         } break;
335         case DATA_INT : {
336                 printer_ctx->printer->print_int(printer_ctx, *(int*) value, format, file);
337         } break;
338         case DATA_DOUBLE : {
339                 printer_ctx->printer->print_double(printer_ctx, *(double*) value, format, file);
340         } break;
341         case DATA_STRING : {
342                 printer_ctx->printer->print_string(printer_ctx, value, format, file);
343         } break;
344         case DATA_ARRAY : {
345                 printer_ctx->printer->print_array(printer_ctx, value, format, file);
346         } break;
347         }
348 }
349
350 /* JSON printer */
351 static void print_json_array(data_printer_context_t *printer_ctx, data_array_t *array, char *format, FILE *file) {
352         int element_size = dmt[array->type].array_element_size;
353         char buffer[element_size];
354         fprintf(file, "[");
355         for (int c = 0; c < array->num_values; ++c) {
356                 if (c)
357                         fprintf(file, ", ");
358                 if (!dmt[array->type].array_is_boxed) {
359                         memcpy(buffer, (void**)(array->values + element_size * c), element_size);
360                         print_value(printer_ctx, file, array->type, buffer, format);
361                 } else {
362                         print_value(printer_ctx, file, array->type, *(void**)(array->values + element_size * c), format);
363                 }
364         }
365         fprintf(file, "]");
366 }
367
368 static void print_json_data(data_printer_context_t *printer_ctx, data_t *data, char *format, FILE *file)
369 {
370         _Bool separator = false;
371         fputc('{', file);
372         while (data) {
373                 if (separator)
374                         fprintf(file, ", ");
375                 printer_ctx->printer->print_string(printer_ctx, data->key, NULL, file);
376                 fprintf(file, " : ");
377                 print_value(printer_ctx, file, data->type, data->value, data->format);
378                 separator = true;
379                 data = data->next;
380         }
381         fputc('}', file);
382 }
383
384 static void print_json_string(data_printer_context_t *printer_ctx, const char *str, char *format, FILE *file) {
385         fprintf(file, "\"");
386         while (*str) {
387                 if (*str == '"')
388                         fputc('\\', file);
389                 fputc(*str, file);
390                 ++str;
391         }
392         fprintf(file, "\"");
393 }
394
395 static void print_json_double(data_printer_context_t *printer_ctx, double data, char *format, FILE *file)
396 {
397         fprintf(file, "%.3f", data);
398 }
399
400 static void print_json_int(data_printer_context_t *printer_ctx, int data, char *format, FILE *file)
401 {
402         fprintf(file, "%d", data);
403 }
404
405 /* Key-Value printer */
406 static void print_kv_data(data_printer_context_t *printer_ctx, data_t *data, char *format, FILE *file)
407 {
408         _Bool separator = false;
409         _Bool was_labeled = false;
410         _Bool written_title = false;
411         while (data) {
412                 _Bool labeled = data->pretty_key[0];
413                 /* put a : between the first non-labeled and labeled */
414                 if (separator) {
415                         if (labeled && !was_labeled && !written_title) {
416                                 fprintf(file, "\n");
417                                 written_title = true;
418                                 separator = false;
419                         } else {
420                                 if (was_labeled)
421                                         fprintf(file, "\n");
422                                 else
423                                         fprintf(file, " ");
424                         }
425                 }
426                 if (!strcmp(data->key, "time"))
427                     /* fprintf(file, "") */ ;
428                 else if (!strcmp(data->key, "model"))
429                     fprintf(file, ":\t");
430                 else
431                     fprintf(file, "\t%s:\t", data->pretty_key);
432                 if (labeled)
433                         fputc(' ', file);
434                 print_value(printer_ctx, file, data->type, data->value, data->format);
435                 separator = true;
436                 was_labeled = labeled;
437                 data = data->next;
438         }
439 }
440
441 static void print_kv_double(data_printer_context_t *printer_ctx, double data, char *format, FILE *file)
442 {
443         fprintf(file, format ? format : "%.3f", data);
444 }
445
446 static void print_kv_int(data_printer_context_t *printer_ctx, int data, char *format, FILE *file)
447 {
448         fprintf(file, format ? format : "%d", data);
449 }
450
451
452 static void print_kv_string(data_printer_context_t *printer_ctx, const char *data, char *format, FILE *file)
453 {
454         fprintf(file, format ? format : "%s", data);
455 }
456
457 /* CSV printer; doesn't really support recursive data objects yes */
458 static void print_csv_data(data_printer_context_t *printer_ctx, data_t *data, char *format, FILE *file)
459 {
460         data_csv_aux_t *csv = printer_ctx->aux;
461         const char **fields = csv->fields;
462         int i;
463
464         if (csv->data_recursion)
465                 return;
466
467         ++csv->data_recursion;
468         for (i = 0; fields[i]; ++i) {
469                 const char *key = fields[i];
470                 data_t *found = NULL;
471                 if (i) fprintf(file, "%s", csv->separator);
472                 for (data_t *iter = data; !found && iter; iter = iter->next)
473                         if (strcmp(iter->key, key) == 0)
474                                 found = iter;
475
476                 if (found)
477                         print_value(printer_ctx, file, found->type, found->value, found->format);
478         }
479         --csv->data_recursion;
480 }
481
482 static void print_csv_string(data_printer_context_t *printer_ctx, const char *str, char *format, FILE *file)
483 {
484         data_csv_aux_t *csv = printer_ctx->aux;
485         while (*str) {
486                 if (strncmp(str,  csv->separator, strlen(csv->separator)) == 0)
487                         fputc('\\', file);
488                 fputc(*str, file);
489                 ++str;
490         }
491 }
492
493 static int compare_strings(char** a, char** b)
494 {
495     return strcmp(*a, *b);
496 }
497
498 void *data_csv_init(const char **fields, int num_fields)
499 {
500         data_csv_aux_t *csv = calloc(1, sizeof(data_csv_aux_t));
501         int csv_fields = 0;
502         int i, j;
503         const char **allowed = NULL;
504         int *use_count = NULL;
505         int num_unique_fields;
506         if (!csv)
507                 goto alloc_error;
508
509         csv->separator = ",";
510
511         allowed = calloc(num_fields, sizeof(const char*));
512         memcpy(allowed, fields, sizeof(const char*) * num_fields);
513
514         qsort(allowed, num_fields, sizeof(char*), (void*) compare_strings);
515
516         // overwrite duplicates
517         i = 0;
518         j = 0;
519         while (j < num_fields) {
520                 while (j > 0 && j < num_fields &&
521                        strcmp(allowed[j - 1], allowed[j]) == 0)
522                         ++j;
523
524                 if (j < num_fields) {
525                         allowed[i] = allowed[j];
526                         ++i;
527                         ++j;
528
529                 }
530         }
531         num_unique_fields = i;
532
533         csv->fields = calloc(num_unique_fields + 1, sizeof(const char**));
534         if (!csv->fields)
535                 goto alloc_error;
536
537         use_count = calloc(num_unique_fields, sizeof(*use_count));
538         if (!use_count)
539                 goto alloc_error;
540
541         for (i = 0; i < num_fields; ++i) {
542                 const char **field = bsearch(&fields[i], allowed, num_unique_fields, sizeof(const char*),
543                                              (void*) compare_strings);
544                 int *field_use_count = use_count + (field - allowed);
545                 if (field && !*field_use_count) {
546                         csv->fields[csv_fields] = fields[i];
547                         ++csv_fields;
548                         ++*field_use_count;
549                 }
550         }
551         csv->fields[csv_fields] = NULL;
552         free(allowed);
553         free(use_count);
554
555         // Output the CSV header
556         for (i = 0; csv->fields[i]; ++i) {
557                 printf("%s%s", i > 0 ? csv->separator : "", csv->fields[i]);
558         }
559         printf("\n");
560         return csv;
561
562  alloc_error:
563         free(use_count);
564         free(allowed);
565         if (csv) free(csv->fields);
566         free(csv);
567         return NULL;
568 }
569
570 void data_csv_free(void *aux)
571 {
572         data_csv_aux_t *csv = aux;
573         free(csv->fields);
574         free(csv);
575 }