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