source: mainline/uspace/lib/c/generic/cfg.c@ f044e96

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f044e96 was 47fecbb, checked in by Martin Decky <martin@…>, 14 years ago

configuration file parser
cherrypicked from lp:~helenos-nicf/helenos/nicf and sanitized

  • Property mode set to 100644
File size: 9.6 KB
Line 
1/*
2 * Copyright (c) 2011 Radim Vansa
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/**
30 * @addtogroup libc
31 * @{
32 */
33/** @file cfg.c
34 * @brief Configuration files manipulation implementation.
35 */
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <str.h>
40#include <errno.h>
41#include <assert.h>
42#include <dirent.h>
43#include <fcntl.h>
44#include <sys/stat.h>
45#include <ctype.h>
46#include <cfg.h>
47
48/**
49 * @param data Configuration file data
50 *
51 * @return Anonymous section in the configuration file.
52 * @return NULL if there is no such section (it is empty)
53 *
54 */
55const cfg_section_t *cfg_anonymous(const cfg_file_t *data)
56{
57 assert(data != NULL);
58
59 if (list_empty(&data->sections))
60 return NULL;
61
62 link_t *link = list_first(&data->sections);
63 const cfg_section_t *section = cfg_section_instance(link);
64 if (section->title[0] != 0)
65 return NULL;
66
67 return section;
68}
69
70/**
71 * @param data Configuration file data
72 *
73 * @return True if the file contains no data
74 * (no sections, neither anonymous).
75 * @return False otherwise.
76 *
77 */
78bool cfg_empty(const cfg_file_t *data)
79{
80 assert(data != NULL);
81
82 if (list_empty(&data->sections))
83 return true;
84
85 list_foreach(data->sections, link) {
86 const cfg_section_t *section = cfg_section_instance(link);
87
88 if (!list_empty(&section->entries))
89 return false;
90 }
91
92 return true;
93}
94
95/** Read file contents into memory.
96 *
97 * @param path Path to the file
98 * @param buf Pointer to pointer to buffer where
99 * the file contents will be stored
100 *
101 * @return EOK if the file was successfully loaded.
102 * @return ENOMEM if there was not enough memory to load the file
103 * @return EIO if there was a problem with reading the file
104 * @return Other error code if there was a problem openening the file
105 *
106 */
107static int cfg_read(const char *path, char **buf)
108{
109 assert(buf != NULL);
110
111 int fd = open(path, O_RDONLY);
112 if (fd < 0)
113 return fd;
114
115 size_t len = lseek(fd, 0, SEEK_END);
116 lseek(fd, 0, SEEK_SET);
117
118 *buf = malloc(len + 1);
119 if (*buf == NULL) {
120 close(fd);
121 return ENOMEM;
122 }
123
124 ssize_t rd = read_all(fd, *buf, len);
125 if (rd < 0) {
126 free(*buf);
127 close(fd);
128 return EIO;
129 }
130
131 (*buf)[len] = 0;
132 close(fd);
133
134 return EOK;
135}
136
137static inline void null_back(char *back)
138{
139 do {
140 *back = 0;
141 back--;
142 } while (isspace(*back));
143}
144
145/** Allocate and initialize a new entry.
146 *
147 * @param key Entry key
148 * @param value Entry value
149 *
150 * @return New entry
151 * @return NULL if there was not enough memory
152 *
153 */
154static cfg_entry_t *cfg_new_entry(const char *key, const char *value)
155{
156 cfg_entry_t *entry = malloc(sizeof(cfg_entry_t));
157 if (entry == NULL)
158 return NULL;
159
160 link_initialize(&entry->link);
161 entry->key = key;
162 entry->value = value;
163
164 return entry;
165}
166
167/** Allocate and initialize a new section.
168 *
169 * @param title Section title
170 *
171 * @return New section
172 * @return NULL if there was not enough memory
173 *
174 */
175static cfg_section_t *cfg_new_section(const char *title)
176{
177 cfg_section_t *sec = malloc(sizeof(cfg_section_t));
178 if (sec == NULL)
179 return NULL;
180
181 link_initialize(&sec->link);
182
183 if (title != NULL)
184 sec->title = title;
185 else
186 sec->title = "";
187
188 list_initialize(&sec->entries);
189 sec->entry_count = 0;
190
191 return sec;
192}
193
194/** Skip whitespaces
195 *
196 */
197static inline void skip_whitespaces(char **buffer)
198{
199 while (isspace(**buffer))
200 (*buffer)++;
201}
202
203static inline int starts_comment(char c)
204{
205 return ((c == ';') || (c == '#'));
206}
207
208/** Load file content into memory
209 *
210 * Parse the file into sections and entries
211 * and initialize data with this info.
212 *
213 * @param path Path to the configuration file
214 * @param data Configuration file data
215 *
216 * @return EOK if the file was successfully loaded
217 * @return EBADF if the configuration file has bad format.
218 * @return ENOMEM if there was not enough memory
219 * @return Error code from cfg_read()
220 *
221 */
222int cfg_load(const char *path, cfg_file_t *data)
223{
224 char *buffer;
225 int res = cfg_read(path, &buffer);
226 if (res != EOK)
227 return res;
228
229 list_initialize(&data->sections);
230 data->section_count = 0;
231 data->data = buffer;
232
233 cfg_section_t *curr_section = NULL;
234 skip_whitespaces(&buffer);
235
236 while (*buffer) {
237 while (starts_comment(*buffer)) {
238 while ((*buffer) && (*buffer != '\n'))
239 buffer++;
240
241 skip_whitespaces(&buffer);
242 }
243
244 if (*buffer == '[') {
245 buffer++;
246 skip_whitespaces(&buffer);
247
248 const char *title = buffer;
249 while ((*buffer) && (*buffer != ']') && (*buffer != '\n'))
250 buffer++;
251
252 if (*buffer != ']') {
253 cfg_unload(data);
254 return EBADF;
255 }
256
257 null_back(buffer);
258 buffer++;
259
260 cfg_section_t *sec = cfg_new_section(title);
261 if (sec == NULL) {
262 cfg_unload(data);
263 return ENOMEM;
264 }
265
266 list_append(&sec->link, &data->sections);
267 data->section_count++;
268 curr_section = sec;
269 } else if (*buffer) {
270 const char *key = buffer;
271 while ((*buffer) && (*buffer != '=') && (*buffer != '\n'))
272 buffer++;
273
274 if (*buffer != '=') {
275 cfg_unload(data);
276 return EBADF;
277 }
278
279 /* null = and whitespaces before */
280 null_back(buffer);
281 buffer++;
282 skip_whitespaces(&buffer);
283
284 while (starts_comment(*buffer)) {
285 while ((*buffer) && (*buffer != '\n'))
286 buffer++;
287
288 skip_whitespaces(&buffer);
289 }
290
291 const char *value = buffer;
292 /* Empty value is correct value */
293 if (*buffer) {
294 while ((*buffer) && (*buffer != '\n'))
295 buffer++;
296
297 if (*buffer) {
298 null_back(buffer);
299 buffer++;
300 } else
301 null_back(buffer);
302 }
303
304 /* Create anonymous section if not present */
305 if (curr_section == NULL) {
306 curr_section = cfg_new_section(NULL);
307 if (curr_section == NULL) {
308 cfg_unload(data);
309 return ENOMEM;
310 }
311
312 list_append(&curr_section->link, &data->sections);
313 }
314
315 cfg_entry_t *entry = cfg_new_entry(key, value);
316 if (entry == NULL) {
317 cfg_unload(data);
318 return ENOMEM;
319 }
320
321 list_append(&entry->link, &curr_section->entries);
322 curr_section->entry_count++;
323 }
324
325 skip_whitespaces(&buffer);
326 }
327
328 return EOK;
329}
330
331/** Load file content into memory (with path)
332 *
333 * Parse the file (with path) into sections and entries
334 * and initialize data with this info.
335 *
336 */
337int cfg_load_path(const char *path, const char *fname, cfg_file_t *data)
338{
339 size_t sz = str_size(path) + str_size(fname) + 2;
340 char *name = malloc(sz);
341 if (name == NULL)
342 return ENOMEM;
343
344 snprintf(name, sz, "%s/%s", path, fname);
345 int rc = cfg_load(name, data);
346
347 free(name);
348
349 return rc;
350}
351
352/** Deallocate memory used by entry
353 *
354 */
355static void cfg_free_entry(const cfg_entry_t *entry)
356{
357 assert(entry != NULL);
358 free(entry);
359}
360
361/** Deallocate memory used by all entries
362 *
363 * Deallocate memory used by all entries within a section
364 * and the memory used by the section itself.
365 *
366 */
367static void cfg_free_section(const cfg_section_t *section)
368{
369 assert(section != NULL);
370
371 link_t *cur;
372 link_t *next;
373
374 for (cur = section->entries.head.next;
375 cur != &section->entries.head;
376 cur = next) {
377 next = cur->next;
378 cfg_free_entry(cfg_entry_instance(cur));
379 }
380
381 free(section);
382}
383
384/** Deallocate memory used by configuration data
385 *
386 * Deallocate all inner sections and entries.
387 *
388 */
389void cfg_unload(cfg_file_t *data)
390{
391 assert(data != NULL);
392
393 link_t *cur, *next;
394 for (cur = data->sections.head.next;
395 cur != &data->sections.head;
396 cur = next) {
397 next = cur->next;
398 cfg_free_section(cfg_section_instance(cur));
399 }
400
401 free(data->data);
402}
403
404/** Find a section in the configuration data
405 *
406 * @param data Configuration data
407 * @param title Title of the section to search for
408 *
409 * @return Found section
410 * @return NULL if there is no section with such title
411 *
412 */
413const cfg_section_t *cfg_find_section(const cfg_file_t *data, const char *title)
414{
415 list_foreach(data->sections, link) {
416 const cfg_section_t *section = cfg_section_instance(link);
417
418 if (str_cmp(section->title, title) == 0)
419 return section;
420 }
421
422 return NULL;
423}
424
425/** Find entry value in the configuration data
426 *
427 * @param section Section in which to search
428 * @param key Key of the entry we to search for
429 *
430 * @return Value of the entry
431 * @return NULL if there is no entry with such key
432 *
433 */
434const char *cfg_find_value(const cfg_section_t *section, const char *key)
435{
436 list_foreach(section->entries, link) {
437 const cfg_entry_t *entry = cfg_entry_instance(link);
438
439 if (str_cmp(entry->key, key) == 0)
440 return entry->value;
441 }
442
443 return NULL;
444}
445
446/** @}
447 */
Note: See TracBrowser for help on using the repository browser.