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