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