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
21struct ConfigurationHandlerData {
22 void (*handler)(const char* key, const char* value, void* data);
23 void* data;
24};
25
26static void _tableDeinit(void* table) {
27 TableDeinit(table);
28 free(table);
29}
30
31static void _sectionDeinit(void* string) {
32 free(string);
33}
34
35static int _iniRead(void* configuration, const char* section, const char* key, const char* value) {
36 if (section && !section[0]) {
37 section = 0;
38 }
39 ConfigurationSetValue(configuration, section, key, value);
40 return 1;
41}
42
43static void _keyHandler(const char* key, void* value, void* user) {
44 char line[256];
45 struct VFile* vf = user;
46 size_t len = snprintf(line, sizeof(line), "%s=%s\n", key, (const char*) value);
47 if (len >= sizeof(line)) {
48 len = sizeof(line) - 1;
49 }
50 vf->write(vf, line, len);
51}
52
53static void _sectionHandler(const char* key, void* section, void* user) {
54 char line[256];
55 struct VFile* vf = user;
56 size_t len = snprintf(line, sizeof(line), "[%s]\n", key);
57 if (len >= sizeof(line)) {
58 len = sizeof(line) - 1;
59 }
60 vf->write(vf, line, len);
61 HashTableEnumerate(section, _keyHandler, user);
62 vf->write(vf, "\n", 1);
63}
64
65static void _sectionEnumHandler(const char* key, void* section, void* user) {
66 struct ConfigurationSectionHandlerData* data = user;
67 UNUSED(section);
68 data->handler(key, data->data);
69}
70
71static void _enumHandler(const char* key, void* value, void* user) {
72 struct ConfigurationHandlerData* data = user;
73 data->handler(key, value, data->data);
74}
75
76void ConfigurationInit(struct Configuration* configuration) {
77 HashTableInit(&configuration->sections, 0, _tableDeinit);
78 HashTableInit(&configuration->root, 0, _sectionDeinit);
79}
80
81void ConfigurationDeinit(struct Configuration* configuration) {
82 HashTableDeinit(&configuration->sections);
83 HashTableDeinit(&configuration->root);
84}
85
86void ConfigurationSetValue(struct Configuration* configuration, const char* section, const char* key, const char* value) {
87 struct Table* currentSection = &configuration->root;
88 if (section) {
89 currentSection = HashTableLookup(&configuration->sections, section);
90 if (!currentSection) {
91 if (value) {
92 currentSection = malloc(sizeof(*currentSection));
93 HashTableInit(currentSection, 0, _sectionDeinit);
94 HashTableInsert(&configuration->sections, section, currentSection);
95 } else {
96 return;
97 }
98 }
99 }
100 if (value) {
101 HashTableInsert(currentSection, key, strdup(value));
102 } else {
103 HashTableRemove(currentSection, key);
104 }
105}
106
107void ConfigurationSetIntValue(struct Configuration* configuration, const char* section, const char* key, int value) {
108 char charValue[12];
109 sprintf(charValue, "%i", value);
110 ConfigurationSetValue(configuration, section, key, charValue);
111}
112
113void ConfigurationSetUIntValue(struct Configuration* configuration, const char* section, const char* key, unsigned value) {
114 char charValue[12];
115 sprintf(charValue, "%u", value);
116 ConfigurationSetValue(configuration, section, key, charValue);
117}
118
119void ConfigurationSetFloatValue(struct Configuration* configuration, const char* section, const char* key, float value) {
120 char charValue[16];
121 ftostr_u(charValue, sizeof(charValue), value);
122 ConfigurationSetValue(configuration, section, key, charValue);
123}
124
125void ConfigurationClearValue(struct Configuration* configuration, const char* section, const char* key) {
126 struct Table* currentSection = &configuration->root;
127 if (section) {
128 currentSection = HashTableLookup(&configuration->sections, section);
129 if (!currentSection) {
130 return;
131 }
132 }
133 HashTableRemove(currentSection, key);
134}
135
136bool ConfigurationHasSection(const struct Configuration* configuration, const char* section) {
137 return HashTableLookup(&configuration->sections, section);
138}
139
140const char* ConfigurationGetValue(const struct Configuration* configuration, const char* section, const char* key) {
141 const struct Table* currentSection = &configuration->root;
142 if (section) {
143 currentSection = HashTableLookup(&configuration->sections, section);
144 if (!currentSection) {
145 return 0;
146 }
147 }
148 return HashTableLookup(currentSection, key);
149}
150
151static char* _vfgets(char* stream, int size, void* user) {
152 struct VFile* vf = user;
153 if (vf->readline(vf, stream, size) > 0) {
154 return stream;
155 }
156 return 0;
157}
158
159bool ConfigurationRead(struct Configuration* configuration, const char* path) {
160 struct VFile* vf = VFileOpen(path, O_RDONLY);
161 if (!vf) {
162 return false;
163 }
164 bool res = ConfigurationReadVFile(configuration, vf);
165 vf->close(vf);
166 return res;
167}
168
169bool ConfigurationReadVFile(struct Configuration* configuration, struct VFile* vf) {
170 HashTableClear(&configuration->root);
171 HashTableClear(&configuration->sections);
172 return ini_parse_stream(_vfgets, vf, _iniRead, configuration) == 0;
173}
174
175bool ConfigurationWrite(const struct Configuration* configuration, const char* path) {
176 struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
177 if (!vf) {
178 return false;
179 }
180 HashTableEnumerate(&configuration->root, _keyHandler, vf);
181 HashTableEnumerate(&configuration->sections, _sectionHandler, vf);
182 vf->close(vf);
183 return true;
184}
185
186bool ConfigurationWriteSection(const struct Configuration* configuration, const char* path, const char* section) {
187 const struct Table* currentSection = &configuration->root;
188 struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_APPEND);
189 if (!vf) {
190 return false;
191 }
192 if (section) {
193 currentSection = HashTableLookup(&configuration->sections, section);
194 char line[256];
195 size_t len = snprintf(line, sizeof(line), "[%s]\n", section);
196 if (len >= sizeof(line)) {
197 len = sizeof(line) - 1;
198 }
199 vf->write(vf, line, len);
200 }
201 if (currentSection) {
202 HashTableEnumerate(currentSection, _sectionHandler, vf);
203 }
204 vf->close(vf);
205 return true;
206}
207
208void ConfigurationEnumerateSections(const struct Configuration* configuration, void (*handler)(const char* sectionName, void* user), void* user) {
209 struct ConfigurationSectionHandlerData handlerData = { handler, user };
210 HashTableEnumerate(&configuration->sections, _sectionEnumHandler, &handlerData);
211}
212
213void ConfigurationEnumerate(const struct Configuration* configuration, const char* section, void (*handler)(const char* key, const char* value, void* user), void* user) {
214 struct ConfigurationHandlerData handlerData = { handler, user };
215 const struct Table* currentSection = &configuration->root;
216 if (section) {
217 currentSection = HashTableLookup(&configuration->sections, section);
218 }
219 if (currentSection) {
220 HashTableEnumerate(currentSection, _enumHandler, &handlerData);
221 }
222}