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