src/util/configuration.c (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba-util/configuration.h>
7
8#include <mgba-util/formatting.h>
9#include <mgba-util/string.h>
10#include <mgba-util/vfs.h>
11
12#include "third-party/inih/ini.h"
13
14#include <float.h>
15
16struct ConfigurationSectionHandlerData {
17 void (*handler)(const char* section, void* data);
18 void* data;
19};
20
21static void _tableDeinit(void* table) {
22 TableDeinit(table);
23 free(table);
24}
25
26static void _sectionDeinit(void* string) {
27 free(string);
28}
29
30static int _iniRead(void* configuration, const char* section, const char* key, const char* value) {
31 if (section && !section[0]) {
32 section = 0;
33 }
34 ConfigurationSetValue(configuration, section, key, value);
35 return 1;
36}
37
38static void _keyHandler(const char* key, void* value, void* user) {
39 char line[256];
40 struct VFile* vf = user;
41 size_t len = snprintf(line, sizeof(line), "%s=%s\n", key, (const char*) value);
42 if (len >= sizeof(line)) {
43 len = sizeof(line) - 1;
44 }
45 vf->write(vf, line, len);
46}
47
48static void _sectionHandler(const char* key, void* section, void* user) {
49 char line[256];
50 struct VFile* vf = user;
51 size_t len = snprintf(line, sizeof(line), "[%s]\n", key);
52 if (len >= sizeof(line)) {
53 len = sizeof(line) - 1;
54 }
55 vf->write(vf, line, len);
56 HashTableEnumerate(section, _keyHandler, user);
57 vf->write(vf, "\n", 1);
58}
59
60static void _sectionEnumHandler(const char* key, void* section, void* user) {
61 struct ConfigurationSectionHandlerData* data = user;
62 UNUSED(section);
63 data->handler(key, data->data);
64}
65
66void ConfigurationInit(struct Configuration* configuration) {
67 HashTableInit(&configuration->sections, 0, _tableDeinit);
68 HashTableInit(&configuration->root, 0, _sectionDeinit);
69}
70
71void ConfigurationDeinit(struct Configuration* configuration) {
72 HashTableDeinit(&configuration->sections);
73 HashTableDeinit(&configuration->root);
74}
75
76void ConfigurationSetValue(struct Configuration* configuration, const char* section, const char* key, const char* value) {
77 struct Table* currentSection = &configuration->root;
78 if (section) {
79 currentSection = HashTableLookup(&configuration->sections, section);
80 if (!currentSection) {
81 if (value) {
82 currentSection = malloc(sizeof(*currentSection));
83 HashTableInit(currentSection, 0, _sectionDeinit);
84 HashTableInsert(&configuration->sections, section, currentSection);
85 } else {
86 return;
87 }
88 }
89 }
90 if (value) {
91 HashTableInsert(currentSection, key, strdup(value));
92 } else {
93 HashTableRemove(currentSection, key);
94 }
95}
96
97void ConfigurationSetIntValue(struct Configuration* configuration, const char* section, const char* key, int value) {
98 char charValue[12];
99 sprintf(charValue, "%i", value);
100 ConfigurationSetValue(configuration, section, key, charValue);
101}
102
103void ConfigurationSetUIntValue(struct Configuration* configuration, const char* section, const char* key, unsigned value) {
104 char charValue[12];
105 sprintf(charValue, "%u", value);
106 ConfigurationSetValue(configuration, section, key, charValue);
107}
108
109void ConfigurationSetFloatValue(struct Configuration* configuration, const char* section, const char* key, float value) {
110 char charValue[16];
111 ftostr_u(charValue, sizeof(charValue), value);
112 ConfigurationSetValue(configuration, section, key, charValue);
113}
114
115void ConfigurationClearValue(struct Configuration* configuration, const char* section, const char* key) {
116 struct Table* currentSection = &configuration->root;
117 if (section) {
118 currentSection = HashTableLookup(&configuration->sections, section);
119 if (!currentSection) {
120 return;
121 }
122 }
123 HashTableRemove(currentSection, key);
124}
125
126bool ConfigurationHasSection(const struct Configuration* configuration, const char* section) {
127 return HashTableLookup(&configuration->sections, section);
128}
129
130const char* ConfigurationGetValue(const struct Configuration* configuration, const char* section, const char* key) {
131 const struct Table* currentSection = &configuration->root;
132 if (section) {
133 currentSection = HashTableLookup(&configuration->sections, section);
134 if (!currentSection) {
135 return 0;
136 }
137 }
138 return HashTableLookup(currentSection, key);
139}
140
141static char* _vfgets(char* stream, int size, void* user) {
142 struct VFile* vf = user;
143 if (vf->readline(vf, stream, size) > 0) {
144 return stream;
145 }
146 return 0;
147}
148
149bool ConfigurationRead(struct Configuration* configuration, const char* path) {
150 struct VFile* vf = VFileOpen(path, O_RDONLY);
151 if (!vf) {
152 return false;
153 }
154 bool res = ConfigurationReadVFile(configuration, vf);
155 vf->close(vf);
156 return res;
157}
158
159bool ConfigurationReadVFile(struct Configuration* configuration, struct VFile* vf) {
160 HashTableClear(&configuration->root);
161 HashTableClear(&configuration->sections);
162 return ini_parse_stream(_vfgets, vf, _iniRead, configuration) == 0;
163}
164
165bool ConfigurationWrite(const struct Configuration* configuration, const char* path) {
166 struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
167 if (!vf) {
168 return false;
169 }
170 HashTableEnumerate(&configuration->root, _keyHandler, vf);
171 HashTableEnumerate(&configuration->sections, _sectionHandler, vf);
172 vf->close(vf);
173 return true;
174}
175
176bool ConfigurationWriteSection(const struct Configuration* configuration, const char* path, const char* section) {
177 const struct Table* currentSection = &configuration->root;
178 struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_APPEND);
179 if (!vf) {
180 return false;
181 }
182 if (section) {
183 currentSection = HashTableLookup(&configuration->sections, section);
184 char line[256];
185 size_t len = snprintf(line, sizeof(line), "[%s]\n", section);
186 if (len >= sizeof(line)) {
187 len = sizeof(line) - 1;
188 }
189 vf->write(vf, line, len);
190 }
191 if (currentSection) {
192 HashTableEnumerate(currentSection, _sectionHandler, vf);
193 }
194 vf->close(vf);
195 return true;
196}
197
198void ConfigurationEnumerateSections(const struct Configuration* configuration, void (*handler)(const char* sectionName, void* user), void* user) {
199 struct ConfigurationSectionHandlerData handlerData = { handler, user };
200 HashTableEnumerate(&configuration->sections, _sectionEnumHandler, &handlerData);
201}