yaml官方提供了一套文件解析的方法,动态库是libyaml.so。官方地址:https://yaml.org/spec/1.1/
但是这个库的解析方式还是太原始,不好用。我们想一个比较好用的方式获取数据。想到suricata(库在github上,地址:https://github.com/OISF/suricata)有一套yaml的解析方法。于是把suricata的代码拿过来,封装一下,改成了lib库,方便以后使用。
不过suricata的解析方法不支持 锚点 和 引用,使用时要注意配置文件的编写。
可以下载直接使用,代码也在下边列出来了,方便随时分析学习。
【2025-01-07】最近整理一下,把代码放在了 gitee 上:https://gitee.com/madbullsite/libparseyaml
目录结构

代码 Makefile
libs=libmyyaml.so
LIBS = -lyaml -lbsd -g -Wall
DLFLAGS = -shared -fPIC
%.o: %.c
gcc -fPIC -o $@ -c $^ -g
all: cmptest
libmyyaml.so: conf-yaml.o conf-yaml-loader.o
gcc $(DLFLAGS) -o $@ $^ $(LIBS)
install: $(libs)
mv $(libs) /usr/local/lib/
cp conf-yaml.h conf-yaml-loader.h queue.h /usr/local/include/
ldconfig
cmptest: install test
test: test.o
gcc -o $@ $^ -lmyyaml $(LIBS)
clean:
rm -f /usr/local/lib/$(libs) *.o test $(libs)
代码 conf-yaml-loader.c
#include <stdio.h>
#include <stdlib.h>
#include <yaml.h>
#include <limits.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <bsd/string.h>
#include "conf-yaml.h"
#include "conf-yaml-loader.h"
// 支持的版本号
#define YAML_VERSION_MAJOR 1
#define YAML_VERSION_MINOR 1
/* The maximum level of recursion allowed while parsing the YAML
* file. */
#define RECURSION_LIMIT 128
/* Sometimes we'll have to create a node name on the fly (integer
* conversion, etc), so this is a default length to allocate that will
* work most of the time. */
#define DEFAULT_NAME_LEN 16
#define MANGLE_ERRORS_MAX 10
static int mangle_errors = 0;
// yaml 配置文件路径
static char *conf_dirname = NULL;
static int ConfYamlParse(yaml_parser_t *parser, ConfNode *parent, int inseq, int rlevel, int state);
/* Configuration processing states. */
enum conf_state {
CONF_KEY = 0,
CONF_VAL,
CONF_INCLUDE,
};
/**
* 转换不支持的字符 _ 转换成 -
*/
static void
Mangle(char *string)
{
char *c;
while ((c = strchr(string, '_')))
*c = '-';
return;
}
/**
* 设置配置文件的目录,include引入的时候,如果是相对路径,则根据设置的路径拼接
* 全局变量 conf_dirname 赋值
* 参数 配置文件路径
*/
static void
ConfYamlSetConfDirname(const char *filename)
{
char *ep;
ep = strrchr(filename, '\\');
if (ep == NULL)
ep = strrchr(filename, '/');
if (ep == NULL) { // 没找到,用 . 作为当前目录
conf_dirname = strdup(".");
if (conf_dirname == NULL) {
fprintf(stderr, "ERROR: Failed to allocate memory while loading configuration.\n");
}
}
else { // 找到,copy出目录副本,并报错
conf_dirname = strdup(filename);
if (conf_dirname == NULL) {
fprintf(stderr, "ERROR: Failed to allocate memory while loading configuration.\n");
}
conf_dirname[ep - filename] = '\0';
}
}
/**
* 在配置里引入一个文件
* 参数
* parent 解析的数据都放在此节点
* filename 引入的文件名
* 返回值 0 - 解析成功
* 1 - 解析失败
*/
int ConfYamlHandleInclude(ConfNode *parent, const char *filename)
{
yaml_parser_t parser;
char include_filename[PATH_MAX];
FILE *file = NULL;
int ret = -1;
// 重新初始化新的解析句柄
if (yaml_parser_initialize(&parser) != 1) {
fprintf(stderr, "Failed to initialize YAML parser\n");
return -1;
}
// 判断是否为绝对路径
if (PathIsAbsolute(filename)) {
strlcpy(include_filename, filename, sizeof(include_filename));
}
else {
// 拼写出路径
snprintf(include_filename, sizeof(include_filename), "%s/%s",
conf_dirname, filename);
}
file = fopen(include_filename, "r");
if (file == NULL) {
fprintf(stderr, "Failed to open configuration include file %s: %s\n", include_filename,
strerror(errno));
goto done;
}
// 设置要解析的文件句柄
yaml_parser_set_input_file(&parser, file);
// include引入的文件 解析
if (ConfYamlParse(&parser, parent, 0, 0, 0) != 0) {
fprintf(stderr, "Failed to include configuration file %s\n", filename);
goto done;
}
ret = 0;
done:
yaml_parser_delete(&parser);
if (file != NULL) {
fclose(file);
}
return ret;
}
/**
* 解析yaml
*
* 参数 parser 活动的 yaml_parser_t 指针
* parent 父节点 node
* inseq 列表的解析
* rlevel
* state 当前 解析状态
*
* 返回值:0 - success, -1 - failure.
*
* 调用的地方:
* 1、 首次文档解析
* 2、 include引入的文件 解析
* 3、 列表的解析
* 4、 mapping的解析
*/
static int ConfYamlParse(yaml_parser_t *parser, ConfNode *parent, int inseq, int rlevel, int state)
{
ConfNode *node = parent;
yaml_event_t event;
memset(&event, 0, sizeof(event));
int done = 0;
int seq_idx = 0; // 列表的索引, 从 0 开始
int retval = 0;
int was_empty = -1;
int include_count = 0;
// rlevel 给递归层级增加,从新调用一次,就增加一次
if (rlevel++ > RECURSION_LIMIT) {
fprintf(stderr, "Recursion limit reached while parsing "
"configuration file, aborting.\n");
return -1;
}
while (!done) {
if (!yaml_parser_parse(parser, &event)) { // 解析下一个事件
fprintf(stderr, "Failed to parse configuration file at line %" PRIuMAX ": %s\n",
(uintmax_t)parser->problem_mark.line, parser->problem);
retval = -1;
break;
}
if (event.type == YAML_DOCUMENT_START_EVENT) { // 文档事件
fprintf(stdout, "event.type=YAML_DOCUMENT_START_EVENT; state=%d\n", state);
/* 验证 YAML 版本号 */
yaml_version_directive_t *ver =
event.data.document_start.version_directive;
if (ver == NULL) {
fprintf(stderr, "ERROR: Invalid configuration file.\n");
fprintf(stderr, "The configuration file must begin with the following two lines: %%YAML "
"1.1 and ---\n");
goto fail;
}
int major = ver->major;
int minor = ver->minor;
if (!(major == YAML_VERSION_MAJOR && minor == YAML_VERSION_MINOR)) {
fprintf(stderr, "ERROR: Invalid YAML version. Must be 1.1\n");
goto fail;
}
}
else if (event.type == YAML_SCALAR_EVENT) { // 标量
char *value = (char *)event.data.scalar.value; // 值
char *tag = (char *)event.data.scalar.tag; // 键
fprintf(stdout, "event.type=YAML_SCALAR_EVENT; state=%d; value=%s; "
"tag=%s; inseq=%d\n", state, value, tag, inseq);
// 跳过没有值的空标量
if (state == CONF_KEY && strlen(value) == 0) {
goto next;
}
/* If the value is unquoted, certain strings in YAML represent NULL. */
// 如果值没有引用,YAML中有些字符串表示NULL
if ((inseq || state == CONF_VAL) &&
event.data.scalar.style == YAML_PLAIN_SCALAR_STYLE) {
// 表示NULL的一些值
if (strlen(value) == 0 || strcmp(value, "~") == 0 || strcmp(value, "null") == 0 ||
strcmp(value, "Null") == 0 || strcmp(value, "NULL") == 0) {
value = NULL;
}
}
// 列表的处理
if (inseq) {
// 引入文件状态的处理
if (state == CONF_INCLUDE) {
if (value != NULL) {
fprintf(stdout, "Including configuration file %s.\n", value);
if (ConfYamlHandleInclude(parent, value) != 0) {
goto fail;
}
}
goto next;
}
// 把 索引作为 路径节点名字, 从 0 开始
char sequence_node_name[DEFAULT_NAME_LEN];
snprintf(sequence_node_name, DEFAULT_NAME_LEN, "%d", seq_idx++);
ConfNode *seq_node = NULL;
if (was_empty < 0) {
// initialize was_empty
if (TAILQ_EMPTY(&parent->head)) {
was_empty = 1;
} else {
was_empty = 0;
}
}
// 判断节点是否已经存在
if (was_empty == 0) {
seq_node = ConfNodeLookupChild(parent, sequence_node_name);
}
if (seq_node != NULL) {
// 这里先从兄弟队列里删除,20行之后等会还会加上,这里不会清数据
TAILQ_REMOVE(&parent->head, seq_node, next);
}
else {
// 创建节点,并赋值
seq_node = ConfNodeNew();
if (unlikely(seq_node == NULL)) {
goto fail;
}
seq_node->name = strdup(sequence_node_name);
if (unlikely(seq_node->name == NULL)) {
free(seq_node);
goto fail;
}
if (value != NULL) { // 如果value有值,则赋值
seq_node->val = strdup(value);
if (unlikely(seq_node->val == NULL)) {
free(seq_node->name);
goto fail;
}
} else { // 否则为 NULL
seq_node->val = NULL;
}
}
// 把节点插入到
TAILQ_INSERT_TAIL(&parent->head, seq_node, next);
}
else {
if (state == CONF_INCLUDE) {
fprintf(stdout, "Including configuration file %s.\n", value);
// 解析子文件,parent用来接收解析的数据,把值给到处理函数
if (ConfYamlHandleInclude(parent, value) != 0) {
goto fail;
}
// 解析完了,修改解析状态为:需要解析KEY
state = CONF_KEY;
}
else if (state == CONF_KEY) {
if (strcmp(value, "include") == 0) {
// 修改状态:包含子文件状态
state = CONF_INCLUDE;
if (++include_count > 1) {
fprintf(stderr, "Multipline \"include\" fields at the same level are "
"deprecated and will not work in Suricata 8, please move "
"to an array of include files: line: %zu\n",
parser->mark.line);
}
goto next;
}
if (parent->is_seq) {
if (parent->val == NULL) {
parent->val = strdup(value);
if (parent->val && strchr(parent->val, '_'))
// _ 转换成 -
Mangle(parent->val);
}
}
if (strchr(value, '.') != NULL) {
node = ConfNodeGetNodeOrCreate(parent, value, 0);
if (node == NULL) {
/* Error message already logged. */
goto fail;
}
} else {
ConfNode *existing = ConfNodeLookupChild(parent, value);
if (existing != NULL) {
if (!existing->final) {
fprintf(stdout, "Configuration node '%s' redefined.\n", existing->name);
ConfNodePrune(existing);
}
node = existing;
} else {
node = ConfNodeNew();
node->name = strdup(value);
node->parent = parent;
if (node->name && strchr(node->name, '_')) {
if (!(parent->name &&
((strcmp(parent->name, "address-groups") == 0) ||
(strcmp(parent->name, "port-groups") == 0)))) {
// _ 转换成 -
Mangle(node->name);
if (mangle_errors < MANGLE_ERRORS_MAX) {
fprintf(stderr,
"%s is deprecated. Please use %s on line %" PRIuMAX
".\n",
value, node->name,
(uintmax_t)parser->mark.line + 1);
mangle_errors++;
if (mangle_errors >= MANGLE_ERRORS_MAX)
fprintf(stderr,
"not showing more "
"parameter name warnings.\n");
}
}
}
TAILQ_INSERT_TAIL(&parent->head, node, next);
}
}
// 修改状态,需要获得值
state = CONF_VAL;
}
else {
if (value != NULL && (tag != NULL) && (strcmp(tag, "!include") == 0)) {
fprintf(stdout, "Including configuration file %s at "
"parent node %s.\n", value, node->name);
if (ConfYamlHandleInclude(node, value) != 0)
goto fail;
} else if (!node->final && value != NULL) {
if (node->val != NULL)
free(node->val);
node->val = strdup(value);
}
// 修改状态:需要解析KEY
state = CONF_KEY;
}
}
}
else if (event.type == YAML_SEQUENCE_START_EVENT) {
fprintf(stdout, "event.type=YAML_SEQUENCE_START_EVENT; state=%d\n", state);
// 如果处理列表是include列表,使用 当前节点
// 状态如果是 CONF_INCLUDE 下一级解析传递 parent节点,否则传递 当前节点
// inseq 标记为 1 此次处理的是被别的文件引入的
// 列表 解析进入 递归调用
if (ConfYamlParse(parser, state == CONF_INCLUDE ? parent : node, 1, rlevel,
state == CONF_INCLUDE ? CONF_INCLUDE : 0) != 0)
goto fail;
// 设置此节点是 列表
node->is_seq = 1;
// 修改状态:需要解析KEY
state = CONF_KEY;
}
else if (event.type == YAML_SEQUENCE_END_EVENT) { // 列表结束
fprintf(stderr, "event.type=YAML_SEQUENCE_END_EVENT; state=%d\n", state);
done = 1; // 结束,函数退出
}
else if (event.type == YAML_MAPPING_START_EVENT) { // MAPPING 开始事件
fprintf(stdout, "event.type=YAML_MAPPING_START_EVENT; state=%d\n", state);
if (state == CONF_INCLUDE) {
fprintf(stderr, "Include fields cannot be a mapping: line %zu\n", parser->mark.line);
goto fail;
}
if (inseq) { // 列表里 mapping 的处理方式
char sequence_node_name[DEFAULT_NAME_LEN];
// 把 索引作为 路径节点名字, 从 0 开始
snprintf(sequence_node_name, DEFAULT_NAME_LEN, "%d", seq_idx++);
// 查找对应的 节点
ConfNode *seq_node = ConfNodeLookupChild(node,
sequence_node_name);
if (seq_node != NULL) {
// 先从解析结果里删除,后面再添加
TAILQ_REMOVE(&node->head, seq_node, next);
}
else {
// 没有节点,就创建节点
seq_node = ConfNodeNew();
if (unlikely(seq_node == NULL)) {
goto fail;
}
seq_node->name = strdup(sequence_node_name);
if (unlikely(seq_node->name == NULL)) {
free(seq_node);
goto fail;
}
}
seq_node->is_seq = 1;
TAILQ_INSERT_TAIL(&node->head, seq_node, next);
// mapping的解析,进入递归调用
if (ConfYamlParse(parser, seq_node, 0, rlevel, 0) != 0)
goto fail;
}
else {
if (ConfYamlParse(parser, node, inseq, rlevel, 0) != 0)
goto fail;
}
state = CONF_KEY;
}
else if (event.type == YAML_MAPPING_END_EVENT) {
fprintf(stdout, "event.type=YAML_MAPPING_END_EVENT; state=%d\n", state);
done = 1; // 结束,函数退出
}
else if (event.type == YAML_STREAM_END_EVENT) {
fprintf(stdout, "event.type=YAML_STREAM_END_EVENT; state=%d\n", state);
done = 1; // 结束,函数退出
}
next:
// 清理本次事件,进入下一循环,继续处理数据
yaml_event_delete(&event);
continue;
fail:
// 失败跳出循环
yaml_event_delete(&event);
retval = -1;
break;
}
rlevel--;
return retval;
}
/**
* 解析yaml文件
* 返回值:0 - success, -1 - failure.
*/
int
ConfYamlLoadFile(const char *filename)
{
FILE *infile;
yaml_parser_t parser; // yaml解析句柄
int ret;
ConfNode *root = ConfGetRootNode();
if (yaml_parser_initialize(&parser) != 1) {
fprintf(stderr, "failed to initialize yaml parser.\n");
return -1;
}
struct stat stat_buf;
if (stat(filename, &stat_buf) == 0) {
if (stat_buf.st_mode & S_IFDIR) {
fprintf(stderr, "yaml argument is not a file but a directory: %s. "
"Please specify the yaml file in your -c option.\n",
filename);
yaml_parser_delete(&parser);
return -1;
}
}
// coverity[toctou : FALSE]
infile = fopen(filename, "r");
if (infile == NULL) {
fprintf(stderr, "failed to open file: %s: %s\n", filename, strerror(errno));
yaml_parser_delete(&parser);
return -1;
}
if (conf_dirname == NULL) {
// 设置配置文件路径
ConfYamlSetConfDirname(filename);
}
// 设置要解析的文件句柄
yaml_parser_set_input_file(&parser, infile);
ret = ConfYamlParse(&parser, root, 0, 0, 0);
yaml_parser_delete(&parser);
fclose(infile);
return ret;
}
/**
* 加载解析 yaml文件的 字符串
*/
int
ConfYamlLoadString(const char *string, size_t len)
{
ConfNode *root = ConfGetRootNode();
yaml_parser_t parser;
int ret;
if (yaml_parser_initialize(&parser) != 1) {
fprintf(stderr, "Failed to initialize yaml parser.\n");
exit(EXIT_FAILURE);
}
yaml_parser_set_input_string(&parser, (const unsigned char *)string, len);
ret = ConfYamlParse(&parser, root, 0, 0, 0);
yaml_parser_delete(&parser);
return ret;
}
/**
* 解析一个文件 filename,把解析的数据放在 prefix 节点下
* 如果没有此节点,就创建一个空节点,并把解析的数据挂载此节点下。
*
* 返回值 0 - 成功
* 1 - 失败
*/
int
ConfYamlLoadFileWithPrefix(const char *filename, const char *prefix)
{
FILE *infile;
yaml_parser_t parser;
int ret;
// 找到这个节点
ConfNode *root = ConfGetNode(prefix);
if (yaml_parser_initialize(&parser) != 1) {
fprintf(stderr, "failed to initialize yaml parser.\n");
return -1;
}
// 检测文件
struct stat stat_buf;
if (stat(filename, &stat_buf) == 0) {
if (stat_buf.st_mode & S_IFDIR) {
fprintf(stderr, "yaml argument is not a file but a directory: %s. "
"Please specify the yaml file in your -c option.\n",
filename);
return -1;
}
}
// 打开文件
infile = fopen(filename, "r");
if (infile == NULL) {
fprintf(stderr, "failed to open file: %s: %s\n", filename, strerror(errno));
yaml_parser_delete(&parser);
return -1;
}
if (conf_dirname == NULL) {
ConfYamlSetConfDirname(filename);
}
if (root == NULL) {
// 没找到此节点,添加一个空节点,值设置为 "<prefix root node>"
ConfSet(prefix, "<prefix root node>");
// 再次获取此节点
root = ConfGetNode(prefix);
if (root == NULL) {
// 获取不到就算了,不解析了
fclose(infile);
yaml_parser_delete(&parser);
return -1;
}
}
// 设置yaml文件描述符
yaml_parser_set_input_file(&parser, infile);
ret = ConfYamlParse(&parser, root, 0, 0, 0);
yaml_parser_delete(&parser);
fclose(infile);
return ret;
}
代码 conf-yaml-loader.h
#ifndef __CONF_YAML_LOADER_H__
#define __CONF_YAML_LOADER_H__
#include "conf-yaml.h"
int ConfYamlLoadFile(const char *);
int ConfYamlLoadString(const char *, size_t);
int ConfYamlLoadFileWithPrefix(const char *filename, const char *prefix);
int ConfYamlHandleInclude(ConfNode *parent, const char *filename);
void ConfYamlRegisterTests(void);
#endif /* !__CONF_YAML_LOADER_H__ */
代码 conf-yaml.c
#include <stdio.h>
#include <stdlib.h>
#include <yaml.h>
#include <limits.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <bsd/string.h>
#include "conf-yaml.h"
/** 节点全名称最大长度。全名称指,以 . 连接各路径的最终名称 */
#define NODE_NAME_MAX 1024
// 全局变量,保存 解析后的 yaml结构根的地址
static ConfNode *yaml_root = NULL;
// 地址备份使用
static ConfNode *yaml_root_backup = NULL;
/**
* 获得节点,如果没有找到,则创建节点,并加入到指定位置并返回
* 在全节点查找
*
* 参数: parent 用来作为父节点的节点
* name 想要获取的节点名称
* final 设置final标记
* 返回值: 成功则返回 找到的或者创建的node
* 失败则返回 NULL
*/
ConfNode *ConfNodeGetNodeOrCreate(ConfNode *parent, const char *name, int final)
{
ConfNode *node = NULL;
char node_name[NODE_NAME_MAX];
char *key;
char *next;
if (strlcpy(node_name, name, sizeof(node_name)) >= sizeof(node_name)) {
fprintf(stderr, "Configuration name too long: %s\n", name);
return NULL;
}
key = node_name;
do {
if ((next = strchr(key, '.')) != NULL)
*next++ = '\0';
if ((node = ConfNodeLookupChild(parent, key)) == NULL) {
// 没找到,创建,并设置name
node = ConfNodeNew();
if (unlikely(node == NULL)) {
fprintf(stderr, "Failed to allocate memory for configuration.\n");
goto end;
}
node->name = strdup(key);
if (unlikely(node->name == NULL)) {
ConfNodeFree(node);
node = NULL;
fprintf(stderr, "Failed to allocate memory for configuration.\n");
goto end;
}
node->parent = parent;
node->final = final;
// 在父节点插入刚申请的节点
TAILQ_INSERT_TAIL(&parent->head, node, next);
}
// 接续准备下一级查找或者创建
key = next;
parent = node;
} while (next != NULL);
end:
// 最后返回节点
return node;
}
/**
* 封装 ConfNodeGetNodeOrCreate, 从 yaml_root 查找节点
*/
static ConfNode *ConfGetNodeOrCreate(const char *name, int final)
{
return ConfNodeGetNodeOrCreate(yaml_root, name, final);
}
/**
* 初始化配置系统,给全局变量 yaml_root 申请 ConfNode 节点
*/
void ConfInit(void)
{
if (yaml_root != NULL) {
fprintf(stdout, "already initialized\n");
return;
}
yaml_root = ConfNodeNew();
if (yaml_root == NULL) {
fprintf(stderr, "ERROR: Failed to allocate memory for yaml_root configuration node, "
"aborting.\n");
}
fprintf(stdout, "configuration module initialized\n");
}
/**
* 申请一个 ConfNode 节点
*
* 返回值 成功 - 返回申请的配置节点 node
* 失败 - NULL
*/
ConfNode *ConfNodeNew(void)
{
ConfNode *new;
new = calloc(1, sizeof(*new));
if (unlikely(new == NULL)) {
return NULL;
}
TAILQ_INIT(&new->head);
return new;
}
/**
* 释放节点,并递归释放所有子节点
*/
void ConfNodeFree(ConfNode *node)
{
ConfNode *tmp;
while ((tmp = TAILQ_FIRST(&node->head))) { // 没有了子节点
// 从兄弟节点上去掉
TAILQ_REMOVE(&node->head, tmp, next);
// 释放节点和子节点
ConfNodeFree(tmp);
}
if (node->name != NULL)
free(node->name);
if (node->val != NULL)
free(node->val);
free(node);
}
/**
* 根据节点名字返回节点
* 从 yaml_root 开始查找,输入 节点的全路径
* 深度查找
*
* 返回值 查询到节点,返回此节点。
* 如果节点没找到,返回NULL
*/
ConfNode *ConfGetNode(const char *name)
{
ConfNode *node = yaml_root;
char node_name[NODE_NAME_MAX];
char *key;
char *next;
if (strlcpy(node_name, name, sizeof(node_name)) >= sizeof(node_name)) {
fprintf(stderr, "Configuration name too long: %s\n", name);
return NULL;
}
key = node_name;
do {
if ((next = strchr(key, '.')) != NULL)
*next++ = '\0';
// root 节点是没有数据的,所有从孩子节点中查找
node = ConfNodeLookupChild(node, key);
key = next;
} while (next != NULL && node != NULL);
return node;
}
/**
* 返回 yaml_root 值
*/
ConfNode *ConfGetRootNode(void)
{
return yaml_root;
}
/**
* 设置配置值,
* 如果节点找到,并且是final则返回失败
*
* 返回值 1 - 设置成功
* 0 - 设置失败
*/
int ConfSet(const char *name, const char *val)
{
// 找到节点,没有就创建节点
ConfNode *node = ConfGetNodeOrCreate(name, 0);
if (node == NULL || node->final) {
return 0;
}
if (node->val != NULL)
free(node->val);
node->val = strdup(val);
if (unlikely(node->val == NULL)) {
return 0;
}
return 1;
}
/**
* 从字符串解析yaml节点
* 举例: aaa.bbb.ccc = hello world
* 会被设置成 aaa的子节点bbb的子节点ccc的val值 = "hello world"
*
* 返回值 1 - value被设置上
* 0 - value没有被设置
*/
int ConfSetFromString(const char *input, int final)
{
int retval = 0;
char *name = strdup(input), *val = NULL;
if (unlikely(name == NULL)) {
goto done;
}
val = strchr(name, '=');
if (val == NULL) {
goto done;
}
*val++ = '\0';
// 处理name的数据,去掉后方的空白字符
while (isspace((int)name[strlen(name) - 1])) {
name[strlen(name) - 1] = '\0';
}
// 处理val的数据,去掉前方的空白字符
while (isspace((int)*val)) {
val++;
}
// 添加 节点 根据 final 设置
if (final) {
if (!ConfSetFinal(name, val)) {
goto done;
}
}
else {
if (!ConfSet(name, val)) {
goto done;
}
}
retval = 1;
done:
if (name != NULL) {
free(name);
}
return retval;
}
/**
* 设置最终值,并设置为最终值,不能被覆盖,final设置为1
*
* 返回值 1 - 设置成功
* 0 - 设置失败
*/
int ConfSetFinal(const char *name, const char *val)
{
ConfNode *node = ConfGetNodeOrCreate(name, 1);
if (node == NULL) {
return 0;
}
if (node->val != NULL)
free(node->val);
node->val = strdup(val);
if (unlikely(node->val == NULL)) {
return 0;
}
node->final = 1;
return 1;
}
/**
* 根据名字获取节点值,从yaml_root结点开始查询
* 输入全路径 和 指向val值的指针的地址。
* 给 *vptr 赋值的是数据原地址,不是副本。
* 深度查找
*
* 返回值 1 - 节点找到 0 - 没找到节点
*/
int ConfGet(const char *name, const char **vptr)
{
ConfNode *node = ConfGetNode(name);
if (node == NULL) {
fprintf(stdout, "failed to lookup configuration parameter '%s'\n", name);
return 0;
}
else {
*vptr = node->val;
return 1;
}
}
/**
* 根据 name 查找指定节点 base 的子节点,并把节点值的地址给到 *vptr
* 只在子节点查找
*
* 返回值 1 - 成功
* 0 - 失败
*/
int ConfGetChildValue(const ConfNode *base, const char *name, const char **vptr)
{
ConfNode *node = ConfNodeLookupChild(base, name);
if (node == NULL) {
fprintf(stdout, "failed to lookup configuration parameter '%s'\n", name);
return 0;
}
else {
if (node->val == NULL)
return 0;
*vptr = node->val;
return 1;
}
}
/**
* 从base的子节点中获取名字是name的节点,
* 如果base节点的子节点找不到则从默认的节点dflt的子节点查找name节点
* 如果两个都找不到,则返回NULL
*
* 返回值: 找到,返回name的节点
* 找不到,返回NULL
*/
ConfNode *ConfGetChildWithDefault(const ConfNode *base, const ConfNode *dflt,
const char *name)
{
ConfNode *node = ConfNodeLookupChild(base, name);
if (node != NULL)
return node;
/* Get 'default' value */
if (dflt) {
return ConfNodeLookupChild(dflt, name);
}
return NULL;
}
int ConfGetChildValueWithDefault(const ConfNode *base, const ConfNode *dflt,
const char *name, const char **vptr)
{
int ret = ConfGetChildValue(base, name, vptr);
/* Get 'default' value */
if (ret == 0 && dflt) {
return ConfGetChildValue(dflt, name, vptr);
}
return ret;
}
/**
* 尝试找到 name 节点并把值转换成 integer 类型的值,赋值给 *val
*
* 返回值 1 - 名字被找到,并且争取转换成 integer
* 0 - 失败
*/
int ConfGetInt(const char *name, intmax_t *val)
{
const char *strval = NULL;
intmax_t tmpint;
char *endptr;
if (ConfGet(name, &strval) == 0)
return 0;
if (strval == NULL) {
fprintf(stderr, "malformed integer value "
"for %s: NULL\n",
name);
return 0;
}
errno = 0;
tmpint = strtoimax(strval, &endptr, 0);
if (strval[0] == '\0' || *endptr != '\0') {
fprintf(stderr, "malformed integer value "
"for %s: '%s'\n",
name, strval);
return 0;
}
if (errno == ERANGE && (tmpint == INTMAX_MAX || tmpint == INTMAX_MIN)) {
fprintf(stderr, "integer value for %s out "
"of range: '%s'\n",
name, strval);
return 0;
}
*val = tmpint;
return 1;
}
/**
* 从给定的节点 base 的子节点中,查找名字为 name 的配置,然后转换成int类型给 *val 赋值
*
* 返回值 1 - 名字被找到,并且正确转换成 integer
* 0 - 失败
*/
int ConfGetChildValueInt(const ConfNode *base, const char *name, intmax_t *val)
{
const char *strval = NULL;
intmax_t tmpint;
char *endptr;
if (ConfGetChildValue(base, name, &strval) == 0)
return 0;
errno = 0;
tmpint = strtoimax(strval, &endptr, 0);
if (strval[0] == '\0' || *endptr != '\0') {
fprintf(stderr, "malformed integer value "
"for %s with base %s: '%s'\n",
name, base->name, strval);
return 0;
}
if (errno == ERANGE && (tmpint == INTMAX_MAX || tmpint == INTMAX_MIN)) {
fprintf(stderr, "integer value for %s with "
" base %s out of range: '%s'\n",
name, base->name, strval);
return 0;
}
*val = tmpint;
return 1;
}
/**
* 给 ConfGetChildValueInt 加了个壳,base 的子节点 找不到就从 dflt 的子节点里找
*
* 返回值 1 - 名字被找到,并且正确转换成 integer
* 0 - 失败
*/
int ConfGetChildValueIntWithDefault(const ConfNode *base, const ConfNode *dflt,
const char *name, intmax_t *val)
{
int ret = ConfGetChildValueInt(base, name, val);
/* Get 'default' value */
if (ret == 0 && dflt) {
return ConfGetChildValueInt(dflt, name, val);
}
return ret;
}
/**
* 根据name用boolean类型检索一个配置值
*
* 返回值 1 - 名字被找到,并且正确转换成 Bool 类型 1 或者 0
* 0 - 失败
*/
int ConfGetBool(const char *name, int *val)
{
const char *strval = NULL;
*val = 0;
if (ConfGet(name, &strval) != 1)
return 0;
// 判断是否为真
*val = ConfValIsTrue(strval);
return 1;
}
/**
* 根据name在 base 节点的子节点中用boolean类型检索一个配置值,把转换的值赋值给 *val
*
* 返回值 1 - 名字被找到,并且正确转换成 Bool 类型 1 或者 0
* 0 - 失败
*/
int ConfGetChildValueBool(const ConfNode *base, const char *name, int *val)
{
const char *strval = NULL;
*val = 0;
if (ConfGetChildValue(base, name, &strval) == 0)
return 0;
*val = ConfValIsTrue(strval);
return 1;
}
/**
* ConfGetChildValueBool的一个封装,如果base的子节点没有,从默认节点 dflt中获取
*
* 返回值 1 - 名字被找到,并且正确转换成 Bool 类型 1 或者 0
* 0 - 失败
*/
int ConfGetChildValueBoolWithDefault(const ConfNode *base, const ConfNode *dflt,
const char *name, int *val)
{
int ret = ConfGetChildValueBool(base, name, val);
/* Get 'default' value */
if (ret == 0 && dflt) {
return ConfGetChildValueBool(dflt, name, val);
}
return ret;
}
/**
* 判断是否为真
* 判断是否为 1, yes, true or on
*
* 返回值 1 - 是真
* 0 - 不是真
*/
int ConfValIsTrue(const char *val)
{
const char *trues[] = {"1", "yes", "true", "on"};
size_t u;
for (u = 0; u < sizeof(trues) / sizeof(trues[0]); u++) {
if (strcasecmp(val, trues[u]) == 0) {
return 1;
}
}
return 0;
}
/**
* 判断是否为假,为假返回 1
* 判断是否为 0, no, false or off
*
* 返回值 1 - 是假
* 0 - 不是假
*/
int ConfValIsFalse(const char *val)
{
const char *falses[] = {"0", "no", "false", "off"};
size_t u;
for (u = 0; u < sizeof(falses) / sizeof(falses[0]); u++) {
if (strcasecmp(val, falses[u]) == 0) {
return 1;
}
}
return 0;
}
/**
* 使用 dobule 检索一个配置的值给 *val
*
* 返回值 1 - 成功
* 0 - 失败
*/
int ConfGetDouble(const char *name, double *val)
{
const char *strval = NULL;
double tmpdo;
char *endptr;
if (ConfGet(name, &strval) == 0)
return 0;
errno = 0;
tmpdo = strtod(strval, &endptr);
if (strval[0] == '\0' || *endptr != '\0')
return 0;
if (errno == ERANGE)
return 0;
*val = tmpdo;
return 1;
}
/**
* 使用 float 检索一个配置的值给 *val
*
* 返回值 1 - 成功
* 0 - 失败
*/
int ConfGetFloat(const char *name, float *val)
{
const char *strval = NULL;
double tmpfl;
char *endptr;
if (ConfGet(name, &strval) == 0)
return 0;
errno = 0;
tmpfl = strtof(strval, &endptr);
if (strval[0] == '\0' || *endptr != '\0')
return 0;
if (errno == ERANGE)
return 0;
*val = tmpfl;
return 1;
}
/**
* 删除一个给定的节点
*/
void ConfNodeRemove(ConfNode *node)
{
if (node->parent != NULL)
// 从兄弟节点上去掉 next
TAILQ_REMOVE(&node->parent->head, node, next);
ConfNodeFree(node);
}
/**
*
* 根据名字删除节点
*
* 返回值 1 - 成功删除 0 - 没有发现节点
*/
int ConfRemove(const char *name)
{
ConfNode *node;
node = ConfGetNode(name);
if (node == NULL)
return 0;
else {
ConfNodeRemove(node);
return 1;
}
}
/**
* 备份 yaml_root 指向的结构的地址 给 yaml_root_backup
* 没有数据拷贝
*/
void ConfCreateContextBackup(void)
{
yaml_root_backup = yaml_root;
yaml_root = NULL;
return;
}
/**
* 还原 yaml_root_backup 指向的结构的地址 给 yaml_root,
* 没有数据拷贝
*/
void ConfRestoreContextBackup(void)
{
yaml_root = yaml_root_backup;
yaml_root_backup = NULL;
return;
}
/**
* 清理 yaml_root 节点
*/
void ConfDeInit(void)
{
if (yaml_root != NULL) {
ConfNodeFree(yaml_root);
yaml_root = NULL;
}
fprintf(stdout, "configuration module de-initialized\n");
}
/**
* 打印 key 的名称
*/
static char *ConfPrintNameArray(char **name_arr, int level)
{
static char name[128*128];
int i;
name[0] = '\0';
for (i = 0; i <= level; i++) {
strlcat(name, name_arr[i], sizeof(name));
if (i < level)
strlcat(name, ".", sizeof(name));
}
return name;
}
/**
* 打印节点下子节点的数据到标准输出
* 注意:不打印当前节点
*/
void ConfNodeDump(const ConfNode *node, const char *prefix)
{
ConfNode *child;
// 递归调用不重复申请这两个变量
static char *name[128]; // 128个名字的指针数组
static int level = -1; // 层级,yaml_root 属于第0层
level++;
// 遍历子节点
TAILQ_FOREACH(child, &node->head, next) {
// 申请新的name空间
name[level] = strdup(child->name);
if (unlikely(name[level] == NULL)) {
continue;
}
if (prefix == NULL) {
printf("%s = %s\n", ConfPrintNameArray(name, level),
child->val);
}
else {
printf("%s.%s = %s\n", prefix,
ConfPrintNameArray(name, level), child->val);
}
ConfNodeDump(child, prefix);
free(name[level]);
}
level--;
}
/**
* 把 yaml_root 打印到标准输出
*/
void ConfDump(void)
{
ConfNodeDump(yaml_root, NULL);
}
/*
* 判断给定的节点有没有子节点
* 返回 包含节点 返回 true
* 不包含节点 返回 false
*/
bool ConfNodeHasChildren(const ConfNode *node)
{
if (TAILQ_EMPTY(&node->head)) {
return false;
}
return true;
}
/*
* 根据名字 从 子结点中 根据名字查找,不进行递归查找
* 没找到返回 NULL
* 找到返回 ConfNode 节点地址
* 只在子节点查找
*/
ConfNode *ConfNodeLookupChild(const ConfNode *node, const char *name)
{
ConfNode *child;
if (node == NULL || name == NULL) {
return NULL;
}
// 遍历子节点
TAILQ_FOREACH(child, &node->head, next) {
if (child->name != NULL && strcmp(child->name, name) == 0)
return child;
}
return NULL;
}
/**
* 从子节点中,根据名字找到对应的值
* 只在子节点查找
*
* 参数: node 父节点
* name 节点名字,父节点的路径
*
* 返回值: 找到节点,则返回值val的地址
* 没有找到节点,返回 NULL
*/
const char *ConfNodeLookupChildValue(const ConfNode *node, const char *name)
{
ConfNode *child;
child = ConfNodeLookupChild(node, name);
if (child != NULL)
return child->val;
return NULL;
}
/**
* 在指定的节点下,查找一个key的值
* 只在子节点查找
*
* 返回值 匹配上 返回匹配的 节点
* 没匹配上 返回NULL
*/
ConfNode *ConfNodeLookupKeyValue(const ConfNode *base, const char *key,
const char *value)
{
ConfNode *child;
TAILQ_FOREACH(child, &base->head, next) {
if (!strncmp(child->val, key, strlen(child->val))) {
ConfNode *subchild;
TAILQ_FOREACH(subchild, &child->head, next) {
if ((!strcmp(subchild->name, key)) && (!strcmp(subchild->val, value))) {
return child;
}
}
}
}
return NULL;
}
/**
* 测试一个配置节点是否有值
* 只在子节点查找
*
* 返回值 1 - 有节点,也有值
* 0 - 没找到节点或者节点没值
*/
int ConfNodeChildValueIsTrue(const ConfNode *node, const char *key)
{
const char *val;
val = ConfNodeLookupChildValue(node, key);
return val != NULL ? ConfValIsTrue(val) : 0;
}
/**
* 加载一个include引入的yaml文件
*
* \retval str Pointer to the string path + sig_file
*/
char *ConfLoadCompleteIncludePath(const char *file)
{
const char *defaultpath = NULL;
char *path = NULL;
// 相对路径拼写路径
if (PathIsRelative(file)) {
// 获取 include-path 配置文件的相对路径
if (ConfGet("include-path", &defaultpath) == 1) {
fprintf(stdout, "Default path: %s\n", defaultpath);
size_t path_len = sizeof(char) * (strlen(defaultpath) +
strlen(file) + 2);
path = malloc(path_len);
if (unlikely(path == NULL))
return NULL;
strlcpy(path, defaultpath, path_len);
if (path[strlen(path) - 1] != '/')
strlcat(path, "/", path_len);
strlcat(path, file, path_len);
} else {
path = strdup(file);
if (unlikely(path == NULL))
return NULL;
}
} else {
path = strdup(file);
if (unlikely(path == NULL))
return NULL;
}
return path;
}
/**
* 清空节点 node 里的value值,
* 如果 node 里的子节点是可被覆盖的,则一并删除。
* node 节点本身并不被删除,node->name 保留,node->val 被删除
*/
void ConfNodePrune(ConfNode *node)
{
ConfNode *item, *it;
for (item = TAILQ_FIRST(&node->head); item != NULL; item = it) { // 遍历所有子节点
it = TAILQ_NEXT(item, next) ;
if (!item->final) { // 不是最终参数(可被覆盖)
ConfNodePrune(item) ; // 递归删除子节点下的数据和自己本身
if (TAILQ_EMPTY(&item->head)) { // 子节点为空
TAILQ_REMOVE(&node->head, item, next) ; // 节点从数据结构上脱离
if (item->name != NULL)
free(item->name) ; // 释放 name
if (item->val != NULL)
free(item->val) ; // 释放 val,可能调用不到,val 在 递归调用 ConfNodePrune是释放了。且 val 指向了 NULL
free(item); // 释放节点
}
}
}
// 1、没有子节点的节点,val在此处被删除
// 2、递归前的 node 节点(需要被清理的节点),val值在这里清理
// 3、node->val = NULL 为了防止重复释放 val
if (node->val != NULL) {
free(node->val);
node->val = NULL;
}
}
/**
* 判断节点是不是列表
*
* 返回值 1 - 是列表
* 0 - 不是列表
*/
int ConfNodeIsSequence(const ConfNode *node)
{
return node->is_seq == 0 ? 0 : 1;
}
/**
* 判断路径是不是绝对路径
*
* 返回值 1 - 是绝对路径
* 0 - 不是绝对路径 --相对路径
*/
int PathIsAbsolute(const char *path)
{
if (strlen(path) > 1 && path[0] == '/') {
return 1;
}
return 0;
}
/**
* 判断路径是不是相对路径
*
* 返回值 1 - 是相对路径
* 0 - 不是相对路径 --绝对路径
*/
int PathIsRelative(const char *path)
{
return PathIsAbsolute(path) ? 0 : 1;
}
/**
* 尝试找到 LIST 的所有数据
*
* 返回值 >=0 - 找到的数量
* -1 - 失败
*/
int ConfGetList(const char *name, const char *(**val)[])
{
int ret = -1 ;
// 获取节点
ConfNode * base = ConfGetNode(name) ;
if( base == NULL ) {
return -1 ;
}
char idx_str[8] ;
// 查看有多少个子节点
int idx = 0 ;
snprintf(idx_str, sizeof(idx_str), "%d", idx) ;
while(ConfNodeLookupChild(base, idx_str)) {
idx++ ;
snprintf(idx_str, sizeof(idx_str), "%d", idx) ;
}
const char *(*tmpval)[] = calloc(1, sizeof(void *) * idx) ;
const char * vptr ;
// 获取每个子节点的value
idx = 0 ;
snprintf(idx_str, sizeof(idx_str), "%d", idx) ;
while( ret = ConfGetChildValue( base, idx_str, &vptr ) ) {
(*tmpval)[idx] = vptr ;
idx++ ;
snprintf(idx_str, sizeof(idx_str), "%d", idx) ;
}
*val = tmpval ;
return idx ;
}
代码 conf-yaml.h
#ifndef __CONF_YAML_H__
#define __CONF_YAML_H__
#include <inttypes.h>
#include <stdbool.h>
#include "queue.h"
/**
* yaml配置文件解析结构
*/
typedef struct ConfNode_ {
char *name; // 指针,赋值需要申请空间
char *val; // 赋值需要申请空间
int is_seq; // 标记此节点是列表的一个节点
int final; // 最终标记,设置为 final 之后,值不能被覆盖
struct ConfNode_ *parent; // 父节点
TAILQ_HEAD(, ConfNode_) head; // 孩子节点们
TAILQ_ENTRY(ConfNode_) next; // 兄弟节点们
} ConfNode;
/**
* 默认日志地址
*/
#define DEFAULT_LOG_DIR "/var/log/yaml.log"
#define DEFAULT_DATA_DIR DATA_DIR
#define unlikely(expr) __builtin_expect(!!(expr), 0)
void ConfInit(void);
void ConfDeInit(void);
ConfNode *ConfGetRootNode(void);
int ConfGet(const char *name, const char **vptr);
int ConfGetInt(const char *name, intmax_t *val);
int ConfGetBool(const char *name, int *val);
int ConfGetDouble(const char *name, double *val);
int ConfGetFloat(const char *name, float *val);
int ConfSet(const char *name, const char *val);
int ConfSetFromString(const char *input, int final);
int ConfSetFinal(const char *name, const char *val);
void ConfDump(void);
void ConfNodeDump(const ConfNode *node, const char *prefix);
ConfNode *ConfNodeNew(void);
void ConfNodeFree(ConfNode *);
ConfNode *ConfGetNode(const char *key);
void ConfCreateContextBackup(void);
void ConfRestoreContextBackup(void);
ConfNode *ConfNodeLookupChild(const ConfNode *node, const char *key);
const char *ConfNodeLookupChildValue(const ConfNode *node, const char *key);
void ConfNodeRemove(ConfNode *);
void ConfRegisterTests(void);
int ConfNodeChildValueIsTrue(const ConfNode *node, const char *key);
int ConfValIsTrue(const char *val);
int ConfValIsFalse(const char *val);
void ConfNodePrune(ConfNode *node);
int ConfRemove(const char *name);
bool ConfNodeHasChildren(const ConfNode *node);
ConfNode *ConfGetChildWithDefault(const ConfNode *base, const ConfNode *dflt, const char *name);
ConfNode *ConfNodeLookupKeyValue(const ConfNode *base, const char *key, const char *value);
int ConfGetChildValue(const ConfNode *base, const char *name, const char **vptr);
int ConfGetChildValueInt(const ConfNode *base, const char *name, intmax_t *val);
int ConfGetChildValueBool(const ConfNode *base, const char *name, int *val);
int ConfGetChildValueWithDefault(const ConfNode *base, const ConfNode *dflt, const char *name, const char **vptr);
int ConfGetChildValueIntWithDefault(const ConfNode *base, const ConfNode *dflt, const char *name, intmax_t *val);
int ConfGetChildValueBoolWithDefault(const ConfNode *base, const ConfNode *dflt, const char *name, int *val);
char *ConfLoadCompleteIncludePath(const char *);
int ConfNodeIsSequence(const ConfNode *node);
ConfNode *ConfSetIfaceNode(const char *ifaces_node_name, const char *iface);
int ConfSetRootAndDefaultNodes(
const char *ifaces_node_name, const char *iface, ConfNode **if_root, ConfNode **if_default);
ConfNode *ConfNodeGetNodeOrCreate(ConfNode *parent, const char *name, int final);
int PathIsAbsolute(const char *path) ;
int PathIsRelative(const char *path) ;
int ConfGetList(const char *name, const char *(**val)[]) ;
#endif /* !__CONF_YAML_H__ */
代码 queue.h
#ifndef YAML_QUEUE_H
#define YAML_QUEUE_H
if defined(HAVE_SYS_QUEUE_H) && !defined(__clang_analyzer__)
#include <sys/queue.h>
#endif
#if defined(__clang_analyzer__)
#define _Q_ASSERT(a) assert((a))
#else
#define _Q_ASSERT(a)
#endif
/* The BSDs have removed CIRCLEQ but it still exists in Linux.
*
* This implementation from OpenBSD sys/queue.h v1.40 as it still has
* CIRCLEQ and includes the safe variations.
* - _Q_INVALIDATE for some extra debugging has been removed as its
* not available on the Linux version of CIRCLEQ.
*/
#ifndef CIRCLEQ_HEAD
/*
* Circular queue definitions.
*/
#define CIRCLEQ_HEAD(name, type) \
struct name { \
struct type *cqh_first; /* first element */ \
struct type *cqh_last; /* last element */ \
}
#define CIRCLEQ_HEAD_INITIALIZER(head) \
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
#define CIRCLEQ_ENTRY(type) \
struct { \
struct type *cqe_next; /* next element */ \
struct type *cqe_prev; /* previous element */ \
}
/*
* Circular queue access methods
*/
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
#define CIRCLEQ_EMPTY(head) \
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
#define CIRCLEQ_FOREACH(var, head, field) \
for((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_NEXT(var, field))
#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head) && \
((tvar) = CIRCLEQ_NEXT(var, field), 1); \
(var) = (tvar))
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
for((var) = CIRCLEQ_LAST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_PREV(var, field))
#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = CIRCLEQ_LAST(head, headname); \
(var) != CIRCLEQ_END(head) && \
((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \
(var) = (tvar))
/*
* Circular queue functions.
*/
#define CIRCLEQ_INIT(head) do { \
(head)->cqh_first = CIRCLEQ_END(head); \
(head)->cqh_last = CIRCLEQ_END(head); \
} while (0)
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
(elm)->field.cqe_prev = (listelm); \
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
(listelm)->field.cqe_next = (elm); \
} while (0)
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm); \
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
(listelm)->field.cqe_prev = (elm); \
} while (0)
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
(elm)->field.cqe_next = (head)->cqh_first; \
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
if ((head)->cqh_last == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(head)->cqh_first->field.cqe_prev = (elm); \
(head)->cqh_first = (elm); \
} while (0)
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.cqe_next = CIRCLEQ_END(head); \
(elm)->field.cqe_prev = (head)->cqh_last; \
if ((head)->cqh_first == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(head)->cqh_last->field.cqe_next = (elm); \
(head)->cqh_last = (elm); \
} while (0)
#define CIRCLEQ_REMOVE(head, elm, field) do { \
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm)->field.cqe_prev; \
else \
(elm)->field.cqe_next->field.cqe_prev = \
(elm)->field.cqe_prev; \
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm)->field.cqe_next; \
else \
(elm)->field.cqe_prev->field.cqe_next = \
(elm)->field.cqe_next; \
} while (0)
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
CIRCLEQ_END(head)) \
(head)->cqh_last = (elm2); \
else \
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
CIRCLEQ_END(head)) \
(head)->cqh_first = (elm2); \
else \
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
} while (0)
#endif /* !CIRCLEQ_HEAD */
/* Required by local implementation as well as _SAFE variations. */
#ifndef CIRCLEQ_END
#define CIRCLEQ_END(head) ((void *)(head))
#endif /* !CIRCLEQ_END */
#ifndef CIRCLEQ_FOREACH_SAFE
#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head) && \
((tvar) = CIRCLEQ_NEXT(var, field), 1); \
(var) = (tvar))
#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = CIRCLEQ_LAST(head, headname); \
(var) != CIRCLEQ_END(head) && \
((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \
(var) = (tvar))
#endif /* !CIRCLEQ_FOREACH_SAFE */
/*
* Complete TAILQ implementation as sys/queue.h is not available on Windows
* and used by Suricata.
*
* This implementation copied from FreeBSD sys/queue.h with the addition
* of our _Q_ASSERT macros to satisfy scan-build.
*/
#ifndef TAILQ_HEAD
/*
* Tail queue declarations.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
/*
* Tail queue functions.
*/
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_FOREACH(var, head, field) \
for ((var) = TAILQ_FIRST((head)); \
(var); \
(var) = TAILQ_NEXT((var), field))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for ((var) = TAILQ_LAST((head), headname); \
(var); \
(var) = TAILQ_PREV((var), headname, field))
#define TAILQ_INIT(head) do { \
TAILQ_FIRST((head)) = NULL; \
(head)->tqh_last = &TAILQ_FIRST((head)); \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
TAILQ_NEXT((elm), field)->field.tqe_prev = \
&TAILQ_NEXT((elm), field); \
else \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
TAILQ_NEXT((listelm), field) = (elm); \
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
TAILQ_NEXT((elm), field) = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
TAILQ_FIRST((head))->field.tqe_prev = \
&TAILQ_NEXT((elm), field); \
else \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
TAILQ_FIRST((head)) = (elm); \
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
_Q_ASSERT((elm)); \
_Q_ASSERT((head)); \
TAILQ_NEXT((elm), field) = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
_Q_ASSERT(*(head)->tqh_last); \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
} while (0)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_REMOVE(head, elm, field) do { \
if ((TAILQ_NEXT((elm), field)) != NULL) \
TAILQ_NEXT((elm), field)->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
_Q_ASSERT((head)->tqh_first != (elm)); \
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
} while (0)
#endif /* !TAILQ_HEAD */
/* Not included in Linux, but are in FreeBSD and friends.
*
* This implementation from FreeBSD's sys/queue.h.
*/
#ifndef TAILQ_FOREACH_SAFE
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST((head)); \
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#endif
#endif /* !YAML_QUEUE_H */
测试代码 test.c
#include <stdio.h>
#include <stdlib.h>
#include "conf-yaml.h"
#include "conf-yaml-loader.h"
const char * yaml_path = "./tt.yaml" ;
int main(int argc, int argv) {
ConfInit() ;
ConfYamlLoadFile(yaml_path) ;
ConfNode *yaml_root = ConfGetRootNode() ;
printf("\n\n\n\n============== result ===============\n\n") ;
ConfDump() ;
const char * vptr ;
char * tnm = "score.0.name" ;
ConfGet(tnm, &vptr) ;
printf("\n\n---------%s: %s\n", tnm, vptr) ;
tnm = "aa" ;
ConfGet(tnm, &vptr) ;
printf("---------%s: %s\n", tnm, vptr) ;
tnm = "t-inc.cc" ;
ConfGet(tnm, &vptr) ;
printf("---------%s: %s\n", tnm, vptr) ;
const char *(*val)[] ;
int ret = ConfGetList("score.1.fruit", &val) ;
printf("ret [%d] \n", ret) ;
for(int i = 0 ; i < ret ; ++i ) {
printf("idx [%d] [%s]\n", i, (*val)[i]) ;
}
free(val) ;
return 0 ;
}
测试数据 tt.yaml
%YAML 1.1
---
# --- 代表文档的开始
score:
# - 代表这些是数组
- name: Hanmeimei # #号往后全是注释
age: 11
sessions:
chineses: 100
math: 100
english: 100
science: 100
- name: Lilei
age: 12
# 中括号代表是一个列表
# 列表中的数据字段可以不一致
fruit: [apple, pear, banana, peach, orange]
# 只要下一级的数据缩进长度相同,且比本级长就可以
sessions:
chineses: 95
math: 95
english: 95
science: 95
...
# ... 代表文档的结束
%YAML 1.1
---
teachers:
&anchorrrr
- name: Qiaofeng
age: 32
sex: 1
address: >
CN
Shandong
Qingdao
- name: Wangyuyan
age: 19
sex: 2
# !!表示用字符串解释下边的数据
banner: !!str
Hello,
my name is Wangyuyan.
I am your english teacher.
address: |
CN
Shandong
Jinan
...
%YAML 1.1
---
root:
pppp:
name: Murongfu
age: 28
anchor_t:
kongfu: Douzhuanxingyi
include: t1.yaml
t_inc:
include: t1.yaml
...
测试数据 t1.yaml
%YAML 1.1
---
aa: aaaaaaaaaa
bb: bbbbbbbbbbbbbb
cc: ccccccccccccccc
编译测试

最后打印结果:

发表回复