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