1 /*
2 * Hedgewars, a free turn based strategy game
3 * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #include "inihelper.h"
21 #include "../iniparser/dictionary.h"
22 #include "../iniparser/iniparser.h"
23
24 #include "logging.h"
25 #include "util.h"
26
27 #include <string.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30 #include <limits.h>
31 #include <errno.h>
32 #include <stdarg.h>
33
34 struct _flib_ini {
35 dictionary *inidict;
36 char *currentSection;
37 };
38
createDictKey(const char * sectionName,const char * keyName)39 static char *createDictKey(const char *sectionName, const char *keyName) {
40 return flib_asprintf("%s:%s", sectionName, keyName);
41 }
42
43 /**
44 * Turns a string into a lowercase string, in-place.
45 */
strToLower(char * str)46 static void strToLower(char *str) {
47 if(str) {
48 while(*str) {
49 *str = tolower(*str);
50 str++;
51 }
52 }
53 }
54
flib_ini_create(const char * filename)55 flib_ini *flib_ini_create(const char *filename) {
56 flib_ini *result = NULL;
57 flib_ini *tmpIni = flib_calloc(1, sizeof(flib_ini));
58 if(tmpIni) {
59 if(filename) {
60 tmpIni->inidict = iniparser_load(filename);
61 }
62 if(!tmpIni->inidict) {
63 tmpIni->inidict = dictionary_new(0);
64 }
65 if(tmpIni->inidict) {
66 result = tmpIni;
67 tmpIni = NULL;
68 }
69 }
70 flib_ini_destroy(tmpIni);
71 return result;
72 }
73
flib_ini_load(const char * filename)74 flib_ini *flib_ini_load(const char *filename) {
75 flib_ini *result = NULL;
76 if(!log_badargs_if(filename==NULL)) {
77 flib_ini *tmpIni = flib_calloc(1, sizeof(flib_ini));
78 if(tmpIni) {
79 tmpIni->inidict = iniparser_load(filename);
80 if(tmpIni->inidict) {
81 result = tmpIni;
82 tmpIni = NULL;
83 }
84 }
85 flib_ini_destroy(tmpIni);
86 }
87 return result;
88 }
89
flib_ini_save(flib_ini * ini,const char * filename)90 int flib_ini_save(flib_ini *ini, const char *filename) {
91 int result = INI_ERROR_OTHER;
92 if(!log_badargs_if2(ini==NULL, filename==NULL)) {
93 FILE *file = fopen(filename, "wb");
94 if(!file) {
95 flib_log_e("Error opening file \"%s\" for writing.", filename);
96 } else {
97 iniparser_dump_ini(ini->inidict, file);
98 if(fclose(file)) {
99 flib_log_e("Write error on ini file \"%s\"", filename);
100 } else {
101 result = 0;
102 }
103 }
104 }
105 return result;
106 }
107
flib_ini_destroy(flib_ini * ini)108 void flib_ini_destroy(flib_ini *ini) {
109 if(ini) {
110 if(ini->inidict) {
111 iniparser_freedict(ini->inidict);
112 }
113 free(ini->currentSection);
114 free(ini);
115 }
116 }
117
flib_ini_enter_section(flib_ini * ini,const char * section)118 int flib_ini_enter_section(flib_ini *ini, const char *section) {
119 int result = INI_ERROR_OTHER;
120 if(ini) {
121 free(ini->currentSection);
122 ini->currentSection = NULL;
123 }
124 if(!log_badargs_if2(ini==NULL, section==NULL)) {
125 if(!iniparser_find_entry(ini->inidict, section)) {
126 flib_log_d("Ini section %s not found", section);
127 result = INI_ERROR_NOTFOUND;
128 } else {
129 ini->currentSection = flib_strdupnull(section);
130 if(ini->currentSection) {
131 // Usually iniparser ignores case, but some section-handling functions don't,
132 // so we set it to lowercase manually
133 strToLower(ini->currentSection);
134 result = 0;
135 }
136 }
137 }
138 return result;
139 }
140
flib_ini_create_section(flib_ini * ini,const char * section)141 int flib_ini_create_section(flib_ini *ini, const char *section) {
142 int result = INI_ERROR_OTHER;
143 if(!log_badargs_if2(ini==NULL, section==NULL)) {
144 result = flib_ini_enter_section(ini, section);
145 if(result == INI_ERROR_NOTFOUND) {
146 if(iniparser_set(ini->inidict, section, NULL)) {
147 flib_log_e("Error creating ini section %s", section);
148 result = INI_ERROR_OTHER;
149 } else {
150 result = flib_ini_enter_section(ini, section);
151 }
152 }
153 }
154 return result;
155 }
156
157 /**
158 * The result is an internal string of the iniparser, don't free it.
159 */
findValue(dictionary * dict,const char * section,const char * key)160 static char *findValue(dictionary *dict, const char *section, const char *key) {
161 char *result = NULL;
162 char *dictKey = createDictKey(section, key);
163 if(dictKey) {
164 result = iniparser_getstring(dict, dictKey, NULL);
165 }
166 free(dictKey);
167 return result;
168 }
169
flib_ini_get_str(flib_ini * ini,char ** outVar,const char * key)170 int flib_ini_get_str(flib_ini *ini, char **outVar, const char *key) {
171 char *tmpValue = NULL;
172 int result = flib_ini_get_str_opt(ini, &tmpValue, key, NULL);
173 if(result==0) {
174 if(tmpValue == NULL) {
175 result = INI_ERROR_NOTFOUND;
176 } else {
177 *outVar = tmpValue;
178 tmpValue = NULL;
179 }
180 }
181 free(tmpValue);
182 return result;
183 }
184
flib_ini_get_str_opt(flib_ini * ini,char ** outVar,const char * key,const char * def)185 int flib_ini_get_str_opt(flib_ini *ini, char **outVar, const char *key, const char *def) {
186 int result = INI_ERROR_OTHER;
187 if(!log_badargs_if4(ini==NULL, ini->currentSection==NULL, outVar==NULL, key==NULL)) {
188 const char *value = findValue(ini->inidict, ini->currentSection, key);
189 if(!value) {
190 value = def;
191 }
192 char *valueDup = flib_strdupnull(value);
193 if(valueDup || !def) {
194 *outVar = valueDup;
195 result = 0;
196 }
197 }
198 return result;
199 }
200
flib_ini_get_int(flib_ini * ini,int * outVar,const char * key)201 int flib_ini_get_int(flib_ini *ini, int *outVar, const char *key) {
202 char *tmpValue = NULL;
203 int result = flib_ini_get_str(ini, &tmpValue, key);
204 if(result==0) {
205 errno = 0;
206 long val = strtol(tmpValue, NULL, 10);
207 if(errno!=0 || val<INT_MIN || val>INT_MAX) {
208 flib_log_w("Cannot parse ini setting %s/%s = \"%s\" as integer.", ini->currentSection, key, tmpValue);
209 result = INI_ERROR_FORMAT;
210 } else {
211 *outVar = val;
212 }
213 }
214 free(tmpValue);
215 return result;
216 }
217
flib_ini_get_int_opt(flib_ini * ini,int * outVar,const char * key,int def)218 int flib_ini_get_int_opt(flib_ini *ini, int *outVar, const char *key, int def) {
219 int tmpValue;
220 int result = flib_ini_get_int(ini, &tmpValue, key);
221 if(result == 0) {
222 *outVar = tmpValue;
223 } else if(result == INI_ERROR_NOTFOUND || result == INI_ERROR_FORMAT) {
224 *outVar = def;
225 result = 0;
226 }
227 return result;
228 }
229
flib_ini_get_bool(flib_ini * ini,bool * outVar,const char * key)230 int flib_ini_get_bool(flib_ini *ini, bool *outVar, const char *key) {
231 char *tmpValue = NULL;
232 int result = flib_ini_get_str(ini, &tmpValue, key);
233 if(result==0) {
234 bool trueval = strchr("1tTyY", tmpValue[0]);
235 bool falseval = strchr("0fFnN", tmpValue[0]);
236 if(!trueval && !falseval) {
237 flib_log_w("ini setting %s/%s = \"%s\" is not a recognized truth value.", ini->currentSection, key, tmpValue);
238 result = INI_ERROR_FORMAT;
239 } else {
240 *outVar = trueval;
241 }
242 }
243 free(tmpValue);
244 return result;
245 }
246
flib_ini_get_bool_opt(flib_ini * ini,bool * outVar,const char * key,bool def)247 int flib_ini_get_bool_opt(flib_ini *ini, bool *outVar, const char *key, bool def) {
248 bool tmpValue;
249 int result = flib_ini_get_bool(ini, &tmpValue, key);
250 if(result == 0) {
251 *outVar = tmpValue;
252 } else if(result == INI_ERROR_NOTFOUND || result == INI_ERROR_FORMAT) {
253 *outVar = def;
254 result = 0;
255 }
256 return result;
257 }
258
flib_ini_set_str(flib_ini * ini,const char * key,const char * value)259 int flib_ini_set_str(flib_ini *ini, const char *key, const char *value) {
260 int result = INI_ERROR_OTHER;
261 if(!log_badargs_if4(ini==NULL, ini->currentSection==NULL, key==NULL, value==NULL)) {
262 char *dictKey = createDictKey(ini->currentSection, key);
263 if(dictKey) {
264 result = iniparser_set(ini->inidict, dictKey, value);
265 if(result) {
266 flib_log_e("Error setting ini entry %s to %s", dictKey, value);
267 }
268 }
269 free(dictKey);
270 }
271 return result;
272 }
273
flib_ini_set_int(flib_ini * ini,const char * key,int value)274 int flib_ini_set_int(flib_ini *ini, const char *key, int value) {
275 int result = INI_ERROR_OTHER;
276 char *strvalue = flib_asprintf("%i", value);
277 if(strvalue) {
278 result = flib_ini_set_str(ini, key, strvalue);
279 }
280 free(strvalue);
281 return result;
282 }
283
flib_ini_set_bool(flib_ini * ini,const char * key,bool value)284 int flib_ini_set_bool(flib_ini *ini, const char *key, bool value) {
285 return flib_ini_set_str(ini, key, value ? "true" : "false");
286 }
287
flib_ini_get_sectioncount(flib_ini * ini)288 int flib_ini_get_sectioncount(flib_ini *ini) {
289 if(!log_badargs_if(ini==NULL)) {
290 return iniparser_getnsec(ini->inidict);
291 }
292 return INI_ERROR_OTHER;
293 }
294
flib_ini_get_sectionname(flib_ini * ini,int number)295 char *flib_ini_get_sectionname(flib_ini *ini, int number) {
296 if(!log_badargs_if2(ini==NULL, number<0)) {
297 return flib_strdupnull(iniparser_getsecname(ini->inidict, number));
298 }
299 return NULL;
300 }
301
flib_ini_get_keycount(flib_ini * ini)302 int flib_ini_get_keycount(flib_ini *ini) {
303 if(!log_badargs_if2(ini==NULL, ini->currentSection==NULL)) {
304 return iniparser_getsecnkeys(ini->inidict, ini->currentSection);
305 }
306 return INI_ERROR_OTHER;
307 }
308
flib_ini_get_keyname(flib_ini * ini,int number)309 char *flib_ini_get_keyname(flib_ini *ini, int number) {
310 char *result = NULL;
311 if(!log_badargs_if3(ini==NULL, ini->currentSection==NULL, number<0)) {
312 int keyCount = iniparser_getsecnkeys(ini->inidict, ini->currentSection);
313 char **keys = iniparser_getseckeys(ini->inidict, ini->currentSection);
314 if(keys && keyCount>number) {
315 // The keys are in the format section:key, so we have to skip the section and colon.
316 result = flib_strdupnull(keys[number]+strlen(ini->currentSection)+1);
317 }
318 free(keys);
319 }
320 return result;
321 }
322