2 /*-------------------------------------------------------------------------*/
8 @brief Parser for ini files.
11 /*---------------------------- Includes ------------------------------------*/
14 #include <atalk/iniparser.h>
15 #include <atalk/logger.h>
17 /*---------------------------- Defines -------------------------------------*/
18 #define ASCIILINESZ (1024)
19 #define INI_INVALID_KEY ((char*)-1)
21 /*---------------------------------------------------------------------------
22 Private to this module
23 ---------------------------------------------------------------------------*/
25 * This enum stores the status for each parsed line (internal use only).
27 typedef enum _line_status_ {
36 /*-------------------------------------------------------------------------*/
38 @brief Remove blanks at the beginning and the end of a string.
39 @param s String to parse.
40 @return ptr to statically allocated string.
42 This function returns a pointer to a statically allocated string,
43 which is identical to the input string, except that all blank
44 characters at the end and the beg. of the string have been removed.
45 Do not free or modify the returned string! Since the returned string
46 is statically allocated, it will be modified at each function call
49 /*--------------------------------------------------------------------------*/
50 static char * strstrip(char * s)
52 static char l[ASCIILINESZ+1];
55 if (s==NULL) return NULL ;
57 while (isspace((int)*s) && *s) s++;
58 memset(l, 0, ASCIILINESZ+1);
62 if (!isspace((int)*(last-1)))
70 /*-------------------------------------------------------------------------*/
72 @brief Get number of sections in a dictionary
73 @param d Dictionary to examine
74 @return int Number of sections found in dictionary
76 This function returns the number of sections found in a dictionary.
77 The test to recognize sections is done on the string stored in the
78 dictionary: a section name is given as "section" whereas a key is
79 stored as "section:key", thus the test looks for entries that do not
82 This clearly fails in the case a section name contains a colon, but
83 this should simply be avoided.
85 This function returns -1 in case of error.
87 /*--------------------------------------------------------------------------*/
88 int iniparser_getnsec(const dictionary * d)
93 if (d==NULL) return -1 ;
95 for (i=0 ; i<d->size ; i++) {
98 if (strchr(d->key[i], ':')==NULL) {
105 /*-------------------------------------------------------------------------*/
107 @brief Get name for section n in a dictionary.
108 @param d Dictionary to examine
109 @param n Section number (from 0 to nsec-1).
110 @return Pointer to char string
112 This function locates the n-th section in a dictionary and returns
113 its name as a pointer to a string statically allocated inside the
114 dictionary. Do not free or modify the returned string!
116 This function returns NULL in case of error.
118 /*--------------------------------------------------------------------------*/
119 const char * iniparser_getsecname(const dictionary * d, int n)
124 if (d==NULL || n<0) return NULL ;
126 for (i=0 ; i<d->size ; i++) {
129 if (strchr(d->key[i], ':')==NULL) {
141 /*-------------------------------------------------------------------------*/
143 @brief Dump a dictionary to an opened file pointer.
144 @param d Dictionary to dump.
145 @param f Opened file pointer to dump to.
148 This function prints out the contents of a dictionary, one element by
149 line, onto the provided file pointer. It is OK to specify @c stderr
150 or @c stdout as output files. This function is meant for debugging
153 /*--------------------------------------------------------------------------*/
154 void iniparser_dump(const dictionary * d, FILE * f)
158 if (d==NULL || f==NULL) return ;
159 for (i=0 ; i<d->size ; i++) {
162 if (d->val[i]!=NULL) {
163 fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
165 fprintf(f, "[%s]=UNDEF\n", d->key[i]);
171 /*-------------------------------------------------------------------------*/
173 @brief Save a dictionary to a loadable ini file
174 @param d Dictionary to dump
175 @param f Opened file pointer to dump to
178 This function dumps a given dictionary into a loadable ini file.
179 It is Ok to specify @c stderr or @c stdout as output files.
181 /*--------------------------------------------------------------------------*/
182 void iniparser_dump_ini(const dictionary * d, FILE * f)
185 char keym[ASCIILINESZ+1];
187 const char * secname ;
190 if (d==NULL || f==NULL) return ;
192 nsec = iniparser_getnsec(d);
194 /* No section in file: dump all keys as they are */
195 for (i=0 ; i<d->size ; i++) {
198 fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
202 for (i=0 ; i<nsec ; i++) {
203 secname = iniparser_getsecname(d, i) ;
204 seclen = (int)strlen(secname);
205 fprintf(f, "\n[%s]\n", secname);
206 sprintf(keym, "%s:", secname);
207 for (j=0 ; j<d->size ; j++) {
210 if (!strncmp(d->key[j], keym, seclen+1)) {
214 d->val[j] ? d->val[j] : "");
222 /*-------------------------------------------------------------------------*/
224 @brief Get the string associated to a key
225 @param d Dictionary to search
226 @param section Section to search
227 @param key Key string to look for
228 @param def Default value to return if key not found.
229 @return pointer to statically allocated character string
231 This function queries a dictionary for a key. A key as read from an
232 ini file is given as "section:key". If the key cannot be found,
233 the pointer passed as 'def' is returned.
234 The returned char pointer is pointing to a string allocated in
235 the dictionary, do not free or modify it.
237 /*--------------------------------------------------------------------------*/
238 const char * iniparser_getstring(const dictionary * d, const char *section, const char * key, const char * def)
242 if (d==NULL || key==NULL)
245 sval = dictionary_get(d, section, key, def);
249 /*-------------------------------------------------------------------------*/
251 @brief Get the string associated to a key
252 @param d Dictionary to search
253 @param section Section to search
254 @param key Key string to look for
255 @param def Default value to return if key not found.
256 @return pointer to statically allocated character string
258 This function queries a dictionary for a key. A key as read from an
259 ini file is given as "section:key". If the key cannot be found,
260 the pointer passed as 'def' is returned.
261 The returned char pointer a strdup'ed allocated string, so the caller must free it.
263 /*--------------------------------------------------------------------------*/
264 char * iniparser_getstrdup(const dictionary * d, const char *section, const char * key, const char * def)
268 if (d==NULL || key==NULL)
271 if ((sval = dictionary_get(d, section, key, def)))
276 /*-------------------------------------------------------------------------*/
278 @brief Get the string associated to a key, convert to an int
279 @param d Dictionary to search
280 @param section Section to search
281 @param key Key string to look for
282 @param notfound Value to return in case of error
285 This function queries a dictionary for a key. A key as read from an
286 ini file is given as "section:key". If the key cannot be found,
287 the notfound value is returned.
289 Supported values for integers include the usual C notation
290 so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
291 are supported. Examples:
294 "042" -> 34 (octal -> decimal)
295 "0x42" -> 66 (hexa -> decimal)
297 Warning: the conversion may overflow in various ways. Conversion is
298 totally outsourced to strtol(), see the associated man page for overflow
301 Credits: Thanks to A. Becker for suggesting strtol()
303 /*--------------------------------------------------------------------------*/
304 int iniparser_getint(const dictionary * d, const char *section, const char * key, int notfound)
308 str = iniparser_getstring(d, section, key, INI_INVALID_KEY);
309 if (str==INI_INVALID_KEY) return notfound ;
310 return (int)strtol(str, NULL, 0);
313 /*-------------------------------------------------------------------------*/
315 @brief Get the string associated to a key, convert to a double
316 @param d Dictionary to search
317 @param section Section to search
318 @param key Key string to look for
319 @param notfound Value to return in case of error
322 This function queries a dictionary for a key. A key as read from an
323 ini file is given as "section:key". If the key cannot be found,
324 the notfound value is returned.
326 /*--------------------------------------------------------------------------*/
327 double iniparser_getdouble(const dictionary * d, const char *section, const char * key, double notfound)
331 str = iniparser_getstring(d, section, key, INI_INVALID_KEY);
332 if (str==INI_INVALID_KEY) return notfound ;
336 /*-------------------------------------------------------------------------*/
338 @brief Get the string associated to a key, convert to a boolean
339 @param d Dictionary to search
340 @param section Section to search
341 @param key Key string to look for
342 @param notfound Value to return in case of error
345 This function queries a dictionary for a key. A key as read from an
346 ini file is given as "section:key". If the key cannot be found,
347 the notfound value is returned.
349 A true boolean is found if one of the following is matched:
351 - A string starting with 'y'
352 - A string starting with 'Y'
353 - A string starting with 't'
354 - A string starting with 'T'
355 - A string starting with '1'
357 A false boolean is found if one of the following is matched:
359 - A string starting with 'n'
360 - A string starting with 'N'
361 - A string starting with 'f'
362 - A string starting with 'F'
363 - A string starting with '0'
365 The notfound value returned if no boolean is identified, does not
366 necessarily have to be 0 or 1.
368 /*--------------------------------------------------------------------------*/
369 int iniparser_getboolean(const dictionary * d, const char *section, const char * key, int notfound)
374 c = iniparser_getstring(d, section, key, INI_INVALID_KEY);
375 if (c==INI_INVALID_KEY) return notfound ;
376 if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
378 } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
386 /*-------------------------------------------------------------------------*/
388 @brief Finds out if a given entry exists in a dictionary
389 @param ini Dictionary to search
390 @param entry Name of the entry to look for
391 @return integer 1 if entry exists, 0 otherwise
393 Finds out if a given entry exists in the dictionary. Since sections
394 are stored as keys with NULL associated values, this is the only way
395 of querying for the presence of sections in a dictionary.
397 /*--------------------------------------------------------------------------*/
398 int iniparser_find_entry(const dictionary *ini, const char *entry)
401 if (iniparser_getstring(ini, entry, NULL, INI_INVALID_KEY)!=INI_INVALID_KEY) {
407 /*-------------------------------------------------------------------------*/
409 @brief Set an entry in a dictionary.
410 @param ini Dictionary to modify.
411 @param section Entry to modify (entry section)
412 @param key Entry to modify (entry key)
413 @param val New value to associate to the entry.
414 @return int 0 if Ok, -1 otherwise.
416 If the given entry can be found in the dictionary, it is modified to
417 contain the provided value. If it cannot be found, -1 is returned.
418 It is Ok to set val to NULL.
420 /*--------------------------------------------------------------------------*/
421 int iniparser_set(dictionary * ini, char *section, char * key, char * val)
423 return dictionary_set(ini, section, key, val) ;
426 /*-------------------------------------------------------------------------*/
428 @brief Delete an entry in a dictionary
429 @param ini Dictionary to modify
430 @param section Entry to delete (entry section)
431 @param key Entry to delete (entry key)
434 If the given entry can be found, it is deleted from the dictionary.
436 /*--------------------------------------------------------------------------*/
437 void iniparser_unset(dictionary * ini, char *section, char * key)
439 dictionary_unset(ini, section, key);
442 /*-------------------------------------------------------------------------*/
444 @brief Load a single line from an INI file
445 @param input_line Input line, may be concatenated multi-line input
446 @param section Output space to store section
447 @param key Output space to store key
448 @param value Output space to store value
449 @return line_status value
451 /*--------------------------------------------------------------------------*/
452 static line_status iniparser_line(
459 char line[ASCIILINESZ+1];
462 strcpy(line, strstrip(input_line));
463 len = (int)strlen(line);
465 sta = LINE_UNPROCESSED ;
469 } else if (line[0]=='#' || line[0]==';') {
472 } else if (line[0]=='[' && line[len-1]==']') {
474 sscanf(line, "[%[^]]", section);
475 strcpy(section, strstrip(section));
476 strcpy(section, section);
478 } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
479 || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2
480 || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {
481 /* Usual key=value, with or without comments */
482 strcpy(key, strstrip(key));
484 strcpy(value, strstrip(value));
486 * sscanf cannot handle '' or "" as empty values
489 if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
493 } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
494 || sscanf(line, "%[^=] %[=]", key, value) == 2) {
501 strcpy(key, strstrip(key));
506 /* Generate syntax error */
512 /*-------------------------------------------------------------------------*/
514 @brief Parse an ini file and return an allocated dictionary object
515 @param ininame Name of the ini file to read.
516 @return Pointer to newly allocated dictionary
518 This is the parser for ini files. This function is called, providing
519 the name of the file to be read. It returns a dictionary object that
520 should not be accessed directly, but through accessor functions
523 The returned dictionary must be freed using iniparser_freedict().
525 /*--------------------------------------------------------------------------*/
526 dictionary * iniparser_load(const char * ininame)
528 FILE *in, *include = NULL, *inifile;
530 char line [ASCIILINESZ+1] ;
531 char section [ASCIILINESZ+1] ;
532 char key [ASCIILINESZ+1] ;
533 char tmp [ASCIILINESZ+1] ;
534 char val [ASCIILINESZ+1] ;
543 if ((inifile=fopen(ininame, "r"))==NULL) {
544 LOG(logtype_default, log_error, "iniparser: cannot open \"%s\"", ininame);
548 dict = dictionary_new(0) ;
554 memset(line, 0, ASCIILINESZ);
555 memset(section, 0, ASCIILINESZ);
556 memset(key, 0, ASCIILINESZ);
557 memset(val, 0, ASCIILINESZ);
562 if (fgets(line+last, ASCIILINESZ-last, in) == NULL) {
572 len = (int)strlen(line)-1;
575 /* Safety check against buffer overflows */
576 if (line[len]!='\n') {
577 LOG(logtype_default, log_error, "iniparser: input line too long in \"%s\" (lineno: %d)",
579 dictionary_del(dict);
583 /* Get rid of \n and spaces at end of line */
585 ((line[len]=='\n') || (isspace(line[len])))) {
589 /* Detect multi-line */
590 if (line[len]=='\\') {
591 /* Multi-line value */
597 switch (iniparser_line(line, section, key, val)) {
602 errs = dictionary_set(dict, section, NULL, NULL);
605 if (strcmp(key, "include") == 0) {
606 if ((include = fopen(val, "r")) == NULL) {
607 LOG(logtype_default, log_error, "iniparser: cannot open \"%s\"", val);
613 errs = dictionary_set(dict, section, key, val) ;
616 LOG(logtype_default, log_error, "iniparser: syntax error in %s (lineno: %d): %s",
617 ininame, lineno, line);
623 memset(line, 0, ASCIILINESZ);
626 LOG(logtype_default, log_error, "iniparser: memory allocation failure");
631 dictionary_del(dict);
638 /*-------------------------------------------------------------------------*/
640 @brief Free all memory associated to an ini dictionary
641 @param d Dictionary to free
644 Free all memory associated to an ini dictionary.
645 It is mandatory to call this function before the dictionary object
646 gets out of the current context.
648 /*--------------------------------------------------------------------------*/
649 void iniparser_freedict(dictionary * d)