source: mainline/uspace/lib/conf/src/ini.c@ 09a8006

Last change on this file since 09a8006 was 09a8006, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

Add license and copyright to new files

  • Property mode set to 100644
File size: 10.9 KB
Line 
1/*
2 * Copyright (c) 2015 Michal Koutny
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <adt/hash.h>
30#include <adt/hash_table.h>
31#include <assert.h>
32#include <errno.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <str.h>
36
37#include "conf/ini.h"
38
39#define LINE_BUFFER 256
40
41/** Representation of key-value pair from INI file
42 *
43 * @note Structure is owner of its string data.
44 */
45typedef struct {
46 ht_link_t ht_link;
47
48 /** First line from where the item was extracted. */
49 size_t lineno;
50
51 char *key;
52 char *value;
53} ini_item_t;
54
55
56/* Necessary forward declarations */
57static void ini_section_destroy(ini_section_t **);
58static void ini_item_destroy(ini_item_t **);
59
60/* Hash table functions */
61static size_t ini_section_ht_hash(const ht_link_t *item)
62{
63 ini_section_t *section =
64 hash_table_get_inst(item, ini_section_t, ht_link);
65 /* Nameless default section */
66 if (section->name == NULL) {
67 return 0;
68 }
69
70 return hash_string(section->name);
71}
72
73static size_t ini_section_ht_key_hash(void *key)
74{
75 return hash_string((const char *)key);
76}
77
78static bool ini_section_ht_equal(const ht_link_t *item1, const ht_link_t *item2)
79{
80 return str_cmp(
81 hash_table_get_inst(item1, ini_section_t, ht_link)->name,
82 hash_table_get_inst(item2, ini_section_t, ht_link)->name) == 0;
83}
84
85static bool ini_section_ht_key_equal(void *key, const ht_link_t *item)
86{
87 return str_cmp((const char *)key,
88 hash_table_get_inst(item, ini_section_t, ht_link)->name) == 0;
89}
90
91static void ini_section_ht_remove(ht_link_t *item)
92{
93 ini_section_t *section = hash_table_get_inst(item, ini_section_t, ht_link);
94 ini_section_destroy(&section);
95}
96
97
98static size_t ini_item_ht_hash(const ht_link_t *item)
99{
100 ini_item_t *ini_item =
101 hash_table_get_inst(item, ini_item_t, ht_link);
102 assert(ini_item->key);
103 return hash_string(ini_item->key);
104}
105
106static size_t ini_item_ht_key_hash(void *key)
107{
108 return hash_string((const char *)key);
109}
110
111static bool ini_item_ht_equal(const ht_link_t *item1, const ht_link_t *item2)
112{
113 return str_cmp(
114 hash_table_get_inst(item1, ini_item_t, ht_link)->key,
115 hash_table_get_inst(item2, ini_item_t, ht_link)->key) == 0;
116}
117
118static bool ini_item_ht_key_equal(void *key, const ht_link_t *item)
119{
120 return str_cmp((const char *)key,
121 hash_table_get_inst(item, ini_item_t, ht_link)->key) == 0;
122}
123
124static void ini_item_ht_remove(ht_link_t *item)
125{
126 ini_item_t *ini_item = hash_table_get_inst(item, ini_item_t, ht_link);
127 ini_item_destroy(&ini_item);
128}
129
130
131static hash_table_ops_t configuration_ht_ops = {
132 .hash = &ini_section_ht_hash,
133 .key_hash = &ini_section_ht_key_hash,
134 .equal = &ini_section_ht_equal,
135 .key_equal = &ini_section_ht_key_equal,
136 .remove_callback = &ini_section_ht_remove
137};
138
139static hash_table_ops_t section_ht_ops = {
140 .hash = &ini_item_ht_hash,
141 .key_hash = &ini_item_ht_key_hash,
142 .equal = &ini_item_ht_equal,
143 .key_equal = &ini_item_ht_key_equal,
144 .remove_callback = &ini_item_ht_remove
145};
146
147/* Actual INI functions */
148
149void ini_configuration_init(ini_configuration_t *conf)
150{
151 hash_table_create(&conf->sections, 0, 0, &configuration_ht_ops);
152}
153
154/** INI configuration destructor
155 *
156 * Release all resources of INI structure but the structure itself.
157 */
158void ini_configuration_deinit(ini_configuration_t *conf)
159{
160 hash_table_destroy(&conf->sections);
161}
162
163static void ini_section_init(ini_section_t *section)
164{
165 hash_table_create(&section->items, 0, 0, &section_ht_ops);
166 section->name = NULL;
167}
168
169static ini_section_t* ini_section_create(void)
170{
171 ini_section_t *section = malloc(sizeof(ini_section_t));
172 if (section != NULL) {
173 ini_section_init(section);
174 }
175 return section;
176}
177
178static void ini_section_destroy(ini_section_t **section_ptr)
179{
180 ini_section_t *section = *section_ptr;
181 if (section == NULL) {
182 return;
183 }
184 hash_table_destroy(&section->items);
185 free(section->name);
186 free(section);
187 *section_ptr = NULL;
188}
189
190static void ini_item_init(ini_item_t *item)
191{
192 item->key = NULL;
193 item->value = NULL;
194}
195
196static ini_item_t *ini_item_create(void)
197{
198 ini_item_t *item = malloc(sizeof(ini_item_t));
199 if (item != NULL) {
200 ini_item_init(item);
201 }
202 return item;
203}
204
205static void ini_item_destroy(ini_item_t **item_ptr)
206{
207 ini_item_t *item = *item_ptr;
208 if (item == NULL) {
209 return;
210 }
211 free(item->key);
212 free(item->value);
213 free(item);
214 *item_ptr = NULL;
215}
216
217/** Parse file contents to INI structure
218 *
219 * @param[in] filename
220 * @param[out] conf initialized structure for configuration
221 * @param[out] parse initialized structure to keep parsing errors
222 *
223 * @return EOK on success
224 * @return EIO when file cannot be opened
225 * @return ENOMEM
226 * @return EINVAL on parse error (details in parse structure)
227 */
228int ini_parse_file(const char *filename, ini_configuration_t *conf,
229 text_parse_t *parse)
230{
231 int rc = EOK;
232 FILE *f = NULL;
233 char *line_buffer = NULL;
234
235 f = fopen(filename, "r");
236 if (f == NULL) {
237 rc = EIO;
238 goto finish;
239 }
240
241 line_buffer = malloc(LINE_BUFFER);
242 if (line_buffer == NULL) {
243 rc = ENOMEM;
244 goto finish;
245 }
246
247 char *line = NULL;
248 ini_section_t *cur_section = NULL;
249 size_t lineno = 0;
250
251 while ((line = fgets(line_buffer, LINE_BUFFER - 1, f))) {
252 ++lineno;
253 size_t line_len = str_size(line);
254 if (line[line_len - 1] != '\n') {
255 text_parse_raise_error(parse, lineno, INI_ETOO_LONG);
256 rc = EINVAL;
257 /* Cannot recover terminate parsing */
258 goto finish;
259 }
260 /* Ingore leading/trailing whitespace */
261 str_rtrim(line, '\n');
262 str_ltrim(line, ' ');
263 str_rtrim(line, ' ');
264
265 /* Empty line */
266 if (str_size(line) == 0) {
267 continue;
268 }
269
270 /* Comment line */
271 if (line[0] == ';' || line[0] == '#') {
272 continue;
273 }
274
275 /* Start new section */
276 if (line[0] == '[') {
277 cur_section = ini_section_create();
278 if (cur_section == NULL) {
279 rc = ENOMEM;
280 goto finish;
281 }
282
283 char *close_bracket = str_chr(line, ']');
284 if (close_bracket == NULL) {
285 ini_section_destroy(&cur_section);
286 text_parse_raise_error(parse, lineno,
287 INI_EBRACKET_EXPECTED);
288 rc = EINVAL;
289 goto finish;
290 }
291
292 cur_section->lineno = lineno;
293 *close_bracket = '\0';
294 cur_section->name = str_dup(line + 1);
295
296 if (!hash_table_insert_unique(&conf->sections,
297 &cur_section->ht_link)) {
298 ini_section_destroy(&cur_section);
299 text_parse_raise_error(parse, lineno,
300 INI_EDUP_SECTION);
301 rc = EINVAL;
302 goto finish;
303 }
304
305 continue;
306 }
307
308 /* Create a default section if none was specified */
309 if (cur_section == NULL) {
310 cur_section = ini_section_create();
311 if (cur_section == NULL) {
312 rc = ENOMEM;
313 goto finish;
314 }
315 cur_section->lineno = lineno;
316
317 bool inserted = hash_table_insert_unique(&conf->sections,
318 &cur_section->ht_link);
319 assert(inserted);
320 }
321
322 /* Parse key-value pairs */
323 ini_item_t *item = ini_item_create();
324 if (item == NULL) {
325 rc = ENOMEM;
326 goto finish;
327 }
328 item->lineno = lineno;
329
330 char *assign_char = str_chr(line, '=');
331 if (assign_char == NULL) {
332 rc = EINVAL;
333 text_parse_raise_error(parse, lineno,
334 INI_EASSIGN_EXPECTED);
335 goto finish;
336 }
337
338 *assign_char = '\0';
339 char *key = line;
340 str_ltrim(key, ' ');
341 str_rtrim(key, ' ');
342 item->key = str_dup(key);
343 if (item->key == NULL) {
344 ini_item_destroy(&item);
345 rc = ENOMEM;
346 goto finish;
347 }
348
349 char *value = assign_char + 1;
350 str_ltrim(value, ' ');
351 str_rtrim(value, ' ');
352 item->value = str_dup(value);
353 if (item->value == NULL) {
354 ini_item_destroy(&item);
355 rc = ENOMEM;
356 goto finish;
357 }
358
359 hash_table_insert(&cur_section->items, &item->ht_link);
360 }
361
362finish:
363 if (f) {
364 fclose(f);
365 }
366 free(line_buffer);
367
368 return rc;
369}
370
371
372/** Get a section from configuration
373 *
374 * @param[in] ini_configuration
375 * @param[in] section_name name of section or NULL for default section
376 *
377 * @return Section with given name
378 * @return NULL when no such section exits
379 */
380ini_section_t *ini_get_section(ini_configuration_t *ini_conf,
381 const char *section_name)
382{
383 ht_link_t *item = hash_table_find(&ini_conf->sections,
384 (void *)section_name);
385 if (item == NULL) {
386 return NULL;
387 }
388
389 return hash_table_get_inst(item, ini_section_t, ht_link);
390}
391
392/** Get item iterator to items with given key in the section
393 *
394 * @param[in] section
395 * @param[in] key
396 *
397 * @return Always return iterator (even when there's no item with given key)
398 */
399ini_item_iterator_t ini_section_get_iterator(ini_section_t *section,
400 const char *key)
401{
402 ini_item_iterator_t result;
403 result.first_item = hash_table_find(&section->items, (void *)key);
404 result.cur_item = result.first_item;
405 result.table = &section->items;
406 result.incremented = false;
407
408 return result;
409}
410
411bool ini_item_iterator_valid(ini_item_iterator_t *iterator)
412{
413 bool empty = (iterator->cur_item != NULL);
414 bool looped = (iterator->cur_item == iterator->first_item);
415 return empty || (looped && iterator->incremented);
416}
417
418/** Move iterator to next item (of the same key)
419 *
420 * @param[in] iterator valid iterator
421 */
422void ini_item_iterator_inc(ini_item_iterator_t *iterator)
423{
424 iterator->cur_item =
425 hash_table_find_next(iterator->table, iterator->cur_item);
426 iterator->incremented = true;
427}
428
429/** Get item value for current iterator
430 *
431 * @param[in] iterator valid iterator
432 */
433const char *ini_item_iterator_value(ini_item_iterator_t *iterator)
434{
435 ini_item_t *ini_item =
436 hash_table_get_inst(iterator->cur_item, ini_item_t, ht_link);
437 return ini_item->value;
438}
439
440/** Get item line number for current iterator
441 *
442 * @param[in] iterator valid iterator
443 *
444 * @return Line number of input where item was originally defined.
445 */
446size_t ini_item_iterator_lineno(ini_item_iterator_t *iterator)
447{
448 ini_item_t *ini_item =
449 hash_table_get_inst(iterator->cur_item, ini_item_t, ht_link);
450 return ini_item->lineno;
451}
Note: See TracBrowser for help on using the repository browser.