libacfutils
A general purpose library of utility functions designed to make it easier to develop addons for the X-Plane flight simulator.
Loading...
Searching...
No Matches
conf.c
1/*
2 * CDDL HEADER START
3 *
4 * This file and its contents are supplied under the terms of the
5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 * You may only use this file in accordance with the terms of version
7 * 1.0 of the CDDL.
8 *
9 * A full copy of the text of the CDDL should have accompanied this
10 * source. A copy of the CDDL is also available via the Internet at
11 * http://www.illumos.org/license/CDDL.
12 *
13 * CDDL HEADER END
14 */
15/*
16 * Copyright 2023 Saso Kiselkov. All rights reserved.
17 */
18
19#include <ctype.h>
20#include <errno.h>
21#include <stddef.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <stdarg.h>
26#include <zlib.h>
27
28#include <curl/curl.h>
29
30#include "acfutils/assert.h"
31#include "acfutils/avl.h"
32#include "acfutils/base64.h"
33#include "acfutils/conf.h"
34#include "acfutils/helpers.h"
35#include "acfutils/log.h"
36#include "acfutils/safe_alloc.h"
37
38/* For conf_get_da and conf_set_da. */
39CTASSERT(sizeof (double) == sizeof (unsigned long long));
40
41struct conf {
42 avl_tree_t tree;
43};
44
45typedef enum {
46 CONF_KEY_STR,
47 CONF_KEY_DATA
48} conf_key_type_t;
49
50typedef struct {
51 char *key;
52 conf_key_type_t type;
53 union {
54 char *str;
55 struct {
56 void *buf;
57 size_t sz;
58 } data;
59 };
60 avl_node_t node;
62
63static void conf_set_common(conf_t *conf, const char *key,
64 const char *fmt, ...) PRINTF_ATTR(3);
65static int conf_write_impl(const conf_t *conf, void *fp, size_t bufsz,
66 bool_t compressed, bool_t is_buf);
67
68static int
69conf_key_compar(const void *a, const void *b)
70{
71 const conf_key_t *ka = a, *kb = b;
72 int c = strcmp(ka->key, kb->key);
73 if (c < 0)
74 return (-1);
75 else if (c == 0)
76 return (0);
77 else
78 return (1);
79}
80
86conf_t *
88{
89 conf_t *conf = safe_calloc(1, sizeof (*conf));
90 avl_create(&conf->tree, conf_key_compar, sizeof (conf_key_t),
91 offsetof(conf_key_t, node));
92 return (conf);
93}
94
99conf_t *
100conf_create_copy(const conf_t *conf2)
101{
102 conf_t *conf = conf_create_empty();
103
104 ASSERT(conf2 != NULL);
105 conf_merge(conf2, conf);
106
107 return (conf);
108}
109
118void
119conf_merge(const conf_t *conf_from, conf_t *conf_to)
120{
121 ASSERT(conf_from != NULL);
122 ASSERT(conf_to != NULL);
123
124 for (const conf_key_t *key = avl_first(&conf_from->tree); key != NULL;
125 key = AVL_NEXT(&conf_from->tree, key)) {
126 switch (key->type) {
127 case CONF_KEY_STR:
128 conf_set_str(conf_to, key->key, key->str);
129 break;
130 case CONF_KEY_DATA:
131 conf_set_data(conf_to, key->key, key->data.buf,
132 key->data.sz);
133 break;
134 }
135 }
136}
137
141void
143{
144 void *cookie = NULL;
145 conf_key_t *ck;
146
147 while ((ck = avl_destroy_nodes(&conf->tree, &cookie)) != NULL) {
148 free(ck->key);
149 switch (ck->type) {
150 case CONF_KEY_STR:
151 free(ck->str);
152 break;
153 case CONF_KEY_DATA:
154 free(ck->data.buf);
155 break;
156 default:
157 VERIFY(0);
158 }
159 free(ck);
160 }
161 avl_destroy(&conf->tree);
162 free(conf);
163}
164
173conf_t *
174conf_read_file(const char *filename, int *errline)
175{
176 uint8_t gz_magic[2];
177 FILE *fp = fopen(filename, "rb");
178 conf_t *conf;
179
180 if (fp == NULL) {
181 if (errline != NULL)
182 *errline = -1;
183 return (NULL);
184 }
185 /*
186 * We automatically detect the 16-bit Gzip magic header. We know
187 * a valid conf file will never contain this header unless it really
188 * is Gzip compressed.
189 */
190 if (fread(gz_magic, 1, sizeof (gz_magic), fp) != sizeof (gz_magic)) {
191 if (errline != NULL)
192 *errline = -1;
193 fclose(fp);
194 return (NULL);
195 }
196 rewind(fp);
197 if (gz_magic[0] == 0x1f && gz_magic[1] == 0x8b) {
198 gzFile gz_fp = gzopen(filename, "r");
199
200 fclose(fp);
201 if (gz_fp == NULL) {
202 if (errline != NULL)
203 *errline = -1;
204 return (NULL);
205 }
206 conf = conf_read2(gz_fp, errline, B_TRUE);
207 gzclose(gz_fp);
208 } else {
209 conf = conf_read2(fp, errline, B_FALSE);
210 fclose(fp);
211 }
212
213 return (conf);
214}
215
216static inline void
217ck_free_value(conf_key_t *ck)
218{
219 ASSERT(ck != NULL);
220 switch (ck->type) {
221 case CONF_KEY_STR:
222 free(ck->str);
223 ck->str = NULL;
224 break;
225 case CONF_KEY_DATA:
226 free(ck->data.buf);
227 memset(&ck->data, 0, sizeof (ck->data));
228 break;
229 default:
230 VERIFY(0);
231 }
232}
233
245conf_t *
246conf_read(FILE *fp, int *errline)
247{
248 return (conf_read2(fp, errline, B_FALSE));
249}
250
251static bool_t
252conf_parse_line(char *line, conf_t *conf)
253{
254 char *sep;
255 conf_key_t srch;
256 conf_key_t *ck;
257 avl_index_t where;
258 conf_key_type_t type;
259 bool unescape = false;
260
261 ASSERT(line != NULL);
262 ASSERT(conf != NULL);
263
264 sep = strstr(line, "`");
265 if (sep != NULL) {
266 type = CONF_KEY_DATA;
267 sep[0] = '\0';
268 } else {
269 sep = strstr(line, "%=");
270 if (sep != NULL) {
271 type = CONF_KEY_STR;
272 sep[0] = '\0';
273 sep++;
274 sep[0] = '\0';
275 unescape = true;
276 } else {
277 sep = strstr(line, "=");
278 if (sep != NULL) {
279 sep[0] = '\0';
280 type = CONF_KEY_STR;
281 } else {
282 return (B_FALSE);
283 }
284 }
285 }
286 strip_space(line);
287 strip_space(&sep[1]);
288
289 srch.key = safe_malloc(strlen(line) + 1);
290 srch.type = type;
291 strcpy(srch.key, line);
292 strtolower(srch.key); /* keys are case-insensitive */
293 ck = avl_find(&conf->tree, &srch, &where);
294 if (ck == NULL) {
295 /* if the key didn't exist yet, create a new one */
296 ck = safe_calloc(1, sizeof (*ck));
297 ck->key = srch.key;
298 avl_insert(&conf->tree, ck, where);
299 } else {
300 /* key already exists, free the search one */
301 free(srch.key);
302 }
303 ck_free_value(ck);
304 ck->type = type;
305 if (type == CONF_KEY_STR) {
306 ck->str = safe_malloc(strlen(&sep[1]) + 1);
307 strcpy(ck->str, &sep[1]);
308 if (unescape)
309 unescape_percent(ck->str);
310 } else {
311 size_t l = strlen(&sep[1]);
312 ssize_t sz_est = BASE64_DEC_SIZE(l);
313 ssize_t sz_dec;
314 ck->data.buf = safe_malloc(sz_est);
315 sz_dec = lacf_base64_decode((const uint8_t *)&sep[1],
316 l, ck->data.buf);
317 if (sz_dec <= 0)
318 return (B_FALSE);
319 ck->data.sz = sz_dec;
320 }
321 return (B_TRUE);
322}
323
334conf_t *
335conf_read2(void *fp, int *errline, bool_t compressed)
336{
337 conf_t *conf;
338 char *line = NULL;
339 size_t linecap = 0;
340 unsigned linenum = 0;
341 ASSERT(fp != NULL);
342 FILE *f_fp = compressed ? NULL : fp;
343 gzFile gz_fp = compressed ? fp : NULL;
344
346 while (compressed ? !gzeof(gz_fp) : !feof(f_fp)) {
347 if (compressed) {
348 if (parser_get_next_gzline(gz_fp, &line, &linecap,
349 &linenum) <= 0) {
350 break;
351 }
352 } else {
353 if (parser_get_next_line(f_fp, &line, &linecap,
354 &linenum) <= 0) {
355 break;
356 }
357 }
358 if (!conf_parse_line(line, conf))
359 goto errout;
360 }
361 free(line);
362 return (conf);
363errout:
364 free(line);
366 if (errline != NULL)
367 *errline = linenum;
368 return (NULL);
369}
370
384conf_t *
385conf_read_buf(const void *buf, size_t cap, int *errline)
386{
387 uint8_t *tmpbuf = NULL;
388 const char *instr;
389 conf_t *conf = conf_create_empty();
390 size_t n_lines;
391 char **lines;
392
393 ASSERT(buf != NULL);
394 ASSERT(cap != 0);
395 /*
396 * Check if the input is NUL-terminated. If not, copy it into
397 * tmpbuf and place a NUL byte at the end manually.
398 */
399 if (((const uint8_t *)buf)[cap - 1] == '\0') {
400 instr = buf;
401 } else {
402 tmpbuf = safe_malloc(cap + 1);
403 memcpy(tmpbuf, buf, cap);
404 tmpbuf[cap] = '\0';
405 instr = (char *)tmpbuf;
406 }
407 /*
408 * Split at line breaks. If the file uses \r\n line terminators,
409 * a strip_space of each line gets rid of the trailing \r.
410 */
411 lines = strsplit(instr, "\n", true, &n_lines);
412 for (size_t i = 0; i < n_lines; i++) {
413 char *comment;
414 /*
415 * Strip away comments
416 */
417 comment = strchr(lines[i], '#');
418 if (comment != NULL)
419 *comment = '\0';
420 comment = strstr(lines[i], "--");
421 if (comment != NULL)
422 *comment = '\0';
423 strip_space(lines[i]);
424 if (lines[i][0] != '\0') {
425 if (!conf_parse_line(lines[i], conf)) {
426 if (errline != NULL)
427 *errline = i + 1;
428 goto errout;
429 }
430 }
431 }
432 free_strlist(lines, n_lines);
433 if (tmpbuf != NULL)
434 free(tmpbuf);
435 return (conf);
436errout:
437 free_strlist(lines, n_lines);
438 if (tmpbuf != NULL)
439 free(tmpbuf);
441 return (NULL);
442}
443
448bool_t
449conf_write_file(const conf_t *conf, const char *filename)
450{
451 return (conf_write_file2(conf, filename, B_FALSE));
452}
453
458bool_t
459conf_write_file2(const conf_t *conf, const char *filename, bool_t compressed)
460{
461 ssize_t res;
462 char *filename_tmp;
463 int rename_err = 0;
464
465 ASSERT(conf != NULL);
466 ASSERT(filename != NULL);
467 /*
468 * Initially we write the file into a .tmp temporary file on the side.
469 * We when atomically replace the target file to avoid the possibility
470 * of writing an incomplete file.
471 */
472 filename_tmp = sprintf_alloc("%s.tmp", filename);
473
474 if (!compressed) {
475 FILE *fp = fopen(filename_tmp, "wb");
476
477 if (fp == NULL) {
478 free(filename_tmp);
479 return (B_FALSE);
480 }
481 res = (conf_write_impl(conf, fp, 0, B_FALSE, B_FALSE) >= 0);
482 fclose(fp);
483 } else {
484 gzFile fp = gzopen(filename_tmp, "w");
485
486 if (fp == NULL) {
487 free(filename_tmp);
488 return (B_FALSE);
489 }
490 res = (conf_write_impl(conf, fp, 0, B_TRUE, B_FALSE) >= 0);
491 gzclose(fp);
492 }
493 if (res) {
494#if IBM
495 /*
496 * Windows needs special handling, because it doesn't let us
497 * use rename for the replace operation.
498 */
499 if (file_exists(filename, NULL)) {
500 if (!ReplaceFileA(filename, filename_tmp, NULL,
501 REPLACEFILE_IGNORE_MERGE_ERRORS |
502 REPLACEFILE_IGNORE_ACL_ERRORS, NULL, NULL)) {
503 win_perror(GetLastError(), "Error writing %s: "
504 "ReplaceFile failed", filename);
505 }
506 } else {
507 rename_err = rename(filename_tmp, filename);
508 }
509#else /* !IBM */
510 rename_err = rename(filename_tmp, filename);
511#endif /* !IBM */
512 if (rename_err != 0) {
513 logMsg("Error writing %s: atomic rename failed: %s",
514 filename, strerror(errno));
515 res = B_FALSE;
516 }
517 }
518 free(filename_tmp);
519
520 return (res);
521}
522
542size_t
543conf_write_buf(const conf_t *conf, void *buf, size_t cap)
544{
545 int res = conf_write_impl(conf, buf, cap, B_FALSE, B_TRUE);
546 // buffer writing must never fail, as it does no I/O
547 ASSERT3S(res, >=, 0);
548 return ((size_t)res);
549}
550
551static bool_t
552needs_escape(const char *str)
553{
554 ASSERT(str != NULL);
555
556 for (unsigned i = 0, n = strlen(str); i < n; i++) {
557 if ((i == 0 && isspace(str[i])) ||
558 (i + 1 == n && isspace(str[i]))) {
559 /* String begins and ends in whitespace */
560 return (true);
561 }
562 if (str[i] < 32 || str[i] == '#' || str[i] == '%' ||
563 str[i] == '`') {
564 /* String contains chars that could confuse us */
565 return (true);
566 }
567 }
568 return (false);
569}
570
571static int
572conf_write_impl(const conf_t *conf, void *fp, size_t bufsz,
573 bool_t compressed, bool_t is_buf)
574{
575#define SNPRINTF_ADV(...) \
576 do { \
577 int req_here = snprintf(buf, (fp + bufsz) - buf, __VA_ARGS__); \
578 ASSERT3S(req_here, >=, 0); \
579 req_total += req_here; \
580 buf = MIN(buf + req_here, fp + bufsz); \
581 } while (0)
582
583 char *data_buf = NULL;
584 size_t cap = 0;
585 ASSERT(fp != NULL || (is_buf && bufsz == 0));
586 void *buf = (is_buf ? fp : NULL);
587 FILE *f_fp = compressed ? NULL : fp;
588 gzFile gz_fp = compressed ? fp : NULL;
589 size_t req_total = 0;
590 /* This is only used for generating escape sequences */
591 CURL *curl = curl_easy_init();
592 ASSERT(curl != NULL);
593
594 ASSERT(conf != NULL);
595
596 if (!compressed && !is_buf &&
597 fprintf(f_fp, "# libacfutils configuration file - "
598 "DO NOT EDIT!\n") < 0) {
599 goto errout;
600 }
601 for (conf_key_t *ck = avl_first(&conf->tree); ck != NULL;
602 ck = AVL_NEXT(&conf->tree, ck)) {
603 switch (ck->type) {
604 case CONF_KEY_STR:
605 if (!needs_escape(ck->str)) {
606 if (is_buf) {
607 SNPRINTF_ADV("%s = %s\n",
608 ck->key, ck->str);
609 } else if ((compressed ?
610 gzprintf(gz_fp, "%s = %s\n", ck->key,
611 ck->str) < 0 :
612 fprintf(f_fp, "%s = %s\n", ck->key,
613 ck->str) < 0)) {
614 goto errout;
615 }
616 } else {
617 char *str = curl_easy_escape(curl, ck->str, 0);
618 if (is_buf) {
619 SNPRINTF_ADV("%s %%= %s\n",
620 ck->key, str);
621 } else if ((compressed ?
622 gzprintf(gz_fp, "%s %%= %s\n", ck->key,
623 str) < 0 :
624 fprintf(f_fp, "%s %%= %s\n", ck->key,
625 str) < 0)) {
626 curl_free(str);
627 goto errout;
628 }
629 curl_free(str);
630 }
631 break;
632 case CONF_KEY_DATA: {
633 size_t req = BASE64_ENC_SIZE(ck->data.sz);
634 size_t act;
635 if (req > cap) {
636 free(data_buf);
637 cap = req;
638 data_buf = safe_malloc(cap + 1);
639 }
640 act = lacf_base64_encode(ck->data.buf, ck->data.sz,
641 (uint8_t*)data_buf);
642 data_buf[act] = '\0';
643 if (is_buf) {
644 SNPRINTF_ADV("%s`%s\n", ck->key, data_buf);
645 } else if (compressed) {
646 gzwrite(gz_fp, ck->key, strlen(ck->key));
647 gzwrite(gz_fp, "`", 1);
648 gzwrite(gz_fp, data_buf, strlen(data_buf));
649 gzwrite(gz_fp, "\n", 1);
650 } else {
651 fprintf(f_fp, "%s`%s\n", ck->key, data_buf);
652 }
653 break;
654 }
655 default:
656 VERIFY(0);
657 }
658 }
659 free(data_buf);
660 curl_easy_cleanup(curl);
661 /* Add room for the terminating NUL byte */
662 if (is_buf)
663 req_total++;
664 return (req_total);
665errout:
666 free(data_buf);
667 curl_easy_cleanup(curl);
668 return (-1);
669#undef SNPRINTF_ADV
670}
671
676bool_t
677conf_write(const conf_t *conf, FILE *fp)
678{
679 return (conf_write_impl(conf, fp, 0, B_FALSE, B_FALSE));
680}
681
687static conf_key_t *
688conf_find(const conf_t *conf, const char *key, avl_index_t *where)
689{
690 char *buf = safe_strdup(key);
691 strtolower(buf);
692 const conf_key_t srch = { .key = buf };
693 conf_key_t *result = avl_find(&conf->tree, &srch, where);
694 free(buf);
695 return (result);
696}
697
703bool_t
704conf_get_str(const conf_t *conf, const char *key, const char **value)
705{
706 const conf_key_t *ck;
707
708 ASSERT(conf != NULL);
709 ASSERT(key != NULL);
710 ASSERT(value != NULL);
711 ck = conf_find(conf, key, NULL);
712 if (ck == NULL || ck->type != CONF_KEY_STR)
713 return (B_FALSE);
714 *value = ck->str;
715 return (B_TRUE);
716}
717
718/*
719 * Retrieves the 32-bit int value of a configuration key. If found, the value
720 * is placed in `value`.
721 * @return B_TRUE if the key was found, else B_FALSE.
722 */
723bool_t
724conf_get_i(const conf_t *conf, const char *key, int *value)
725{
726 const conf_key_t *ck;
727
728 ASSERT(conf != NULL);
729 ASSERT(key != NULL);
730 ASSERT(value != NULL);
731 ck = conf_find(conf, key, NULL);
732 if (ck == NULL || ck->type != CONF_KEY_STR)
733 return (B_FALSE);
734 *value = atoi(ck->str);
735 return (B_TRUE);
736}
737
743bool_t
744conf_get_lli(const conf_t *conf, const char *key, long long *value)
745{
746 const conf_key_t *ck;
747
748 ASSERT(conf != NULL);
749 ASSERT(key != NULL);
750 ASSERT(value != NULL);
751 ck = conf_find(conf, key, NULL);
752 if (ck == NULL || ck->type != CONF_KEY_STR)
753 return (B_FALSE);
754 *value = atoll(ck->str);
755 return (B_TRUE);
756}
757
763bool_t
764conf_get_d(const conf_t *conf, const char *key, double *value)
765{
766 const conf_key_t *ck;
767
768 ASSERT(conf != NULL);
769 ASSERT(key != NULL);
770 ASSERT(value != NULL);
771 ck = conf_find(conf, key, NULL);
772 if (ck == NULL || ck->type != CONF_KEY_STR)
773 return (B_FALSE);
774 if (strcmp(ck->str, "nan") == 0) {
775 *value = NAN;
776 return (true);
777 } else {
778 return (sscanf(ck->str, "%lf", value) == 1);
779 }
780}
781
785bool_t
786conf_get_f(const conf_t *conf, const char *key, float *value)
787{
788 const conf_key_t *ck;
789
790 ASSERT(conf != NULL);
791 ASSERT(key != NULL);
792 ASSERT(value != NULL);
793 ck = conf_find(conf, key, NULL);
794 if (ck == NULL || ck->type != CONF_KEY_STR)
795 return (B_FALSE);
796 if (strcmp(ck->str, "nan") == 0) {
797 *value = NAN;
798 return (true);
799 } else {
800 return (sscanf(ck->str, "%f", value) == 1);
801 }
802}
803
815bool_t
816conf_get_da(const conf_t *conf, const char *key, double *value)
817{
818 const conf_key_t *ck;
819 unsigned long long x;
820
821 ASSERT(conf != NULL);
822 ASSERT(key != NULL);
823 ASSERT(value != NULL);
824 ck = conf_find(conf, key, NULL);
825 if (ck == NULL || ck->type != CONF_KEY_STR)
826 return (B_FALSE);
827#if IBM
828#pragma GCC diagnostic push
829#pragma GCC diagnostic ignored "-Wformat" /* Workaround for MinGW crap */
830#pragma GCC diagnostic ignored "-Wformat-extra-args"
831#endif /* IBM */
832 if (sscanf(ck->str, "%llx", &x) != 1)
833 return (B_FALSE);
834#if IBM
835#pragma GCC diagnostic pop
836#endif
837#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
838 x = BSWAP64(x);
839#endif
840 memcpy(value, &x, sizeof (double));
841 return (B_TRUE);
842}
843
849bool_t
850conf_get_b(const conf_t *conf, const char *key, bool_t *value)
851{
852 const conf_key_t *ck;
853
854 ASSERT(conf != NULL);
855 ASSERT(key != NULL);
856 ASSERT(value != NULL);
857 ck = conf_find(conf, key, NULL);
858 if (ck == NULL || ck->type != CONF_KEY_STR)
859 return (B_FALSE);
860 *value = (strcmp(ck->str, "true") == 0 ||
861 strcmp(ck->str, "1") == 0 ||
862 strcmp(ck->str, "yes") == 0);
863 return (B_TRUE);
864}
865
870bool
871conf_get_b2(const conf_t *conf, const char *key, bool *value)
872{
873 bool_t tmp;
874 if (!conf_get_b(conf, key, &tmp))
875 return (false);
876 *value = tmp;
877 return (true);
878}
879
901size_t
902conf_get_data(const conf_t *conf, const char *key, void *buf, size_t cap)
903{
904 const conf_key_t *ck;
905
906 ASSERT(conf != NULL);
907 ASSERT(key != NULL);
908 ASSERT(buf != NULL || cap == 0);
909
910 ck = conf_find(conf, key, NULL);
911 if (ck == NULL || ck->type != CONF_KEY_DATA)
912 return (0);
913 ASSERT(ck->data.buf != NULL);
914 ASSERT(ck->data.sz != 0);
915 memcpy(buf, ck->data.buf, MIN(ck->data.sz, cap));
916
917 return (ck->data.sz);
918}
919
927void
928conf_set_str(conf_t *conf, const char *key, const char *value)
929{
930 conf_key_t *ck;
931 avl_index_t where;
932
933 ASSERT(conf != NULL);
934 ASSERT(key != NULL);
935 ck = conf_find(conf, key, &where);
936 if (ck == NULL) {
937 if (value == NULL)
938 return;
939 ck = safe_calloc(1, sizeof (*ck));
940 ck->key = safe_strdup(key);
941 strtolower(ck->key);
942 avl_insert(&conf->tree, ck, where);
943 }
944 ck_free_value(ck);
945 if (value == NULL) {
946 avl_remove(&conf->tree, ck);
947 free(ck->key);
948 free(ck);
949 return;
950 } else {
951 ck->type = CONF_KEY_STR;
952 ck->str = safe_strdup(value);
953 }
954}
955
956/*
957 * Common setter back-end for conf_set_{i,d,b}.
958 */
959static void
960conf_set_common(conf_t *conf, const char *key, const char *fmt, ...)
961{
962 int n;
963 avl_index_t where;
964 conf_key_t *ck = conf_find(conf, key, &where);
965 va_list ap1, ap2;
966
967 va_start(ap1, fmt);
968 va_copy(ap2, ap1);
969
970 if (ck == NULL) {
971 ck = safe_calloc(1, sizeof (*ck));
972 ck->key = safe_strdup(key);
973 strtolower(ck->key);
974 avl_insert(&conf->tree, ck, where);
975 } else {
976 ck_free_value(ck);
977 }
978 ck->type = CONF_KEY_STR;
979 n = vsnprintf(NULL, 0, fmt, ap1);
980 ASSERT3S(n, >, 0);
981 ck->str = safe_malloc(n + 1);
982 (void) vsnprintf(ck->str, n + 1, fmt, ap2);
983 va_end(ap1);
984 va_end(ap2);
985}
986
991void
992conf_set_i(conf_t *conf, const char *key, int value)
993{
994 ASSERT(conf != NULL);
995 ASSERT(key != NULL);
996 conf_set_common(conf, key, "%i", value);
997}
998
1003void
1004conf_set_lli(conf_t *conf, const char *key, long long value)
1005{
1006 ASSERT(conf != NULL);
1007 ASSERT(key != NULL);
1008#if IBM
1009#pragma GCC diagnostic push
1010#pragma GCC diagnostic ignored "-Wformat" /* Workaround for MinGW crap */
1011#pragma GCC diagnostic ignored "-Wformat-extra-args"
1012#endif /* IBM */
1013 conf_set_common(conf, key, "%lld", value);
1014#if IBM
1015#pragma GCC diagnostic pop
1016#endif
1017}
1018
1023void
1024conf_set_d(conf_t *conf, const char *key, double value)
1025{
1026 ASSERT(conf != NULL);
1027 ASSERT(key != NULL);
1028 if (isnan(value))
1029 conf_set_str(conf, key, "nan");
1030 else
1031 conf_set_common(conf, key, "%.15f", value);
1032}
1033
1038void
1039conf_set_f(conf_t *conf, const char *key, float value)
1040{
1041 ASSERT(conf != NULL);
1042 ASSERT(key != NULL);
1043 if (isnan(value))
1044 conf_set_str(conf, key, "nan");
1045 else
1046 conf_set_common(conf, key, "%.12f", value);
1047}
1048
1054void
1055conf_set_da(conf_t *conf, const char *key, double value)
1056{
1057 unsigned long long x;
1058
1059 ASSERT(conf != NULL);
1060 ASSERT(key != NULL);
1061 memcpy(&x, &value, sizeof (value));
1062#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1063 x = BSWAP64(x);
1064#endif
1065#if IBM
1066#pragma GCC diagnostic push
1067#pragma GCC diagnostic ignored "-Wformat" /* Workaround for MinGW crap */
1068#pragma GCC diagnostic ignored "-Wformat-extra-args"
1069#endif /* IBM */
1070 conf_set_common(conf, key, "%llx", x);
1071#if IBM
1072#pragma GCC diagnostic pop
1073#endif
1074}
1075
1080void
1081conf_set_b(conf_t *conf, const char *key, bool_t value)
1082{
1083 ASSERT(conf != NULL);
1084 ASSERT(key != NULL);
1085 conf_set_common(conf, key, "%s", value ? "true" : "false");
1086}
1087
1091void
1092conf_set_b2(conf_t *conf, const char *key, bool value)
1093{
1094 ASSERT(conf != NULL);
1095 ASSERT(key != NULL);
1096 conf_set_common(conf, key, "%s", value ? "true" : "false");
1097}
1098
1107void
1108conf_set_data(conf_t *conf, const char *key, const void *buf, size_t sz)
1109{
1110 conf_key_t *ck;
1111 avl_index_t where;
1112
1113 ASSERT(conf != NULL);
1114 ASSERT(key != NULL);
1115
1116 ck = conf_find(conf, key, &where);
1117 if (buf == NULL || sz == 0) {
1118 if (ck != NULL) {
1119 avl_remove(&conf->tree, ck);
1120 ck_free_value(ck);
1121 free(ck->key);
1122 free(ck);
1123 }
1124 return;
1125 }
1126 if (ck == NULL) {
1127 ck = safe_calloc(1, sizeof (*ck));
1128 ck->key = safe_strdup(key);
1129 strtolower(ck->key);
1130 avl_insert(&conf->tree, ck, where);
1131 } else {
1132 ck_free_value(ck);
1133 }
1134 ck->type = CONF_KEY_DATA;
1135 ck->data.buf = safe_malloc(sz);
1136 memcpy(ck->data.buf, buf, sz);
1137 ck->data.sz = sz;
1138}
1139
1140#define VARIABLE_GET(getfunc, last_arg, ...) \
1141 do { \
1142 va_list ap, ap2; \
1143 int l; \
1144 char *key; \
1145 int64_t res; \
1146 va_start(ap, last_arg); \
1147 va_copy(ap2, ap); \
1148 l = vsnprintf(NULL, 0, fmt, ap); \
1149 key = safe_malloc(l + 1); \
1150 vsnprintf(key, l + 1, fmt, ap2); \
1151 res = getfunc(conf, key, __VA_ARGS__); \
1152 free(key); \
1153 va_end(ap); \
1154 va_end(ap2); \
1155 return (res); \
1156 } while (0)
1157
1158#define VARIABLE_SET(setfunc, last_arg, ...) \
1159 do { \
1160 va_list ap, ap2; \
1161 int l; \
1162 char *key; \
1163 va_start(ap, last_arg); \
1164 va_copy(ap2, ap); \
1165 l = vsnprintf(NULL, 0, fmt, ap); \
1166 key = safe_malloc(l + 1); \
1167 vsnprintf(key, l + 1, fmt, ap2); \
1168 setfunc(conf, key, __VA_ARGS__); \
1169 free(key); \
1170 va_end(ap); \
1171 va_end(ap2); \
1172 } while (0)
1173
1179bool_t
1180conf_get_str_v(const conf_t *conf, const char *fmt, const char **value, ...)
1181{
1182 ASSERT(conf != NULL);
1183 ASSERT(fmt != NULL);
1184 ASSERT(value != NULL);
1185 VARIABLE_GET(conf_get_str, value, value);
1186}
1187
1191bool_t
1192conf_get_i_v(const conf_t *conf, const char *fmt, int *value, ...)
1193{
1194 ASSERT(conf != NULL);
1195 ASSERT(fmt != NULL);
1196 ASSERT(value != NULL);
1197 VARIABLE_GET(conf_get_i, value, value);
1198}
1199
1204bool_t
1205conf_get_lli_v(const conf_t *conf, const char *fmt, long long *value, ...)
1206{
1207 ASSERT(conf != NULL);
1208 ASSERT(fmt != NULL);
1209 ASSERT(value != NULL);
1210 VARIABLE_GET(conf_get_lli, value, value);
1211}
1212
1216bool_t
1217conf_get_f_v(const conf_t *conf, const char *fmt, float *value, ...)
1218{
1219 ASSERT(conf != NULL);
1220 ASSERT(fmt != NULL);
1221 ASSERT(value != NULL);
1222 VARIABLE_GET(conf_get_f, value, value);
1223}
1224
1228bool_t
1229conf_get_d_v(const conf_t *conf, const char *fmt, double *value, ...)
1230{
1231 ASSERT(conf != NULL);
1232 ASSERT(fmt != NULL);
1233 ASSERT(value != NULL);
1234 VARIABLE_GET(conf_get_d, value, value);
1235}
1236
1241bool_t
1242conf_get_da_v(const conf_t *conf, const char *fmt, double *value, ...)
1243{
1244 ASSERT(conf != NULL);
1245 ASSERT(fmt != NULL);
1246 ASSERT(value != NULL);
1247 VARIABLE_GET(conf_get_da, value, value);
1248}
1249
1254size_t
1255conf_get_data_v(const conf_t *conf, const char *fmt, void *buf,
1256 size_t cap, ...)
1257{
1258 ASSERT(conf != NULL);
1259 ASSERT(fmt != NULL);
1260 ASSERT(buf != NULL || cap == 0);
1261 VARIABLE_GET(conf_get_data, cap, buf, cap);
1262}
1263
1267bool_t
1268conf_get_b_v(const conf_t *conf, const char *fmt, bool_t *value, ...)
1269{
1270 ASSERT(conf != NULL);
1271 ASSERT(fmt != NULL);
1272 ASSERT(value != NULL);
1273 VARIABLE_GET(conf_get_b, value, value);
1274}
1275
1279bool
1280conf_get_b2_v(const conf_t *conf, const char *fmt, bool *value, ...)
1281{
1282 ASSERT(conf != NULL);
1283 ASSERT(fmt != NULL);
1284 ASSERT(value != NULL);
1285 VARIABLE_GET(conf_get_b2, value, value);
1286}
1287
1288/*
1289 * Same as conf_set_str(), but with dynamic name-construction as
1290 * conf_get_str_v().
1291 */
1292void
1293conf_set_str_v(conf_t *conf, const char *fmt, const char *value, ...)
1294{
1295 ASSERT(conf != NULL);
1296 ASSERT(fmt != NULL);
1297 VARIABLE_SET(conf_set_str, value, value);
1298}
1299
1303void
1304conf_set_i_v(conf_t *conf, const char *fmt, int value, ...)
1305{
1306 ASSERT(conf != NULL);
1307 ASSERT(fmt != NULL);
1308 VARIABLE_SET(conf_set_i, value, value);
1309}
1310
1315void
1316conf_set_lli_v(conf_t *conf, const char *fmt, long long value, ...)
1317{
1318 ASSERT(conf != NULL);
1319 ASSERT(fmt != NULL);
1320 VARIABLE_SET(conf_set_lli, value, value);
1321}
1322
1326void
1327conf_set_f_v(conf_t *conf, const char *fmt, double value, ...)
1328{
1329 ASSERT(conf != NULL);
1330 ASSERT(fmt != NULL);
1331 VARIABLE_SET(conf_set_f, value, value);
1332}
1333
1337void
1338conf_set_d_v(conf_t *conf, const char *fmt, double value, ...)
1339{
1340 ASSERT(conf != NULL);
1341 ASSERT(fmt != NULL);
1342 VARIABLE_SET(conf_set_d, value, value);
1343}
1344
1349void
1350conf_set_da_v(conf_t *conf, const char *fmt, double value, ...)
1351{
1352 ASSERT(conf != NULL);
1353 ASSERT(fmt != NULL);
1354 VARIABLE_SET(conf_set_da, value, value);
1355}
1356
1360void
1361conf_set_b_v(conf_t *conf, const char *fmt, bool_t value, ...)
1362{
1363 ASSERT(conf != NULL);
1364 ASSERT(fmt != NULL);
1365 VARIABLE_SET(conf_set_b, value, value);
1366}
1367
1372void conf_set_data_v(conf_t *conf, const char *fmt, const void *buf,
1373 size_t sz, ...)
1374{
1375 ASSERT(conf != NULL);
1376 ASSERT(fmt != NULL);
1377 VARIABLE_SET(conf_set_data, sz, buf, sz);
1378}
1379
1405API_EXPORT bool_t
1406conf_walk(const conf_t *conf, const char **key, const char **value,
1407 void **cookie)
1408{
1409 static conf_key_t eol;
1410 conf_key_t *ck = *cookie;
1411
1412 if (ck == &eol) {
1413 /* end of tree */
1414 return (B_FALSE);
1415 }
1416 if (ck == NULL) {
1417 /* first call */
1418 ck = avl_first(&conf->tree);
1419 if (ck == NULL) {
1420 /* tree is empty */
1421 *cookie = &eol;
1422 return (B_FALSE);
1423 }
1424 }
1425 do {
1426 *key = ck->key;
1427 *value = ck->str;
1428 ck = AVL_NEXT(&conf->tree, ck);
1429 /* conf_walk is only meant for string keys */
1430 } while (ck != NULL && ck->type != CONF_KEY_STR);
1431 if (ck != NULL) {
1432 *cookie = ck;
1433 } else {
1434 /* end of tree */
1435 *cookie = &eol;
1436 }
1437
1438 return (B_TRUE);
1439}
#define VERIFY(x)
Definition assert.h:78
#define ASSERT3S(x, op, y)
Definition assert.h:209
#define ASSERT(x)
Definition assert.h:208
void * avl_destroy_nodes(avl_tree_t *tree, void **cookie)
Definition avl.c:938
void * avl_first(const avl_tree_t *tree)
Definition avl.c:172
uintptr_t avl_index_t
Definition avl.h:119
void avl_remove(avl_tree_t *tree, void *node)
Definition avl.c:660
void avl_insert(avl_tree_t *tree, void *node, avl_index_t where)
Definition avl.c:471
void avl_create(avl_tree_t *tree, int(*compar)(const void *, const void *), size_t size, size_t offset)
Definition avl.c:867
#define AVL_NEXT(tree, node)
Definition avl.h:212
void avl_destroy(avl_tree_t *tree)
Definition avl.c:890
void * avl_find(const avl_tree_t *tree, const void *node, avl_index_t *where)
Definition avl.c:244
#define BASE64_ENC_SIZE(__raw_size__)
Definition base64.h:32
size_t lacf_base64_encode(const uint8_t *raw, size_t raw_size, uint8_t *encoded)
Definition base64.c:93
#define BASE64_DEC_SIZE(__enc_size__)
Definition base64.h:41
ssize_t lacf_base64_decode(const uint8_t *encoded, size_t encoded_size, uint8_t *raw)
Definition base64.c:182
bool_t conf_get_da(const conf_t *conf, const char *key, double *value)
Definition conf.c:816
void conf_set_b(conf_t *conf, const char *key, bool_t value)
Definition conf.c:1081
bool_t conf_get_lli_v(const conf_t *conf, const char *fmt, long long *value,...)
Definition conf.c:1205
bool_t conf_get_d_v(const conf_t *conf, const char *fmt, double *value,...)
Definition conf.c:1229
void conf_set_data_v(conf_t *conf, const char *fmt, const void *buf, size_t sz,...)
Definition conf.c:1372
void conf_set_f(conf_t *conf, const char *key, float value)
Definition conf.c:1039
bool_t conf_get_i_v(const conf_t *conf, const char *fmt, int *value,...)
Definition conf.c:1192
bool_t conf_get_lli(const conf_t *conf, const char *key, long long *value)
Definition conf.c:744
void conf_set_da(conf_t *conf, const char *key, double value)
Definition conf.c:1055
void conf_set_str(conf_t *conf, const char *key, const char *value)
Definition conf.c:928
void conf_set_data(conf_t *conf, const char *key, const void *buf, size_t sz)
Definition conf.c:1108
bool_t conf_get_da_v(const conf_t *conf, const char *fmt, double *value,...)
Definition conf.c:1242
bool_t conf_get_d(const conf_t *conf, const char *key, double *value)
Definition conf.c:764
bool_t conf_write(const conf_t *conf, FILE *fp)
Definition conf.c:677
void conf_set_d_v(conf_t *conf, const char *fmt, double value,...)
Definition conf.c:1338
bool_t conf_get_b(const conf_t *conf, const char *key, bool_t *value)
Definition conf.c:850
void conf_set_lli_v(conf_t *conf, const char *fmt, long long value,...)
Definition conf.c:1316
bool_t conf_walk(const conf_t *conf, const char **key, const char **value, void **cookie)
Definition conf.c:1406
void conf_set_i_v(conf_t *conf, const char *fmt, int value,...)
Definition conf.c:1304
conf_t * conf_read2(void *fp, int *errline, bool_t compressed)
Definition conf.c:335
conf_t * conf_read_buf(const void *buf, size_t cap, int *errline)
Definition conf.c:385
void conf_merge(const conf_t *conf_from, conf_t *conf_to)
Definition conf.c:119
bool_t conf_get_b_v(const conf_t *conf, const char *fmt, bool_t *value,...)
Definition conf.c:1268
void conf_set_da_v(conf_t *conf, const char *fmt, double value,...)
Definition conf.c:1350
conf_t * conf_read_file(const char *filename, int *errline)
Definition conf.c:174
void conf_set_f_v(conf_t *conf, const char *fmt, double value,...)
Definition conf.c:1327
bool_t conf_get_str_v(const conf_t *conf, const char *fmt, const char **value,...)
Definition conf.c:1180
conf_t * conf_read(FILE *fp, int *errline)
Definition conf.c:246
size_t conf_get_data_v(const conf_t *conf, const char *fmt, void *buf, size_t cap,...)
Definition conf.c:1255
bool_t conf_get_f(const conf_t *conf, const char *key, float *value)
Definition conf.c:786
void conf_free(conf_t *conf)
Definition conf.c:142
void conf_set_i(conf_t *conf, const char *key, int value)
Definition conf.c:992
bool_t conf_get_f_v(const conf_t *conf, const char *fmt, float *value,...)
Definition conf.c:1217
bool_t conf_write_file(const conf_t *conf, const char *filename)
Definition conf.c:449
void conf_set_b_v(conf_t *conf, const char *fmt, bool_t value,...)
Definition conf.c:1361
bool_t conf_write_file2(const conf_t *conf, const char *filename, bool_t compressed)
Definition conf.c:459
bool_t conf_get_str(const conf_t *conf, const char *key, const char **value)
Definition conf.c:704
conf_t * conf_create_empty(void)
Definition conf.c:87
void conf_set_d(conf_t *conf, const char *key, double value)
Definition conf.c:1024
size_t conf_get_data(const conf_t *conf, const char *key, void *buf, size_t cap)
Definition conf.c:902
size_t conf_write_buf(const conf_t *conf, void *buf, size_t cap)
Definition conf.c:543
conf_t * conf_create_copy(const conf_t *conf2)
Definition conf.c:100
void conf_set_lli(conf_t *conf, const char *key, long long value)
Definition conf.c:1004
char ** strsplit(const char *input, const char *sep, bool_t skip_empty, size_t *num)
Definition helpers.c:650
void unescape_percent(char *str)
Definition helpers.c:753
static ssize_t parser_get_next_line(FILE *fp, char **linep, size_t *linecap, unsigned *linenum)
Definition helpers.h:387
static char * sprintf_alloc(const char *fmt,...)
Definition helpers.h:572
void free_strlist(char **comps, size_t num)
Definition helpers.c:703
void strtolower(char *str)
Definition helpers.c:773
bool_t file_exists(const char *path, bool_t *isdir)
Definition helpers.c:1222
#define logMsg(...)
Definition log.h:112
static void strip_space(char *line)
static char * safe_strdup(const char *str2)
Definition safe_alloc.h:201
static void * safe_calloc(size_t nmemb, size_t size)
Definition safe_alloc.h:71
static void * safe_malloc(size_t size)
Definition safe_alloc.h:56
Definition conf.c:41