1 /* Copyright (c) 2000-2007 by Nicolas Devillard.
2 * Copyright (x) 2009 by Tim Post <tinkertim@gmail.com>
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
24 /** @addtogroup ciniparser
29 * @author N. Devillard
32 * @brief Parser for ini files.
36 #include "ciniparser.h"
38 #define ASCIILINESZ (1024)
39 #define INI_INVALID_KEY ((char*) NULL)
42 * This enum stores the status for each parsed line (internal use only).
44 typedef enum _line_status_ {
55 * @brief Convert a string to lowercase.
56 * @param s String to convert.
57 * @return ptr to statically allocated string.
59 * This function returns a pointer to a statically allocated string
60 * containing a lowercased version of the input string. Do not free
61 * or modify the returned string! Since the returned string is statically
62 * allocated, it will be modified at each function call (not re-entrant).
64 static char *strlwc(const char *s)
66 static char l[ASCIILINESZ+1];
72 memset(l, 0, ASCIILINESZ+1);
75 while (s[i] && i < ASCIILINESZ) {
76 l[i] = (char)tolower((int)s[i]);
80 l[ASCIILINESZ] = (char) 0;
86 * @brief Remove blanks at the beginning and the end of a string.
87 * @param s String to parse.
88 * @return ptr to statically allocated string.
90 * This function returns a pointer to a statically allocated string,
91 * which is identical to the input string, except that all blank
92 * characters at the end and the beg. of the string have been removed.
93 * Do not free or modify the returned string! Since the returned string
94 * is statically allocated, it will be modified at each function call
97 static char *strstrip(char *s)
99 static char l[ASCIILINESZ+1];
105 while (isspace((int)*s) && *s)
108 memset(l, 0, ASCIILINESZ+1);
110 last = l + strlen(l);
113 if (!isspace((int)*(last-1)))
124 * @brief Load a single line from an INI file
125 * @param input_line Input line, may be concatenated multi-line input
126 * @param section Output space to store section
127 * @param key Output space to store key
128 * @param value Output space to store value
129 * @return line_status value
132 line_status ciniparser_line(char *input_line, char *section,
133 char *key, char *value)
136 char line[ASCIILINESZ+1];
139 strcpy(line, strstrip(input_line));
140 len = (int) strlen(line);
142 sta = LINE_UNPROCESSED;
146 } else if (line[0] == '#') {
149 } else if (line[0] == '[' && line[len-1] == ']') {
151 sscanf(line, "[%[^]]", section);
152 strcpy(section, strstrip(section));
153 strcpy(section, strlwc(section));
155 } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
156 || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2
157 || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {
158 /* Usual key=value, with or without comments */
159 strcpy(key, strstrip(key));
160 strcpy(key, strlwc(key));
161 strcpy(value, strstrip(value));
163 * sscanf cannot handle '' or "" as empty values
166 if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
170 } else if (sscanf(line, "%[^=] = %[;#]", key, value) == 2
171 || sscanf(line, "%[^=] %[=]", key, value) == 2) {
178 strcpy(key, strstrip(key));
179 strcpy(key, strlwc(key));
183 /* Generate syntax error */
189 /* The remaining public functions are documented in ciniparser.h */
191 int ciniparser_getnsec(dictionary *d)
200 for (i = 0; i < d->size; i++) {
201 if (d->key[i] == NULL)
203 if (strchr(d->key[i], ':') == NULL) {
211 char *ciniparser_getsecname(dictionary *d, int n)
216 if (d == NULL || n < 0)
224 for (i = 0; i < d->size; i++) {
225 if (d->key[i] == NULL)
227 if (! strchr(d->key[i], ':')) {
238 return (char *) NULL;
241 void ciniparser_dump(dictionary *d, FILE *f)
245 if (d == NULL || f == NULL)
248 for (i = 0; i < d->size; i++) {
249 if (d->key[i] == NULL)
251 if (d->val[i] != NULL) {
252 fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
254 fprintf(f, "[%s]=UNDEF\n", d->key[i]);
261 void ciniparser_dump_ini(dictionary *d, FILE *f)
264 char keym[ASCIILINESZ+1];
269 if (d == NULL || f == NULL)
272 memset(keym, 0, ASCIILINESZ + 1);
274 nsec = ciniparser_getnsec(d);
276 /* No section in file: dump all keys as they are */
277 for (i = 0; i < d->size; i++) {
278 if (d->key[i] == NULL)
280 fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
285 for (i = 0; i < nsec; i++) {
286 secname = ciniparser_getsecname(d, i);
287 seclen = (int)strlen(secname);
288 fprintf(f, "\n[%s]\n", secname);
289 snprintf(keym, ASCIILINESZ + 1, "%s:", secname);
290 for (j = 0; j < d->size; j++) {
291 if (d->key[j] == NULL)
293 if (!strncmp(d->key[j], keym, seclen+1)) {
294 fprintf(f, "%-30s = %s\n",
296 d->val[j] ? d->val[j] : "");
305 char *ciniparser_getstring(dictionary *d, const char *key, char *def)
310 if (d == NULL || key == NULL)
313 lc_key = strlwc(key);
314 sval = dictionary_get(d, lc_key, def);
319 int ciniparser_getint(dictionary *d, const char *key, int notfound)
323 str = ciniparser_getstring(d, key, INI_INVALID_KEY);
325 if (str == INI_INVALID_KEY)
328 return (int) strtol(str, NULL, 10);
331 double ciniparser_getdouble(dictionary *d, char *key, double notfound)
335 str = ciniparser_getstring(d, key, INI_INVALID_KEY);
337 if (str == INI_INVALID_KEY)
343 int ciniparser_getboolean(dictionary *d, const char *key, int notfound)
348 c = ciniparser_getstring(d, key, INI_INVALID_KEY);
349 if (c == INI_INVALID_KEY)
353 case 'y': case 'Y': case '1': case 't': case 'T':
356 case 'n': case 'N': case '0': case 'f': case 'F':
367 int ciniparser_find_entry(dictionary *ini, char *entry)
371 if (ciniparser_getstring(ini, entry, INI_INVALID_KEY) != INI_INVALID_KEY) {
378 int ciniparser_set(dictionary *d, char *entry, char *val)
380 return dictionary_set(d, strlwc(entry), val);
383 void ciniparser_unset(dictionary *ini, char *entry)
385 dictionary_unset(ini, strlwc(entry));
388 dictionary *ciniparser_load(const char *ininame)
391 char line[ASCIILINESZ+1];
392 char section[ASCIILINESZ+1];
393 char key[ASCIILINESZ+1];
394 char tmp[ASCIILINESZ+1];
395 char val[ASCIILINESZ+1];
396 int last = 0, len, lineno = 0, errs = 0;
399 if ((in = fopen(ininame, "r")) == NULL) {
400 fprintf(stderr, "ciniparser: cannot open %s\n", ininame);
404 dict = dictionary_new(0);
410 memset(line, 0, ASCIILINESZ + 1);
411 memset(section, 0, ASCIILINESZ + 1);
412 memset(key, 0, ASCIILINESZ + 1);
413 memset(val, 0, ASCIILINESZ + 1);
416 while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
418 len = (int) strlen(line)-1;
419 /* Safety check against buffer overflows */
420 if (line[len] != '\n') {
422 "ciniparser: input line too long in %s (%d)\n",
425 dictionary_del(dict);
430 /* Get rid of \n and spaces at end of line */
432 ((line[len] == '\n') || (isspace(line[len])))) {
437 /* Detect multi-line */
438 if (line[len] == '\\') {
439 /* Multi-line value */
446 switch (ciniparser_line(line, section, key, val)) {
452 errs = dictionary_set(dict, section, NULL);
456 snprintf(tmp, ASCIILINESZ + 1, "%s:%s", section, key);
457 errs = dictionary_set(dict, tmp, val);
461 fprintf(stderr, "ciniparser: syntax error in %s (%d):\n",
463 fprintf(stderr, "-> %s\n", line);
470 memset(line, 0, ASCIILINESZ);
473 fprintf(stderr, "ciniparser: memory allocation failure\n");
479 dictionary_del(dict);
488 void ciniparser_freedict(dictionary *d)