|
|
|
|
/**
|
|
|
|
|
* Copyright (c) 2016 rxi
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
|
*
|
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
|
* SOFTWARE.
|
|
|
|
|
*/
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <strings.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include "ini.h"
|
|
|
|
|
|
|
|
|
|
// 去除字符串首尾的空白字符
|
|
|
|
|
char* trim(char* str) {
|
|
|
|
|
char *end;
|
|
|
|
|
|
|
|
|
|
// 去除首部空白
|
|
|
|
|
while(isspace((unsigned char)*str)) str++;
|
|
|
|
|
|
|
|
|
|
if(*str == 0) return str;
|
|
|
|
|
|
|
|
|
|
// 去除尾部空白
|
|
|
|
|
end = str + strlen(str) - 1;
|
|
|
|
|
while(end > str && isspace((unsigned char)*end)) end--;
|
|
|
|
|
|
|
|
|
|
end[1] = '\0';
|
|
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初始化INI文件结构
|
|
|
|
|
INI_File* ini_init() {
|
|
|
|
|
INI_File *ini = malloc(sizeof(INI_File));
|
|
|
|
|
if (!ini) return NULL;
|
|
|
|
|
|
|
|
|
|
ini->section_capacity = 10;
|
|
|
|
|
ini->section_count = 0;
|
|
|
|
|
ini->sections = malloc(sizeof(INI_Section) * ini->section_capacity);
|
|
|
|
|
|
|
|
|
|
return ini;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 释放INI文件结构
|
|
|
|
|
void ini_free(INI_File *ini) {
|
|
|
|
|
if (!ini) return;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < ini->section_count; i++) {
|
|
|
|
|
free(ini->sections[i].entries);
|
|
|
|
|
}
|
|
|
|
|
free(ini->sections);
|
|
|
|
|
free(ini);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 解析INI文件
|
|
|
|
|
int ini_parse(INI_File *ini, const char *filename) {
|
|
|
|
|
FILE *file = fopen(filename, "r");
|
|
|
|
|
if (!file) {
|
|
|
|
|
return -1; // 文件打开失败
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char line[MAX_LINE_LENGTH];
|
|
|
|
|
char current_section[MAX_SECTION_LENGTH] = "";
|
|
|
|
|
int line_num = 0;
|
|
|
|
|
|
|
|
|
|
while (fgets(line, sizeof(line), file)) {
|
|
|
|
|
line_num++;
|
|
|
|
|
char *trimmed_line = trim(line);
|
|
|
|
|
|
|
|
|
|
// 跳过空行和注释
|
|
|
|
|
if (strlen(trimmed_line) == 0 || trimmed_line[0] == ';' || trimmed_line[0] == '#') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理节(section)
|
|
|
|
|
if (trimmed_line[0] == '[' && trimmed_line[strlen(trimmed_line)-1] == ']') {
|
|
|
|
|
strncpy(current_section, trimmed_line + 1, strlen(trimmed_line) - 2);
|
|
|
|
|
current_section[strlen(trimmed_line) - 2] = '\0';
|
|
|
|
|
trim(current_section);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理键值对
|
|
|
|
|
char *equal_pos = strchr(trimmed_line, '=');
|
|
|
|
|
if (equal_pos) {
|
|
|
|
|
*equal_pos = '\0';
|
|
|
|
|
char *key = trim(trimmed_line);
|
|
|
|
|
char *value = trim(equal_pos + 1);
|
|
|
|
|
|
|
|
|
|
// 如果key或value为空,跳过
|
|
|
|
|
if (strlen(key) == 0 || strlen(value) == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加到INI结构
|
|
|
|
|
ini_set_value(ini, current_section, key, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fclose(file);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设置键值对
|
|
|
|
|
int ini_set_value(INI_File *ini, const char *section, const char *key, const char *value) {
|
|
|
|
|
// 查找或创建section
|
|
|
|
|
INI_Section *current_section = NULL;
|
|
|
|
|
for (int i = 0; i < ini->section_count; i++) {
|
|
|
|
|
if (strcmp(ini->sections[i].section, section) == 0) {
|
|
|
|
|
current_section = &ini->sections[i];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果section不存在,创建新的
|
|
|
|
|
if (!current_section) {
|
|
|
|
|
if (ini->section_count >= ini->section_capacity) {
|
|
|
|
|
ini->section_capacity *= 2;
|
|
|
|
|
ini->sections = realloc(ini->sections, sizeof(INI_Section) * ini->section_capacity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
current_section = &ini->sections[ini->section_count];
|
|
|
|
|
strncpy(current_section->section, section, MAX_SECTION_LENGTH - 1);
|
|
|
|
|
current_section->section[MAX_SECTION_LENGTH - 1] = '\0';
|
|
|
|
|
current_section->entry_capacity = 10;
|
|
|
|
|
current_section->entry_count = 0;
|
|
|
|
|
current_section->entries = malloc(sizeof(INI_Entry) * current_section->entry_capacity);
|
|
|
|
|
ini->section_count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查找是否已存在该key
|
|
|
|
|
for (int i = 0; i < current_section->entry_count; i++) {
|
|
|
|
|
if (strcmp(current_section->entries[i].key, key) == 0) {
|
|
|
|
|
// 更新已存在的值
|
|
|
|
|
strncpy(current_section->entries[i].value, value, MAX_VALUE_LENGTH - 1);
|
|
|
|
|
current_section->entries[i].value[MAX_VALUE_LENGTH - 1] = '\0';
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加新的键值对
|
|
|
|
|
if (current_section->entry_count >= current_section->entry_capacity) {
|
|
|
|
|
current_section->entry_capacity *= 2;
|
|
|
|
|
current_section->entries = realloc(current_section->entries,
|
|
|
|
|
sizeof(INI_Entry) * current_section->entry_capacity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
INI_Entry *entry = ¤t_section->entries[current_section->entry_count];
|
|
|
|
|
strncpy(entry->key, key, MAX_KEY_LENGTH - 1);
|
|
|
|
|
entry->key[MAX_KEY_LENGTH - 1] = '\0';
|
|
|
|
|
strncpy(entry->value, value, MAX_VALUE_LENGTH - 1);
|
|
|
|
|
entry->value[MAX_VALUE_LENGTH - 1] = '\0';
|
|
|
|
|
current_section->entry_count++;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 保存INI文件到磁盘
|
|
|
|
|
int ini_save(INI_File *ini, const char *filename) {
|
|
|
|
|
FILE *file = fopen(filename, "w");
|
|
|
|
|
if (!file) {
|
|
|
|
|
return -1; // 文件打开失败
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 首先处理空section(全局设置)
|
|
|
|
|
for (int i = 0; i < ini->section_count; i++) {
|
|
|
|
|
if (strlen(ini->sections[i].section) == 0) {
|
|
|
|
|
for (int j = 0; j < ini->sections[i].entry_count; j++) {
|
|
|
|
|
fprintf(file, "%s = %s\n",
|
|
|
|
|
ini->sections[i].entries[j].key,
|
|
|
|
|
ini->sections[i].entries[j].value);
|
|
|
|
|
}
|
|
|
|
|
if (ini->sections[i].entry_count > 0) {
|
|
|
|
|
fprintf(file, "\n"); // 在节之间添加空行
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理其他section
|
|
|
|
|
for (int i = 0; i < ini->section_count; i++) {
|
|
|
|
|
if (strlen(ini->sections[i].section) > 0) {
|
|
|
|
|
// 写入section头
|
|
|
|
|
fprintf(file, "[%s]\n", ini->sections[i].section);
|
|
|
|
|
|
|
|
|
|
// 写入该section的所有键值对
|
|
|
|
|
for (int j = 0; j < ini->sections[i].entry_count; j++) {
|
|
|
|
|
fprintf(file, "%s = %s\n",
|
|
|
|
|
ini->sections[i].entries[j].key,
|
|
|
|
|
ini->sections[i].entries[j].value);
|
|
|
|
|
}
|
|
|
|
|
fprintf(file, "\n"); // 在节之间添加空行
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fclose(file);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 删除指定的键
|
|
|
|
|
int ini_delete_key(INI_File *ini, const char *section, const char *key) {
|
|
|
|
|
for (int i = 0; i < ini->section_count; i++) {
|
|
|
|
|
if (strcmp(ini->sections[i].section, section) == 0) {
|
|
|
|
|
for (int j = 0; j < ini->sections[i].entry_count; j++) {
|
|
|
|
|
if (strcmp(ini->sections[i].entries[j].key, key) == 0) {
|
|
|
|
|
// 将后面的元素前移
|
|
|
|
|
for (int k = j; k < ini->sections[i].entry_count - 1; k++) {
|
|
|
|
|
memcpy(&ini->sections[i].entries[k],
|
|
|
|
|
&ini->sections[i].entries[k + 1],
|
|
|
|
|
sizeof(INI_Entry));
|
|
|
|
|
}
|
|
|
|
|
ini->sections[i].entry_count--;
|
|
|
|
|
return 0; // 成功删除
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1; // 未找到
|
|
|
|
|
}
|
|
|
|
|
// 获取键值
|
|
|
|
|
char* ini_get_value(INI_File *ini, const char *section, const char *key,char *def) {
|
|
|
|
|
for (int i = 0; i < ini->section_count; i++) {
|
|
|
|
|
if (strcmp(ini->sections[i].section, section) == 0) {
|
|
|
|
|
for (int j = 0; j < ini->sections[i].entry_count; j++) {
|
|
|
|
|
if (strcmp(ini->sections[i].entries[j].key, key) == 0) {
|
|
|
|
|
return ini->sections[i].entries[j].value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return def; // 未找到
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取整数值
|
|
|
|
|
int ini_get_int(INI_File *ini, const char *section, const char *key, int default_value) {
|
|
|
|
|
const char *value = ini_get_value(ini, section, key,"0");
|
|
|
|
|
if (!value) return default_value;
|
|
|
|
|
return atoi(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取浮点数值
|
|
|
|
|
double ini_get_double(INI_File *ini, const char *section, const char *key, double default_value) {
|
|
|
|
|
const char *value = ini_get_value(ini, section, key,"0");
|
|
|
|
|
if (!value) return default_value;
|
|
|
|
|
return atof(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取布尔值
|
|
|
|
|
int ini_get_bool(INI_File *ini, const char *section, const char *key, int default_value) {
|
|
|
|
|
const char *value = ini_get_value(ini, section, key,"0");
|
|
|
|
|
if (!value) return default_value;
|
|
|
|
|
|
|
|
|
|
if (strcasecmp(value, "true") == 0 || strcasecmp(value, "yes") == 0 ||
|
|
|
|
|
strcasecmp(value, "1") == 0 || strcasecmp(value, "on") == 0) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (strcasecmp(value, "false") == 0 || strcasecmp(value, "no") == 0 ||
|
|
|
|
|
strcasecmp(value, "0") == 0 || strcasecmp(value, "off") == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return default_value;
|
|
|
|
|
}
|