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