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