/src/adhd/external/iniparser/src/iniparser.c
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | /*-------------------------------------------------------------------------*/ |
3 | | /** |
4 | | @file iniparser.c |
5 | | @author N. Devillard |
6 | | @brief Parser for ini files. |
7 | | */ |
8 | | /*--------------------------------------------------------------------------*/ |
9 | | /*---------------------------- Includes ------------------------------------*/ |
10 | | #include <ctype.h> |
11 | | #include <stdarg.h> |
12 | | #include "iniparser.h" |
13 | | |
14 | | /*---------------------------- Defines -------------------------------------*/ |
15 | 0 | #define ASCIILINESZ (1024) |
16 | 240 | #define INI_INVALID_KEY ((char*)-1) |
17 | | |
18 | | /*--------------------------------------------------------------------------- |
19 | | Private to this module |
20 | | ---------------------------------------------------------------------------*/ |
21 | | /** |
22 | | * This enum stores the status for each parsed line (internal use only). |
23 | | */ |
24 | | typedef enum _line_status_ { |
25 | | LINE_UNPROCESSED, |
26 | | LINE_ERROR, |
27 | | LINE_EMPTY, |
28 | | LINE_COMMENT, |
29 | | LINE_SECTION, |
30 | | LINE_VALUE |
31 | | } line_status ; |
32 | | |
33 | | /*-------------------------------------------------------------------------*/ |
34 | | /** |
35 | | @brief Convert a string to lowercase. |
36 | | @param in String to convert. |
37 | | @param out Output buffer. |
38 | | @param len Size of the out buffer. |
39 | | @return ptr to the out buffer or NULL if an error occured. |
40 | | |
41 | | This function convert a string into lowercase. |
42 | | At most len - 1 elements of the input string will be converted. |
43 | | */ |
44 | | /*--------------------------------------------------------------------------*/ |
45 | | static const char * strlwc(const char * in, char *out, unsigned len) |
46 | 132 | { |
47 | 132 | unsigned i ; |
48 | | |
49 | 132 | if (in==NULL || out == NULL || len==0) return NULL ; |
50 | 132 | i=0 ; |
51 | 3.76k | while (in[i] != '\0' && i < len-1) { |
52 | 3.63k | out[i] = (char)tolower((int)in[i]); |
53 | 3.63k | i++ ; |
54 | 3.63k | } |
55 | 132 | out[i] = '\0'; |
56 | 132 | return out ; |
57 | 132 | } |
58 | | |
59 | | /*-------------------------------------------------------------------------*/ |
60 | | /** |
61 | | @brief Duplicate a string |
62 | | @param s String to duplicate |
63 | | @return Pointer to a newly allocated string, to be freed with free() |
64 | | |
65 | | This is a replacement for strdup(). This implementation is provided |
66 | | for systems that do not have it. |
67 | | */ |
68 | | /*--------------------------------------------------------------------------*/ |
69 | | static char * xstrdup(const char * s) |
70 | 0 | { |
71 | 0 | char * t ; |
72 | 0 | size_t len ; |
73 | 0 | if (!s) |
74 | 0 | return NULL ; |
75 | | |
76 | 0 | len = strlen(s) + 1 ; |
77 | 0 | t = (char*) malloc(len) ; |
78 | 0 | if (t) { |
79 | 0 | memcpy(t, s, len) ; |
80 | 0 | } |
81 | 0 | return t ; |
82 | 0 | } |
83 | | |
84 | | /*-------------------------------------------------------------------------*/ |
85 | | /** |
86 | | @brief Remove blanks at the beginning and the end of a string. |
87 | | @param str String to parse and alter. |
88 | | @return unsigned New size of the string. |
89 | | */ |
90 | | /*--------------------------------------------------------------------------*/ |
91 | | static unsigned strstrip(char * s) |
92 | 0 | { |
93 | 0 | char *last = NULL ; |
94 | 0 | char *dest = s; |
95 | |
|
96 | 0 | if (s==NULL) return 0; |
97 | | |
98 | 0 | last = s + strlen(s); |
99 | 0 | while (isspace((int)*s) && *s) s++; |
100 | 0 | while (last > s) { |
101 | 0 | if (!isspace((int)*(last-1))) |
102 | 0 | break ; |
103 | 0 | last -- ; |
104 | 0 | } |
105 | 0 | *last = (char)0; |
106 | |
|
107 | 0 | memmove(dest,s,last - s + 1); |
108 | 0 | return last - s; |
109 | 0 | } |
110 | | |
111 | | /*-------------------------------------------------------------------------*/ |
112 | | /** |
113 | | @brief Default error callback for iniparser: wraps `fprintf(stderr, ...)`. |
114 | | */ |
115 | | /*--------------------------------------------------------------------------*/ |
116 | | static int default_error_callback(const char *format, ...) |
117 | 0 | { |
118 | 0 | int ret; |
119 | 0 | va_list argptr; |
120 | 0 | va_start(argptr, format); |
121 | 0 | ret = vfprintf(stderr, format, argptr); |
122 | 0 | va_end(argptr); |
123 | 0 | return ret; |
124 | 0 | } |
125 | | |
126 | | static int (*iniparser_error_callback)(const char*, ...) = default_error_callback; |
127 | | |
128 | | /*-------------------------------------------------------------------------*/ |
129 | | /** |
130 | | @brief Configure a function to receive the error messages. |
131 | | @param errback Function to call. |
132 | | |
133 | | By default, the error will be printed on stderr. If a null pointer is passed |
134 | | as errback the error callback will be switched back to default. |
135 | | */ |
136 | | /*--------------------------------------------------------------------------*/ |
137 | | void iniparser_set_error_callback(int (*errback)(const char *, ...)) |
138 | 0 | { |
139 | 0 | if (errback) { |
140 | 0 | iniparser_error_callback = errback; |
141 | 0 | } else { |
142 | 0 | iniparser_error_callback = default_error_callback; |
143 | 0 | } |
144 | 0 | } |
145 | | |
146 | | /*-------------------------------------------------------------------------*/ |
147 | | /** |
148 | | @brief Get number of sections in a dictionary |
149 | | @param d Dictionary to examine |
150 | | @return int Number of sections found in dictionary |
151 | | |
152 | | This function returns the number of sections found in a dictionary. |
153 | | The test to recognize sections is done on the string stored in the |
154 | | dictionary: a section name is given as "section" whereas a key is |
155 | | stored as "section:key", thus the test looks for entries that do not |
156 | | contain a colon. |
157 | | |
158 | | This clearly fails in the case a section name contains a colon, but |
159 | | this should simply be avoided. |
160 | | |
161 | | This function returns -1 in case of error. |
162 | | */ |
163 | | /*--------------------------------------------------------------------------*/ |
164 | | int iniparser_getnsec(const dictionary * d) |
165 | 0 | { |
166 | 0 | int i ; |
167 | 0 | int nsec ; |
168 | |
|
169 | 0 | if (d==NULL) return -1 ; |
170 | 0 | nsec=0 ; |
171 | 0 | for (i=0 ; i<d->size ; i++) { |
172 | 0 | if (d->key[i]==NULL) |
173 | 0 | continue ; |
174 | 0 | if (strchr(d->key[i], ':')==NULL) { |
175 | 0 | nsec ++ ; |
176 | 0 | } |
177 | 0 | } |
178 | 0 | return nsec ; |
179 | 0 | } |
180 | | |
181 | | /*-------------------------------------------------------------------------*/ |
182 | | /** |
183 | | @brief Get name for section n in a dictionary. |
184 | | @param d Dictionary to examine |
185 | | @param n Section number (from 0 to nsec-1). |
186 | | @return Pointer to char string |
187 | | |
188 | | This function locates the n-th section in a dictionary and returns |
189 | | its name as a pointer to a string statically allocated inside the |
190 | | dictionary. Do not free or modify the returned string! |
191 | | |
192 | | This function returns NULL in case of error. |
193 | | */ |
194 | | /*--------------------------------------------------------------------------*/ |
195 | | const char * iniparser_getsecname(const dictionary * d, int n) |
196 | 0 | { |
197 | 0 | int i ; |
198 | 0 | int foundsec ; |
199 | |
|
200 | 0 | if (d==NULL || n<0) return NULL ; |
201 | 0 | foundsec=0 ; |
202 | 0 | for (i=0 ; i<d->size ; i++) { |
203 | 0 | if (d->key[i]==NULL) |
204 | 0 | continue ; |
205 | 0 | if (strchr(d->key[i], ':')==NULL) { |
206 | 0 | foundsec++ ; |
207 | 0 | if (foundsec>n) |
208 | 0 | break ; |
209 | 0 | } |
210 | 0 | } |
211 | 0 | if (foundsec<=n) { |
212 | 0 | return NULL ; |
213 | 0 | } |
214 | 0 | return d->key[i] ; |
215 | 0 | } |
216 | | |
217 | | /*-------------------------------------------------------------------------*/ |
218 | | /** |
219 | | @brief Dump a dictionary to an opened file pointer. |
220 | | @param d Dictionary to dump. |
221 | | @param f Opened file pointer to dump to. |
222 | | @return void |
223 | | |
224 | | This function prints out the contents of a dictionary, one element by |
225 | | line, onto the provided file pointer. It is OK to specify @c stderr |
226 | | or @c stdout as output files. This function is meant for debugging |
227 | | purposes mostly. |
228 | | */ |
229 | | /*--------------------------------------------------------------------------*/ |
230 | | void iniparser_dump(const dictionary * d, FILE * f) |
231 | 0 | { |
232 | 0 | int i ; |
233 | |
|
234 | 0 | if (d==NULL || f==NULL) return ; |
235 | 0 | for (i=0 ; i<d->size ; i++) { |
236 | 0 | if (d->key[i]==NULL) |
237 | 0 | continue ; |
238 | 0 | if (d->val[i]!=NULL) { |
239 | 0 | fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); |
240 | 0 | } else { |
241 | 0 | fprintf(f, "[%s]=UNDEF\n", d->key[i]); |
242 | 0 | } |
243 | 0 | } |
244 | 0 | return ; |
245 | 0 | } |
246 | | |
247 | | /*-------------------------------------------------------------------------*/ |
248 | | /** |
249 | | @brief Save a dictionary to a loadable ini file |
250 | | @param d Dictionary to dump |
251 | | @param f Opened file pointer to dump to |
252 | | @return void |
253 | | |
254 | | This function dumps a given dictionary into a loadable ini file. |
255 | | It is Ok to specify @c stderr or @c stdout as output files. |
256 | | */ |
257 | | /*--------------------------------------------------------------------------*/ |
258 | | void iniparser_dump_ini(const dictionary * d, FILE * f) |
259 | 0 | { |
260 | 0 | int i ; |
261 | 0 | int nsec ; |
262 | 0 | const char * secname ; |
263 | |
|
264 | 0 | if (d==NULL || f==NULL) return ; |
265 | | |
266 | 0 | nsec = iniparser_getnsec(d); |
267 | 0 | if (nsec<1) { |
268 | | /* No section in file: dump all keys as they are */ |
269 | 0 | for (i=0 ; i<d->size ; i++) { |
270 | 0 | if (d->key[i]==NULL) |
271 | 0 | continue ; |
272 | 0 | fprintf(f, "%s = %s\n", d->key[i], d->val[i]); |
273 | 0 | } |
274 | 0 | return ; |
275 | 0 | } |
276 | 0 | for (i=0 ; i<nsec ; i++) { |
277 | 0 | secname = iniparser_getsecname(d, i) ; |
278 | 0 | iniparser_dumpsection_ini(d, secname, f); |
279 | 0 | } |
280 | 0 | fprintf(f, "\n"); |
281 | 0 | return ; |
282 | 0 | } |
283 | | |
284 | | /*-------------------------------------------------------------------------*/ |
285 | | /** |
286 | | @brief Save a dictionary section to a loadable ini file |
287 | | @param d Dictionary to dump |
288 | | @param s Section name of dictionary to dump |
289 | | @param f Opened file pointer to dump to |
290 | | @return void |
291 | | |
292 | | This function dumps a given section of a given dictionary into a loadable ini |
293 | | file. It is Ok to specify @c stderr or @c stdout as output files. |
294 | | */ |
295 | | /*--------------------------------------------------------------------------*/ |
296 | | void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f) |
297 | 0 | { |
298 | 0 | int j ; |
299 | 0 | char keym[ASCIILINESZ+1]; |
300 | 0 | int seclen ; |
301 | |
|
302 | 0 | if (d==NULL || f==NULL) return ; |
303 | 0 | if (! iniparser_find_entry(d, s)) return ; |
304 | | |
305 | 0 | seclen = (int)strlen(s); |
306 | 0 | fprintf(f, "\n[%s]\n", s); |
307 | 0 | sprintf(keym, "%s:", s); |
308 | 0 | for (j=0 ; j<d->size ; j++) { |
309 | 0 | if (d->key[j]==NULL) |
310 | 0 | continue ; |
311 | 0 | if (!strncmp(d->key[j], keym, seclen+1)) { |
312 | 0 | fprintf(f, |
313 | 0 | "%-30s = %s\n", |
314 | 0 | d->key[j]+seclen+1, |
315 | 0 | d->val[j] ? d->val[j] : ""); |
316 | 0 | } |
317 | 0 | } |
318 | 0 | fprintf(f, "\n"); |
319 | 0 | return ; |
320 | 0 | } |
321 | | |
322 | | /*-------------------------------------------------------------------------*/ |
323 | | /** |
324 | | @brief Get the number of keys in a section of a dictionary. |
325 | | @param d Dictionary to examine |
326 | | @param s Section name of dictionary to examine |
327 | | @return Number of keys in section |
328 | | */ |
329 | | /*--------------------------------------------------------------------------*/ |
330 | | int iniparser_getsecnkeys(const dictionary * d, const char * s) |
331 | 0 | { |
332 | 0 | int seclen, nkeys ; |
333 | 0 | char keym[ASCIILINESZ+1]; |
334 | 0 | int j ; |
335 | |
|
336 | 0 | nkeys = 0; |
337 | |
|
338 | 0 | if (d==NULL) return nkeys; |
339 | 0 | if (! iniparser_find_entry(d, s)) return nkeys; |
340 | | |
341 | 0 | seclen = (int)strlen(s); |
342 | 0 | strlwc(s, keym, sizeof(keym)); |
343 | 0 | keym[seclen] = ':'; |
344 | |
|
345 | 0 | for (j=0 ; j<d->size ; j++) { |
346 | 0 | if (d->key[j]==NULL) |
347 | 0 | continue ; |
348 | 0 | if (!strncmp(d->key[j], keym, seclen+1)) |
349 | 0 | nkeys++; |
350 | 0 | } |
351 | |
|
352 | 0 | return nkeys; |
353 | |
|
354 | 0 | } |
355 | | |
356 | | /*-------------------------------------------------------------------------*/ |
357 | | /** |
358 | | @brief Get the number of keys in a section of a dictionary. |
359 | | @param d Dictionary to examine |
360 | | @param s Section name of dictionary to examine |
361 | | @param keys Already allocated array to store the keys in |
362 | | @return The pointer passed as `keys` argument or NULL in case of error |
363 | | |
364 | | This function queries a dictionary and finds all keys in a given section. |
365 | | The keys argument should be an array of pointers which size has been |
366 | | determined by calling `iniparser_getsecnkeys` function prior to this one. |
367 | | |
368 | | Each pointer in the returned char pointer-to-pointer is pointing to |
369 | | a string allocated in the dictionary; do not free or modify them. |
370 | | */ |
371 | | /*--------------------------------------------------------------------------*/ |
372 | | const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys) |
373 | 0 | { |
374 | 0 | int i, j, seclen ; |
375 | 0 | char keym[ASCIILINESZ+1]; |
376 | |
|
377 | 0 | if (d==NULL || keys==NULL) return NULL; |
378 | 0 | if (! iniparser_find_entry(d, s)) return NULL; |
379 | | |
380 | 0 | seclen = (int)strlen(s); |
381 | 0 | strlwc(s, keym, sizeof(keym)); |
382 | 0 | keym[seclen] = ':'; |
383 | |
|
384 | 0 | i = 0; |
385 | |
|
386 | 0 | for (j=0 ; j<d->size ; j++) { |
387 | 0 | if (d->key[j]==NULL) |
388 | 0 | continue ; |
389 | 0 | if (!strncmp(d->key[j], keym, seclen+1)) { |
390 | 0 | keys[i] = d->key[j]; |
391 | 0 | i++; |
392 | 0 | } |
393 | 0 | } |
394 | |
|
395 | 0 | return keys; |
396 | 0 | } |
397 | | |
398 | | /*-------------------------------------------------------------------------*/ |
399 | | /** |
400 | | @brief Get the string associated to a key |
401 | | @param d Dictionary to search |
402 | | @param key Key string to look for |
403 | | @param def Default value to return if key not found. |
404 | | @return pointer to statically allocated character string |
405 | | |
406 | | This function queries a dictionary for a key. A key as read from an |
407 | | ini file is given as "section:key". If the key cannot be found, |
408 | | the pointer passed as 'def' is returned. |
409 | | The returned char pointer is pointing to a string allocated in |
410 | | the dictionary, do not free or modify it. |
411 | | */ |
412 | | /*--------------------------------------------------------------------------*/ |
413 | | const char * iniparser_getstring(const dictionary * d, const char * key, const char * def) |
414 | 132 | { |
415 | 132 | const char * lc_key ; |
416 | 132 | const char * sval ; |
417 | 132 | char tmp_str[ASCIILINESZ+1]; |
418 | | |
419 | 132 | if (d==NULL || key==NULL) |
420 | 0 | return def ; |
421 | | |
422 | 132 | lc_key = strlwc(key, tmp_str, sizeof(tmp_str)); |
423 | 132 | sval = dictionary_get(d, lc_key, def); |
424 | 132 | return sval ; |
425 | 132 | } |
426 | | |
427 | | /*-------------------------------------------------------------------------*/ |
428 | | /** |
429 | | @brief Get the string associated to a key, convert to an long int |
430 | | @param d Dictionary to search |
431 | | @param key Key string to look for |
432 | | @param notfound Value to return in case of error |
433 | | @return long integer |
434 | | |
435 | | This function queries a dictionary for a key. A key as read from an |
436 | | ini file is given as "section:key". If the key cannot be found, |
437 | | the notfound value is returned. |
438 | | |
439 | | Supported values for integers include the usual C notation |
440 | | so decimal, octal (starting with 0) and hexadecimal (starting with 0x) |
441 | | are supported. Examples: |
442 | | |
443 | | "42" -> 42 |
444 | | "042" -> 34 (octal -> decimal) |
445 | | "0x42" -> 66 (hexa -> decimal) |
446 | | |
447 | | Warning: the conversion may overflow in various ways. Conversion is |
448 | | totally outsourced to strtol(), see the associated man page for overflow |
449 | | handling. |
450 | | |
451 | | Credits: Thanks to A. Becker for suggesting strtol() |
452 | | */ |
453 | | /*--------------------------------------------------------------------------*/ |
454 | | long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound) |
455 | 120 | { |
456 | 120 | const char * str ; |
457 | | |
458 | 120 | str = iniparser_getstring(d, key, INI_INVALID_KEY); |
459 | 120 | if (str==INI_INVALID_KEY) return notfound ; |
460 | 0 | return strtol(str, NULL, 0); |
461 | 120 | } |
462 | | |
463 | | |
464 | | /*-------------------------------------------------------------------------*/ |
465 | | /** |
466 | | @brief Get the string associated to a key, convert to an int |
467 | | @param d Dictionary to search |
468 | | @param key Key string to look for |
469 | | @param notfound Value to return in case of error |
470 | | @return integer |
471 | | |
472 | | This function queries a dictionary for a key. A key as read from an |
473 | | ini file is given as "section:key". If the key cannot be found, |
474 | | the notfound value is returned. |
475 | | |
476 | | Supported values for integers include the usual C notation |
477 | | so decimal, octal (starting with 0) and hexadecimal (starting with 0x) |
478 | | are supported. Examples: |
479 | | |
480 | | "42" -> 42 |
481 | | "042" -> 34 (octal -> decimal) |
482 | | "0x42" -> 66 (hexa -> decimal) |
483 | | |
484 | | Warning: the conversion may overflow in various ways. Conversion is |
485 | | totally outsourced to strtol(), see the associated man page for overflow |
486 | | handling. |
487 | | |
488 | | Credits: Thanks to A. Becker for suggesting strtol() |
489 | | */ |
490 | | /*--------------------------------------------------------------------------*/ |
491 | | int iniparser_getint(const dictionary * d, const char * key, int notfound) |
492 | 120 | { |
493 | 120 | return (int)iniparser_getlongint(d, key, notfound); |
494 | 120 | } |
495 | | |
496 | | /*-------------------------------------------------------------------------*/ |
497 | | /** |
498 | | @brief Get the string associated to a key, convert to a double |
499 | | @param d Dictionary to search |
500 | | @param key Key string to look for |
501 | | @param notfound Value to return in case of error |
502 | | @return double |
503 | | |
504 | | This function queries a dictionary for a key. A key as read from an |
505 | | ini file is given as "section:key". If the key cannot be found, |
506 | | the notfound value is returned. |
507 | | */ |
508 | | /*--------------------------------------------------------------------------*/ |
509 | | double iniparser_getdouble(const dictionary * d, const char * key, double notfound) |
510 | 0 | { |
511 | 0 | const char * str ; |
512 | |
|
513 | 0 | str = iniparser_getstring(d, key, INI_INVALID_KEY); |
514 | 0 | if (str==INI_INVALID_KEY) return notfound ; |
515 | 0 | return atof(str); |
516 | 0 | } |
517 | | |
518 | | /*-------------------------------------------------------------------------*/ |
519 | | /** |
520 | | @brief Get the string associated to a key, convert to a boolean |
521 | | @param d Dictionary to search |
522 | | @param key Key string to look for |
523 | | @param notfound Value to return in case of error |
524 | | @return integer |
525 | | |
526 | | This function queries a dictionary for a key. A key as read from an |
527 | | ini file is given as "section:key". If the key cannot be found, |
528 | | the notfound value is returned. |
529 | | |
530 | | A true boolean is found if one of the following is matched: |
531 | | |
532 | | - A string starting with 'y' |
533 | | - A string starting with 'Y' |
534 | | - A string starting with 't' |
535 | | - A string starting with 'T' |
536 | | - A string starting with '1' |
537 | | |
538 | | A false boolean is found if one of the following is matched: |
539 | | |
540 | | - A string starting with 'n' |
541 | | - A string starting with 'N' |
542 | | - A string starting with 'f' |
543 | | - A string starting with 'F' |
544 | | - A string starting with '0' |
545 | | |
546 | | The notfound value returned if no boolean is identified, does not |
547 | | necessarily have to be 0 or 1. |
548 | | */ |
549 | | /*--------------------------------------------------------------------------*/ |
550 | | int iniparser_getboolean(const dictionary * d, const char * key, int notfound) |
551 | 0 | { |
552 | 0 | int ret ; |
553 | 0 | const char * c ; |
554 | |
|
555 | 0 | c = iniparser_getstring(d, key, INI_INVALID_KEY); |
556 | 0 | if (c==INI_INVALID_KEY) return notfound ; |
557 | 0 | if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { |
558 | 0 | ret = 1 ; |
559 | 0 | } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { |
560 | 0 | ret = 0 ; |
561 | 0 | } else { |
562 | 0 | ret = notfound ; |
563 | 0 | } |
564 | 0 | return ret; |
565 | 0 | } |
566 | | |
567 | | /*-------------------------------------------------------------------------*/ |
568 | | /** |
569 | | @brief Finds out if a given entry exists in a dictionary |
570 | | @param ini Dictionary to search |
571 | | @param entry Name of the entry to look for |
572 | | @return integer 1 if entry exists, 0 otherwise |
573 | | |
574 | | Finds out if a given entry exists in the dictionary. Since sections |
575 | | are stored as keys with NULL associated values, this is the only way |
576 | | of querying for the presence of sections in a dictionary. |
577 | | */ |
578 | | /*--------------------------------------------------------------------------*/ |
579 | | int iniparser_find_entry(const dictionary * ini, const char * entry) |
580 | 0 | { |
581 | 0 | int found=0 ; |
582 | 0 | if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { |
583 | 0 | found = 1 ; |
584 | 0 | } |
585 | 0 | return found ; |
586 | 0 | } |
587 | | |
588 | | /*-------------------------------------------------------------------------*/ |
589 | | /** |
590 | | @brief Set an entry in a dictionary. |
591 | | @param ini Dictionary to modify. |
592 | | @param entry Entry to modify (entry name) |
593 | | @param val New value to associate to the entry. |
594 | | @return int 0 if Ok, -1 otherwise. |
595 | | |
596 | | If the given entry can be found in the dictionary, it is modified to |
597 | | contain the provided value. If it cannot be found, the entry is created. |
598 | | It is Ok to set val to NULL. |
599 | | */ |
600 | | /*--------------------------------------------------------------------------*/ |
601 | | int iniparser_set(dictionary * ini, const char * entry, const char * val) |
602 | 0 | { |
603 | 0 | char tmp_str[ASCIILINESZ+1]; |
604 | 0 | return dictionary_set(ini, strlwc(entry, tmp_str, sizeof(tmp_str)), val) ; |
605 | 0 | } |
606 | | |
607 | | /*-------------------------------------------------------------------------*/ |
608 | | /** |
609 | | @brief Delete an entry in a dictionary |
610 | | @param ini Dictionary to modify |
611 | | @param entry Entry to delete (entry name) |
612 | | @return void |
613 | | |
614 | | If the given entry can be found, it is deleted from the dictionary. |
615 | | */ |
616 | | /*--------------------------------------------------------------------------*/ |
617 | | void iniparser_unset(dictionary * ini, const char * entry) |
618 | 0 | { |
619 | 0 | char tmp_str[ASCIILINESZ+1]; |
620 | 0 | dictionary_unset(ini, strlwc(entry, tmp_str, sizeof(tmp_str))); |
621 | 0 | } |
622 | | |
623 | | /*-------------------------------------------------------------------------*/ |
624 | | /** |
625 | | @brief Load a single line from an INI file |
626 | | @param input_line Input line, may be concatenated multi-line input |
627 | | @param section Output space to store section |
628 | | @param key Output space to store key |
629 | | @param value Output space to store value |
630 | | @return line_status value |
631 | | */ |
632 | | /*--------------------------------------------------------------------------*/ |
633 | | static line_status iniparser_line( |
634 | | const char * input_line, |
635 | | char * section, |
636 | | char * key, |
637 | | char * value) |
638 | 0 | { |
639 | 0 | line_status sta ; |
640 | 0 | char * line = NULL; |
641 | 0 | size_t len ; |
642 | |
|
643 | 0 | line = xstrdup(input_line); |
644 | 0 | len = strstrip(line); |
645 | |
|
646 | 0 | sta = LINE_UNPROCESSED ; |
647 | 0 | if (len<1) { |
648 | | /* Empty line */ |
649 | 0 | sta = LINE_EMPTY ; |
650 | 0 | } else if (line[0]=='#' || line[0]==';') { |
651 | | /* Comment line */ |
652 | 0 | sta = LINE_COMMENT ; |
653 | 0 | } else if (line[0]=='[' && line[len-1]==']') { |
654 | | /* Section name */ |
655 | 0 | sscanf(line, "[%[^]]", section); |
656 | 0 | strstrip(section); |
657 | 0 | strlwc(section, section, len); |
658 | 0 | sta = LINE_SECTION ; |
659 | 0 | } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 |
660 | 0 | || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2) { |
661 | | /* Usual key=value with quotes, with or without comments */ |
662 | 0 | strstrip(key); |
663 | 0 | strlwc(key, key, len); |
664 | | /* Don't strip spaces from values surrounded with quotes */ |
665 | 0 | sta = LINE_VALUE ; |
666 | 0 | } else if (sscanf (line, "%[^=] = %[^;#]", key, value) == 2) { |
667 | | /* Usual key=value without quotes, with or without comments */ |
668 | 0 | strstrip(key); |
669 | 0 | strlwc(key, key, len); |
670 | 0 | strstrip(value); |
671 | | /* |
672 | | * sscanf cannot handle '' or "" as empty values |
673 | | * this is done here |
674 | | */ |
675 | 0 | if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { |
676 | 0 | value[0]=0 ; |
677 | 0 | } |
678 | 0 | sta = LINE_VALUE ; |
679 | 0 | } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 |
680 | 0 | || sscanf(line, "%[^=] %[=]", key, value) == 2) { |
681 | | /* |
682 | | * Special cases: |
683 | | * key= |
684 | | * key=; |
685 | | * key=# |
686 | | */ |
687 | 0 | strstrip(key); |
688 | 0 | strlwc(key, key, len); |
689 | 0 | value[0]=0 ; |
690 | 0 | sta = LINE_VALUE ; |
691 | 0 | } else { |
692 | | /* Generate syntax error */ |
693 | 0 | sta = LINE_ERROR ; |
694 | 0 | } |
695 | |
|
696 | 0 | free(line); |
697 | 0 | return sta ; |
698 | 0 | } |
699 | | |
700 | | /*-------------------------------------------------------------------------*/ |
701 | | /** |
702 | | @brief Parse an ini file and return an allocated dictionary object |
703 | | @param ininame Name of the ini file to read. |
704 | | @return Pointer to newly allocated dictionary |
705 | | |
706 | | This is the parser for ini files. This function is called, providing |
707 | | the name of the file to be read. It returns a dictionary object that |
708 | | should not be accessed directly, but through accessor functions |
709 | | instead. |
710 | | |
711 | | The returned dictionary must be freed using iniparser_freedict(). |
712 | | */ |
713 | | /*--------------------------------------------------------------------------*/ |
714 | | dictionary * iniparser_load(const char * ininame) |
715 | 0 | { |
716 | 0 | FILE * in ; |
717 | |
|
718 | 0 | char line [ASCIILINESZ+1] ; |
719 | 0 | char section [ASCIILINESZ+1] ; |
720 | 0 | char key [ASCIILINESZ+1] ; |
721 | 0 | char tmp [(ASCIILINESZ * 2) + 1] ; |
722 | 0 | char val [ASCIILINESZ+1] ; |
723 | |
|
724 | 0 | int last=0 ; |
725 | 0 | int len ; |
726 | 0 | int lineno=0 ; |
727 | 0 | int errs=0; |
728 | 0 | int mem_err=0; |
729 | |
|
730 | 0 | dictionary * dict ; |
731 | |
|
732 | 0 | if ((in=fopen(ininame, "r"))==NULL) { |
733 | 0 | iniparser_error_callback("iniparser: cannot open %s\n", ininame); |
734 | 0 | return NULL ; |
735 | 0 | } |
736 | | |
737 | 0 | dict = dictionary_new(0) ; |
738 | 0 | if (!dict) { |
739 | 0 | fclose(in); |
740 | 0 | return NULL ; |
741 | 0 | } |
742 | | |
743 | 0 | memset(line, 0, ASCIILINESZ); |
744 | 0 | memset(section, 0, ASCIILINESZ); |
745 | 0 | memset(key, 0, ASCIILINESZ); |
746 | 0 | memset(val, 0, ASCIILINESZ); |
747 | 0 | last=0 ; |
748 | |
|
749 | 0 | while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { |
750 | 0 | lineno++ ; |
751 | 0 | len = (int)strlen(line)-1; |
752 | 0 | if (len<=0) |
753 | 0 | continue; |
754 | | /* Safety check against buffer overflows */ |
755 | 0 | if (line[len]!='\n' && !feof(in)) { |
756 | 0 | iniparser_error_callback( |
757 | 0 | "iniparser: input line too long in %s (%d)\n", |
758 | 0 | ininame, |
759 | 0 | lineno); |
760 | 0 | dictionary_del(dict); |
761 | 0 | fclose(in); |
762 | 0 | return NULL ; |
763 | 0 | } |
764 | | /* Get rid of \n and spaces at end of line */ |
765 | 0 | while ((len>=0) && |
766 | 0 | ((line[len]=='\n') || (isspace(line[len])))) { |
767 | 0 | line[len]=0 ; |
768 | 0 | len-- ; |
769 | 0 | } |
770 | 0 | if (len < 0) { /* Line was entirely \n and/or spaces */ |
771 | 0 | len = 0; |
772 | 0 | } |
773 | | /* Detect multi-line */ |
774 | 0 | if (line[len]=='\\') { |
775 | | /* Multi-line value */ |
776 | 0 | last=len ; |
777 | 0 | continue ; |
778 | 0 | } else { |
779 | 0 | last=0 ; |
780 | 0 | } |
781 | 0 | switch (iniparser_line(line, section, key, val)) { |
782 | 0 | case LINE_EMPTY: |
783 | 0 | case LINE_COMMENT: |
784 | 0 | break ; |
785 | | |
786 | 0 | case LINE_SECTION: |
787 | 0 | mem_err = dictionary_set(dict, section, NULL); |
788 | 0 | break ; |
789 | | |
790 | 0 | case LINE_VALUE: |
791 | 0 | sprintf(tmp, "%s:%s", section, key); |
792 | 0 | mem_err = dictionary_set(dict, tmp, val); |
793 | 0 | break ; |
794 | | |
795 | 0 | case LINE_ERROR: |
796 | 0 | iniparser_error_callback( |
797 | 0 | "iniparser: syntax error in %s (%d):\n-> %s\n", |
798 | 0 | ininame, |
799 | 0 | lineno, |
800 | 0 | line); |
801 | 0 | errs++ ; |
802 | 0 | break; |
803 | | |
804 | 0 | default: |
805 | 0 | break ; |
806 | 0 | } |
807 | 0 | memset(line, 0, ASCIILINESZ); |
808 | 0 | last=0; |
809 | 0 | if (mem_err<0) { |
810 | 0 | iniparser_error_callback("iniparser: memory allocation failure\n"); |
811 | 0 | break ; |
812 | 0 | } |
813 | 0 | } |
814 | 0 | if (errs) { |
815 | 0 | dictionary_del(dict); |
816 | 0 | dict = NULL ; |
817 | 0 | } |
818 | 0 | fclose(in); |
819 | 0 | return dict ; |
820 | 0 | } |
821 | | |
822 | | /*-------------------------------------------------------------------------*/ |
823 | | /** |
824 | | @brief Free all memory associated to an ini dictionary |
825 | | @param d Dictionary to free |
826 | | @return void |
827 | | |
828 | | Free all memory associated to an ini dictionary. |
829 | | It is mandatory to call this function before the dictionary object |
830 | | gets out of the current context. |
831 | | */ |
832 | | /*--------------------------------------------------------------------------*/ |
833 | | void iniparser_freedict(dictionary * d) |
834 | 6 | { |
835 | 6 | dictionary_del(d); |
836 | 6 | } |