source: mainline/uspace/lib/c/generic/cfg.c@ 38db6288

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 38db6288 was feeac0d, checked in by Jiri Svoboda <jiri@…>, 12 years ago

Simplify use of list_foreach.

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