Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2010-2014 Intel Corporation
3 : : */
4 : :
5 : : #include <stdio.h>
6 : : #include <stdlib.h>
7 : : #include <string.h>
8 : : #include <ctype.h>
9 : : #include <errno.h>
10 : : #include <rte_string_fns.h>
11 : : #include <rte_common.h>
12 : : #include <rte_log.h>
13 : :
14 : : #include "rte_cfgfile.h"
15 : :
16 : : struct rte_cfgfile_section {
17 : : char name[CFG_NAME_LEN];
18 : : int num_entries;
19 : : int allocated_entries;
20 : : struct rte_cfgfile_entry *entries;
21 : : };
22 : :
23 : : struct rte_cfgfile {
24 : : int flags;
25 : : int num_sections;
26 : : int allocated_sections;
27 : : struct rte_cfgfile_section *sections;
28 : : };
29 : :
30 : : /* Setting up dynamic logging 8< */
31 [ - + ]: 251 : RTE_LOG_REGISTER_DEFAULT(cfgfile_logtype, INFO);
32 : : #define RTE_LOGTYPE_CFGFILE cfgfile_logtype
33 : :
34 : : #define CFG_LOG(level, ...) \
35 : : RTE_LOG_LINE_PREFIX(level, CFGFILE, "%s(): ", __func__, __VA_ARGS__)
36 : : /* >8 End of setting up dynamic logging */
37 : :
38 : : /** when we resize a file structure, how many extra entries
39 : : * for new sections do we add in */
40 : : #define CFG_ALLOC_SECTION_BATCH 8
41 : : /** when we resize a section structure, how many extra entries
42 : : * for new entries do we add in */
43 : : #define CFG_ALLOC_ENTRY_BATCH 16
44 : :
45 : : /**
46 : : * Default cfgfile load parameters.
47 : : */
48 : : static const struct rte_cfgfile_parameters default_cfgfile_params = {
49 : : .comment_character = CFG_DEFAULT_COMMENT_CHARACTER,
50 : : };
51 : :
52 : : /**
53 : : * Defines the list of acceptable comment characters supported by this
54 : : * library.
55 : : */
56 : : static const char valid_comment_chars[] = {
57 : : '!',
58 : : '#',
59 : : '%',
60 : : ';',
61 : : '@'
62 : : };
63 : :
64 : : static unsigned
65 : 420 : _strip(char *str, unsigned len)
66 : : {
67 : 420 : int newlen = len;
68 [ + + ]: 420 : if (len == 0)
69 : : return 0;
70 : :
71 [ + + ]: 406 : if (isspace(str[len-1])) {
72 : : /* strip trailing whitespace */
73 [ + + + + ]: 302 : while (newlen > 0 && isspace(str[newlen - 1]))
74 : 151 : str[--newlen] = '\0';
75 : : }
76 : :
77 [ - + ]: 406 : if (isspace(str[0])) {
78 : : /* strip leading whitespace */
79 : : int i, start = 1;
80 [ # # # # ]: 0 : while (isspace(str[start]) && start < newlen)
81 : 0 : start++
82 : : ; /* do nothing */
83 : 0 : newlen -= start;
84 [ # # ]: 0 : for (i = 0; i < newlen; i++)
85 : 0 : str[i] = str[i+start];
86 : 0 : str[i] = '\0';
87 : : }
88 : 406 : return newlen;
89 : : }
90 : :
91 : : static struct rte_cfgfile_section *
92 : 24 : _get_section(struct rte_cfgfile *cfg, const char *sectionname)
93 : : {
94 : : int i;
95 : :
96 [ + - ]: 58 : for (i = 0; i < cfg->num_sections; i++) {
97 [ + + ]: 58 : if (strncmp(cfg->sections[i].name, sectionname,
98 : : sizeof(cfg->sections[0].name)) == 0)
99 : 24 : return &cfg->sections[i];
100 : : }
101 : : return NULL;
102 : : }
103 : :
104 : : static int
105 : 119 : _add_entry(struct rte_cfgfile_section *section, const char *entryname,
106 : : const char *entryvalue)
107 : : {
108 : : /* resize entry structure if we don't have room for more entries */
109 [ + + ]: 119 : if (section->num_entries == section->allocated_entries) {
110 : 4 : struct rte_cfgfile_entry *n_entries = realloc(
111 : 4 : section->entries,
112 : : sizeof(struct rte_cfgfile_entry) *
113 : 4 : ((section->allocated_entries) +
114 : : CFG_ALLOC_ENTRY_BATCH));
115 : :
116 [ + - ]: 4 : if (n_entries == NULL)
117 : : return -ENOMEM;
118 : :
119 : 4 : section->entries = n_entries;
120 : 4 : section->allocated_entries += CFG_ALLOC_ENTRY_BATCH;
121 : : }
122 : : /* fill up entry fields with key name and value */
123 : 119 : struct rte_cfgfile_entry *curr_entry =
124 : 119 : §ion->entries[section->num_entries];
125 : :
126 : 119 : strlcpy(curr_entry->name, entryname, sizeof(curr_entry->name));
127 : 119 : strlcpy(curr_entry->value, entryvalue, sizeof(curr_entry->value));
128 : 119 : section->num_entries++;
129 : :
130 : 119 : return 0;
131 : : }
132 : :
133 : : static int
134 : 10 : rte_cfgfile_check_params(const struct rte_cfgfile_parameters *params)
135 : : {
136 : : unsigned int valid_comment;
137 : : unsigned int i;
138 : :
139 [ - + ]: 10 : if (!params) {
140 : 0 : CFG_LOG(ERR, "missing cfgfile parameters");
141 : 0 : return -EINVAL;
142 : : }
143 : :
144 : : valid_comment = 0;
145 [ + + ]: 40 : for (i = 0; i < RTE_DIM(valid_comment_chars); i++) {
146 [ + + ]: 39 : if (params->comment_character == valid_comment_chars[i]) {
147 : : valid_comment = 1;
148 : : break;
149 : : }
150 : : }
151 : :
152 [ + + ]: 10 : if (valid_comment == 0) {
153 : 1 : CFG_LOG(ERR, "invalid comment characters %c",
154 : : params->comment_character);
155 : 1 : return -ENOTSUP;
156 : : }
157 : :
158 : : return 0;
159 : : }
160 : :
161 : : struct rte_cfgfile *
162 : 8 : rte_cfgfile_load(const char *filename, int flags)
163 : : {
164 : 8 : return rte_cfgfile_load_with_params(filename, flags,
165 : : &default_cfgfile_params);
166 : : }
167 : :
168 : : struct rte_cfgfile *
169 : 10 : rte_cfgfile_load_with_params(const char *filename, int flags,
170 : : const struct rte_cfgfile_parameters *params)
171 : : {
172 : : char buffer[CFG_NAME_LEN + CFG_VALUE_LEN + 4];
173 : : int lineno = 0;
174 : : struct rte_cfgfile *cfg;
175 : :
176 [ + + ]: 10 : if (rte_cfgfile_check_params(params))
177 : : return NULL;
178 : :
179 : 9 : FILE *f = fopen(filename, "r");
180 [ + - ]: 9 : if (f == NULL)
181 : : return NULL;
182 : :
183 : 9 : cfg = rte_cfgfile_create(flags);
184 : :
185 [ + + ]: 178 : while (fgets(buffer, sizeof(buffer), f) != NULL) {
186 : : char *pos;
187 : 163 : size_t len = strnlen(buffer, sizeof(buffer));
188 : 163 : lineno++;
189 [ - + - - ]: 163 : if ((len >= sizeof(buffer) - 1) && (buffer[len-1] != '\n')) {
190 : 0 : CFG_LOG(ERR, " line %d - no \\n found on string. "
191 : : "Check if line too long", lineno);
192 : 0 : goto error1;
193 : : }
194 : : /* skip parsing if comment character found */
195 : 163 : pos = memchr(buffer, params->comment_character, len);
196 [ + + + + ]: 163 : if (pos != NULL &&
197 [ + - ]: 2 : (pos == buffer || *(pos-1) != '\\')) {
198 : 14 : *pos = '\0';
199 : 14 : len = pos - buffer;
200 : : }
201 : :
202 : 163 : len = _strip(buffer, len);
203 : : /* skip lines without useful content */
204 [ + + + + ]: 163 : if (buffer[0] != '[' && memchr(buffer, '=', len) == NULL)
205 : 26 : continue;
206 : :
207 [ + + ]: 137 : if (buffer[0] == '[') {
208 : : /* section heading line */
209 : 16 : char *end = memchr(buffer, ']', len);
210 [ + + ]: 16 : if (end == NULL) {
211 : 1 : CFG_LOG(ERR,
212 : : "line %d - no terminating ']' character found",
213 : : lineno);
214 : 1 : goto error1;
215 : : }
216 : 15 : *end = '\0';
217 : 15 : _strip(&buffer[1], end - &buffer[1]);
218 : :
219 : 15 : rte_cfgfile_add_section(cfg, &buffer[1]);
220 : : } else {
221 : : /* key and value line */
222 : : char *split[2] = {NULL};
223 : :
224 : : split[0] = buffer;
225 : 121 : split[1] = memchr(buffer, '=', len);
226 [ - + ]: 121 : if (split[1] == NULL) {
227 : 0 : CFG_LOG(ERR,
228 : : "line %d - no '=' character found",
229 : : lineno);
230 : 0 : goto error1;
231 : : }
232 : 121 : *split[1] = '\0';
233 : 121 : split[1]++;
234 : :
235 : 121 : _strip(split[0], strlen(split[0]));
236 : 121 : _strip(split[1], strlen(split[1]));
237 : 121 : char *end = memchr(split[1], '\\', strlen(split[1]));
238 : :
239 : 121 : size_t split_len = strlen(split[1]) + 1;
240 [ - + ]: 121 : while (end != NULL) {
241 [ # # ]: 0 : if (*(end+1) == params->comment_character) {
242 : 0 : *end = '\0';
243 : 0 : strlcat(split[1], end+1, split_len);
244 : : } else
245 : 0 : end++;
246 : 0 : end = memchr(end, '\\', strlen(end));
247 : : }
248 : :
249 [ + + ]: 121 : if (!(flags & CFG_FLAG_EMPTY_VALUES) &&
250 [ + + ]: 120 : (*split[1] == '\0')) {
251 : 1 : CFG_LOG(ERR,
252 : : "line %d - cannot use empty values",
253 : : lineno);
254 : 1 : goto error1;
255 : : }
256 : :
257 [ + + ]: 120 : if (cfg->num_sections == 0)
258 : 1 : goto error1;
259 : :
260 : 119 : _add_entry(&cfg->sections[cfg->num_sections - 1],
261 : : split[0], split[1]);
262 : : }
263 : : }
264 : 6 : fclose(f);
265 : 6 : return cfg;
266 : 3 : error1:
267 : 3 : rte_cfgfile_close(cfg);
268 : 3 : fclose(f);
269 : 3 : return NULL;
270 : : }
271 : :
272 : : struct rte_cfgfile *
273 : 9 : rte_cfgfile_create(int flags)
274 : : {
275 : : int i;
276 : : struct rte_cfgfile *cfg;
277 : :
278 : : /* future proof flags usage */
279 [ + - ]: 9 : if (flags & ~(CFG_FLAG_GLOBAL_SECTION | CFG_FLAG_EMPTY_VALUES))
280 : : return NULL;
281 : :
282 : 9 : cfg = malloc(sizeof(*cfg));
283 : :
284 [ + - ]: 9 : if (cfg == NULL)
285 : : return NULL;
286 : :
287 : 9 : cfg->flags = flags;
288 : 9 : cfg->num_sections = 0;
289 : :
290 : : /* allocate first batch of sections and entries */
291 : 9 : cfg->sections = calloc(CFG_ALLOC_SECTION_BATCH,
292 : : sizeof(struct rte_cfgfile_section));
293 [ - + ]: 9 : if (cfg->sections == NULL)
294 : 0 : goto error1;
295 : :
296 : 9 : cfg->allocated_sections = CFG_ALLOC_SECTION_BATCH;
297 : :
298 [ + + ]: 81 : for (i = 0; i < CFG_ALLOC_SECTION_BATCH; i++) {
299 : 72 : cfg->sections[i].entries = calloc(CFG_ALLOC_ENTRY_BATCH,
300 : : sizeof(struct rte_cfgfile_entry));
301 : :
302 [ - + ]: 72 : if (cfg->sections[i].entries == NULL)
303 : 0 : goto error1;
304 : :
305 : 72 : cfg->sections[i].num_entries = 0;
306 : 72 : cfg->sections[i].allocated_entries = CFG_ALLOC_ENTRY_BATCH;
307 : : }
308 : :
309 [ + + ]: 9 : if (flags & CFG_FLAG_GLOBAL_SECTION)
310 : 1 : rte_cfgfile_add_section(cfg, "GLOBAL");
311 : :
312 : : return cfg;
313 : 0 : error1:
314 [ # # ]: 0 : if (cfg->sections != NULL) {
315 [ # # ]: 0 : for (i = 0; i < cfg->allocated_sections; i++) {
316 [ # # ]: 0 : if (cfg->sections[i].entries != NULL) {
317 : 0 : free(cfg->sections[i].entries);
318 : 0 : cfg->sections[i].entries = NULL;
319 : : }
320 : : }
321 : 0 : free(cfg->sections);
322 : : cfg->sections = NULL;
323 : : }
324 : 0 : free(cfg);
325 : 0 : return NULL;
326 : : }
327 : :
328 : : int
329 : 16 : rte_cfgfile_add_section(struct rte_cfgfile *cfg, const char *sectionname)
330 : : {
331 : : int i;
332 : :
333 [ + - ]: 16 : if (cfg == NULL)
334 : : return -EINVAL;
335 : :
336 [ + - ]: 16 : if (sectionname == NULL)
337 : : return -EINVAL;
338 : :
339 : : /* resize overall struct if we don't have room for more sections */
340 [ + + ]: 16 : if (cfg->num_sections == cfg->allocated_sections) {
341 : :
342 : : struct rte_cfgfile_section *n_sections =
343 : 1 : realloc(cfg->sections,
344 : : sizeof(struct rte_cfgfile_section) *
345 : 1 : ((cfg->allocated_sections) +
346 : : CFG_ALLOC_SECTION_BATCH));
347 : :
348 [ + - ]: 1 : if (n_sections == NULL)
349 : : return -ENOMEM;
350 : :
351 [ + + ]: 9 : for (i = 0; i < CFG_ALLOC_SECTION_BATCH; i++) {
352 : 8 : n_sections[i + cfg->allocated_sections].num_entries = 0;
353 : : n_sections[i +
354 : 8 : cfg->allocated_sections].allocated_entries = 0;
355 : 8 : n_sections[i + cfg->allocated_sections].entries = NULL;
356 : : }
357 : 1 : cfg->sections = n_sections;
358 : 1 : cfg->allocated_sections += CFG_ALLOC_SECTION_BATCH;
359 : : }
360 : :
361 : 16 : strlcpy(cfg->sections[cfg->num_sections].name, sectionname,
362 : : sizeof(cfg->sections[0].name));
363 : 16 : cfg->sections[cfg->num_sections].num_entries = 0;
364 : 16 : cfg->num_sections++;
365 : :
366 : 16 : return 0;
367 : : }
368 : :
369 : 0 : int rte_cfgfile_add_entry(struct rte_cfgfile *cfg,
370 : : const char *sectionname, const char *entryname,
371 : : const char *entryvalue)
372 : : {
373 : : int ret;
374 : :
375 [ # # ]: 0 : if ((cfg == NULL) || (sectionname == NULL) || (entryname == NULL)
376 [ # # ]: 0 : || (entryvalue == NULL))
377 : : return -EINVAL;
378 : :
379 [ # # ]: 0 : if (rte_cfgfile_has_entry(cfg, sectionname, entryname) != 0)
380 : : return -EEXIST;
381 : :
382 : : /* search for section pointer by sectionname */
383 : 0 : struct rte_cfgfile_section *curr_section = _get_section(cfg,
384 : : sectionname);
385 [ # # ]: 0 : if (curr_section == NULL)
386 : : return -EINVAL;
387 : :
388 : 0 : ret = _add_entry(curr_section, entryname, entryvalue);
389 : :
390 : 0 : return ret;
391 : : }
392 : :
393 : 0 : int rte_cfgfile_set_entry(struct rte_cfgfile *cfg, const char *sectionname,
394 : : const char *entryname, const char *entryvalue)
395 : : {
396 : : int i;
397 : :
398 [ # # # # ]: 0 : if ((cfg == NULL) || (sectionname == NULL) || (entryname == NULL))
399 : : return -EINVAL;
400 : :
401 : : /* search for section pointer by sectionname */
402 : 0 : struct rte_cfgfile_section *curr_section = _get_section(cfg,
403 : : sectionname);
404 [ # # ]: 0 : if (curr_section == NULL)
405 : : return -EINVAL;
406 : :
407 [ # # ]: 0 : if (entryvalue == NULL)
408 : : entryvalue = "";
409 : :
410 [ # # ]: 0 : for (i = 0; i < curr_section->num_entries; i++)
411 [ # # ]: 0 : if (!strcmp(curr_section->entries[i].name, entryname)) {
412 : 0 : strlcpy(curr_section->entries[i].value, entryvalue,
413 : : sizeof(curr_section->entries[i].value));
414 : 0 : return 0;
415 : : }
416 : :
417 : 0 : CFG_LOG(ERR, "entry name doesn't exist");
418 : 0 : return -EINVAL;
419 : : }
420 : :
421 : 1 : int rte_cfgfile_save(struct rte_cfgfile *cfg, const char *filename)
422 : : {
423 : : int i, j;
424 : :
425 [ + - ]: 1 : if ((cfg == NULL) || (filename == NULL))
426 : : return -EINVAL;
427 : :
428 : 1 : FILE *f = fopen(filename, "w");
429 : :
430 [ + - ]: 1 : if (f == NULL)
431 : : return -EINVAL;
432 : :
433 [ + + ]: 10 : for (i = 0; i < cfg->num_sections; i++) {
434 : 9 : fprintf(f, "[%s]\n", cfg->sections[i].name);
435 : :
436 [ + + ]: 120 : for (j = 0; j < cfg->sections[i].num_entries; j++) {
437 : : fprintf(f, "%s=%s\n",
438 : 111 : cfg->sections[i].entries[j].name,
439 : 111 : cfg->sections[i].entries[j].value);
440 : : }
441 : : }
442 : 1 : return fclose(f);
443 : : }
444 : :
445 : 9 : int rte_cfgfile_close(struct rte_cfgfile *cfg)
446 : : {
447 : : int i;
448 : :
449 [ + - ]: 9 : if (cfg == NULL)
450 : : return -1;
451 : :
452 [ + - ]: 9 : if (cfg->sections != NULL) {
453 [ + + ]: 89 : for (i = 0; i < cfg->allocated_sections; i++) {
454 [ + + ]: 80 : if (cfg->sections[i].entries != NULL) {
455 : 73 : free(cfg->sections[i].entries);
456 : 73 : cfg->sections[i].entries = NULL;
457 : : }
458 : : }
459 : 9 : free(cfg->sections);
460 : : cfg->sections = NULL;
461 : : }
462 : 9 : free(cfg);
463 : : cfg = NULL;
464 : :
465 : 9 : return 0;
466 : : }
467 : :
468 : : int
469 : 6 : rte_cfgfile_num_sections(struct rte_cfgfile *cfg, const char *sectionname,
470 : : size_t length)
471 : : {
472 : : int i;
473 : : int num_sections = 0;
474 [ + + ]: 21 : for (i = 0; i < cfg->num_sections; i++) {
475 [ + - ]: 15 : if (strncmp(cfg->sections[i].name, sectionname, length) == 0)
476 : 15 : num_sections++;
477 : : }
478 : 6 : return num_sections;
479 : : }
480 : :
481 : : int
482 : 0 : rte_cfgfile_sections(struct rte_cfgfile *cfg, char *sections[],
483 : : int max_sections)
484 : : {
485 : : int i;
486 : :
487 [ # # # # ]: 0 : for (i = 0; i < cfg->num_sections && i < max_sections; i++)
488 : 0 : strlcpy(sections[i], cfg->sections[i].name, CFG_NAME_LEN);
489 : :
490 : 0 : return i;
491 : : }
492 : :
493 : : int
494 : 7 : rte_cfgfile_has_section(struct rte_cfgfile *cfg, const char *sectionname)
495 : : {
496 : 7 : return _get_section(cfg, sectionname) != NULL;
497 : : }
498 : :
499 : : int
500 : 8 : rte_cfgfile_section_num_entries(struct rte_cfgfile *cfg,
501 : : const char *sectionname)
502 : : {
503 : 8 : const struct rte_cfgfile_section *s = _get_section(cfg, sectionname);
504 [ + - ]: 8 : if (s == NULL)
505 : : return -1;
506 : 8 : return s->num_entries;
507 : : }
508 : :
509 : : int
510 : 0 : rte_cfgfile_section_num_entries_by_index(struct rte_cfgfile *cfg,
511 : : char *sectionname, int index)
512 : : {
513 [ # # # # ]: 0 : if (index < 0 || index >= cfg->num_sections)
514 : : return -1;
515 : :
516 : 0 : const struct rte_cfgfile_section *sect = &(cfg->sections[index]);
517 : :
518 : 0 : strlcpy(sectionname, sect->name, CFG_NAME_LEN);
519 : 0 : return sect->num_entries;
520 : : }
521 : : int
522 : 0 : rte_cfgfile_section_entries(struct rte_cfgfile *cfg, const char *sectionname,
523 : : struct rte_cfgfile_entry *entries, int max_entries)
524 : : {
525 : : int i;
526 : 0 : const struct rte_cfgfile_section *sect = _get_section(cfg, sectionname);
527 [ # # ]: 0 : if (sect == NULL)
528 : : return -1;
529 [ # # # # ]: 0 : for (i = 0; i < max_entries && i < sect->num_entries; i++)
530 : 0 : entries[i] = sect->entries[i];
531 : : return i;
532 : : }
533 : :
534 : : int
535 : 0 : rte_cfgfile_section_entries_by_index(struct rte_cfgfile *cfg, int index,
536 : : char *sectionname,
537 : : struct rte_cfgfile_entry *entries, int max_entries)
538 : : {
539 : : int i;
540 : : const struct rte_cfgfile_section *sect;
541 : :
542 [ # # # # ]: 0 : if (index < 0 || index >= cfg->num_sections)
543 : : return -1;
544 : 0 : sect = &cfg->sections[index];
545 : 0 : strlcpy(sectionname, sect->name, CFG_NAME_LEN);
546 [ # # # # ]: 0 : for (i = 0; i < max_entries && i < sect->num_entries; i++)
547 : 0 : entries[i] = sect->entries[i];
548 : : return i;
549 : : }
550 : :
551 : : const char *
552 : 9 : rte_cfgfile_get_entry(struct rte_cfgfile *cfg, const char *sectionname,
553 : : const char *entryname)
554 : : {
555 : : int i;
556 : 9 : const struct rte_cfgfile_section *sect = _get_section(cfg, sectionname);
557 [ + - ]: 9 : if (sect == NULL)
558 : : return NULL;
559 [ + - ]: 18 : for (i = 0; i < sect->num_entries; i++)
560 [ + + ]: 18 : if (strncmp(sect->entries[i].name, entryname, CFG_NAME_LEN)
561 : : == 0)
562 : 9 : return sect->entries[i].value;
563 : : return NULL;
564 : : }
565 : :
566 : : int
567 : 0 : rte_cfgfile_has_entry(struct rte_cfgfile *cfg, const char *sectionname,
568 : : const char *entryname)
569 : : {
570 : 0 : return rte_cfgfile_get_entry(cfg, sectionname, entryname) != NULL;
571 : : }
|