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