/* Copyright (c) 2013-2014 Jeffrey Pfau * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "configuration.h" #include "util/formatting.h" #include "util/string.h" #include "util/vfs.h" #include "third-party/inih/ini.h" #include struct ConfigurationSectionHandlerData { void (*handler)(const char* section, void* data); void* data; }; static void _tableDeinit(void* table) { TableDeinit(table); free(table); } static void _sectionDeinit(void* string) { free(string); } static int _iniRead(void* configuration, const char* section, const char* key, const char* value) { if (section && !section[0]) { section = 0; } ConfigurationSetValue(configuration, section, key, value); return 1; } static void _keyHandler(const char* key, void* value, void* user) { char line[256]; struct VFile* vf = user; size_t len = snprintf(line, sizeof(line), "%s=%s\n", key, (const char*) value); if (len >= sizeof(line)) { len = sizeof(line) - 1; } vf->write(vf, line, len); } static void _sectionHandler(const char* key, void* section, void* user) { char line[256]; struct VFile* vf = user; size_t len = snprintf(line, sizeof(line), "[%s]\n", key); if (len >= sizeof(line)) { len = sizeof(line) - 1; } vf->write(vf, line, len); HashTableEnumerate(section, _keyHandler, user); vf->write(vf, "\n", 1); } static void _sectionEnumHandler(const char* key, void* section, void* user) { struct ConfigurationSectionHandlerData* data = user; UNUSED(section); data->handler(key, data->data); } void ConfigurationInit(struct Configuration* configuration) { HashTableInit(&configuration->sections, 0, _tableDeinit); HashTableInit(&configuration->root, 0, _sectionDeinit); } void ConfigurationDeinit(struct Configuration* configuration) { HashTableDeinit(&configuration->sections); HashTableDeinit(&configuration->root); } void ConfigurationSetValue(struct Configuration* configuration, const char* section, const char* key, const char* value) { struct Table* currentSection = &configuration->root; if (section) { currentSection = HashTableLookup(&configuration->sections, section); if (!currentSection) { if (value) { currentSection = malloc(sizeof(*currentSection)); HashTableInit(currentSection, 0, _sectionDeinit); HashTableInsert(&configuration->sections, section, currentSection); } else { return; } } } if (value) { HashTableInsert(currentSection, key, strdup(value)); } else { HashTableRemove(currentSection, key); } } void ConfigurationSetIntValue(struct Configuration* configuration, const char* section, const char* key, int value) { char charValue[12]; sprintf(charValue, "%i", value); ConfigurationSetValue(configuration, section, key, charValue); } void ConfigurationSetUIntValue(struct Configuration* configuration, const char* section, const char* key, unsigned value) { char charValue[12]; sprintf(charValue, "%u", value); ConfigurationSetValue(configuration, section, key, charValue); } void ConfigurationSetFloatValue(struct Configuration* configuration, const char* section, const char* key, float value) { char charValue[16]; ftostr_u(charValue, sizeof(charValue), value); ConfigurationSetValue(configuration, section, key, charValue); } void ConfigurationClearValue(struct Configuration* configuration, const char* section, const char* key) { struct Table* currentSection = &configuration->root; if (section) { currentSection = HashTableLookup(&configuration->sections, section); if (!currentSection) { return; } } HashTableRemove(currentSection, key); } bool ConfigurationHasSection(const struct Configuration* configuration, const char* section) { return HashTableLookup(&configuration->sections, section); } const char* ConfigurationGetValue(const struct Configuration* configuration, const char* section, const char* key) { const struct Table* currentSection = &configuration->root; if (section) { currentSection = HashTableLookup(&configuration->sections, section); if (!currentSection) { return 0; } } return HashTableLookup(currentSection, key); } static char* _vfgets(char* stream, int size, void* user) { struct VFile* vf = user; if (vf->readline(vf, stream, size) > 0) { return stream; } return 0; } bool ConfigurationRead(struct Configuration* configuration, const char* path) { struct VFile* vf = VFileOpen(path, O_RDONLY); if (!vf) { return false; } bool res = ConfigurationReadVFile(configuration, vf); vf->close(vf); return res; } bool ConfigurationReadVFile(struct Configuration* configuration, struct VFile* vf) { HashTableClear(&configuration->root); HashTableClear(&configuration->sections); return ini_parse_stream(_vfgets, vf, _iniRead, configuration) == 0; } bool ConfigurationWrite(const struct Configuration* configuration, const char* path) { struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC); if (!vf) { return false; } HashTableEnumerate(&configuration->root, _keyHandler, vf); HashTableEnumerate(&configuration->sections, _sectionHandler, vf); vf->close(vf); return true; } bool ConfigurationWriteSection(const struct Configuration* configuration, const char* path, const char* section) { const struct Table* currentSection = &configuration->root; struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_APPEND); if (!vf) { return false; } if (section) { currentSection = HashTableLookup(&configuration->sections, section); char line[256]; size_t len = snprintf(line, sizeof(line), "[%s]\n", section); if (len >= sizeof(line)) { len = sizeof(line) - 1; } vf->write(vf, line, len); } if (currentSection) { HashTableEnumerate(currentSection, _sectionHandler, vf); } vf->close(vf); return true; } void ConfigurationEnumerateSections(const struct Configuration* configuration, void (*handler)(const char* sectionName, void* user), void* user) { struct ConfigurationSectionHandlerData handlerData = { handler, user }; HashTableEnumerate(&configuration->sections, _sectionEnumHandler, &handlerData); }