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 #define ASCIILINESZ         (1024)
16 #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 /*--------------------------------------------------------------------------*/
strlwc(const char * in,char * out,unsigned len)45 static const char * strlwc(const char * in, char *out, unsigned len)
46 {
47     unsigned i ;
48 
49     if (in==NULL || out == NULL || len==0) return NULL ;
50     i=0 ;
51     while (in[i] != '\0' && i < len-1) {
52         out[i] = (char)tolower((int)in[i]);
53         i++ ;
54     }
55     out[i] = '\0';
56     return out ;
57 }
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 /*--------------------------------------------------------------------------*/
xstrdup(const char * s)69 static char * xstrdup(const char * s)
70 {
71     char * t ;
72     size_t len ;
73     if (!s)
74         return NULL ;
75 
76     len = strlen(s) + 1 ;
77     t = (char*) malloc(len) ;
78     if (t) {
79         memcpy(t, s, len) ;
80     }
81     return t ;
82 }
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 /*--------------------------------------------------------------------------*/
strstrip(char * s)91 static unsigned strstrip(char * s)
92 {
93     char *last = NULL ;
94     char *dest = s;
95 
96     if (s==NULL) return 0;
97 
98     last = s + strlen(s);
99     while (isspace((int)*s) && *s) s++;
100     while (last > s) {
101         if (!isspace((int)*(last-1)))
102             break ;
103         last -- ;
104     }
105     *last = (char)0;
106 
107     memmove(dest,s,last - s + 1);
108     return last - s;
109 }
110 
111 /*-------------------------------------------------------------------------*/
112 /**
113   @brief    Default error callback for iniparser: wraps `fprintf(stderr, ...)`.
114  */
115 /*--------------------------------------------------------------------------*/
default_error_callback(const char * format,...)116 static int default_error_callback(const char *format, ...)
117 {
118   int ret;
119   va_list argptr;
120   va_start(argptr, format);
121   ret = vfprintf(stderr, format, argptr);
122   va_end(argptr);
123   return ret;
124 }
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 /*--------------------------------------------------------------------------*/
iniparser_set_error_callback(int (* errback)(const char *,...))137 void iniparser_set_error_callback(int (*errback)(const char *, ...))
138 {
139   if (errback) {
140     iniparser_error_callback = errback;
141   } else {
142     iniparser_error_callback = default_error_callback;
143   }
144 }
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 /*--------------------------------------------------------------------------*/
iniparser_getnsec(const dictionary * d)164 int iniparser_getnsec(const dictionary * d)
165 {
166     int i ;
167     int nsec ;
168 
169     if (d==NULL) return -1 ;
170     nsec=0 ;
171     for (i=0 ; i<d->size ; i++) {
172         if (d->key[i]==NULL)
173             continue ;
174         if (strchr(d->key[i], ':')==NULL) {
175             nsec ++ ;
176         }
177     }
178     return nsec ;
179 }
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 /*--------------------------------------------------------------------------*/
iniparser_getsecname(const dictionary * d,int n)195 const char * iniparser_getsecname(const dictionary * d, int n)
196 {
197     int i ;
198     int foundsec ;
199 
200     if (d==NULL || n<0) return NULL ;
201     foundsec=0 ;
202     for (i=0 ; i<d->size ; i++) {
203         if (d->key[i]==NULL)
204             continue ;
205         if (strchr(d->key[i], ':')==NULL) {
206             foundsec++ ;
207             if (foundsec>n)
208                 break ;
209         }
210     }
211     if (foundsec<=n) {
212         return NULL ;
213     }
214     return d->key[i] ;
215 }
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 /*--------------------------------------------------------------------------*/
iniparser_dump(const dictionary * d,FILE * f)230 void iniparser_dump(const dictionary * d, FILE * f)
231 {
232     int     i ;
233 
234     if (d==NULL || f==NULL) return ;
235     for (i=0 ; i<d->size ; i++) {
236         if (d->key[i]==NULL)
237             continue ;
238         if (d->val[i]!=NULL) {
239             fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
240         } else {
241             fprintf(f, "[%s]=UNDEF\n", d->key[i]);
242         }
243     }
244     return ;
245 }
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 /*--------------------------------------------------------------------------*/
iniparser_dump_ini(const dictionary * d,FILE * f)258 void iniparser_dump_ini(const dictionary * d, FILE * f)
259 {
260     int          i ;
261     int          nsec ;
262     const char * secname ;
263 
264     if (d==NULL || f==NULL) return ;
265 
266     nsec = iniparser_getnsec(d);
267     if (nsec<1) {
268         /* No section in file: dump all keys as they are */
269         for (i=0 ; i<d->size ; i++) {
270             if (d->key[i]==NULL)
271                 continue ;
272             fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
273         }
274         return ;
275     }
276     for (i=0 ; i<nsec ; i++) {
277         secname = iniparser_getsecname(d, i) ;
278         iniparser_dumpsection_ini(d, secname, f);
279     }
280     fprintf(f, "\n");
281     return ;
282 }
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 /*--------------------------------------------------------------------------*/
iniparser_dumpsection_ini(const dictionary * d,const char * s,FILE * f)296 void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f)
297 {
298     int     j ;
299     char    keym[ASCIILINESZ+1];
300     int     seclen ;
301 
302     if (d==NULL || f==NULL) return ;
303     if (! iniparser_find_entry(d, s)) return ;
304 
305     seclen  = (int)strlen(s);
306     fprintf(f, "\n[%s]\n", s);
307     sprintf(keym, "%s:", s);
308     for (j=0 ; j<d->size ; j++) {
309         if (d->key[j]==NULL)
310             continue ;
311         if (!strncmp(d->key[j], keym, seclen+1)) {
312             fprintf(f,
313                     "%-30s = %s\n",
314                     d->key[j]+seclen+1,
315                     d->val[j] ? d->val[j] : "");
316         }
317     }
318     fprintf(f, "\n");
319     return ;
320 }
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 /*--------------------------------------------------------------------------*/
iniparser_getsecnkeys(const dictionary * d,const char * s)330 int iniparser_getsecnkeys(const dictionary * d, const char * s)
331 {
332     int     seclen, nkeys ;
333     char    keym[ASCIILINESZ+1];
334     int j ;
335 
336     nkeys = 0;
337 
338     if (d==NULL) return nkeys;
339     if (! iniparser_find_entry(d, s)) return nkeys;
340 
341     seclen  = (int)strlen(s);
342     strlwc(s, keym, sizeof(keym));
343     keym[seclen] = ':';
344 
345     for (j=0 ; j<d->size ; j++) {
346         if (d->key[j]==NULL)
347             continue ;
348         if (!strncmp(d->key[j], keym, seclen+1))
349             nkeys++;
350     }
351 
352     return nkeys;
353 
354 }
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 /*--------------------------------------------------------------------------*/
iniparser_getseckeys(const dictionary * d,const char * s,const char ** keys)372 const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys)
373 {
374     int i, j, seclen ;
375     char keym[ASCIILINESZ+1];
376 
377     if (d==NULL || keys==NULL) return NULL;
378     if (! iniparser_find_entry(d, s)) return NULL;
379 
380     seclen  = (int)strlen(s);
381     strlwc(s, keym, sizeof(keym));
382     keym[seclen] = ':';
383 
384     i = 0;
385 
386     for (j=0 ; j<d->size ; j++) {
387         if (d->key[j]==NULL)
388             continue ;
389         if (!strncmp(d->key[j], keym, seclen+1)) {
390             keys[i] = d->key[j];
391             i++;
392         }
393     }
394 
395     return keys;
396 }
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 /*--------------------------------------------------------------------------*/
iniparser_getstring(const dictionary * d,const char * key,const char * def)413 const char * iniparser_getstring(const dictionary * d, const char * key, const char * def)
414 {
415     const char * lc_key ;
416     const char * sval ;
417     char tmp_str[ASCIILINESZ+1];
418 
419     if (d==NULL || key==NULL)
420         return def ;
421 
422     lc_key = strlwc(key, tmp_str, sizeof(tmp_str));
423     sval = dictionary_get(d, lc_key, def);
424     return sval ;
425 }
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 /*--------------------------------------------------------------------------*/
iniparser_getlongint(const dictionary * d,const char * key,long int notfound)454 long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound)
455 {
456     const char * str ;
457 
458     str = iniparser_getstring(d, key, INI_INVALID_KEY);
459     if (str==INI_INVALID_KEY) return notfound ;
460     return strtol(str, NULL, 0);
461 }
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 /*--------------------------------------------------------------------------*/
iniparser_getint(const dictionary * d,const char * key,int notfound)491 int iniparser_getint(const dictionary * d, const char * key, int notfound)
492 {
493     return (int)iniparser_getlongint(d, key, notfound);
494 }
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 /*--------------------------------------------------------------------------*/
iniparser_getdouble(const dictionary * d,const char * key,double notfound)509 double iniparser_getdouble(const dictionary * d, const char * key, double notfound)
510 {
511     const char * str ;
512 
513     str = iniparser_getstring(d, key, INI_INVALID_KEY);
514     if (str==INI_INVALID_KEY) return notfound ;
515     return atof(str);
516 }
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 /*--------------------------------------------------------------------------*/
iniparser_getboolean(const dictionary * d,const char * key,int notfound)550 int iniparser_getboolean(const dictionary * d, const char * key, int notfound)
551 {
552     int          ret ;
553     const char * c ;
554 
555     c = iniparser_getstring(d, key, INI_INVALID_KEY);
556     if (c==INI_INVALID_KEY) return notfound ;
557     if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
558         ret = 1 ;
559     } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
560         ret = 0 ;
561     } else {
562         ret = notfound ;
563     }
564     return ret;
565 }
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 /*--------------------------------------------------------------------------*/
iniparser_find_entry(const dictionary * ini,const char * entry)579 int iniparser_find_entry(const dictionary * ini, const char * entry)
580 {
581     int found=0 ;
582     if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
583         found = 1 ;
584     }
585     return found ;
586 }
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 /*--------------------------------------------------------------------------*/
iniparser_set(dictionary * ini,const char * entry,const char * val)601 int iniparser_set(dictionary * ini, const char * entry, const char * val)
602 {
603     char tmp_str[ASCIILINESZ+1];
604     return dictionary_set(ini, strlwc(entry, tmp_str, sizeof(tmp_str)), val) ;
605 }
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 /*--------------------------------------------------------------------------*/
iniparser_unset(dictionary * ini,const char * entry)617 void iniparser_unset(dictionary * ini, const char * entry)
618 {
619     char tmp_str[ASCIILINESZ+1];
620     dictionary_unset(ini, strlwc(entry, tmp_str, sizeof(tmp_str)));
621 }
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 /*--------------------------------------------------------------------------*/
iniparser_line(const char * input_line,char * section,char * key,char * value)633 static line_status iniparser_line(
634     const char * input_line,
635     char * section,
636     char * key,
637     char * value)
638 {
639     line_status sta ;
640     char * line = NULL;
641     size_t      len ;
642 
643     line = xstrdup(input_line);
644     len = strstrip(line);
645 
646     sta = LINE_UNPROCESSED ;
647     if (len<1) {
648         /* Empty line */
649         sta = LINE_EMPTY ;
650     } else if (line[0]=='#' || line[0]==';') {
651         /* Comment line */
652         sta = LINE_COMMENT ;
653     } else if (line[0]=='[' && line[len-1]==']') {
654         /* Section name */
655         sscanf(line, "[%[^]]", section);
656         strstrip(section);
657         strlwc(section, section, len);
658         sta = LINE_SECTION ;
659     } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
660            ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2) {
661         /* Usual key=value with quotes, with or without comments */
662         strstrip(key);
663         strlwc(key, key, len);
664         /* Don't strip spaces from values surrounded with quotes */
665         sta = LINE_VALUE ;
666     } else if (sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {
667         /* Usual key=value without quotes, with or without comments */
668         strstrip(key);
669         strlwc(key, key, len);
670         strstrip(value);
671         /*
672          * sscanf cannot handle '' or "" as empty values
673          * this is done here
674          */
675         if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
676             value[0]=0 ;
677         }
678         sta = LINE_VALUE ;
679     } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
680            ||  sscanf(line, "%[^=] %[=]", key, value) == 2) {
681         /*
682          * Special cases:
683          * key=
684          * key=;
685          * key=#
686          */
687         strstrip(key);
688         strlwc(key, key, len);
689         value[0]=0 ;
690         sta = LINE_VALUE ;
691     } else {
692         /* Generate syntax error */
693         sta = LINE_ERROR ;
694     }
695 
696     free(line);
697     return sta ;
698 }
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 /*--------------------------------------------------------------------------*/
iniparser_load(const char * ininame)714 dictionary * iniparser_load(const char * ininame)
715 {
716     FILE * in ;
717 
718     char line    [ASCIILINESZ+1] ;
719     char section [ASCIILINESZ+1] ;
720     char key     [ASCIILINESZ+1] ;
721     char tmp     [(ASCIILINESZ * 2) + 2] ;
722     char val     [ASCIILINESZ+1] ;
723 
724     int  last=0 ;
725     int  len ;
726     int  lineno=0 ;
727     int  errs=0;
728     int  mem_err=0;
729 
730     dictionary * dict ;
731 
732     if ((in=fopen(ininame, "r"))==NULL) {
733         iniparser_error_callback("iniparser: cannot open %s\n", ininame);
734         return NULL ;
735     }
736 
737     dict = dictionary_new(0) ;
738     if (!dict) {
739         fclose(in);
740         return NULL ;
741     }
742 
743     memset(line,    0, ASCIILINESZ);
744     memset(section, 0, ASCIILINESZ);
745     memset(key,     0, ASCIILINESZ);
746     memset(val,     0, ASCIILINESZ);
747     last=0 ;
748 
749     while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
750         lineno++ ;
751         len = (int)strlen(line)-1;
752         if (len<=0)
753             continue;
754         /* Safety check against buffer overflows */
755         if (line[len]!='\n' && !feof(in)) {
756             iniparser_error_callback(
757               "iniparser: input line too long in %s (%d)\n",
758               ininame,
759               lineno);
760             dictionary_del(dict);
761             fclose(in);
762             return NULL ;
763         }
764         /* Get rid of \n and spaces at end of line */
765         while ((len>=0) &&
766                 ((line[len]=='\n') || (isspace(line[len])))) {
767             line[len]=0 ;
768             len-- ;
769         }
770         if (len < 0) { /* Line was entirely \n and/or spaces */
771             len = 0;
772         }
773         /* Detect multi-line */
774         if (line[len]=='\\') {
775             /* Multi-line value */
776             last=len ;
777             continue ;
778         } else {
779             last=0 ;
780         }
781         switch (iniparser_line(line, section, key, val)) {
782             case LINE_EMPTY:
783             case LINE_COMMENT:
784             break ;
785 
786             case LINE_SECTION:
787             mem_err = dictionary_set(dict, section, NULL);
788             break ;
789 
790             case LINE_VALUE:
791             sprintf(tmp, "%s:%s", section, key);
792             mem_err = dictionary_set(dict, tmp, val);
793             break ;
794 
795             case LINE_ERROR:
796             iniparser_error_callback(
797               "iniparser: syntax error in %s (%d):\n-> %s\n",
798               ininame,
799               lineno,
800               line);
801             errs++ ;
802             break;
803 
804             default:
805             break ;
806         }
807         memset(line, 0, ASCIILINESZ);
808         last=0;
809         if (mem_err<0) {
810             iniparser_error_callback("iniparser: memory allocation failure\n");
811             break ;
812         }
813     }
814     if (errs) {
815         dictionary_del(dict);
816         dict = NULL ;
817     }
818     fclose(in);
819     return dict ;
820 }
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 /*--------------------------------------------------------------------------*/
iniparser_freedict(dictionary * d)833 void iniparser_freedict(dictionary * d)
834 {
835     dictionary_del(d);
836 }
837