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