本文记录在C语言环境使用libyaml库解析YAML文件的方法及相关数据结构,并进一步封装libyaml作为配置文件解析模块。

参考libyaml版本: commit 690a781 on 2019/3/13

libyaml官方地址: https://github.com/yaml/libyaml

什么是YAML

YAML是一种对人可读性高,用于数据序列化的格式,很多编程语言都有该格式库。更多参考wikiyaml.org

libyaml

libyaml是一个用于解析和组装YAML格式数据的C语言库。这里仅关注YAML的解析。

libyaml中解析YAML有三种方式:

  • token-based
  • event-based
  • document-based

其中基于token和基于event的解析方式可以参考以下两个链接,这里对基于document的解析方式做一些说明作为补充。
https://pyyaml.org/wiki/LibYAML
https://www.wpsoftware.net/andrew/pages/libyaml.html

编译

参考项目README文件既可编译安装。

如果需要在另外的项目中生成动态链接库并静态链接libyaml.a,可以在configure命令中加入参数:

./configure CFLAGS="-g -O2 -fPIC"

但是,在动态链接库中静态链接其他库并不是一个好主意,除非你确定知道自己在做什么以及可能会有什么问题。一个原因是可能发生冲突的符号表,可以参考为什么不能在动态库里静态链接?

document-based parsing

document-based解析方式中初始化的部分与前面基于token和基于event的解析方式一致。

  1. 首先初始化一个parser
    yaml_parser_initialize
  2. 然后为parser设置输入,这里输入可以有三种
    • 字符串输入
      yaml_parser_set_input_string
    • 文件输入
      yaml_parser_set_input_file
    • 自定义输入回调函数。字符串和文件输入也是调用该方式实现的,libyaml集成了相应了回调函数。
      yaml_parser_set_input

parser初始化完成后开始解析,函数为

yaml_parser_load(yaml_parser_t *parser, yaml_document_t *document)

该函数返回1表示解析成功完成,返回0表示失败。由于YAML支持连续的多个文档,因此这里的成功只代表成功解析完成一个文档,后续可能还有其他文档,通过检查解析完成文档的root是否存在来判断该文档是否为空,空文档也就意味着输入中不再存在YAML文档了,如果root不为空,可以继续调用yaml_parser_load加载后续文档。

yaml_node_t *yaml_document_get_root_node(yaml_document_t *document)

文档解析完成后,就需要读取document中的内容了,内容以yaml_node_t结构体为核心组织,该结构如下

yaml_node_t
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
typedef struct yaml_node_s yaml_node_t;

struct yaml_node_s {

/** The node type. */
yaml_node_type_t type;

/** The node tag. */
yaml_char_t *tag;

/** The node data. */
union {

/** The scalar parameters (for @c YAML_SCALAR_NODE). */
struct {
/** The scalar value. */
yaml_char_t *value;
/** The length of the scalar value. */
size_t length;
/** The scalar style. */
yaml_scalar_style_t style;
} scalar;

/** The sequence parameters (for @c YAML_SEQUENCE_NODE). */
struct {
/** The stack of sequence items. */
struct {
/** The beginning of the stack. */
yaml_node_item_t *start;
/** The end of the stack. */
yaml_node_item_t *end;
/** The top of the stack. */
yaml_node_item_t *top;
} items;
/** The sequence style. */
yaml_sequence_style_t style;
} sequence;

/** The mapping parameters (for @c YAML_MAPPING_NODE). */
struct {
/** The stack of mapping pairs (key, value). */
struct {
/** The beginning of the stack. */
yaml_node_pair_t *start;
/** The end of the stack. */
yaml_node_pair_t *end;
/** The top of the stack. */
yaml_node_pair_t *top;
} pairs;
/** The mapping style. */
yaml_mapping_style_t style;
} mapping;

} data;

/** The beginning of the node. */
yaml_mark_t start_mark;
/** The end of the node. */
yaml_mark_t end_mark;

};

这个结构体看起来有点大,但是理解起来并不很复杂,主要三个部分

  • type
    标识了该节点的具体类型,不同的类型在读取时对应联合体data中的不同成员。

    yaml_node_type_t
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    typedef enum yaml_node_type_e {
    /** An empty node. */
    YAML_NO_NODE,

    /** A scalar node. */
    YAML_SCALAR_NODE,
    /** A sequence node. */
    YAML_SEQUENCE_NODE,
    /** A mapping node. */
    YAML_MAPPING_NODE
    } yaml_node_type_t;
  • tag
    标识了该节点的tag,应用程序可能需要根据该tag做更上层的处理,比如!include这种tag就需要应用程序自己处理,libyaml仅提供tag内容的支持。

  • data
    这个联合体保存了该节点的数据,根据type的类型不同,使用联合体中不同的成员,每种类型成员有不同的组织方式。

    • scalar
      标量类型数据,可以理解为字节串。最简单的节点数据类型,value指针指向的字节串,长度由length标记。
    • sequence
      序列类型数据,序列中每个成员节点依然是yaml_node_t类型。这里通过指针指向了一个栈(start对应栈底,top对应栈顶,end对应栈顶top可以移动的内存边界,libyaml在mapping和document中均使用了该种逻辑记录栈的地址),这个栈保存了序列中每个成员节点的一个‘引用’,这里所说的引用实际上是一个int型索引,通过该索引可以查询到对应节点的地址。实际实现中所有yaml_node_t节点均保存在一个栈上(这个栈以数组方式实现,因此可以通过索引直接定位数组具体成员),索引数代表了该节点位于该栈的顺序位置。这个保存了所有节点的栈的地址保存在yaml_document_t成员nodes中,start、top、end意义与之前相同。以下伪代码用于遍历sequence

      sequence遍历
      1
      2
      3
      4
      5
      6
      7
      yaml_node_item_t    *item;
      yaml_node_t *value;
      for (item = node->data.sequence.items.start; item < node->data.sequence.items.top; item ++) {
      struct dp_conf_node *conf;
      value = yaml_document_get_node(doc, *item);
      /* 处理sequence中每个节点value */
      }
    • mapping
      散列表类型数据,成员pairs依然是一个栈(或者说数组),通过遍历该数组可以获得每一对节点对yaml_node_pair_t,节点对中成员key和value均为int型,可以猜到这里也是节点的索引。

      yaml_node_pair_t
      1
      2
      3
      4
      5
      6
      typedef struct yaml_node_pair_s {
      /** The key of the element. */
      int key;
      /** The value of the element. */
      int value;
      } yaml_node_pair_t;

      以下伪代码用于遍历mapping

      mapping遍历
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      yaml_node_pair_t    *pair;
      yaml_node_t *key;
      yaml_node_t *value;
      for (pair = node->data.mapping.pairs.start; pair < node->data.mapping.pairs.top; pair ++) {
      struct dp_conf_node *conf;
      key = yaml_document_get_node(doc, pair->key);
      value = yaml_document_get_node(doc, pair->value);

      /* key类型应该是YAML_SCALAR_NODE */

      /* 处理value节点 */
      }

document中节点内存结构

以一个YAML文件为例,展示document中节点内存结构如下:

1
2
3
4
kscalar: value1
ksequence:
- value2
- value3

document中节点内存结构

一种配置模块实现

使用libyaml解析YAML格式文件,参考suricata配置模块的api。

conf.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#ifndef __DP_CONF_H
#define __DP_CONF_H

#define LOG_ERROR(fmt, ...) printf("%s:%d " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_WARN(fmt, ...) printf("%s:%d " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_INFO(fmt, ...) printf("%s:%d " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_DEBUG(fmt, ...) printf("%s:%d " fmt, __FILE__, __LINE__, ##__VA_ARGS__)

#define DP_CONF_NODE_NAME_MAX 1024
#define DP_CONF_NODE_LEVEL_MAX 128

enum dp_conf_value_type {
DP_CONF_VALUE_TYPE_NONE,
DP_CONF_VALUE_TYPE_SCALAR,
DP_CONF_VALUE_TYPE_MAPPING,
DP_CONF_VALUE_TYPE_SEQUENCE,
};

struct dp_conf_node {
enum dp_conf_value_type value_type;

char *name;
char *value;

struct dp_conf_node *parent;
struct dp_conf_node *head;
struct dp_conf_node *next;
};

/**
* 解析YAML格式文件
* 返回配置根
* */
extern struct dp_conf_node *conf_parse_file(const char *yaml_file);

/**
* 仅查找单层子节点
* */
extern struct dp_conf_node *conf_node_lookup_child(struct dp_conf_node *node, const char *key);

/**
* 查找配置树形结构,通过点分割字符串定位配置节点,比如"server.redis.port"
* */
extern struct dp_conf_node *conf_get_node(struct dp_conf_node *node, const char *name);

/**
* 将一个配置节点从所在的树形结构中移除
* 该节点下属的子节点结构不变,且无内存释放
* */
extern void conf_remove_node(struct dp_conf_node *node);

/**
* 向日志输出配置信息
* */
extern void conf_dump(struct dp_conf_node *node, const char *prefix);

/**
* 释放以该节点为根的所有结构内存,内部隐含移除操作
* */
extern void conf_free_root(struct dp_conf_node *root);

/**
* 获取配置的字符串值,通过点分割字符串定位配置
* 返回0成功,其他失败
* */
extern int conf_get_value(struct dp_conf_node *node, const char *name, char **ptr);

/**
* 获取配置的有符号长整型值,通过点分割字符串定位配置
* 返回0成功,其他失败
* */
extern int conf_get_long(struct dp_conf_node *node, const char *name, long *ptr);

/**
* 获取配置的布尔值,通过点分割字符串定位配置。
* 配置文件中真值允许设置为"true, yes, on, 1"
* 返回0成功,其他失败
* 返回成功后,*ptr为1代表布尔为真,*ptr为0代表布尔为假
* */
extern int conf_get_bool(struct dp_conf_node *node, const char *name, int *ptr);

/**
* 设置或更新一个配置,通过点分割字符串定位配置,中间层级类型必须是mapping
* 返回0成功,其他失败
* */
extern int conf_set(struct dp_conf_node *node, const char *name, char *value);

/**
* 填充从根到配置节点的全路径名
* 返回写入的字符串长度,不包含结尾的NULL
* 如果缓冲区长度不足,则会截断,并返回期望的缓冲区长度,不包含结尾的NULL
* */
extern int conf_fill_path(char *buf, int len, const struct dp_conf_node *node);

#endif
conf.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <yaml.h>

#include "conf.h"

static char *type_to_string(enum dp_conf_value_type type) {
switch (type) {
case DP_CONF_VALUE_TYPE_NONE:
return "none";
case DP_CONF_VALUE_TYPE_SCALAR:
return "scalar";
case DP_CONF_VALUE_TYPE_SEQUENCE:
return "sequence";
case DP_CONF_VALUE_TYPE_MAPPING:
return "mapping";
default:
return "unknown";
}
}

/**
* 填充从根开始的全路径
* 返回写入的字符串长度,不包含结尾的NULL
* 如果缓冲区长度不足,则会截断,并返回期望的缓冲区长度,不包含结尾的NULL
* */
static int fill_path_name(char *buf, int len, const struct dp_conf_node *conf) {
int key_len;
int n;

if (conf->parent) {
n = fill_path_name(buf, len, conf->parent);

if (n >= len - 1) {
// 已经满了,缓冲区不足
key_len = 0;
} else {
// 有空间继续写
if (conf->parent->value_type == DP_CONF_VALUE_TYPE_SEQUENCE) {
key_len = snprintf(buf + n, len - n, "[%s]", conf->name);
} else if (conf->parent->value_type == DP_CONF_VALUE_TYPE_MAPPING) {
if (conf->parent->parent)
key_len = snprintf(buf + n, len - n, ".%s", conf->name);
else
key_len = snprintf(buf + n, len - n, "%s", conf->name);
} else {
LOG_ERROR("fill conf path name failed\n");
key_len = 0;
}
}

return n + key_len;
} else {
if (conf->value_type == DP_CONF_VALUE_TYPE_SEQUENCE) {
return 0;
} else if (conf->value_type == DP_CONF_VALUE_TYPE_MAPPING) {
return 0;
} else {
// 出错
LOG_ERROR("fill conf path name failed\n");
return 0;
}
}
}

static struct dp_conf_node *new_conf_node() {
struct dp_conf_node *conf;
conf = (struct dp_conf_node *)malloc(sizeof(*conf));
if (conf)
memset(conf, '\0', sizeof(*conf));
else {
LOG_ERROR("out of memory\n");
return NULL;
}
conf->value_type = DP_CONF_VALUE_TYPE_NONE;
return conf;
}

static int cpy_yaml_scalar_string(const struct dp_conf_node *conf, char **str, yaml_node_t *node) {
if (node->data.scalar.length <= 0) {
char buf[DP_CONF_NODE_NAME_MAX];
fill_path_name(buf, sizeof(buf), conf);
LOG_ERROR("invalid scalar len %d for configuration %s\n", node->data.scalar.length, buf);
return -1;
}
*str = (char *)malloc(node->data.scalar.length + 1);
if (*str == NULL) {
LOG_ERROR("out of memory\n");
return -1;
}
memcpy(*str, node->data.scalar.value, node->data.scalar.length);
(*str)[node->data.scalar.length] = '\0';
return 0;
}

static int parse_yaml_node(struct dp_conf_node *parent, yaml_document_t *doc, yaml_node_t *node) {
if (node->type == YAML_NO_NODE) {
LOG_ERROR("invalid yaml no node\n");
return -1;
} else if (node->type == YAML_SCALAR_NODE) {
LOG_ERROR("invalid yaml scalar node\n");
return -1;
} else if (node->type == YAML_SEQUENCE_NODE) {
yaml_node_item_t *item;
yaml_node_t *value;
struct dp_conf_node **pprev;
int i, n;

parent->value_type = DP_CONF_VALUE_TYPE_SEQUENCE;
pprev = &(parent->head);

for (item = node->data.sequence.items.start, i = 0; item < node->data.sequence.items.top; item ++, i ++) {
struct dp_conf_node *conf;
value = yaml_document_get_node(doc, *item);

conf = new_conf_node();
if (conf == NULL) {
return -1;
}

conf->parent = parent;
n = snprintf(NULL, 0, "%d", i);
conf->name = (char *)malloc(n + 1);
if (conf->name == NULL) {
LOG_ERROR("out of memory\n");
return -1;
}
snprintf(conf->name, n + 1, "%d", i);

*pprev = conf;
pprev = &(conf->next);

if (value->type == YAML_SCALAR_NODE) {
if (cpy_yaml_scalar_string(conf, &(conf->value), value) != 0) {
return -1;
}
conf->value_type = DP_CONF_VALUE_TYPE_SCALAR;
} else {
if (parse_yaml_node(conf, doc, value) != 0) {
return -1;
}
}
}
} else if (node->type == YAML_MAPPING_NODE) {
yaml_node_pair_t *pair;
yaml_node_t *key;
yaml_node_t *value;
struct dp_conf_node **pprev;

parent->value_type = DP_CONF_VALUE_TYPE_MAPPING;
pprev = &(parent->head);

for (pair = node->data.mapping.pairs.start; pair < node->data.mapping.pairs.top; pair ++) {
struct dp_conf_node *conf;
key = yaml_document_get_node(doc, pair->key);
value = yaml_document_get_node(doc, pair->value);

if (key->type != YAML_SCALAR_NODE) {
LOG_ERROR("invalid key node type %d\n", key->type);
return -1;
}

conf = new_conf_node();
if (conf == NULL) {
return -1;
}

conf->parent = parent;
// 由于还没有name,传入parent作为错误定位
if (cpy_yaml_scalar_string(conf->parent, &(conf->name), key) != 0) {
// 插进去,等待统一释放
*pprev = conf;
pprev = &(conf->next);
return -1;
}

// 处理key冲突
if (conf_node_lookup_child(parent, conf->name)) {
char buf[DP_CONF_NODE_NAME_MAX];
int n;
// 插进去,等待统一释放
*pprev = conf;
pprev = &(conf->next);

n = fill_path_name(buf, sizeof(buf), conf);
LOG_ERROR("conflict configuration key \"%s\"%s\n", buf, n >= sizeof(buf) - 1 ? "(truncated)" : "");
return -1;
}

*pprev = conf;
pprev = &(conf->next);

if (value->type == YAML_SCALAR_NODE) {
if (cpy_yaml_scalar_string(conf, &(conf->value), value) != 0) {
return -1;
}
conf->value_type = DP_CONF_VALUE_TYPE_SCALAR;
} else {
if (parse_yaml_node(conf, doc, value) != 0) {
return -1;
}
}
}
} else {
LOG_ERROR("unknown yaml node type %d\n", node->type);
return -1;
}
return 0;
}

static struct dp_conf_node *parse_yaml_doc(yaml_document_t *doc) {
struct dp_conf_node *conf;
yaml_node_t *root;

root = yaml_document_get_root_node(doc);
if (root == NULL) {
LOG_ERROR("empty yaml document\n");
return NULL;
}

conf = new_conf_node();
if (conf == NULL) {
return NULL;
}

if (parse_yaml_node(conf, doc, root) != 0) {
conf_free_root(conf);
return NULL;
}

return conf;
}

struct dp_conf_node *conf_parse_file(const char *f) {
yaml_parser_t parser;
yaml_document_t doc;
FILE *file;
struct dp_conf_node *root = NULL;

file = fopen(f, "r");
if (file == NULL) {
LOG_ERROR("fopen failed, %s\n", strerror(errno));
goto err_ret;
}

if (yaml_parser_initialize(&parser) != 1) {
LOG_ERROR("yaml_parser_initialize failed\n");
goto err_parser_ret;
}

yaml_parser_set_input_file(&parser, file);

if (yaml_parser_load(&parser, &doc) != 1) {
LOG_ERROR("yaml_parser_load failed\n");
goto err_load_ret;
}

root = parse_yaml_doc(&doc);

yaml_document_delete(&doc);

err_load_ret:
yaml_parser_delete(&parser);

err_parser_ret:
fclose(file);

err_ret:
return root;
}

void conf_free_root(struct dp_conf_node *root) {
struct dp_conf_node *child;

conf_remove_node(root);

child = root->head;
while (child != NULL) {
struct dp_conf_node *tmp = child->next;
conf_free_root(child);
child = tmp;
}

if (root->head)
conf_free_root(root->head);
if (root->name)
free(root->name);
if (root->value)
free(root->value);
free(root);
}

struct dp_conf_node *conf_node_lookup_child(struct dp_conf_node *node, const char *key) {
struct dp_conf_node *child;

for (child = node->head; child != NULL; child = child->next) {
if (child->name && strcmp(child->name, key) == 0) {
return child;
}
}
return NULL;
}

struct dp_conf_node *conf_get_node(struct dp_conf_node *node, const char *name) {
char buf[DP_CONF_NODE_NAME_MAX];
char *key;
char *next;

if (strlen(name) >= sizeof(buf)) {
LOG_ERROR("configuration name too long\n");
return NULL;
}

strcpy(buf, name);

key = buf;
do {
if ((next = strchr(key, '.')) != NULL)
*(next++) = '\0';
node = conf_node_lookup_child(node, key);
key = next;
} while (next != NULL && node != NULL);

return node;
}

/**
* 因为没有填充value和head,还不确定节点类型,因此仅用于内部使用
* */
static struct dp_conf_node *conf_get_node_or_create(struct dp_conf_node *node, const char *name) {

struct dp_conf_node *parent;
char buf[DP_CONF_NODE_NAME_MAX];
char *key;
char *next;

if (strlen(name) >= sizeof(buf)) {
LOG_ERROR("configuration name too long\n");
return NULL;
}

strcpy(buf, name);

key = buf;

parent = node;
do {
if ((next = strchr(key, '.')) != NULL)
*(next++) = '\0';
if ((node = conf_node_lookup_child(parent, key)) == NULL) {
if (parent->value_type != DP_CONF_VALUE_TYPE_MAPPING) {
LOG_ERROR("confituration can not create node in parent node type '%s'\n", type_to_string(parent->value_type));
return NULL;
}

node = new_conf_node();
if (node == NULL) {
return NULL;
}
node->name = strdup(key);
if (node->name == NULL) {
LOG_ERROR("out of memory\n");
free(node);
return NULL;
}
node->parent = parent;
node->next = parent->head;
parent->head = node;
if (next != NULL) {
node->value_type = DP_CONF_VALUE_TYPE_MAPPING;
} else {
node->value_type = DP_CONF_VALUE_TYPE_NONE;
}
}

key = next;
parent = node;
} while (next != '\0');

return node;
}

void conf_remove_node(struct dp_conf_node *node) {
if (node->parent) {
struct dp_conf_node **pprev = &(node->parent->head);
while (*pprev != node && *pprev != NULL) {
pprev = &((*pprev)->next);
}
if (*pprev == NULL) {
char buf[DP_CONF_NODE_NAME_MAX];
fill_path_name(buf, sizeof(buf), node);
LOG_ERROR("configuration remove node '%s' failed\n", buf);
return;
}
*pprev = (*pprev)->next;
node->next = NULL;
node->parent = NULL;
}
}

static void _conf_dump(const struct dp_conf_node *node, const char *prefix, char **name, int level) {
struct dp_conf_node *child;
char buf[DP_CONF_NODE_NAME_MAX];
int i;
int n;
int len;

for (child = node->head; child != NULL; child = child->next) {
name[level] = strdup(child->name);

if (child->value_type == DP_CONF_VALUE_TYPE_SCALAR) {
len = 0;
for (i = 0; i <= level && len < sizeof(buf) - 1; i ++) {
if (i == 0) {
n = snprintf(buf + len, sizeof(buf) - len, "%s", name[i]);
} else {
n = snprintf(buf + len, sizeof(buf) - len, ".%s", name[i]);
}
len += n;
}
if (prefix) {
LOG_DEBUG("%s.%s%s = %s\n", prefix, buf, len >= sizeof(buf) - 1 ? "(truncated)" : "", child->value);
} else {
LOG_DEBUG("%s%s = %s\n", buf, len >= sizeof(buf) - 1 ? "(truncated)" : "", child->value);
}
} else if (child->value_type == DP_CONF_VALUE_TYPE_SEQUENCE || child->value_type == DP_CONF_VALUE_TYPE_MAPPING) {
_conf_dump(child, prefix, name, level + 1);
} else {
LOG_ERROR("invalid configuration node type '%s'\n", type_to_string(child->value_type));
}

free(name[level]);
}
}

void conf_dump(struct dp_conf_node *node, const char *prefix) {
char *name[DP_CONF_NODE_LEVEL_MAX];

_conf_dump(node, prefix, name, 0);
}

int conf_get_value(struct dp_conf_node *node, const char *name, char **ptr) {
node = conf_get_node(node, name);
if (node && node->value_type == DP_CONF_VALUE_TYPE_SCALAR) {
*ptr = node->value;
return 0;
} else {
return -1;
}
}

int conf_get_long(struct dp_conf_node *node, const char *name, long *ptr) {
char *endptr;
node = conf_get_node(node, name);
if (node && node->value_type == DP_CONF_VALUE_TYPE_SCALAR) {
long l = strtol(node->value, &endptr, 0);
if (*(node->value) != '\0' && *endptr == '\0') {
*ptr = l;
return 0;
}
}
return -1;
}

static int conf_value_is_true(char *val) {
char *true_value[] = {
"true",
"yes",
"on",
"1"
};
int i;

for (i = 0; i < sizeof(true_value) / sizeof(true_value[0]); i ++) {
if (strcasecmp(val, true_value[i]) == 0) {
return 1;
}
}
return 0;
}

int conf_get_bool(struct dp_conf_node *node, const char *name, int *ptr) {
char *val;
*ptr = 0;
if (conf_get_value(node, name, &val) == 0) {
*ptr = conf_value_is_true(val);
return 0;
}
return -1;
}

int conf_set(struct dp_conf_node *node, const char *name, char *value) {
char *old_value;

node = conf_get_node_or_create(node, name);
if (node == NULL) {
return -1;
}

if (node->value_type != DP_CONF_VALUE_TYPE_NONE &&
node->value_type != DP_CONF_VALUE_TYPE_SCALAR) {
LOG_ERROR("configuration can not set value for node type '%s'\n", type_to_string(node->value_type));
return -1;
}

old_value = node->value;
node->value = strdup(value);
if (node->value == NULL) {
LOG_ERROR("out of memory\n");
if (node->value_type == DP_CONF_VALUE_TYPE_NONE) {
// 刚刚生成的节点,移除
conf_free_root(node);
} else {
// 已有节点,恢复原值
node->value = old_value;
}
return -1;
}

node->value_type = DP_CONF_VALUE_TYPE_SCALAR;
if (old_value)
free(old_value);
return 0;

}

int conf_fill_path(char *buf, int len, const struct dp_conf_node *node) {
return fill_path_name(buf, len, node);
}
test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

#include "conf.h"

int main(int argc, char **argv) {
struct dp_conf_node *root;

if (argc < 2)
return -1;

root = conf_parse_file(argv[1]);
if (root) {
conf_dump(root, "");
conf_free_root(root);
return 0;
} else {
return -1;
}
}