1 #include "inihelper.h" |
1 #include "inihelper.h" |
|
2 #include "../iniparser/dictionary.h" |
|
3 #include "../iniparser/iniparser.h" |
|
4 |
2 #include "logging.h" |
5 #include "logging.h" |
3 #include "util.h" |
6 #include "util.h" |
4 |
7 |
5 #include <string.h> |
8 #include <string.h> |
6 #include <stdlib.h> |
9 #include <stdlib.h> |
7 #include <ctype.h> |
10 #include <ctype.h> |
8 #include <limits.h> |
11 #include <limits.h> |
9 #include <errno.h> |
12 #include <errno.h> |
10 #include <stdarg.h> |
13 #include <stdarg.h> |
11 |
14 |
12 static bool keychar_needs_urlencoding(char c) { |
15 struct _flib_ini { |
13 return !isalnum(c); |
16 dictionary *inidict; |
14 } |
17 char *currentSection; |
15 |
18 }; |
16 char *inihelper_urlencode(const char *inbuf) { |
19 |
17 if(!inbuf) { |
20 static char *createDictKey(const char *sectionName, const char *keyName) { |
18 return NULL; |
|
19 } |
|
20 size_t insize = strlen(inbuf); |
|
21 if(insize > SIZE_MAX/4) { |
|
22 return NULL; |
|
23 } |
|
24 |
|
25 char *outbuf = flib_malloc(insize*3+1); |
|
26 if(!outbuf) { |
|
27 return NULL; |
|
28 } |
|
29 |
|
30 size_t inpos = 0, outpos = 0; |
|
31 while(inbuf[inpos]) { |
|
32 if(!keychar_needs_urlencoding(inbuf[inpos])) { |
|
33 outbuf[outpos++] = inbuf[inpos++]; |
|
34 } else { |
|
35 if(snprintf(outbuf+outpos, 4, "%%%02X", (unsigned)((uint8_t*)inbuf)[inpos])<0) { |
|
36 free(outbuf); |
|
37 return NULL; |
|
38 } |
|
39 inpos++; |
|
40 outpos += 3; |
|
41 } |
|
42 } |
|
43 outbuf[outpos] = 0; |
|
44 char *shrunk = realloc(outbuf, outpos+1); |
|
45 return shrunk ? shrunk : outbuf; |
|
46 } |
|
47 |
|
48 char *inihelper_urldecode(const char *inbuf) { |
|
49 char *outbuf = flib_malloc(strlen(inbuf)+1); |
|
50 if(!outbuf) { |
|
51 return NULL; |
|
52 } |
|
53 |
|
54 size_t inpos = 0, outpos = 0; |
|
55 while(inbuf[inpos]) { |
|
56 if(inbuf[inpos] == '%' && isxdigit(inbuf[inpos+1]) && isxdigit(inbuf[inpos+2])) { |
|
57 char temp[3] = {inbuf[inpos+1],inbuf[inpos+2],0}; |
|
58 outbuf[outpos++] = strtol(temp, NULL, 16); |
|
59 inpos += 3; |
|
60 } else { |
|
61 outbuf[outpos++] = inbuf[inpos++]; |
|
62 } |
|
63 } |
|
64 outbuf[outpos] = 0; |
|
65 char *shrunk = realloc(outbuf, outpos+1); |
|
66 return shrunk ? shrunk : outbuf; |
|
67 } |
|
68 |
|
69 char *inihelper_createDictKey(const char *sectionName, const char *keyName) { |
|
70 if(!sectionName || !keyName) { |
|
71 flib_log_e("null parameter in inihelper_createDictKey"); |
|
72 return NULL; |
|
73 } |
|
74 return flib_asprintf("%s:%s", sectionName, keyName); |
21 return flib_asprintf("%s:%s", sectionName, keyName); |
75 } |
22 } |
76 |
23 |
77 char *inihelper_getstring(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) { |
24 /** |
78 if(!inifile || !sectionName || !keyName) { |
25 * Turns a string into a lowercase string, in-place. |
79 flib_log_e("null parameter in inihelper_getstring"); |
26 */ |
80 *error = true; |
27 static void strToLower(char *str) { |
81 return NULL; |
28 if(str) { |
82 } |
29 while(*str) { |
83 char *extendedkey = inihelper_createDictKey(sectionName, keyName); |
30 *str = tolower(*str); |
84 if(!extendedkey) { |
31 str++; |
85 *error = true; |
32 } |
86 return NULL; |
33 } |
87 } |
34 } |
88 char *result = iniparser_getstring(inifile, extendedkey, NULL); |
35 |
89 free(extendedkey); |
36 flib_ini *flib_ini_create(const char *filename) { |
90 if(!result) { |
37 flib_ini *result = NULL; |
91 flib_log_d("Missing ini setting: %s/%s", sectionName, keyName); |
38 flib_ini *tmpIni = flib_calloc(1, sizeof(flib_ini)); |
92 *error = true; |
39 if(tmpIni) { |
93 } |
40 if(filename) { |
94 return result; |
41 tmpIni->inidict = iniparser_load(filename); |
95 } |
42 } |
96 |
43 if(!tmpIni->inidict) { |
97 char *inihelper_getstringdup(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) { |
44 tmpIni->inidict = dictionary_new(0); |
98 return flib_strdupnull(inihelper_getstring(inifile, error, sectionName, keyName)); |
45 } |
99 } |
46 if(tmpIni->inidict) { |
100 |
47 result = tmpIni; |
101 int inihelper_getint(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) { |
48 tmpIni = NULL; |
102 char *value = inihelper_getstring(inifile, error, sectionName, keyName); |
49 } |
103 if(!value) { |
50 } |
104 return 0; |
51 flib_ini_destroy(tmpIni); |
105 } else { |
52 return result; |
|
53 } |
|
54 |
|
55 flib_ini *flib_ini_load(const char *filename) { |
|
56 flib_ini *result = NULL; |
|
57 if(!filename) { |
|
58 flib_log_e("null parameter in flib_ini_load"); |
|
59 } else { |
|
60 flib_ini *tmpIni = flib_calloc(1, sizeof(flib_ini)); |
|
61 if(tmpIni) { |
|
62 tmpIni->inidict = iniparser_load(filename); |
|
63 if(tmpIni->inidict) { |
|
64 result = tmpIni; |
|
65 tmpIni = NULL; |
|
66 } |
|
67 } |
|
68 flib_ini_destroy(tmpIni); |
|
69 } |
|
70 return result; |
|
71 } |
|
72 |
|
73 int flib_ini_save(flib_ini *ini, const char *filename) { |
|
74 int result = INI_ERROR_OTHER; |
|
75 if(!ini || !filename) { |
|
76 flib_log_e("null parameter in flib_ini_save"); |
|
77 } else { |
|
78 FILE *file = fopen(filename, "wb"); |
|
79 if(!file) { |
|
80 flib_log_e("Error opening file \"%s\" for writing.", filename); |
|
81 } else { |
|
82 iniparser_dump_ini(ini->inidict, file); |
|
83 if(fclose(file)) { |
|
84 flib_log_e("Write error on ini file \"%s\"", filename); |
|
85 } else { |
|
86 result = 0; |
|
87 } |
|
88 } |
|
89 } |
|
90 return result; |
|
91 } |
|
92 |
|
93 void flib_ini_destroy(flib_ini *ini) { |
|
94 if(ini) { |
|
95 if(ini->inidict) { |
|
96 iniparser_freedict(ini->inidict); |
|
97 } |
|
98 free(ini->currentSection); |
|
99 free(ini); |
|
100 } |
|
101 } |
|
102 |
|
103 int flib_ini_enter_section(flib_ini *ini, const char *section) { |
|
104 int result = INI_ERROR_OTHER; |
|
105 if(ini) { |
|
106 free(ini->currentSection); |
|
107 ini->currentSection = NULL; |
|
108 } |
|
109 if(!ini || !section) { |
|
110 flib_log_e("null parameter in flib_ini_enter_section"); |
|
111 } else { |
|
112 if(!iniparser_find_entry(ini->inidict, section)) { |
|
113 result = INI_ERROR_NOTFOUND; |
|
114 } else { |
|
115 ini->currentSection = flib_strdupnull(section); |
|
116 if(ini->currentSection) { |
|
117 // Usually iniparser ignores case, but some section-handling functions don't, |
|
118 // so we set it to lowercase manually |
|
119 strToLower(ini->currentSection); |
|
120 result = 0; |
|
121 } |
|
122 } |
|
123 } |
|
124 return result; |
|
125 } |
|
126 |
|
127 int flib_ini_create_section(flib_ini *ini, const char *section) { |
|
128 int result = INI_ERROR_OTHER; |
|
129 if(!ini || !section) { |
|
130 flib_log_e("null parameter in flib_ini_create_section"); |
|
131 } else { |
|
132 result = flib_ini_enter_section(ini, section); |
|
133 if(result == INI_ERROR_NOTFOUND) { |
|
134 if(iniparser_set(ini->inidict, section, NULL)) { |
|
135 result = INI_ERROR_OTHER; |
|
136 } else { |
|
137 result = flib_ini_enter_section(ini, section); |
|
138 } |
|
139 } |
|
140 } |
|
141 return result; |
|
142 } |
|
143 |
|
144 /** |
|
145 * The result is an internal string of the iniparser, don't free it. |
|
146 */ |
|
147 static char *findValue(dictionary *dict, const char *section, const char *key) { |
|
148 char *result = NULL; |
|
149 char *dictKey = createDictKey(section, key); |
|
150 if(dictKey) { |
|
151 result = iniparser_getstring(dict, dictKey, NULL); |
|
152 } |
|
153 free(dictKey); |
|
154 return result; |
|
155 } |
|
156 |
|
157 int flib_ini_get_str(flib_ini *ini, char **outVar, const char *key) { |
|
158 char *tmpValue = NULL; |
|
159 int result = flib_ini_get_str_opt(ini, &tmpValue, key, NULL); |
|
160 if(result==0) { |
|
161 if(tmpValue == NULL) { |
|
162 result = INI_ERROR_NOTFOUND; |
|
163 } else { |
|
164 *outVar = tmpValue; |
|
165 tmpValue = NULL; |
|
166 } |
|
167 } |
|
168 free(tmpValue); |
|
169 return result; |
|
170 } |
|
171 |
|
172 int flib_ini_get_str_opt(flib_ini *ini, char **outVar, const char *key, const char *def) { |
|
173 int result = INI_ERROR_OTHER; |
|
174 if(!ini || !outVar || !key || !ini->currentSection) { |
|
175 flib_log_e("null parameter or no current section in flib_ini_get_str_opt"); |
|
176 } else { |
|
177 const char *value = findValue(ini->inidict, ini->currentSection, key); |
|
178 if(!value) { |
|
179 value = def; |
|
180 } |
|
181 char *valueDup = flib_strdupnull(value); |
|
182 if(valueDup) { |
|
183 *outVar = valueDup; |
|
184 result = 0; |
|
185 } |
|
186 } |
|
187 return result; |
|
188 } |
|
189 |
|
190 int flib_ini_get_int(flib_ini *ini, int *outVar, const char *key) { |
|
191 char *tmpValue = NULL; |
|
192 int result = flib_ini_get_str(ini, &tmpValue, key); |
|
193 if(result==0) { |
106 errno = 0; |
194 errno = 0; |
107 long val = strtol(value, NULL, 10); |
195 long val = strtol(tmpValue, NULL, 10); |
108 if(errno!=0) { |
196 if(errno!=0 || val<INT_MIN || val>INT_MAX) { |
109 flib_log_w("Cannot parse ini setting %s/%s = \"%s\" as integer.", sectionName, keyName, value); |
197 flib_log_w("Cannot parse ini setting %s/%s = \"%s\" as integer.", ini->currentSection, key, tmpValue); |
110 *error = true; |
198 result = INI_ERROR_FORMAT; |
111 return 0; |
199 } else { |
112 } |
200 *outVar = val; |
113 if(val<INT_MIN || val>INT_MAX) { |
201 } |
114 flib_log_w("ini setting %s/%s = \"%s\" is too large or too small.", sectionName, keyName, value); |
202 } |
115 *error = true; |
203 free(tmpValue); |
116 return 0; |
204 return result; |
117 } |
205 } |
118 return (int)val; |
206 |
119 } |
207 int flib_ini_get_int_opt(flib_ini *ini, int *outVar, const char *key, int def) { |
120 } |
208 int tmpValue; |
121 |
209 int result = flib_ini_get_int(ini, &tmpValue, key); |
122 bool inihelper_getbool(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) { |
210 if(result == 0) { |
123 char *value = inihelper_getstring(inifile, error, sectionName, keyName); |
211 *outVar = tmpValue; |
124 if(!value) { |
212 } else if(result == INI_ERROR_NOTFOUND) { |
125 return false; |
213 *outVar = def; |
126 } else { |
214 result = 0; |
127 bool trueval = strchr("1tTyY", value[0]); |
215 } |
128 bool falseval = strchr("0fFnN", value[0]); |
216 return result; |
|
217 } |
|
218 |
|
219 int flib_ini_get_bool(flib_ini *ini, bool *outVar, const char *key) { |
|
220 char *tmpValue = NULL; |
|
221 int result = flib_ini_get_str(ini, &tmpValue, key); |
|
222 if(result==0) { |
|
223 bool trueval = strchr("1tTyY", tmpValue[0]); |
|
224 bool falseval = strchr("0fFnN", tmpValue[0]); |
129 if(!trueval && !falseval) { |
225 if(!trueval && !falseval) { |
130 flib_log_w("ini setting %s/%s = \"%s\" is not a recognized truth value.", sectionName, keyName, value); |
226 flib_log_w("ini setting %s/%s = \"%s\" is not a recognized truth value.", ini->currentSection, key, tmpValue); |
131 *error = true; |
227 result = INI_ERROR_FORMAT; |
132 return false; |
228 } else { |
133 } else { |
229 *outVar = trueval; |
134 return trueval; |
230 } |
135 } |
231 } |
136 } |
232 free(tmpValue); |
137 } |
233 return result; |
138 |
234 } |
139 int inihelper_setstr(dictionary *dict, const char *sectionName, const char *keyName, const char *value) { |
235 |
140 int result = -1; |
236 int flib_ini_get_bool_opt(flib_ini *ini, bool *outVar, const char *key, bool def) { |
141 if(!dict || !sectionName || !keyName || !value) { |
237 bool tmpValue; |
142 flib_log_e("null parameter in inihelper_setstr"); |
238 int result = flib_ini_get_bool(ini, &tmpValue, key); |
143 } else { |
239 if(result == 0) { |
144 char *extendedkey = inihelper_createDictKey(sectionName, keyName); |
240 *outVar = tmpValue; |
145 if(extendedkey) { |
241 } else if(result == INI_ERROR_NOTFOUND) { |
146 result = iniparser_set(dict, extendedkey, value); |
242 *outVar = def; |
147 } |
243 result = 0; |
148 free(extendedkey); |
244 } |
149 } |
245 return result; |
150 return result; |
246 } |
151 } |
247 |
152 |
248 int flib_ini_set_str(flib_ini *ini, const char *key, const char *value) { |
153 int inihelper_setint(dictionary *dict, const char *sectionName, const char *keyName, int value) { |
249 int result = INI_ERROR_OTHER; |
154 int result = -1; |
250 if(!ini || !key || !value || !ini->currentSection) { |
155 if(!dict || !sectionName || !keyName) { |
251 flib_log_e("null parameter or no current section in flib_ini_set_str"); |
156 flib_log_e("null parameter in inihelper_setint"); |
252 } else { |
157 } else { |
253 char *dictKey = createDictKey(ini->currentSection, key); |
158 char *strvalue = flib_asprintf("%i", value); |
254 if(dictKey) { |
159 if(strvalue) { |
255 result = iniparser_set(ini->inidict, dictKey, value); |
160 result = inihelper_setstr(dict, sectionName, keyName, strvalue); |
256 } |
161 free(strvalue); |
257 free(dictKey); |
162 } |
258 } |
163 } |
259 return result; |
164 return result; |
260 } |
165 } |
261 |
166 |
262 int flib_ini_set_int(flib_ini *ini, const char *key, int value) { |
167 int inihelper_setbool(dictionary *dict, const char *sectionName, const char *keyName, bool value) { |
263 int result = INI_ERROR_OTHER; |
168 int result = -1; |
264 char *strvalue = flib_asprintf("%i", value); |
169 if(!dict || !sectionName || !keyName) { |
265 if(strvalue) { |
170 flib_log_e("null parameter in inihelper_setbool"); |
266 result = flib_ini_set_str(ini, key, strvalue); |
171 } else { |
267 } |
172 result = inihelper_setstr(dict, sectionName, keyName, value ? "true" : "false"); |
268 free(strvalue); |
173 } |
269 return result; |
174 return result; |
270 } |
175 } |
271 |
|
272 int flib_ini_set_bool(flib_ini *ini, const char *key, bool value) { |
|
273 return flib_ini_set_str(ini, key, value ? "true" : "false"); |
|
274 } |
|
275 |
|
276 int flib_ini_get_sectioncount(flib_ini *ini) { |
|
277 int result = INI_ERROR_OTHER; |
|
278 if(!ini) { |
|
279 flib_log_e("null parameter in flib_ini_get_sectioncount"); |
|
280 } else { |
|
281 result = iniparser_getnsec(ini->inidict); |
|
282 } |
|
283 return result; |
|
284 } |
|
285 |
|
286 char *flib_ini_get_sectionname(flib_ini *ini, int number) { |
|
287 char *result = NULL; |
|
288 if(!ini || number<0) { |
|
289 flib_log_e("bad parameter in flib_ini_get_sectionname"); |
|
290 } else { |
|
291 result = flib_strdupnull(iniparser_getsecname(ini->inidict, number)); |
|
292 } |
|
293 return result; |
|
294 } |
|
295 |
|
296 int flib_ini_get_keycount(flib_ini *ini) { |
|
297 int result = INI_ERROR_OTHER; |
|
298 if(!ini || !ini->currentSection) { |
|
299 flib_log_e("null parameter or no current section in flib_ini_get_keycount"); |
|
300 } else { |
|
301 result = iniparser_getsecnkeys(ini->inidict, ini->currentSection); |
|
302 } |
|
303 return result; |
|
304 } |
|
305 |
|
306 char *flib_ini_get_keyname(flib_ini *ini, int number) { |
|
307 char *result = NULL; |
|
308 if(!ini || number<0 || !ini->currentSection) { |
|
309 flib_log_e("bad parameter or no current section in flib_ini_get_keyname"); |
|
310 } else { |
|
311 int keyCount = iniparser_getsecnkeys(ini->inidict, ini->currentSection); |
|
312 char **keys = iniparser_getseckeys(ini->inidict, ini->currentSection); |
|
313 if(keys && keyCount>number) { |
|
314 // The keys are in the format section:key, so we have to skip the section and colon. |
|
315 result = flib_strdupnull(keys[number]+strlen(ini->currentSection)+1); |
|
316 } |
|
317 free(keys); |
|
318 } |
|
319 return result; |
|
320 } |