]> sourceware.org Git - lvm2.git/blame - libdm/libdm-config.c
metadata: use lv_hash in segment-specific metadata parsing
[lvm2.git] / libdm / libdm-config.c
CommitLineData
e59e2f7c
PR
1/*
2 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
3 * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
4 *
5 * This file is part of LVM2.
6 *
7 * This copyrighted material is made available to anyone wishing to use,
8 * modify, copy, or redistribute it subject to the terms and conditions
9 * of the GNU Lesser General Public License v.2.1.
10 *
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, write to the Free Software Foundation,
fcbef05a 13 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
e59e2f7c
PR
14 */
15
7f97c7ea 16#include "libdm/misc/dmlib.h"
e59e2f7c
PR
17
18#include <sys/stat.h>
19#include <sys/mman.h>
20#include <unistd.h>
21#include <fcntl.h>
22#include <ctype.h>
589983a2 23#include <stdarg.h>
e59e2f7c
PR
24
25#define SECTION_B_CHAR '{'
26#define SECTION_E_CHAR '}'
27
28enum {
29 TOK_INT,
30 TOK_FLOAT,
31 TOK_STRING, /* Single quotes */
32 TOK_STRING_ESCAPED, /* Double quotes */
8bc99667 33 TOK_STRING_BARE, /* No quotes */
e59e2f7c
PR
34 TOK_EQ,
35 TOK_SECTION_B,
36 TOK_SECTION_E,
37 TOK_ARRAY_B,
38 TOK_ARRAY_E,
39 TOK_IDENTIFIER,
40 TOK_COMMA,
41 TOK_EOF
42};
43
44struct parser {
45 const char *fb, *fe; /* file limits */
46
47 int t; /* token limits and type */
48 const char *tb, *te;
49
50 int line; /* line number we are on */
51
52 struct dm_pool *mem;
7563e69c 53 int no_dup_node_check; /* whether to disable dup node checking */
85dbcda1
ZK
54 const char *key; /* last obtained key */
55 unsigned ignored_creation_time;
e59e2f7c
PR
56};
57
e29cd366 58struct config_output {
e59e2f7c
PR
59 struct dm_pool *mem;
60 dm_putline_fn putline;
e29cd366
PR
61 const struct dm_config_node_out_spec *spec;
62 void *baton;
e59e2f7c
PR
63};
64
65static void _get_token(struct parser *p, int tok_prev);
66static void _eat_space(struct parser *p);
67static struct dm_config_node *_file(struct parser *p);
687029cb 68static struct dm_config_node *_section(struct parser *p, struct dm_config_node *parent);
e59e2f7c
PR
69static struct dm_config_value *_value(struct parser *p);
70static struct dm_config_value *_type(struct parser *p);
71static int _match_aux(struct parser *p, int t);
72static struct dm_config_value *_create_value(struct dm_pool *mem);
73static struct dm_config_node *_create_node(struct dm_pool *mem);
74static char *_dup_tok(struct parser *p);
687029cb 75static char *_dup_token(struct dm_pool *mem, const char *b, const char *e);
e59e2f7c 76
e59e2f7c
PR
77#define MAX_INDENT 32
78
79#define match(t) do {\
80 if (!_match_aux(p, (t))) {\
81 log_error("Parse error at byte %" PRIptrdiff_t " (line %d): unexpected token", \
82 p->tb - p->fb + 1, p->line); \
83 return 0;\
84 } \
55c9286d 85} while(0)
e59e2f7c
PR
86
87static int _tok_match(const char *str, const char *b, const char *e)
88{
89 while (*str && (b != e)) {
90 if (*str++ != *b++)
91 return 0;
92 }
93
94 return !(*str || (b != e));
95}
96
e38b557a 97struct dm_config_tree *dm_config_create(void)
e59e2f7c 98{
845b1df6 99 struct dm_config_tree *cft;
e59e2f7c
PR
100 struct dm_pool *mem = dm_pool_create("config", 10 * 1024);
101
102 if (!mem) {
103 log_error("Failed to allocate config pool.");
104 return 0;
105 }
106
845b1df6 107 if (!(cft = dm_pool_zalloc(mem, sizeof(*cft)))) {
e59e2f7c
PR
108 log_error("Failed to allocate config tree.");
109 dm_pool_destroy(mem);
110 return 0;
111 }
845b1df6 112 cft->mem = mem;
35ab841e 113
845b1df6 114 return cft;
e59e2f7c
PR
115}
116
117void dm_config_set_custom(struct dm_config_tree *cft, void *custom)
118{
845b1df6 119 cft->custom = custom;
e59e2f7c
PR
120}
121
122void *dm_config_get_custom(struct dm_config_tree *cft)
123{
845b1df6 124 return cft->custom;
e59e2f7c
PR
125}
126
127void dm_config_destroy(struct dm_config_tree *cft)
128{
845b1df6 129 dm_pool_destroy(cft->mem);
e59e2f7c
PR
130}
131
c82c2beb
AK
132/*
133 * If there's a cascaded dm_config_tree, remove and return it, otherwise
134 * return NULL.
135 */
136struct dm_config_tree *dm_config_remove_cascaded_tree(struct dm_config_tree *cft)
137{
5a6ae6f5 138 struct dm_config_tree *second_cft;
c82c2beb 139
5a6ae6f5
ZK
140 if (!cft)
141 return NULL;
142
143 second_cft = cft->cascade;
c82c2beb
AK
144 cft->cascade = NULL;
145
146 return second_cft;
147}
148
149/*
150 * When searching, first_cft is checked before second_cft.
151 */
152struct dm_config_tree *dm_config_insert_cascaded_tree(struct dm_config_tree *first_cft, struct dm_config_tree *second_cft)
153{
154 first_cft->cascade = second_cft;
155
156 return first_cft;
157}
158
956c1928
PR
159static struct dm_config_node *_config_reverse(struct dm_config_node *head)
160{
956c1928
PR
161 struct dm_config_node *left = head, *middle = NULL, *right = NULL;
162
39cffa4e 163 while (left) {
956c1928
PR
164 right = middle;
165 middle = left;
166 left = left->sib;
167 middle->sib = right;
168 middle->child = _config_reverse(middle->child);
39cffa4e 169 }
956c1928
PR
170
171 return middle;
39cffa4e 172}
956c1928 173
7563e69c 174static int _do_dm_config_parse(struct dm_config_tree *cft, const char *start, const char *end, int no_dup_node_check)
e59e2f7c
PR
175{
176 /* TODO? if (start == end) return 1; */
177
93115ef9
ZK
178 struct parser p = {
179 .mem = cft->mem,
180 .tb = start,
181 .te = start,
182 .fb = start,
183 .fe = end,
184 .line = 1,
185 .no_dup_node_check = no_dup_node_check
186 };
187
188 _get_token(&p, TOK_SECTION_E);
189 if (!(cft->root = _file(&p)))
e59e2f7c
PR
190 return_0;
191
956c1928
PR
192 cft->root = _config_reverse(cft->root);
193
e59e2f7c
PR
194 return 1;
195}
196
7563e69c
PR
197int dm_config_parse(struct dm_config_tree *cft, const char *start, const char *end)
198{
199 return _do_dm_config_parse(cft, start, end, 0);
200}
201
202int dm_config_parse_without_dup_node_check(struct dm_config_tree *cft, const char *start, const char *end)
203{
204 return _do_dm_config_parse(cft, start, end, 1);
205}
206
e59e2f7c
PR
207struct dm_config_tree *dm_config_from_string(const char *config_settings)
208{
209 struct dm_config_tree *cft;
210
845b1df6 211 if (!(cft = dm_config_create()))
e59e2f7c
PR
212 return_NULL;
213
214 if (!dm_config_parse(cft, config_settings, config_settings + strlen(config_settings))) {
215 dm_config_destroy(cft);
216 return_NULL;
217 }
218
219 return cft;
220}
221
e29cd366 222static int _line_start(struct config_output *out)
e59e2f7c 223{
e29cd366 224 if (!dm_pool_begin_object(out->mem, 128)) {
e59e2f7c
PR
225 log_error("dm_pool_begin_object failed for config line");
226 return 0;
227 }
228
229 return 1;
230}
231
347e1afd 232__attribute__ ((format(printf, 2, 3)))
e29cd366 233static int _line_append(struct config_output *out, const char *fmt, ...)
e59e2f7c
PR
234{
235 char buf[4096];
67c5006e 236 char *dyn_buf = NULL;
e59e2f7c
PR
237 va_list ap;
238 int n;
239
967a0889
PR
240 /*
241 * We should be fine with the 4096 char buffer 99% of the time,
242 * but if we need to go beyond that, allocate the buffer dynamically.
243 */
244
e59e2f7c 245 va_start(ap, fmt);
7239a45b 246 n = vsnprintf(buf, sizeof(buf), fmt, ap);
e59e2f7c
PR
247 va_end(ap);
248
967a0889 249 if (n < 0) {
e59e2f7c
PR
250 log_error("vsnprintf failed for config line");
251 return 0;
252 }
253
967a0889
PR
254 if (n > (int) sizeof buf - 1) {
255 /*
256 * Fixed size buffer with sizeof buf is not enough,
257 * so try dynamically allocated buffer now...
258 */
259 va_start(ap, fmt);
67c5006e 260 n = dm_vasprintf(&dyn_buf, fmt, ap);
967a0889
PR
261 va_end(ap);
262
263 if (n < 0) {
264 log_error("dm_vasprintf failed for config line");
265 return 0;
266 }
67c5006e 267 }
967a0889 268
67c5006e 269 if (!dm_pool_grow_object(out->mem, dyn_buf ? : buf, 0)) {
e59e2f7c 270 log_error("dm_pool_grow_object failed for config line");
67c5006e 271 dm_free(dyn_buf);
e59e2f7c
PR
272 return 0;
273 }
274
67c5006e
PR
275 dm_free(dyn_buf);
276
e59e2f7c
PR
277 return 1;
278}
279
e29cd366 280#define line_append(args...) do {if (!_line_append(out, args)) {return_0;}} while (0)
e59e2f7c 281
e29cd366 282static int _line_end(const struct dm_config_node *cn, struct config_output *out)
e59e2f7c
PR
283{
284 const char *line;
285
e29cd366 286 if (!dm_pool_grow_object(out->mem, "\0", 1)) {
e59e2f7c
PR
287 log_error("dm_pool_grow_object failed for config line");
288 return 0;
289 }
290
e29cd366 291 line = dm_pool_end_object(out->mem);
11e52025 292
e29cd366 293 if (!out->putline && !out->spec)
11e52025
PR
294 return 0;
295
e29cd366
PR
296 if (out->putline)
297 out->putline(line, out->baton);
298
299 if (out->spec && out->spec->line_fn)
300 out->spec->line_fn(cn, line, out->baton);
e59e2f7c
PR
301
302 return 1;
303}
304
e29cd366 305static int _write_value(struct config_output *out, const struct dm_config_value *v)
e59e2f7c
PR
306{
307 char *buf;
9465963f 308 const char *s;
e59e2f7c
PR
309
310 switch (v->type) {
311 case DM_CFG_STRING:
b6d3fd62 312 buf = alloca(dm_escaped_len(v->v.str));
9465963f
PR
313 s = (v->format_flags & DM_CONFIG_VALUE_FMT_STRING_NO_QUOTES) ? "" : "\"";
314 line_append("%s%s%s", s, dm_escape_double_quotes(buf, v->v.str), s);
e59e2f7c
PR
315 break;
316
317 case DM_CFG_FLOAT:
fe8f5dbe 318 line_append("%f", v->v.f);
e59e2f7c
PR
319 break;
320
321 case DM_CFG_INT:
9465963f
PR
322 if (v->format_flags & DM_CONFIG_VALUE_FMT_INT_OCTAL)
323 line_append("0%" PRIo64, v->v.i);
324 else
810ab095 325 line_append(FMTd64, v->v.i);
e59e2f7c
PR
326 break;
327
328 case DM_CFG_EMPTY_ARRAY:
9465963f
PR
329 s = (v->format_flags & DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES) ? " " : "";
330 line_append("[%s]", s);
e59e2f7c
PR
331 break;
332
333 default:
334 log_error("_write_value: Unknown value type: %d", v->type);
335
336 }
337
338 return 1;
339}
340
341static int _write_config(const struct dm_config_node *n, int only_one,
e29cd366 342 struct config_output *out, int level)
e59e2f7c 343{
9465963f
PR
344 const char *extra_space;
345 int format_array;
e59e2f7c
PR
346 char space[MAX_INDENT + 1];
347 int l = (level < MAX_INDENT) ? level : MAX_INDENT;
348 int i;
c3539495 349 char *escaped_key = NULL;
e59e2f7c
PR
350
351 if (!n)
352 return 1;
353
354 for (i = 0; i < l; i++)
355 space[i] = '\t';
356 space[i] = '\0';
357
358 do {
9465963f
PR
359 extra_space = (n->v && (n->v->format_flags & DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES)) ? " " : "";
360 format_array = (n->v && (n->v->format_flags & DM_CONFIG_VALUE_FMT_COMMON_ARRAY));
361
e29cd366
PR
362 if (out->spec && out->spec->prefix_fn)
363 out->spec->prefix_fn(n, space, out->baton);
364
365 if (!_line_start(out))
e59e2f7c 366 return_0;
c3539495
PR
367 if (strchr(n->key, '#') || strchr(n->key, '"') || strchr(n->key, '!')) {
368 escaped_key = alloca(dm_escaped_len(n->key) + 2);
369 *escaped_key = '"';
370 dm_escape_double_quotes(escaped_key + 1, n->key);
371 strcat(escaped_key, "\"");
372 }
373 line_append("%s%s", space, escaped_key ? escaped_key : n->key);
374 escaped_key = NULL;
e59e2f7c
PR
375 if (!n->v) {
376 /* it's a sub section */
377 line_append(" {");
e29cd366 378 if (!_line_end(n, out))
e59e2f7c 379 return_0;
9720898c
PR
380 if (!_write_config(n->child, 0, out, level + 1))
381 return_0;
e29cd366 382 if (!_line_start(out))
e59e2f7c
PR
383 return_0;
384 line_append("%s}", space);
385 } else {
386 /* it's a value */
387 const struct dm_config_value *v = n->v;
9465963f 388 line_append("%s=%s", extra_space, extra_space);
e59e2f7c 389 if (v->next) {
9465963f 390 line_append("[%s", extra_space);
e59e2f7c 391 while (v && v->type != DM_CFG_EMPTY_ARRAY) {
e29cd366 392 if (!_write_value(out, v))
e59e2f7c
PR
393 return_0;
394 v = v->next;
395 if (v && v->type != DM_CFG_EMPTY_ARRAY)
9465963f 396 line_append(",%s", extra_space);
e59e2f7c 397 }
9465963f
PR
398 line_append("%s]", extra_space);
399 } else {
400 if (format_array && (v->type != DM_CFG_EMPTY_ARRAY))
401 line_append("[%s", extra_space);
e29cd366 402 if (!_write_value(out, v))
e59e2f7c 403 return_0;
9465963f
PR
404 if (format_array && (v->type != DM_CFG_EMPTY_ARRAY))
405 line_append("%s]", extra_space);
406 }
e59e2f7c 407 }
e29cd366 408 if (!_line_end(n, out))
e59e2f7c 409 return_0;
e29cd366
PR
410
411 if (out->spec && out->spec->suffix_fn)
412 out->spec->suffix_fn(n, space, out->baton);
413
e59e2f7c
PR
414 n = n->sib;
415 } while (n && !only_one);
416 /* FIXME: add error checking */
417 return 1;
418}
419
5e36b86c 420static int _write_node(const struct dm_config_node *cn, int only_one,
e29cd366
PR
421 dm_putline_fn putline,
422 const struct dm_config_node_out_spec *out_spec,
423 void *baton)
e59e2f7c 424{
5e19410d
ZK
425 struct config_output out = {
426 .mem = dm_pool_create("config_output", 1024),
427 .putline = putline,
428 .spec = out_spec,
429 .baton = baton
430 };
431
432 if (!out.mem)
e59e2f7c 433 return_0;
5e19410d 434
e29cd366
PR
435 if (!_write_config(cn, only_one, &out, 0)) {
436 dm_pool_destroy(out.mem);
e59e2f7c
PR
437 return_0;
438 }
e29cd366 439 dm_pool_destroy(out.mem);
e59e2f7c
PR
440 return 1;
441}
442
5e36b86c
PR
443int dm_config_write_one_node(const struct dm_config_node *cn, dm_putline_fn putline, void *baton)
444{
e29cd366 445 return _write_node(cn, 1, putline, NULL, baton);
5e36b86c
PR
446}
447
448int dm_config_write_node(const struct dm_config_node *cn, dm_putline_fn putline, void *baton)
449{
e29cd366
PR
450 return _write_node(cn, 0, putline, NULL, baton);
451}
452
453int dm_config_write_one_node_out(const struct dm_config_node *cn,
454 const struct dm_config_node_out_spec *out_spec,
455 void *baton)
456{
457 return _write_node(cn, 1, NULL, out_spec, baton);
458}
459
460int dm_config_write_node_out(const struct dm_config_node *cn,
461 const struct dm_config_node_out_spec *out_spec,
462 void *baton)
463{
464 return _write_node(cn, 0, NULL, out_spec, baton);
5e36b86c
PR
465}
466
e59e2f7c
PR
467/*
468 * parser
469 */
4f439707
ZK
470static char *_dup_string_tok(struct parser *p)
471{
472 char *str;
473
474 p->tb++, p->te--; /* strip "'s */
475
476 if (p->te < p->tb) {
477 log_error("Parse error at byte %" PRIptrdiff_t " (line %d): "
478 "expected a string token.",
479 p->tb - p->fb + 1, p->line);
480 return NULL;
481 }
482
483 if (!(str = _dup_tok(p)))
484 return_NULL;
485
486 p->te++;
487
488 return str;
489}
490
e59e2f7c
PR
491static struct dm_config_node *_file(struct parser *p)
492{
687029cb
PR
493 struct dm_config_node root = { 0 };
494 root.key = "<root>";
495
496 while (p->t != TOK_EOF)
497 if (!_section(p, &root))
f45106b7 498 return_NULL;
687029cb
PR
499 return root.child;
500}
501
502static struct dm_config_node *_make_node(struct dm_pool *mem,
503 const char *key_b, const char *key_e,
504 struct dm_config_node *parent)
505{
506 struct dm_config_node *n;
507
508 if (!(n = _create_node(mem)))
509 return_NULL;
e59e2f7c 510
687029cb
PR
511 n->key = _dup_token(mem, key_b, key_e);
512 if (parent) {
513 n->parent = parent;
514 n->sib = parent->child;
515 parent->child = n;
e59e2f7c 516 }
687029cb
PR
517 return n;
518}
519
520/* when mem is not NULL, we create the path if it doesn't exist yet */
521static struct dm_config_node *_find_or_make_node(struct dm_pool *mem,
522 struct dm_config_node *parent,
7563e69c
PR
523 const char *path,
524 int no_dup_node_check)
687029cb 525{
026344e8 526 const int sep = '/';
687029cb
PR
527 const char *e;
528 struct dm_config_node *cn = parent ? parent->child : NULL;
529 struct dm_config_node *cn_found = NULL;
530
531 while (cn || mem) {
532 /* trim any leading slashes */
026344e8 533 while (*path && (*path == sep))
687029cb
PR
534 path++;
535
536 /* find the end of this segment */
026344e8 537 for (e = path; *e && (*e != sep); e++) ;
687029cb
PR
538
539 /* hunt for the node */
540 cn_found = NULL;
541
7563e69c
PR
542 if (!no_dup_node_check) {
543 while (cn) {
544 if (_tok_match(cn->key, path, e)) {
545 /* Inefficient */
546 if (!cn_found)
547 cn_found = cn;
548 else
549 log_warn("WARNING: Ignoring duplicate"
550 " config node: %s ("
551 "seeking %s)", cn->key, path);
552 }
687029cb 553
7563e69c
PR
554 cn = cn->sib;
555 }
687029cb
PR
556 }
557
558 if (!cn_found && mem) {
559 if (!(cn_found = _make_node(mem, path, e, parent)))
560 return_NULL;
561 }
562
563 if (cn_found && *e) {
564 parent = cn_found;
565 cn = cn_found->child;
566 } else
567 return cn_found;
568 path = e;
569 }
570
571 return NULL;
e59e2f7c
PR
572}
573
687029cb 574static struct dm_config_node *_section(struct parser *p, struct dm_config_node *parent)
e59e2f7c
PR
575{
576 /* IDENTIFIER SECTION_B_CHAR VALUE* SECTION_E_CHAR */
b6d3fd62 577
4c184e9d 578 struct dm_config_node *root;
0050480c 579 struct dm_config_value *value;
b6d3fd62
AK
580 char *str;
581
c3539495 582 if (p->t == TOK_STRING_ESCAPED) {
b6d3fd62
AK
583 if (!(str = _dup_string_tok(p)))
584 return_NULL;
585 dm_unescape_double_quotes(str);
b6d3fd62 586
c3539495 587 match(TOK_STRING_ESCAPED);
b6d3fd62
AK
588 } else if (p->t == TOK_STRING) {
589 if (!(str = _dup_string_tok(p)))
590 return_NULL;
b6d3fd62
AK
591
592 match(TOK_STRING);
593 } else {
687029cb 594 if (!(str = _dup_tok(p)))
b6d3fd62
AK
595 return_NULL;
596
c3539495 597 match(TOK_IDENTIFIER);
b6d3fd62
AK
598 }
599
687029cb 600 if (!strlen(str)) {
b6d3fd62
AK
601 log_error("Parse error at byte %" PRIptrdiff_t " (line %d): empty section identifier",
602 p->tb - p->fb + 1, p->line);
603 return NULL;
604 }
e59e2f7c 605
7563e69c 606 if (!(root = _find_or_make_node(p->mem, parent, str, p->no_dup_node_check)))
b1c40177 607 return_NULL;
687029cb 608
e59e2f7c
PR
609 if (p->t == TOK_SECTION_B) {
610 match(TOK_SECTION_B);
611 while (p->t != TOK_SECTION_E) {
4c184e9d 612 if (!(_section(p, root)))
f45106b7 613 return_NULL;
e59e2f7c
PR
614 }
615 match(TOK_SECTION_E);
616 } else {
617 match(TOK_EQ);
85dbcda1 618 p->key = root->key;
0050480c 619 if (!(value = _value(p)))
f45106b7 620 return_NULL;
0050480c
PR
621 if (root->v)
622 log_warn("WARNING: Ignoring duplicate"
623 " config value: %s", str);
624 root->v = value;
e59e2f7c
PR
625 }
626
627 return root;
628}
629
630static struct dm_config_value *_value(struct parser *p)
631{
632 /* '[' TYPE* ']' | TYPE */
633 struct dm_config_value *h = NULL, *l, *ll = NULL;
634 if (p->t == TOK_ARRAY_B) {
635 match(TOK_ARRAY_B);
636 while (p->t != TOK_ARRAY_E) {
637 if (!(l = _type(p)))
f45106b7 638 return_NULL;
e59e2f7c
PR
639
640 if (!h)
641 h = l;
642 else
643 ll->next = l;
644 ll = l;
645
646 if (p->t == TOK_COMMA)
647 match(TOK_COMMA);
648 }
649 match(TOK_ARRAY_E);
650 /*
651 * Special case for an empty array.
652 */
653 if (!h) {
d2c25f02
ZK
654 if (!(h = _create_value(p->mem))) {
655 log_error("Failed to allocate value");
656 return NULL;
657 }
e59e2f7c
PR
658
659 h->type = DM_CFG_EMPTY_ARRAY;
660 }
661
662 } else
60444f8b
ZK
663 if (!(h = _type(p)))
664 return_NULL;
e59e2f7c
PR
665
666 return h;
667}
668
669static struct dm_config_value *_type(struct parser *p)
670{
671 /* [+-]{0,1}[0-9]+ | [0-9]*\.[0-9]* | ".*" */
672 struct dm_config_value *v = _create_value(p->mem);
673 char *str;
674
d2c25f02
ZK
675 if (!v) {
676 log_error("Failed to allocate type value");
e59e2f7c 677 return NULL;
d2c25f02 678 }
e59e2f7c
PR
679
680 switch (p->t) {
681 case TOK_INT:
682 v->type = DM_CFG_INT;
ba9820b1 683 errno = 0;
e59e2f7c 684 v->v.i = strtoll(p->tb, NULL, 0); /* FIXME: check error */
ba9820b1 685 if (errno) {
85dbcda1
ZK
686 if (errno == ERANGE && p->key &&
687 strcmp("creation_time", p->key) == 0) {
688 /* Due to a bug in some older 32bit builds (<2.02.169),
689 * lvm was able to produce invalid creation_time string */
690 v->v.i = 1527120000; /* Pick 2018-05-24 day instead */
691 if (!p->ignored_creation_time++)
692 log_warn("WARNING: Invalid creation_time found in metadata (repaired with next metadata update).");
693 } else {
694 log_error("Failed to read int token.");
695 return NULL;
696 }
ba9820b1 697 }
e59e2f7c
PR
698 match(TOK_INT);
699 break;
700
701 case TOK_FLOAT:
702 v->type = DM_CFG_FLOAT;
ba9820b1 703 errno = 0;
fe8f5dbe 704 v->v.f = strtod(p->tb, NULL); /* FIXME: check error */
ba9820b1
ZK
705 if (errno) {
706 log_error("Failed to read float token.");
707 return NULL;
708 }
e59e2f7c
PR
709 match(TOK_FLOAT);
710 break;
711
712 case TOK_STRING:
713 v->type = DM_CFG_STRING;
714
4f439707 715 if (!(v->v.str = _dup_string_tok(p)))
f45106b7 716 return_NULL;
4f439707 717
e59e2f7c
PR
718 match(TOK_STRING);
719 break;
720
8bc99667
PR
721 case TOK_STRING_BARE:
722 v->type = DM_CFG_STRING;
723
724 if (!(v->v.str = _dup_tok(p)))
725 return_NULL;
726
727 match(TOK_STRING_BARE);
728 break;
729
e59e2f7c
PR
730 case TOK_STRING_ESCAPED:
731 v->type = DM_CFG_STRING;
732
4f439707 733 if (!(str = _dup_string_tok(p)))
f45106b7 734 return_NULL;
e59e2f7c
PR
735 dm_unescape_double_quotes(str);
736 v->v.str = str;
e59e2f7c
PR
737 match(TOK_STRING_ESCAPED);
738 break;
739
740 default:
741 log_error("Parse error at byte %" PRIptrdiff_t " (line %d): expected a value",
742 p->tb - p->fb + 1, p->line);
f45106b7 743 return NULL;
e59e2f7c
PR
744 }
745 return v;
746}
747
748static int _match_aux(struct parser *p, int t)
749{
750 if (p->t != t)
751 return 0;
752
753 _get_token(p, t);
754 return 1;
755}
756
757/*
39b7d1ba 758 * tokenizer
e59e2f7c
PR
759 */
760static void _get_token(struct parser *p, int tok_prev)
761{
762 int values_allowed = 0;
763
764 const char *te;
765
766 p->tb = p->te;
767 _eat_space(p);
768 if (p->tb == p->fe || !*p->tb) {
769 p->t = TOK_EOF;
770 return;
771 }
772
773 /* Should next token be interpreted as value instead of identifier? */
774 if (tok_prev == TOK_EQ || tok_prev == TOK_ARRAY_B ||
775 tok_prev == TOK_COMMA)
776 values_allowed = 1;
777
778 p->t = TOK_INT; /* fudge so the fall through for
779 floats works */
780
781 te = p->te;
782 switch (*te) {
783 case SECTION_B_CHAR:
784 p->t = TOK_SECTION_B;
785 te++;
786 break;
787
788 case SECTION_E_CHAR:
789 p->t = TOK_SECTION_E;
790 te++;
791 break;
792
793 case '[':
794 p->t = TOK_ARRAY_B;
795 te++;
796 break;
797
798 case ']':
799 p->t = TOK_ARRAY_E;
800 te++;
801 break;
802
803 case ',':
804 p->t = TOK_COMMA;
805 te++;
806 break;
807
808 case '=':
809 p->t = TOK_EQ;
810 te++;
811 break;
812
813 case '"':
814 p->t = TOK_STRING_ESCAPED;
815 te++;
816 while ((te != p->fe) && (*te) && (*te != '"')) {
817 if ((*te == '\\') && (te + 1 != p->fe) &&
818 *(te + 1))
819 te++;
820 te++;
821 }
822
823 if ((te != p->fe) && (*te))
824 te++;
825 break;
826
827 case '\'':
828 p->t = TOK_STRING;
829 te++;
830 while ((te != p->fe) && (*te) && (*te != '\''))
831 te++;
832
833 if ((te != p->fe) && (*te))
834 te++;
835 break;
836
837 case '.':
838 p->t = TOK_FLOAT;
839 /* Fall through */
840 case '0':
841 case '1':
842 case '2':
843 case '3':
844 case '4':
845 case '5':
846 case '6':
847 case '7':
848 case '8':
849 case '9':
850 case '+':
851 case '-':
852 if (values_allowed) {
853 while (++te != p->fe) {
854 if (!isdigit((int) *te)) {
855 if (*te == '.') {
856 if (p->t != TOK_FLOAT) {
857 p->t = TOK_FLOAT;
858 continue;
859 }
860 }
861 break;
862 }
863 }
864 break;
865 }
866 /* fall through */
867
868 default:
869 p->t = TOK_IDENTIFIER;
870 while ((te != p->fe) && (*te) && !isspace(*te) &&
871 (*te != '#') && (*te != '=') &&
872 (*te != SECTION_B_CHAR) &&
873 (*te != SECTION_E_CHAR))
874 te++;
8bc99667
PR
875 if (values_allowed)
876 p->t = TOK_STRING_BARE;
e59e2f7c
PR
877 break;
878 }
879
880 p->te = te;
881}
882
883static void _eat_space(struct parser *p)
884{
885 while (p->tb != p->fe) {
886 if (*p->te == '#')
887 while ((p->te != p->fe) && (*p->te != '\n') && (*p->te))
888 ++p->te;
889
890 else if (!isspace(*p->te))
891 break;
892
893 while ((p->te != p->fe) && isspace(*p->te)) {
894 if (*p->te == '\n')
895 ++p->line;
896 ++p->te;
897 }
898
899 p->tb = p->te;
900 }
901}
902
903/*
904 * memory management
905 */
906static struct dm_config_value *_create_value(struct dm_pool *mem)
907{
908 return dm_pool_zalloc(mem, sizeof(struct dm_config_value));
909}
910
911static struct dm_config_node *_create_node(struct dm_pool *mem)
912{
913 return dm_pool_zalloc(mem, sizeof(struct dm_config_node));
914}
915
687029cb 916static char *_dup_token(struct dm_pool *mem, const char *b, const char *e)
e59e2f7c 917{
687029cb
PR
918 size_t len = e - b;
919 char *str = dm_pool_alloc(mem, len + 1);
e59e2f7c
PR
920 if (!str) {
921 log_error("Failed to duplicate token.");
922 return 0;
923 }
687029cb 924 memcpy(str, b, len);
e59e2f7c
PR
925 str[len] = '\0';
926 return str;
927}
928
687029cb
PR
929static char *_dup_tok(struct parser *p)
930{
931 return _dup_token(p->mem, p->tb, p->te);
932}
933
e59e2f7c 934/*
fe8f5dbe
AK
935 * Utility functions
936 */
937
938/*
939 * node_lookup_fn is either:
940 * _find_config_node to perform a lookup starting from a given config_node
941 * in a config_tree;
942 * or
943 * _find_first_config_node to find the first config_node in a set of
944 * cascaded trees.
e59e2f7c 945 */
fe8f5dbe
AK
946typedef const struct dm_config_node *node_lookup_fn(const void *start, const char *path);
947
687029cb
PR
948static const struct dm_config_node *_find_config_node(const void *start, const char *path) {
949 struct dm_config_node dummy = { .child = (void *) start };
7563e69c 950 return _find_or_make_node(NULL, &dummy, path, 0);
e59e2f7c
PR
951}
952
441edcb5 953static const struct dm_config_node *_find_first_config_node(const void *start, const char *path)
e59e2f7c
PR
954{
955 const struct dm_config_tree *cft = start;
441edcb5 956 const struct dm_config_node *cn = NULL;
e59e2f7c
PR
957
958 while (cft) {
959 if ((cn = _find_config_node(cft->root, path)))
960 return cn;
961 cft = cft->cascade;
962 }
963
964 return NULL;
965}
966
fe8f5dbe 967static const char *_find_config_str(const void *start, node_lookup_fn find_fn,
7ad1c43b 968 const char *path, const char *fail, int allow_empty)
e59e2f7c 969{
fe8f5dbe 970 const struct dm_config_node *n = find_fn(start, path);
e59e2f7c 971
ebf55527
AK
972 /* Empty strings are ignored if allow_empty is set */
973 if (n && n->v) {
974 if ((n->v->type == DM_CFG_STRING) &&
975 (allow_empty || (*n->v->v.str))) {
9b6a62f9 976 /* log_very_verbose("Setting %s to %s", path, n->v->v.str); */
ebf55527
AK
977 return n->v->v.str;
978 }
979 if ((n->v->type != DM_CFG_STRING) || (!allow_empty && fail))
980 log_warn("WARNING: Ignoring unsupported value for %s.", path);
981 }
e59e2f7c
PR
982
983 if (fail)
984 log_very_verbose("%s not found in config: defaulting to %s",
985 path, fail);
986 return fail;
987}
988
989const char *dm_config_find_str(const struct dm_config_node *cn,
990 const char *path, const char *fail)
991{
7ad1c43b 992 return _find_config_str(cn, _find_config_node, path, fail, 0);
e59e2f7c
PR
993}
994
99a150fc
ZK
995const char *dm_config_find_str_allow_empty(const struct dm_config_node *cn,
996 const char *path, const char *fail)
997{
998 return _find_config_str(cn, _find_config_node, path, fail, 1);
999}
1000
fe8f5dbe 1001static int64_t _find_config_int64(const void *start, node_lookup_fn find,
e59e2f7c
PR
1002 const char *path, int64_t fail)
1003{
1004 const struct dm_config_node *n = find(start, path);
1005
1006 if (n && n->v && n->v->type == DM_CFG_INT) {
9b6a62f9 1007 /* log_very_verbose("Setting %s to %" PRId64, path, n->v->v.i); */
e59e2f7c
PR
1008 return n->v->v.i;
1009 }
1010
1011 log_very_verbose("%s not found in config: defaulting to %" PRId64,
1012 path, fail);
1013 return fail;
1014}
1015
fe8f5dbe 1016static float _find_config_float(const void *start, node_lookup_fn find,
e59e2f7c
PR
1017 const char *path, float fail)
1018{
1019 const struct dm_config_node *n = find(start, path);
1020
1021 if (n && n->v && n->v->type == DM_CFG_FLOAT) {
9b6a62f9 1022 /* log_very_verbose("Setting %s to %f", path, n->v->v.f); */
fe8f5dbe 1023 return n->v->v.f;
e59e2f7c
PR
1024 }
1025
1026 log_very_verbose("%s not found in config: defaulting to %f",
1027 path, fail);
1028
1029 return fail;
1030
1031}
1032
1033static int _str_in_array(const char *str, const char * const values[])
1034{
1035 int i;
1036
1037 for (i = 0; values[i]; i++)
1038 if (!strcasecmp(str, values[i]))
1039 return 1;
1040
1041 return 0;
1042}
1043
1044static int _str_to_bool(const char *str, int fail)
1045{
1046 const char * const _true_values[] = { "y", "yes", "on", "true", NULL };
1047 const char * const _false_values[] = { "n", "no", "off", "false", NULL };
1048
1049 if (_str_in_array(str, _true_values))
1050 return 1;
1051
1052 if (_str_in_array(str, _false_values))
1053 return 0;
1054
1055 return fail;
1056}
1057
fe8f5dbe 1058static int _find_config_bool(const void *start, node_lookup_fn find,
e59e2f7c
PR
1059 const char *path, int fail)
1060{
1061 const struct dm_config_node *n = find(start, path);
1062 const struct dm_config_value *v;
fccc6ea2
AK
1063 int b;
1064
1065 if (n) {
1066 v = n->v;
1067
1068 switch (v->type) {
1069 case DM_CFG_INT:
1070 b = v->v.i ? 1 : 0;
9b6a62f9 1071 /* log_very_verbose("Setting %s to %d", path, b); */
fccc6ea2
AK
1072 return b;
1073
1074 case DM_CFG_STRING:
1075 b = _str_to_bool(v->v.str, fail);
9b6a62f9 1076 /* log_very_verbose("Setting %s to %d", path, b); */
fccc6ea2
AK
1077 return b;
1078 default:
1079 ;
1080 }
e59e2f7c
PR
1081 }
1082
fccc6ea2
AK
1083 log_very_verbose("%s not found in config: defaulting to %d",
1084 path, fail);
1085
e59e2f7c
PR
1086 return fail;
1087}
1088
1089/***********************************
1090 * node-based lookup
1091 **/
1092
00ab1e3f 1093struct dm_config_node *dm_config_find_node(const struct dm_config_node *cn,
e68a6fbf 1094 const char *path)
e59e2f7c 1095{
441edcb5 1096 return (struct dm_config_node *) _find_config_node(cn, path);
e59e2f7c
PR
1097}
1098
1099int dm_config_find_int(const struct dm_config_node *cn, const char *path, int fail)
1100{
1101 /* FIXME Add log_error message on overflow */
1102 return (int) _find_config_int64(cn, _find_config_node, path, (int64_t) fail);
1103}
1104
82326847
PR
1105int64_t dm_config_find_int64(const struct dm_config_node *cn, const char *path, int64_t fail)
1106{
1107 return _find_config_int64(cn, _find_config_node, path, fail);
1108}
1109
e59e2f7c
PR
1110float dm_config_find_float(const struct dm_config_node *cn, const char *path,
1111 float fail)
1112{
1113 return _find_config_float(cn, _find_config_node, path, fail);
1114}
1115
1116int dm_config_find_bool(const struct dm_config_node *cn, const char *path, int fail)
1117{
1118 return _find_config_bool(cn, _find_config_node, path, fail);
1119}
1120
296385c0
PR
1121int dm_config_value_is_bool(const struct dm_config_value *v) {
1122 if (!v)
1123 return 0;
1124
1125 switch(v->type) {
1126 case DM_CFG_INT:
1127 return 1;
1128 case DM_CFG_STRING:
1129 return _str_to_bool(v->v.str, -1) != -1;
1130 default:
1131 return 0;
1132 }
1133}
1134
e59e2f7c
PR
1135/***********************************
1136 * tree-based lookup
1137 **/
1138
1139const struct dm_config_node *dm_config_tree_find_node(const struct dm_config_tree *cft,
1140 const char *path)
1141{
1142 return _find_first_config_node(cft, path);
1143}
1144
1145const char *dm_config_tree_find_str(const struct dm_config_tree *cft, const char *path,
1146 const char *fail)
1147{
7ad1c43b
ZK
1148 return _find_config_str(cft, _find_first_config_node, path, fail, 0);
1149}
1150
1151const char *dm_config_tree_find_str_allow_empty(const struct dm_config_tree *cft, const char *path,
1152 const char *fail)
1153{
1154 return _find_config_str(cft, _find_first_config_node, path, fail, 1);
e59e2f7c
PR
1155}
1156
1157int dm_config_tree_find_int(const struct dm_config_tree *cft, const char *path, int fail)
1158{
1159 /* FIXME Add log_error message on overflow */
1160 return (int) _find_config_int64(cft, _find_first_config_node, path, (int64_t) fail);
1161}
1162
1163int64_t dm_config_tree_find_int64(const struct dm_config_tree *cft, const char *path, int64_t fail)
1164{
1165 return _find_config_int64(cft, _find_first_config_node, path, fail);
1166}
1167
1168float dm_config_tree_find_float(const struct dm_config_tree *cft, const char *path,
1169 float fail)
1170{
1171 return _find_config_float(cft, _find_first_config_node, path, fail);
1172}
1173
1174int dm_config_tree_find_bool(const struct dm_config_tree *cft, const char *path, int fail)
1175{
1176 return _find_config_bool(cft, _find_first_config_node, path, fail);
1177}
1178
1179/************************************/
1180
1181
1182int dm_config_get_uint32(const struct dm_config_node *cn, const char *path,
97a4b516 1183 uint32_t *result)
e59e2f7c
PR
1184{
1185 const struct dm_config_node *n;
1186
97a4b516 1187 n = _find_config_node(cn, path);
e59e2f7c
PR
1188
1189 if (!n || !n->v || n->v->type != DM_CFG_INT)
1190 return 0;
1191
78042463 1192 if (result)
864ec23e 1193 *result = n->v->v.i;
e59e2f7c
PR
1194 return 1;
1195}
1196
1197int dm_config_get_uint64(const struct dm_config_node *cn, const char *path,
1198 uint64_t *result)
1199{
1200 const struct dm_config_node *n;
1201
97a4b516 1202 n = _find_config_node(cn, path);
e59e2f7c
PR
1203
1204 if (!n || !n->v || n->v->type != DM_CFG_INT)
1205 return 0;
1206
78042463 1207 if (result)
864ec23e 1208 *result = (uint64_t) n->v->v.i;
e59e2f7c
PR
1209 return 1;
1210}
1211
1212int dm_config_get_str(const struct dm_config_node *cn, const char *path,
1213 const char **result)
1214{
1215 const struct dm_config_node *n;
1216
97a4b516 1217 n = _find_config_node(cn, path);
e59e2f7c
PR
1218
1219 if (!n || !n->v || n->v->type != DM_CFG_STRING)
1220 return 0;
1221
78042463 1222 if (result)
864ec23e 1223 *result = n->v->v.str;
e59e2f7c
PR
1224 return 1;
1225}
1226
97a4b516
PR
1227int dm_config_get_list(const struct dm_config_node *cn, const char *path,
1228 const struct dm_config_value **result)
1229{
1230 const struct dm_config_node *n;
1231
1232 n = _find_config_node(cn, path);
1233 /* TODO when we represent single-item lists consistently, add a check
1234 * for n->v->next != NULL */
1235 if (!n || !n->v)
1236 return 0;
1237
864ec23e
PR
1238 if (result)
1239 *result = n->v;
97a4b516
PR
1240 return 1;
1241}
1242
1243int dm_config_get_section(const struct dm_config_node *cn, const char *path,
1244 const struct dm_config_node **result)
1245{
1246 const struct dm_config_node *n;
1247
1248 n = _find_config_node(cn, path);
1249 if (!n || n->v)
1250 return 0;
1251
864ec23e
PR
1252 if (result)
1253 *result = n;
97a4b516
PR
1254 return 1;
1255}
1256
1257int dm_config_has_node(const struct dm_config_node *cn, const char *path)
1258{
1259 return _find_config_node(cn, path) ? 1 : 0;
1260}
1261
e59e2f7c
PR
1262/*
1263 * Convert a token type to the char it represents.
1264 */
1265static char _token_type_to_char(int type)
1266{
1267 switch (type) {
1268 case TOK_SECTION_B:
1269 return SECTION_B_CHAR;
1270 case TOK_SECTION_E:
1271 return SECTION_E_CHAR;
1272 default:
1273 return 0;
1274 }
1275}
1276
1277/*
1278 * Returns:
1279 * # of 'type' tokens in 'str'.
1280 */
1281static unsigned _count_tokens(const char *str, unsigned len, int type)
1282{
1283 char c;
1284
1285 c = _token_type_to_char(type);
1286
1287 return dm_count_chars(str, len, c);
1288}
1289
1290const char *dm_config_parent_name(const struct dm_config_node *n)
1291{
1292 return (n->parent ? n->parent->key : "(root)");
1293}
1294/*
1295 * Heuristic function to make a quick guess as to whether a text
1296 * region probably contains a valid config "section". (Useful for
1297 * scanning areas of the disk for old metadata.)
1298 * Config sections contain various tokens, may contain other sections
1299 * and strings, and are delimited by begin (type 'TOK_SECTION_B') and
1300 * end (type 'TOK_SECTION_E') tokens. As a quick heuristic, we just
1301 * count the number of begin and end tokens, and see if they are
1302 * non-zero and the counts match.
1303 * Full validation of the section should be done with another function
1304 * (for example, read_config_fd).
1305 *
1306 * Returns:
1307 * 0 - probably is not a valid config section
1308 * 1 - probably _is_ a valid config section
1309 */
1310unsigned dm_config_maybe_section(const char *str, unsigned len)
1311{
1312 int begin_count;
1313 int end_count;
1314
1315 begin_count = _count_tokens(str, len, TOK_SECTION_B);
1316 end_count = _count_tokens(str, len, TOK_SECTION_E);
1317
1318 if (begin_count && end_count && (begin_count == end_count))
1319 return 1;
1320 else
1321 return 0;
1322}
1323
807a5a7b 1324__attribute__((nonnull(1, 2)))
e59e2f7c
PR
1325static struct dm_config_value *_clone_config_value(struct dm_pool *mem,
1326 const struct dm_config_value *v)
1327{
1328 struct dm_config_value *new_cv;
1329
e59e2f7c
PR
1330 if (!(new_cv = _create_value(mem))) {
1331 log_error("Failed to clone config value.");
1332 return NULL;
1333 }
1334
1335 new_cv->type = v->type;
1336 if (v->type == DM_CFG_STRING) {
1337 if (!(new_cv->v.str = dm_pool_strdup(mem, v->v.str))) {
1338 log_error("Failed to clone config string value.");
1339 return NULL;
1340 }
1341 } else
1342 new_cv->v = v->v;
1343
1344 if (v->next && !(new_cv->next = _clone_config_value(mem, v->next)))
1345 return_NULL;
1346
1347 return new_cv;
1348}
1349
1350struct dm_config_node *dm_config_clone_node_with_mem(struct dm_pool *mem, const struct dm_config_node *cn, int siblings)
1351{
1352 struct dm_config_node *new_cn;
1353
d2c25f02
ZK
1354 if (!cn) {
1355 log_error("Cannot clone NULL config node.");
e59e2f7c 1356 return NULL;
d2c25f02 1357 }
e59e2f7c
PR
1358
1359 if (!(new_cn = _create_node(mem))) {
1360 log_error("Failed to clone config node.");
1361 return NULL;
1362 }
1363
1364 if ((cn->key && !(new_cn->key = dm_pool_strdup(mem, cn->key)))) {
1365 log_error("Failed to clone config node key.");
1366 return NULL;
1367 }
1368
f6de196c
PR
1369 new_cn->id = cn->id;
1370
e59e2f7c
PR
1371 if ((cn->v && !(new_cn->v = _clone_config_value(mem, cn->v))) ||
1372 (cn->child && !(new_cn->child = dm_config_clone_node_with_mem(mem, cn->child, 1))) ||
1373 (siblings && cn->sib && !(new_cn->sib = dm_config_clone_node_with_mem(mem, cn->sib, siblings))))
1374 return_NULL; /* 'new_cn' released with mem pool */
1375
1376 return new_cn;
1377}
1378
1379struct dm_config_node *dm_config_clone_node(struct dm_config_tree *cft, const struct dm_config_node *node, int sib)
1380{
845b1df6 1381 return dm_config_clone_node_with_mem(cft->mem, node, sib);
e59e2f7c
PR
1382}
1383
1384struct dm_config_node *dm_config_create_node(struct dm_config_tree *cft, const char *key)
1385{
e59e2f7c
PR
1386 struct dm_config_node *cn;
1387
845b1df6 1388 if (!(cn = _create_node(cft->mem))) {
e59e2f7c
PR
1389 log_error("Failed to create config node.");
1390 return NULL;
1391 }
845b1df6 1392 if (!(cn->key = dm_pool_strdup(cft->mem, key))) {
e59e2f7c
PR
1393 log_error("Failed to create config node's key.");
1394 return NULL;
1395 }
e59e2f7c 1396 cn->parent = NULL;
847e2856
PR
1397 cn->v = NULL;
1398
e59e2f7c
PR
1399 return cn;
1400}
1401
1402struct dm_config_value *dm_config_create_value(struct dm_config_tree *cft)
1403{
845b1df6 1404 return _create_value(cft->mem);
e59e2f7c
PR
1405}
1406
9465963f
PR
1407void dm_config_value_set_format_flags(struct dm_config_value *cv, uint32_t format_flags)
1408{
1409 if (!cv)
1410 return;
1411
1412 cv->format_flags = format_flags;
1413}
1414
1415uint32_t dm_config_value_get_format_flags(struct dm_config_value *cv)
1416{
1417 if (!cv)
1418 return 0;
1419
1420 return cv->format_flags;
1421}
1422
e59e2f7c
PR
1423struct dm_pool *dm_config_memory(struct dm_config_tree *cft)
1424{
845b1df6 1425 return cft->mem;
e59e2f7c 1426}
274a7a68
PR
1427
1428static int _override_path(const char *path, struct dm_config_node *node, void *baton)
1429{
1430 struct dm_config_tree *cft = baton;
1431 struct dm_config_node dummy, *target;
1432 dummy.child = cft->root;
7563e69c 1433 if (!(target = _find_or_make_node(cft->mem, &dummy, path, 0)))
274a7a68
PR
1434 return_0;
1435 if (!(target->v = _clone_config_value(cft->mem, node->v)))
1436 return_0;
1437 cft->root = dummy.child;
1438 return 1;
1439}
1440
1441static int _enumerate(const char *path, struct dm_config_node *cn, int (*cb)(const char *, struct dm_config_node *, void *), void *baton)
1442{
1443 char *sub = NULL;
1444
1445 while (cn) {
1446 if (dm_asprintf(&sub, "%s/%s", path, cn->key) < 0)
1447 return_0;
1448 if (cn->child) {
1449 if (!_enumerate(sub, cn->child, cb, baton))
1450 goto_bad;
1451 } else
1452 if (!cb(sub, cn, baton))
1453 goto_bad;
1454 dm_free(sub);
1455 cn = cn->sib;
1456 }
1457 return 1;
1458bad:
1459 dm_free(sub);
1460 return 0;
1461}
1462
1463struct dm_config_tree *dm_config_flatten(struct dm_config_tree *cft)
1464{
1465 struct dm_config_tree *res = dm_config_create(), *done = NULL, *current = NULL;
1466
1467 if (!res)
1468 return_NULL;
1469
1470 while (done != cft) {
1471 current = cft;
1472 while (current->cascade != done)
1473 current = current->cascade;
1474 _enumerate("", current->root, _override_path, res);
1475 done = current;
1476 }
1477
1478 return res;
1479}
de2c5ab2 1480
ccdea661 1481int dm_config_remove_node(struct dm_config_node *parent, struct dm_config_node *rem_node)
de2c5ab2
PR
1482{
1483 struct dm_config_node *cn = parent->child, *last = NULL;
1484 while (cn) {
ccdea661 1485 if (cn == rem_node) {
de2c5ab2
PR
1486 if (last)
1487 last->sib = cn->sib;
1488 else
1489 parent->child = cn->sib;
1490 return 1;
1491 }
1492 last = cn;
1493 cn = cn->sib;
1494 }
1495 return 0;
1496}
This page took 0.287846 seconds and 6 git commands to generate.