2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
26 #include <utils/linked_list.h>
29 typedef struct private_settings_t private_settings_t
;
30 typedef struct section_t section_t
;
31 typedef struct kv_t kv_t
;
34 * private data of settings
36 struct private_settings_t
{
55 * section containing subsections and key value pairs
65 * subsections, as section_t
67 linked_list_t
*sections
;
70 * key value pairs, as kv_t
81 * key string, relative
91 static char *find(section_t
*section
, char *key
)
93 char *name
, *pos
, *value
= NULL
;
94 enumerator_t
*enumerator
;
96 section_t
*current
, *found
= NULL
;
105 pos
= strchr(name
, '.');
110 enumerator
= section
->sections
->create_enumerator(section
->sections
);
111 while (enumerator
->enumerate(enumerator
, ¤t
))
113 if (streq(current
->name
, name
))
119 enumerator
->destroy(enumerator
);
122 return find(found
, pos
);
127 enumerator
= section
->kv
->create_enumerator(section
->kv
);
128 while (enumerator
->enumerate(enumerator
, &kv
))
130 if (streq(kv
->key
, name
))
136 enumerator
->destroy(enumerator
);
142 * Implementation of settings_t.get.
144 static char* get_str(private_settings_t
*this, char *key
, char *def
)
148 value
= find(this->top
, key
);
157 * Implementation of settings_t.get_bool.
159 static bool get_bool(private_settings_t
*this, char *key
, bool def
)
163 value
= find(this->top
, key
);
166 if (strcasecmp(value
, "true") == 0 ||
167 strcasecmp(value
, "enabled") == 0 ||
168 strcasecmp(value
, "yes") == 0 ||
169 strcasecmp(value
, "1") == 0)
173 else if (strcasecmp(value
, "false") == 0 ||
174 strcasecmp(value
, "disabled") == 0 ||
175 strcasecmp(value
, "no") == 0 ||
176 strcasecmp(value
, "0") == 0)
185 * Implementation of settings_t.get_int.
187 static int get_int(private_settings_t
*this, char *key
, int def
)
192 value
= find(this->top
, key
);
196 intval
= strtol(value
, NULL
, 10);
208 static void section_destroy(section_t
*this)
210 this->kv
->destroy_function(this->kv
, free
);
211 this->sections
->destroy_function(this->sections
, (void*)section_destroy
);
217 * parse text, truncate "skip" chars, delimited by term respecting brackets.
219 * Chars in "skip" are truncated at the beginning and the end of the resulting
220 * token. "term" contains a list of characters to read up to (first match),
221 * while "br" contains bracket counterparts found in "term" to skip.
223 static char parse(char **text
, char *skip
, char *term
, char *br
, char **token
)
226 char best_term
= '\0';
228 /* skip leading chars */
229 while (strchr(skip
, **text
))
237 /* mark begin of subtext */
244 /* find terminator */
251 else if (br
&& *pos
== *br
)
257 if (best
== NULL
|| best
> pos
)
266 /* try next terminator */
277 /* null trailing bytes */
283 while (best
>= *token
&& strchr(skip
, *best
));
284 /* return found terminator */
293 static section_t
* parse_section(char **text
, char *name
)
295 section_t
*sub
, *section
;
296 bool finished
= FALSE
;
297 char *key
, *value
, *inner
;
302 section
= malloc_thing(section_t
);
303 section
->name
= name
;
304 section
->sections
= linked_list_create();
305 section
->kv
= linked_list_create();
309 switch (parse(text
, "\t\n ", "{=#", NULL
, &key
))
312 if (parse(text
, "\t ", "}", "{", &inner
))
314 sub
= parse_section(&inner
, key
);
317 section
->sections
->insert_last(section
->sections
, sub
);
321 DBG1("matching '}' not found near %s", *text
);
324 if (parse(text
, "\t ", "\n", NULL
, &value
))
326 kv_t
*kv
= malloc_thing(kv_t
);
329 section
->kv
->insert_last(section
->kv
, kv
);
332 DBG1("parsing value failed near %s", *text
);
335 parse(text
, "", "\n", NULL
, &value
);
341 section_destroy(section
);
348 * Implementation of settings_t.destroy
350 static void destroy(private_settings_t
*this)
354 section_destroy(this->top
);
363 settings_t
*settings_create(char *file
)
365 private_settings_t
*this = malloc_thing(private_settings_t
);
367 this->public.get_str
= (char*(*)(settings_t
*, char *key
, char* def
))get_str
;
368 this->public.get_int
= (int(*)(settings_t
*, char *key
, bool def
))get_int
;
369 this->public.get_bool
= (bool(*)(settings_t
*, char *key
, bool def
))get_bool
;
370 this->public.destroy
= (void(*)(settings_t
*))destroy
;
381 fd
= fopen(file
, "r");
384 DBG1("'%s' does not exist or is not readable", file
);
385 return &this->public;
387 fseek(fd
, 0, SEEK_END
);
390 this->text
= malloc(len
+ 1);
391 this->text
[len
] = '\0';
392 if (fread(this->text
, 1, len
, fd
) != len
)
396 return &this->public;
401 this->top
= parse_section(&pos
, NULL
);
402 if (this->top
== NULL
)
406 return &this->public;
409 return &this->public;