c语言中并没有提供解释conf文件的库,因此要自己实现,实际上也不是什么难的事情,下面就是我自己写的一个小代码,测试了下基本能使用,如发现bug及时更新,需要新添加功能自己扩展。
文件【list.h】,代码中使用到的链表实现。
#ifndef __LIST_H__
#define __LIST_H__
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
//tail queue
#define TAILQ_HEAD(name, type) \
struct name \
{ \
struct type *tqh_first; \
struct type **tqh_last; \
} \
#define TAILQ_ENTRY(type) \
struct \
{ \
struct type *tqe_next; \
struct type **tqe_prev; \
} \
/*
* tail queue access methods
*/
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last ))->tqh_last))
/* XXX */
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_EMPTY(head) \
(TAILQ_FIRST(head) == TAILQ_END(head))
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
(var) = TAILQ_PREV(var, headname, field))
#define TAILQ_INIT(head) do{ \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
}while(0) \
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
} while (0)
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
(elm2)->field.tqe_next->field.tqe_prev = \
&(elm2)->field.tqe_next; \
else \
(head)->tqh_last = &(elm2)->field.tqe_next; \
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
*(elm2)->field.tqe_prev = (elm2); \
} while (0)
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif
文件【config.h】,解析库的头文件:
#ifndef __CONFIG_H__
#define __CONFIG_H__
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
#include "list.h"
struct ct_config_comment
{
TAILQ_ENTRY(ct_config_comment) next;
char comment[0];
};
typedef struct ct_config_comment ct_ST_config_comment;
struct ct_config_variable
{
TAILQ_ENTRY(ct_config_variable) next;
char *name;
char *value;
//int line;
TAILQ_HEAD(ct_config_comment_list, ct_config_comment) prevline_comment_list,sameline_comment_list;
//TAILQ_HEAD(ct_config_comment_list, ct_config_comment) sameline_comment_list;
int blanklines;
char buf[0];
};
typedef struct ct_config_variable ct_ST_config_variable;
struct ct_config_section
{
TAILQ_ENTRY(ct_config_section) next;
char *name;
TAILQ_HEAD(ct_config_variable_list, ct_config_variable) variable_list;
//TAILQ_HEAD(ct_config_comment_list, ct_config_comment) prevline_comment_list, sameline_comment_list;
//TAILQ_HEAD(ct_config_comment_list, ct_config_comment) sameline_comment_list;
int blanklines;
char buf[0];
};
typedef struct ct_config_section ct_ST_config_section;
struct ct_config
{
char *filename;
TAILQ_HEAD(ct_config_section_list, ct_config_section) section_list;
char buf[0];
};
typedef struct ct_config ct_ST_config;
//function for config
/*
ct_ST_config_comment *ct_config_comment_new(const char *comment);
int ct_config_comment_destory(ct_ST_config_comment *p);
int ct_config_comment_modify(ct_ST_config *config, const char *section_name, const char *variable_name, const char *comment);
*/
ct_ST_config_variable *ct_config_variable_new(const char *name, const char *value);
void ct_config_variable_destory(ct_ST_config_variable *var);
int ct_config_variable_append(ct_ST_config_section *section, ct_ST_config_variable *var);
int ct_config_variable_delete(ct_ST_config_section *section, const char *name);
int ct_config_variable_update(ct_ST_config_section *section, const char *name, const char *match, const char *new_value);
ct_ST_config_variable *ct_config_variable_find(ct_ST_config_section *section, const char *name);
void ct_config_variable_display(ct_ST_config_variable *var);
ct_ST_config_section *ct_config_section_new(const char *name);
void ct_config_section_destory(ct_ST_config_section *section);
int ct_config_section_append(ct_ST_config *cfg, ct_ST_config_section *section);
int ct_config_section_delete(ct_ST_config *cfg, const char *name);
int ct_config_section_update(ct_ST_config *cfg, const char *match, const char *new_name);
ct_ST_config *ct_config_section_find(ct_ST_config *cfg, const char *match);
void ct_config_section_display(ct_ST_config_section *section);
//ct_ST_config *ct_config_new();
ct_ST_config *ct_config_new(const char *filename);
void ct_config_destory(ct_ST_config *cfg);
int ct_config_load(const char *filename, ct_ST_config **cfg);
int ct_config_save(ct_ST_config *cfg, const char *new_filename);
void ct_config_display(ct_ST_config *cfg);
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif
文件【config.c】,解析库的函数实现,包含了一个main函数:
#include "config.h"
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
ct_ST_config_variable *ct_config_variable_new(const char *name, const char *value)
{
ct_ST_config_variable *var = (ct_ST_config_variable *)malloc(sizeof(ct_ST_config_variable) + strlen(name) + strlen(value) + 2);
if (var == NULL)
return NULL;
memset((char *)var, 0, sizeof(ct_ST_config_variable));
TAILQ_INIT(&var->prevline_comment_list);
TAILQ_INIT(&var->sameline_comment_list);
var->name = var->buf;
var->value = var->buf + strlen(name) + 1;
if (name != NULL)
strcpy(var->name, name);
if (value != NULL)
strcpy(var->value, value);
return var;
}
void ct_config_variable_destory(ct_ST_config_variable *var)
{
if (var == NULL)
return;
ct_ST_config_comment *comment;
TAILQ_FOREACH(comment, &var->prevline_comment_list, next)
{
if (comment != NULL)
{
free(comment->comment);
free(comment);
}
}
TAILQ_FOREACH(comment, &var->sameline_comment_list, next)
{
if (comment != NULL)
{
free(comment->comment);
free(comment);
}
}
free(var);
}
int ct_config_variable_append(ct_ST_config_section *section, ct_ST_config_variable *var)
{
if (section == NULL || var == NULL)
return -1;
//printf("enter ct_config_section_append, %d, %d, %d\n", \
// §ion->variable_list, section->variable_list.tqh_last, var);
TAILQ_INSERT_TAIL(§ion->variable_list, var, next);
return 0;
}
ct_ST_config_variable *ct_config_variable_find(ct_ST_config_section *section, const char *name)
{
if (section == NULL || name == NULL)
return NULL;
ct_ST_config_variable *var;
TAILQ_FOREACH(var, §ion->variable_list, next)
{
if (strcmp(var->name, name) == 0)
return var;
}
return NULL;
}
int ct_config_variable_delete(ct_ST_config_section *section, const char *name)
{
if (section == NULL || name == NULL)
return -1;
ct_ST_config_variable *var;
var = ct_config_variable_find(section, name);
if (var != NULL)
{
TAILQ_REMOVE(§ion->variable_list, var, next);
ct_config_variable_destory(var);
return 1;
}
else
return 0;
}
void ct_config_variable_display(ct_ST_config_variable *var)
{
if (var == NULL)
return NULL;
printf(" {%s} = {%s} \n", var->name, var->value);
}
ct_ST_config_section *ct_config_section_new(const char *name)
{
int len = strlen(name);
ct_ST_config_section *section = malloc(sizeof(ct_ST_config_section) + len + 1);
section->name = section->buf;
TAILQ_INIT(§ion->variable_list);
strcpy(section->name, name);
return section;
}
void ct_config_section_destory(ct_ST_config_section *section)
{
if (section == NULL)
return;
ct_ST_config_variable *var;
TAILQ_FOREACH(var, §ion->variable_list, next)
{
ct_config_variable_destory(var);
}
}
int ct_config_section_append(ct_ST_config *cfg, ct_ST_config_section *section)
{
if (cfg == NULL || section == NULL)
return -1;
//printf("enter ct_config_section_append, %d, %d, %d\n", &(cfg->section_list.tqh_first), cfg->section_list.tqh_last, section);
TAILQ_INSERT_TAIL(&cfg->section_list, section, next);
return 0;
}
ct_ST_config *ct_config_section_find(ct_ST_config *cfg, const char *match)
{
if (cfg == NULL || match == NULL)
return NULL;
ct_ST_config_section *section;
TAILQ_FOREACH(section, &cfg->section_list, next)
{
if (strcmp(section->name, match) == 0)
return section;
}
return NULL;
}
int ct_config_section_delete(ct_ST_config *cfg, const char *name)
{
if (cfg == NULL || name == NULL)
return -1;
ct_ST_config_section *section;
section = ct_config_section_find(cfg, name);
if (section == NULL)
return 0;
else
{
TAILQ_REMOVE(&cfg->section_list, section, next);
ct_config_section_destory(section);
return 0;
}
}
void ct_config_section_display(ct_ST_config_section *section)
{
if (section == NULL)
return;
ct_ST_config_variable *var;
printf(" section:%s\n", section->name);
TAILQ_FOREACH(var, §ion->variable_list, next)
{
ct_config_variable_display(var);
}
}
ct_ST_config *ct_config_new(const char *filename)
{
int len = strlen(filename);
ct_ST_config *cfg = malloc(sizeof(ct_ST_config) + len + 1);
TAILQ_INIT(&cfg->section_list);
cfg->filename = cfg->buf;
strcpy(cfg->filename, filename);
// printf("cfg = %d, cfg->filename = %s, &cfg->section_list = %d, &cfg->section_list.tqh_first = %d, &cfg->section_list.tqh_last = %d\n", \
// cfg, cfg->filename, &cfg->section_list, &cfg->section_list.tqh_first, &cfg->section_list.tqh_last);
return cfg;
}
void ct_config_destory(ct_ST_config *cfg)
{
if (cfg == NULL)
return;
ct_ST_config_section *section;
TAILQ_FOREACH(section, &cfg->section_list, next)
{
ct_config_section_destory(section);
}
}
static char *find_first_equal(char *string)
{
if (string == NULL || !strcmp(string, ""))
return NULL;
char *comment = NULL, *equal = NULL;
comment = strchr(string, '#');
equal = strchr(string, '=');
if (comment != NULL && comment < equal)
equal = NULL;
return equal;
}
static char *find_last_dash(char *string)
{
if (string == NULL || !strcmp(string, ""))
return NULL;
char *comment = NULL, *prev_dash = NULL, *next_dash = NULL;
comment = strchr(string, '#');
while ( (next_dash = strchr(string, ']')) != NULL)
{
if (comment != NULL && next_dash > comment)
break;
else
{
string = next_dash + 1;
prev_dash = next_dash;
}
}
return prev_dash;
}
int clear_space(const char *str, char **new_str)
{
if (str == NULL || !strcmp(str, ""))
return -1;
int len;
const char *start = str, *end = str + strlen(str)-1;
while (*start == ' ' || *start == '\n' || *start == '\t')start++;
while (*end == ' ' || *end == '\n' || *end == '\t')end--;
if (start > end)
{
*new_str = NULL;
return -1;
}
else
{
len = end-start+1;
*new_str = (char *)malloc(10+1);
strncpy(*new_str, start, len);
*(*new_str + len) = '\0';
return 0;
}
}
static int prase_textline(char *start, ct_ST_config *cfg )
{
char *p_comment = NULL, *p_equal = NULL, *p_dash = NULL, *name = NULL, *value = NULL,
*new_name = NULL, *new_value = NULL;
int name_len = 0, value_len = 0, ret = 0;
ct_ST_config_variable *var;
ct_ST_config_section *section;
if (start == NULL || !strcmp(start, "") || cfg == NULL)
return -1;
if (*start == '#')
{
/*process comment*/
}
else if (*start == '[')
{
/* process section */
p_comment = strchr(start, '#');
p_dash = find_last_dash(start+1);
if (p_dash == NULL)
{
//invalid section
printf("invalid section,only left dash : %s", start);
return -1;
}
name_len = p_dash -1 -start;
// printf("len = %d, *p_dash = %c\n", name_len, *p_dash);
name = (char *)malloc(name_len + 1);
strncpy(name, start+1, name_len);
*(name + name_len) = '\0';
clear_space(name, &new_name);
if (new_name == NULL || !strcmp(new_name, ""))
{
printf("invalid section,no name : %s\n", start);
return -1;
}
// printf("insert new section:%s\n", new_name);
if ( ct_config_section_append(cfg, ct_config_section_new(new_name)) == 0)
{
free(name);
free(new_name);
}
}
else
{
/* process variable */
p_comment = strchr(start, '#');
p_equal = find_first_equal(start + 1);
if (p_equal == NULL)
{
printf("invalid variable, no equal : %s", start);
return -1;
}
name_len = p_equal-start;
name = (char *)malloc(name_len + 1);
if (name == NULL)
printf("malloc failed\n");
strncpy(name, start , name_len);
*(name + name_len) = '\0';
clear_space(name, &new_name);
if (new_name == NULL || !strcmp(new_name, ""))
{
printf("invalid name : %s", start);
return -1;
}
if (p_comment != NULL)
value_len = p_comment-p_equal-1;
else
value_len = start + strlen(start)-1;
value = (char *)malloc(value_len + 1);
strncpy(value, p_equal + 1, value_len);
*(value+value_len) = '\0';
clear_space(value, &new_value);
if (new_value == NULL || !strcmp(new_value))
{
printf("invalid value : %s", start);
return -1;
}
// printf("insert new variable: name = %s, value = %s\n", new_name, new_value);
section = TAILQ_LAST(&cfg->section_list, ct_config_section_list);
var = ct_config_variable_new(new_name, new_value);
ret = ct_config_variable_append(section, var);
if (ret != 0)
{
printf("add variable in section error\n");
return -1;
}
}
return 0;
}
int ct_config_load(const char *filename, ct_ST_config **cfg)
{
if (filename == NULL || !strcmp(filename, ""))
return -1;
FILE * fp;
char * line = NULL, *ptr = NULL;
size_t len = 0;
size_t read;
fp = fopen(filename, "r");
if (fp == NULL)
exit(EXIT_FAILURE);
*cfg = ct_config_new(filename);
while ((read = getline(&line, &len, fp)) != -1) {
// printf("read text: byte = %zu, len = %d, text = {%s}:\n", read, len, line);
ptr = line;
while (*ptr == " ") ptr++;
if (*ptr == "\0")
continue;
prase_textline(ptr, *cfg);
}
if (line)
free(line);
}
void ct_config_display(ct_ST_config *cfg)
{
ct_ST_config_section *section;
if (cfg == NULL)
return;
printf("----------------------------------- \n");
printf(" display config parse result\n");
printf("-----------------------------------\n");
printf("parse filename : %s\n", cfg->filename);
TAILQ_FOREACH(section, &cfg->section_list, next)
{
ct_config_section_display(section);
}
}
int main()
{
ct_ST_config *cfg = NULL;
ct_config_load("test.conf", &cfg);
// ct_config_section_display(ct_config_section_find(cfg, "section9"));
// ct_config_variable_display(ct_config_variable_find(ct_config_section_find(cfg, "section1"), "name3"));
// ct_config_section_delete(cfg, "section1");
// ct_config_variable_delete(ct_config_section_find(cfg, "section1"), "name3");
ct_config_display(cfg);
}
文件【test.conf】,自己随便写的测试文件,不是特别规范,很多情况可能都没考虑到:
[global]
[[left]
[right]]
#comment
name1 = 1
name2 = 2
[section1]
name3 = 3 #comment
=2
test2]
hello
#conmm name6=6
[section2
name4 = 4
[section3]
name5 =
name7 = name7=1 #comment
name8 = =name8 #comment
name9 = =name9
编译方法:gcc config.c -o config
运行:./config