1 /* radare - LGPL - Copyright 2006-2021 - pancake */
2
3 #include "r_config.h"
4
r_config_node_new(const char * name,const char * value)5 R_API RConfigNode* r_config_node_new(const char *name, const char *value) {
6 r_return_val_if_fail (name && *name && value, NULL);
7 RConfigNode *node = R_NEW0 (RConfigNode);
8 if (!node) {
9 return NULL;
10 }
11 node->name = strdup (name);
12 node->value = strdup (r_str_get (value));
13 node->flags = CN_RW | CN_STR;
14 node->i_value = r_num_get (NULL, value);
15 node->options = NULL;
16 return node;
17 }
18
r_config_node_to_string(RConfigNode * node)19 R_API char *r_config_node_to_string(RConfigNode *node) {
20 return (node && node->name)? strdup (node->name): NULL;
21 }
22
r_config_node_purge_options(RConfigNode * node)23 R_API void r_config_node_purge_options(RConfigNode *node) {
24 if (node->options) {
25 r_list_purge (node->options);
26 } else {
27 node->options = r_list_newf (free);
28 }
29 }
30
r_config_node_add_option(RConfigNode * node,const char * option)31 R_API void r_config_node_add_option(RConfigNode *node, const char *option) {
32 if (!node->options) {
33 node->options = r_list_newf (free);
34 }
35 r_list_append (node->options, strdup (option));
36 }
37
r_config_node_clone(RConfigNode * n)38 R_API RConfigNode* r_config_node_clone(RConfigNode *n) {
39 r_return_val_if_fail (n, NULL);
40 RConfigNode *cn = R_NEW0 (RConfigNode);
41 if (!cn) {
42 return NULL;
43 }
44 cn->name = strdup (n->name);
45 cn->desc = n->desc ? strdup (n->desc) : NULL;
46 cn->value = strdup (r_str_get (n->value));
47 cn->i_value = n->i_value;
48 cn->flags = n->flags;
49 cn->setter = n->setter;
50 cn->options = n->options? r_list_clone (n->options): NULL;
51 return cn;
52 }
53
r_config_node_free(void * n)54 R_API void r_config_node_free(void *n) {
55 RConfigNode *node = (RConfigNode *)n;
56 if (!node) {
57 return;
58 }
59 free (node->name);
60 free (node->desc);
61 free (node->value);
62 r_list_free (node->options);
63 free (node);
64 }
65
config_print_value_json(RConfig * cfg,RConfigNode * node)66 static void config_print_value_json(RConfig *cfg, RConfigNode *node) {
67 r_return_if_fail (cfg && node);
68 const char *val = node->value;
69 if (!val) {
70 val = "0";
71 }
72 char *sval = r_str_escape (val);
73 if (r_config_node_is_bool (node) || r_config_node_is_int (node)) {
74 if (!strncmp (val, "0x", 2)) {
75 ut64 n = r_num_get (NULL, val);
76 cfg->cb_printf ("%"PFMT64d, n);
77 } else if (r_str_isnumber (val) || (*val /* HACK */ && r_str_is_bool (val))) {
78 cfg->cb_printf ("%s", val); // TODO: always use true/false for bool json str
79 } else {
80 cfg->cb_printf ("\"%s\"", sval);
81 }
82 } else {
83 cfg->cb_printf ("\"%s\"", sval);
84 }
85 free (sval);
86 }
87
config_print_node(RConfig * cfg,RConfigNode * node,const char * pfx,const char * sfx,bool verbose,bool json)88 static void config_print_node(RConfig *cfg, RConfigNode *node, const char *pfx, const char *sfx, bool verbose, bool json) {
89 r_return_if_fail (cfg && node && pfx && sfx);
90 char *option;
91 bool isFirst;
92 RListIter *iter;
93 char *es = NULL;
94
95 if (json) {
96 if (verbose) {
97 cfg->cb_printf ("{");
98 cfg->cb_printf ("\"name\":\"%s\",", node->name);
99 cfg->cb_printf ("\"value\":");
100 config_print_value_json (cfg, node);
101 cfg->cb_printf (",\"type\":\"%s\",", r_config_node_type (node));
102 es = r_str_escape (node->desc);
103 if (es) {
104 cfg->cb_printf ("\"desc\":\"%s\",", es);
105 free (es);
106 }
107 cfg->cb_printf ("\"ro\":%s", r_str_bool (r_config_node_is_ro (node)));
108 if (node->options && !r_list_empty (node->options)) {
109 isFirst = true;
110 cfg->cb_printf (",\"options\":[");
111 r_list_foreach (node->options, iter, option) {
112 es = r_str_escape (option);
113 if (es) {
114 if (isFirst) {
115 isFirst = false;
116 } else {
117 cfg->cb_printf (",");
118 }
119 cfg->cb_printf ("\"%s\"", es);
120 free (es);
121 }
122 }
123 cfg->cb_printf ("]");
124 }
125 cfg->cb_printf ("}");
126 } else {
127 cfg->cb_printf ("\"%s\":", node->name);
128 config_print_value_json (cfg, node);
129 }
130 } else {
131 if (verbose) {
132 cfg->cb_printf ("%s%s = %s%s %s; %s", pfx,
133 node->name, node->value, sfx,
134 r_config_node_is_ro (node) ? "(ro)" : "",
135 node->desc);
136 if (node->options && !r_list_empty (node->options)) {
137 isFirst = true;
138 cfg->cb_printf(" [");
139 r_list_foreach (node->options, iter, option) {
140 if (isFirst) {
141 isFirst = false;
142 } else {
143 cfg->cb_printf (", ");
144 }
145 cfg->cb_printf ("%s", option);
146 }
147 cfg->cb_printf ("]");
148 }
149 cfg->cb_printf ("\n");
150 } else {
151 cfg->cb_printf ("%s%s = %s%s\n", pfx,
152 node->name, node->value, sfx);
153 }
154 }
155 }
156
r_config_list(RConfig * cfg,const char * str,int rad)157 R_API void r_config_list(RConfig *cfg, const char *str, int rad) {
158 r_return_if_fail (cfg);
159 RConfigNode *node;
160 RListIter *iter;
161 const char *sfx = "";
162 const char *pfx = "";
163 int len = 0;
164 bool verbose = false;
165 bool json = false;
166 bool isFirst = false;
167
168 if (!IS_NULLSTR (str)) {
169 str = r_str_trim_head_ro (str);
170 len = strlen (str);
171 if (len > 0 && str[0] == 'j') {
172 str++;
173 len--;
174 json = true;
175 rad = 'J';
176 }
177 if (len > 0 && str[0] == ' ') {
178 str++;
179 len--;
180 }
181 if (strlen (str) == 0) {
182 str = NULL;
183 len = 0;
184 }
185 }
186
187 switch (rad) {
188 case 1:
189 pfx = "\"e ";
190 sfx = "\"";
191 /* fallthrou */
192 case 0:
193 r_list_foreach (cfg->nodes, iter, node) {
194 if (!str || (str && (!strncmp (str, node->name, len)))) {
195 config_print_node (cfg, node, pfx, sfx, verbose, json);
196 }
197 }
198 break;
199 case 2:
200 r_list_foreach (cfg->nodes, iter, node) {
201 if (!str || (str && (!strncmp (str, node->name, len)))) {
202 if (!str || !strncmp (str, node->name, len)) {
203 cfg->cb_printf ("%20s: %s\n", node->name,
204 r_str_get (node->desc));
205 }
206 }
207 }
208 break;
209 case 's':
210 if (str && *str) {
211 r_list_foreach (cfg->nodes, iter, node) {
212 char *space = strdup (node->name);
213 char *dot = strchr (space, '.');
214 if (dot) {
215 *dot = 0;
216 }
217 if (!strcmp (str, space)) {
218 cfg->cb_printf ("%s\n", dot + 1);
219 }
220 free (space);
221 }
222 } else {
223 char *oldSpace = NULL;
224 r_list_foreach (cfg->nodes, iter, node) {
225 char *space = strdup (node->name);
226 char *dot = strchr (space, '.');
227 if (dot) {
228 *dot = 0;
229 }
230 if (oldSpace) {
231 if (!strcmp (space, oldSpace)) {
232 free (space);
233 continue;
234 }
235 free (oldSpace);
236 oldSpace = space;
237 } else {
238 oldSpace = space;
239 }
240 cfg->cb_printf ("%s\n", space);
241 }
242 free (oldSpace);
243 }
244 break;
245 case 'v':
246 verbose = true;
247 r_list_foreach (cfg->nodes, iter, node) {
248 if (!str || (str && (!strncmp (str, node->name, len)))) {
249 config_print_node (cfg, node, pfx, sfx, verbose, json);
250 }
251 }
252 break;
253 case 'q':
254 r_list_foreach (cfg->nodes, iter, node) {
255 if (!str || (str && (!strncmp (str, node->name, len)))) {
256 cfg->cb_printf ("%s\n", node->name);
257 }
258 }
259 break;
260 case 'J':
261 verbose = true;
262 /* fallthrou */
263 case 'j':
264 isFirst = true;
265 if (verbose) {
266 cfg->cb_printf ("[");
267 } else {
268 cfg->cb_printf ("{");
269 }
270 r_list_foreach (cfg->nodes, iter, node) {
271 if (!str || (str && (!strncmp (str, node->name, len)))) {
272 if (!str || !strncmp (str, node->name, len)) {
273 if (isFirst) {
274 isFirst = false;
275 } else {
276 cfg->cb_printf (",");
277 }
278 config_print_node (cfg, node, pfx, sfx, verbose, true);
279 }
280 }
281 }
282 if (verbose) {
283 cfg->cb_printf ("]\n");
284 } else {
285 cfg->cb_printf ("}\n");
286 }
287 break;
288 }
289 }
290
r_config_node_get(RConfig * cfg,const char * name)291 R_API RConfigNode* r_config_node_get(RConfig *cfg, const char *name) {
292 r_return_val_if_fail (cfg && name, NULL);
293 return ht_pp_find (cfg->ht, name, NULL);
294 }
295
r_config_get(RConfig * cfg,const char * name)296 R_API const char* r_config_get(RConfig *cfg, const char *name) {
297 r_return_val_if_fail (cfg && name, NULL);
298 RConfigNode *node = r_config_node_get (cfg, name);
299 if (node) {
300 if (node->getter) {
301 node->getter (cfg->user, node);
302 }
303 if (r_config_node_is_bool (node)) {
304 return r_str_bool (r_str_is_true (node->value));
305 }
306 return node->value;
307 } else {
308 eprintf ("r_config_get: variable '%s' not found\n", name);
309 }
310 return NULL;
311 }
312
r_config_toggle(RConfig * cfg,const char * name)313 R_API bool r_config_toggle(RConfig *cfg, const char *name) {
314 RConfigNode *node = r_config_node_get (cfg, name);
315 if (!node) {
316 return false;
317 }
318 if (!r_config_node_is_bool (node)) {
319 eprintf ("(error: '%s' is not a boolean variable)\n", name);
320 return false;
321 }
322 if (r_config_node_is_ro (node)) {
323 eprintf ("(error: '%s' config key is read only)\n", name);
324 return false;
325 }
326 (void)r_config_set_b (cfg, name, !node->i_value);
327 return true;
328 }
329
r_config_get_b(RConfig * cfg,const char * name)330 R_API bool r_config_get_b(RConfig *cfg, const char *name) {
331 return r_config_get_i (cfg, name) != 0;
332 }
333
r_config_get_i(RConfig * cfg,const char * name)334 R_API ut64 r_config_get_i(RConfig *cfg, const char *name) {
335 r_return_val_if_fail (cfg, 0ULL);
336 RConfigNode *node = r_config_node_get (cfg, name);
337 if (node) {
338 if (node->getter) {
339 node->getter (cfg->user, node);
340 }
341 if (node->i_value) {
342 return node->i_value;
343 }
344 if (!strcmp (node->value, "false")) {
345 return 0;
346 }
347 if (!strcmp (node->value, "true")) {
348 return 1;
349 }
350 return (ut64) r_num_math (cfg->num, node->value);
351 }
352 return 0ULL;
353 }
354
r_config_node_type(RConfigNode * node)355 R_API const char* r_config_node_type(RConfigNode *node) {
356 r_return_val_if_fail (node, "");
357
358 if (r_config_node_is_bool (node)) {
359 return "bool";
360 }
361 if (r_config_node_is_str (node)) {
362 return "str";
363 }
364 if (r_config_node_is_int (node)) {
365 if (!strncmp (node->value, "0x", 2)) {
366 return "addr";
367 }
368 return "int";
369 }
370 return "";
371 }
372
r_config_set_cb(RConfig * cfg,const char * name,const char * value,RConfigCallback cb)373 R_API RConfigNode* r_config_set_cb(RConfig *cfg, const char *name, const char *value, RConfigCallback cb) {
374 RConfigNode *node = r_config_set (cfg, name, value);
375 if (node && (node->setter = cb)) {
376 (void)cb (cfg->user, node);
377 }
378 return node;
379 }
380
r_config_set_i_cb(RConfig * cfg,const char * name,int ivalue,RConfigCallback cb)381 R_API RConfigNode* r_config_set_i_cb(RConfig *cfg, const char *name, int ivalue, RConfigCallback cb) {
382 RConfigNode *node = r_config_set_i (cfg, name, ivalue);
383 if (node && (node->setter = cb)) {
384 if (!node->setter (cfg->user, node)) {
385 return NULL;
386 }
387 }
388 return node;
389 }
390
is_true_or_false(const char * s)391 static inline bool is_true_or_false(const char *s) {
392 return s && (!r_str_casecmp (s, "true") || !r_str_casecmp (s, "false"));
393 }
394
395 /* TODO: reduce number of strdups here */
r_config_set(RConfig * cfg,const char * name,const char * value)396 R_API RConfigNode* r_config_set(RConfig *cfg, const char *name, const char *value) {
397 RConfigNode *node = NULL;
398 char *ov = NULL;
399 ut64 oi;
400
401 r_return_val_if_fail (cfg && cfg->ht, NULL);
402 r_return_val_if_fail (!IS_NULLSTR (name), NULL);
403
404 node = r_config_node_get (cfg, name);
405 if (node) {
406 if (r_config_node_is_ro (node)) {
407 eprintf ("(error: '%s' config key is read only)\n", name);
408 return node;
409 }
410 oi = node->i_value;
411 if (node->value) {
412 ov = strdup (node->value);
413 if (!ov) {
414 goto beach;
415 }
416 } else {
417 free (node->value);
418 node->value = strdup ("");
419 }
420 if (r_config_node_is_bool (node)) {
421 bool b = r_str_is_true (value);
422 node->i_value = b? 1: 0;
423 char *value = strdup (r_str_bool (b));
424 if (value) {
425 free (node->value);
426 node->value = value;
427 }
428 } else {
429 if (!value) {
430 free (node->value);
431 node->value = strdup ("");
432 node->i_value = 0;
433 } else {
434 if (node->value == value) {
435 goto beach;
436 }
437 free (node->value);
438 node->value = strdup (value);
439 if (IS_DIGIT (*value) || (value[0] == '-' && IS_DIGIT (value[1]))) {
440 if (strchr (value, '/')) {
441 node->i_value = r_num_get (cfg->num, value);
442 } else {
443 node->i_value = r_num_math (cfg->num, value);
444 }
445 } else {
446 node->i_value = 0;
447 }
448 node->flags |= CN_INT;
449 }
450 }
451 } else { // Create a new RConfigNode
452 oi = UT64_MAX;
453 if (!cfg->lock) {
454 node = r_config_node_new (name, value);
455 if (node) {
456 if (is_true_or_false (value)) {
457 node->flags |= CN_BOOL;
458 node->i_value = r_str_is_true (value)? 1: 0;
459 }
460 ht_pp_insert (cfg->ht, node->name, node);
461 r_list_append (cfg->nodes, node);
462 } else {
463 eprintf ("r_config_set: unable to create a new RConfigNode\n");
464 }
465 } else {
466 eprintf ("r_config_set: variable '%s' not found\n", name);
467 }
468 }
469
470 if (node && node->setter) {
471 if (!node->setter (cfg->user, node)) {
472 if (oi != UT64_MAX) {
473 node->i_value = oi;
474 }
475 free (node->value);
476 node->value = strdup (r_str_get (ov));
477 if (ov) {
478 free (ov);
479 }
480 return NULL;
481 }
482 }
483 beach:
484 free (ov);
485 return node;
486 }
487
488 /* r_config_desc takes a RConfig and a name,
489 * r_config_node_desc takes a RConfigNode
490 * Both set and return node->desc */
r_config_desc(RConfig * cfg,const char * name,const char * desc)491 R_API const char* r_config_desc(RConfig *cfg, const char *name, const char *desc) {
492 RConfigNode *node = r_config_node_get (cfg, name);
493 return r_config_node_desc (node, desc);
494 }
495
r_config_node_desc(RConfigNode * node,const char * desc)496 R_API const char* r_config_node_desc(RConfigNode *node, const char *desc) {
497 r_return_val_if_fail (node, NULL);
498 if (desc) {
499 free (node->desc);
500 node->desc = strdup (desc);
501 }
502 return node->desc;
503 }
504
r_config_rm(RConfig * cfg,const char * name)505 R_API bool r_config_rm(RConfig *cfg, const char *name) {
506 RConfigNode *node = r_config_node_get (cfg, name);
507 if (node) {
508 ht_pp_delete (cfg->ht, node->name);
509 r_list_delete_data (cfg->nodes, node);
510 return true;
511 }
512 return false;
513 }
514
r_config_node_value_format_i(char * buf,size_t buf_size,const ut64 i,R_NULLABLE RConfigNode * node)515 R_API void r_config_node_value_format_i(char *buf, size_t buf_size, const ut64 i, R_NULLABLE RConfigNode *node) {
516 if (node && r_config_node_is_bool (node)) {
517 r_str_ncpy (buf, r_str_bool ((int) i), buf_size);
518 return;
519 }
520 if (i < 1024) {
521 snprintf (buf, buf_size, "%" PFMT64d "", i);
522 } else {
523 snprintf (buf, buf_size, "0x%08" PFMT64x "", i);
524 }
525 }
526
r_config_set_b(RConfig * cfg,const char * name,bool b)527 R_API RConfigNode* r_config_set_b(RConfig *cfg, const char *name, bool b) {
528 RConfigNode *node = r_config_node_get (cfg, name);
529 if (node && r_config_node_is_bool (node)) {
530 return r_config_set_i (cfg, name, b? 1: 0);
531 }
532 return NULL;
533 }
534
r_config_set_i(RConfig * cfg,const char * name,const ut64 i)535 R_API RConfigNode* r_config_set_i(RConfig *cfg, const char *name, const ut64 i) {
536 char buf[128], *ov = NULL;
537 r_return_val_if_fail (cfg && name, NULL);
538 RConfigNode *node = r_config_node_get (cfg, name);
539 if (node) {
540 if (r_config_node_is_ro (node)) {
541 node = NULL;
542 goto beach;
543 }
544 ov = node->value;
545 r_config_node_value_format_i (buf, sizeof (buf), i, NULL);
546 node->value = strdup (buf);
547 if (!node->value) {
548 node = NULL;
549 goto beach;
550 }
551 node->i_value = i;
552 } else {
553 if (!cfg->lock) {
554 r_config_node_value_format_i (buf, sizeof (buf), i, NULL);
555 node = r_config_node_new (name, buf);
556 if (!node) {
557 node = NULL;
558 goto beach;
559 }
560 node->flags = CN_RW | CN_INT;
561 node->i_value = i;
562 ht_pp_insert (cfg->ht, node->name, node);
563 if (cfg->nodes) {
564 r_list_append (cfg->nodes, node);
565 }
566 } else {
567 eprintf ("(locked: no new keys can be created (%s))\n", name);
568 }
569 }
570
571 if (node && node->setter) {
572 ut64 oi = node->i_value;
573 int ret = node->setter (cfg->user, node);
574 if (!ret) {
575 node->i_value = oi;
576 free (node->value);
577 node->value = ov? ov: strdup ("");
578 ov = NULL;
579 }
580 }
581 beach:
582 free (ov);
583 return node;
584 }
585
__evalString(RConfig * cfg,char * name)586 static void __evalString(RConfig *cfg, char *name) {
587 if (!*name) {
588 return;
589 }
590 char *eq = strchr (name, '=');
591 if (eq) {
592 *eq++ = 0;
593 r_str_trim (name);
594 r_str_trim (eq);
595 if (*name) {
596 (void) r_config_set (cfg, name, eq);
597 }
598 } else {
599 if (r_str_endswith (name, ".")) {
600 r_config_list (cfg, name, 0);
601 } else {
602 const char *v = r_config_get (cfg, name);
603 if (v) {
604 cfg->cb_printf ("%s\n", v);
605 } else {
606 eprintf ("Invalid config key %s\n", name);
607 }
608 }
609 }
610 }
611
r_config_eval(RConfig * cfg,const char * str,bool many)612 R_API bool r_config_eval(RConfig *cfg, const char *str, bool many) {
613 r_return_val_if_fail (cfg && str, false);
614
615 char *s = r_str_trim_dup (str);
616
617 if (!*s || !strcmp (s, "help")) {
618 r_config_list (cfg, NULL, 0);
619 free (s);
620 return false;
621 }
622
623 if (*s == '-') {
624 r_config_rm (cfg, s + 1);
625 free (s);
626 return false;
627 }
628 if (many) {
629 // space separated list of k=v k=v,..
630 // if you want to use spaces go for base64 or e.
631 RList *list = r_str_split_list (s, ",", 0);
632 RListIter *iter;
633 char *name;
634 r_list_foreach (list, iter, name) {
635 __evalString (cfg, name);
636 }
637 free (s);
638 return true;
639 }
640 __evalString (cfg, s);
641 free (s);
642 return true;
643 }
644
cmp(RConfigNode * a,RConfigNode * b)645 static int cmp(RConfigNode *a, RConfigNode *b) {
646 return strcmp (a->name, b->name);
647 }
648
r_config_lock(RConfig * cfg,bool lock)649 R_API void r_config_lock(RConfig *cfg, bool lock) {
650 r_list_sort (cfg->nodes, (RListComparator) cmp);
651 cfg->lock = lock;
652 }
653
r_config_readonly(RConfig * cfg,const char * key)654 R_API bool r_config_readonly(RConfig *cfg, const char *key) {
655 RConfigNode *n = r_config_node_get (cfg, key);
656 if (n) {
657 n->flags |= CN_RO;
658 return true;
659 }
660 return false;
661 }
662
r_config_new(void * user)663 R_API RConfig* r_config_new(void *user) {
664 RConfig *cfg = R_NEW0 (RConfig);
665 if (!cfg) {
666 return NULL;
667 }
668 cfg->ht = ht_pp_new0 ();
669 cfg->nodes = r_list_newf ((RListFree)r_config_node_free);
670 if (!cfg->nodes) {
671 R_FREE (cfg);
672 return NULL;
673 }
674 cfg->user = user;
675 cfg->num = NULL;
676 cfg->lock = false;
677 cfg->cb_printf = (void *) printf;
678 return cfg;
679 }
680
r_config_clone(RConfig * cfg)681 R_API RConfig* r_config_clone(RConfig *cfg) {
682 RListIter *iter;
683 RConfigNode *node;
684 RConfig *c = r_config_new (cfg->user);
685 if (!c) {
686 return NULL;
687 }
688 r_list_foreach (cfg->nodes, iter, node) {
689 RConfigNode *nn = r_config_node_clone (node);
690 ht_pp_insert (c->ht, node->name, nn);
691 r_list_append (c->nodes, nn);
692 }
693 c->lock = cfg->lock;
694 c->cb_printf = cfg->cb_printf;
695 return c;
696 }
697
r_config_free(RConfig * cfg)698 R_API void r_config_free(RConfig *cfg) {
699 if (cfg) {
700 cfg->nodes->free = r_config_node_free; // damn
701 r_list_free (cfg->nodes);
702 ht_pp_free (cfg->ht);
703 free (cfg);
704 }
705 }
706
r_config_visual_hit_i(RConfig * cfg,const char * name,int delta)707 R_API void r_config_visual_hit_i(RConfig *cfg, const char *name, int delta) {
708 RConfigNode *node = r_config_node_get (cfg, name);
709 if (node && r_config_node_is_int (node)) {
710 (void)r_config_set_i (cfg, name, r_config_get_i (cfg, name) + delta);
711 }
712 }
713
r_config_bump(RConfig * cfg,const char * key)714 R_API void r_config_bump(RConfig *cfg, const char *key) {
715 char *orig = strdup (r_config_get (cfg, key));
716 if (orig) {
717 r_config_set (cfg, key, orig);
718 free (orig);
719 }
720 }
721
r_config_serialize(R_NONNULL RConfig * config,R_NONNULL Sdb * db)722 R_API void r_config_serialize(R_NONNULL RConfig *config, R_NONNULL Sdb *db) {
723 RListIter *iter;
724 RConfigNode *node;
725 r_list_foreach (config->nodes, iter, node) {
726 sdb_set (db, node->name, node->value, 0);
727 }
728 }
729
load_config_cb(void * user,const char * k,const char * v)730 static bool load_config_cb(void *user, const char *k, const char *v) {
731 RConfig *config = user;
732 RConfigNode *node = r_config_node_get (config, k);
733 if (node) {
734 r_config_set (config, k, v);
735 }
736 return true;
737 }
738
r_config_unserialize(R_NONNULL RConfig * config,R_NONNULL Sdb * db,R_NULLABLE char ** err)739 R_API bool r_config_unserialize(R_NONNULL RConfig *config, R_NONNULL Sdb *db, R_NULLABLE char **err) {
740 sdb_foreach (db, load_config_cb, config);
741 return true;
742 }
743