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