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 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 _strip(char *str, unsigned len) 66 { 67 int newlen = len; 68 if (len == 0) 69 return 0; 70 71 if (isspace(str[len-1])) { 72 /* strip trailing whitespace */ 73 while (newlen > 0 && isspace(str[newlen - 1])) 74 str[--newlen] = '\0'; 75 } 76 77 if (isspace(str[0])) { 78 /* strip leading whitespace */ 79 int i, start = 1; 80 while (isspace(str[start]) && start < newlen) 81 start++ 82 ; /* do nothing */ 83 newlen -= start; 84 for (i = 0; i < newlen; i++) 85 str[i] = str[i+start]; 86 str[i] = '\0'; 87 } 88 return newlen; 89 } 90 91 static struct rte_cfgfile_section * 92 _get_section(struct rte_cfgfile *cfg, const char *sectionname) 93 { 94 int i; 95 96 for (i = 0; i < cfg->num_sections; i++) { 97 if (strncmp(cfg->sections[i].name, sectionname, 98 sizeof(cfg->sections[0].name)) == 0) 99 return &cfg->sections[i]; 100 } 101 return NULL; 102 } 103 104 static int 105 _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 if (section->num_entries == section->allocated_entries) { 110 struct rte_cfgfile_entry *n_entries = realloc( 111 section->entries, 112 sizeof(struct rte_cfgfile_entry) * 113 ((section->allocated_entries) + 114 CFG_ALLOC_ENTRY_BATCH)); 115 116 if (n_entries == NULL) 117 return -ENOMEM; 118 119 section->entries = n_entries; 120 section->allocated_entries += CFG_ALLOC_ENTRY_BATCH; 121 } 122 /* fill up entry fields with key name and value */ 123 struct rte_cfgfile_entry *curr_entry = 124 §ion->entries[section->num_entries]; 125 126 strlcpy(curr_entry->name, entryname, sizeof(curr_entry->name)); 127 strlcpy(curr_entry->value, entryvalue, sizeof(curr_entry->value)); 128 section->num_entries++; 129 130 return 0; 131 } 132 133 static int 134 rte_cfgfile_check_params(const struct rte_cfgfile_parameters *params) 135 { 136 unsigned int valid_comment; 137 unsigned int i; 138 139 if (!params) { 140 CFG_LOG(ERR, "missing cfgfile parameters"); 141 return -EINVAL; 142 } 143 144 valid_comment = 0; 145 for (i = 0; i < RTE_DIM(valid_comment_chars); i++) { 146 if (params->comment_character == valid_comment_chars[i]) { 147 valid_comment = 1; 148 break; 149 } 150 } 151 152 if (valid_comment == 0) { 153 CFG_LOG(ERR, "invalid comment characters %c", 154 params->comment_character); 155 return -ENOTSUP; 156 } 157 158 return 0; 159 } 160 161 struct rte_cfgfile * 162 rte_cfgfile_load(const char *filename, int flags) 163 { 164 return rte_cfgfile_load_with_params(filename, flags, 165 &default_cfgfile_params); 166 } 167 168 struct rte_cfgfile * 169 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 if (rte_cfgfile_check_params(params)) 177 return NULL; 178 179 FILE *f = fopen(filename, "r"); 180 if (f == NULL) 181 return NULL; 182 183 cfg = rte_cfgfile_create(flags); 184 185 while (fgets(buffer, sizeof(buffer), f) != NULL) { 186 char *pos; 187 size_t len = strnlen(buffer, sizeof(buffer)); 188 lineno++; 189 if ((len >= sizeof(buffer) - 1) && (buffer[len-1] != '\n')) { 190 CFG_LOG(ERR, " line %d - no \\n found on string. " 191 "Check if line too long", lineno); 192 goto error1; 193 } 194 /* skip parsing if comment character found */ 195 pos = memchr(buffer, params->comment_character, len); 196 if (pos != NULL && 197 (pos == buffer || *(pos-1) != '\\')) { 198 *pos = '\0'; 199 len = pos - buffer; 200 } 201 202 len = _strip(buffer, len); 203 /* skip lines without useful content */ 204 if (buffer[0] != '[' && memchr(buffer, '=', len) == NULL) 205 continue; 206 207 if (buffer[0] == '[') { 208 /* section heading line */ 209 char *end = memchr(buffer, ']', len); 210 if (end == NULL) { 211 CFG_LOG(ERR, 212 "line %d - no terminating ']' character found", 213 lineno); 214 goto error1; 215 } 216 *end = '\0'; 217 _strip(&buffer[1], end - &buffer[1]); 218 219 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 split[1] = memchr(buffer, '=', len); 226 if (split[1] == NULL) { 227 CFG_LOG(ERR, 228 "line %d - no '=' character found", 229 lineno); 230 goto error1; 231 } 232 *split[1] = '\0'; 233 split[1]++; 234 235 _strip(split[0], strlen(split[0])); 236 _strip(split[1], strlen(split[1])); 237 char *end = memchr(split[1], '\\', strlen(split[1])); 238 239 size_t split_len = strlen(split[1]) + 1; 240 while (end != NULL) { 241 if (*(end+1) == params->comment_character) { 242 *end = '\0'; 243 strlcat(split[1], end+1, split_len); 244 } else 245 end++; 246 end = memchr(end, '\\', strlen(end)); 247 } 248 249 if (!(flags & CFG_FLAG_EMPTY_VALUES) && 250 (*split[1] == '\0')) { 251 CFG_LOG(ERR, 252 "line %d - cannot use empty values", 253 lineno); 254 goto error1; 255 } 256 257 if (cfg->num_sections == 0) 258 goto error1; 259 260 _add_entry(&cfg->sections[cfg->num_sections - 1], 261 split[0], split[1]); 262 } 263 } 264 fclose(f); 265 return cfg; 266 error1: 267 rte_cfgfile_close(cfg); 268 fclose(f); 269 return NULL; 270 } 271 272 struct rte_cfgfile * 273 rte_cfgfile_create(int flags) 274 { 275 int i; 276 struct rte_cfgfile *cfg; 277 278 /* future proof flags usage */ 279 if (flags & ~(CFG_FLAG_GLOBAL_SECTION | CFG_FLAG_EMPTY_VALUES)) 280 return NULL; 281 282 cfg = malloc(sizeof(*cfg)); 283 284 if (cfg == NULL) 285 return NULL; 286 287 cfg->flags = flags; 288 cfg->num_sections = 0; 289 290 /* allocate first batch of sections and entries */ 291 cfg->sections = calloc(CFG_ALLOC_SECTION_BATCH, 292 sizeof(struct rte_cfgfile_section)); 293 if (cfg->sections == NULL) 294 goto error1; 295 296 cfg->allocated_sections = CFG_ALLOC_SECTION_BATCH; 297 298 for (i = 0; i < CFG_ALLOC_SECTION_BATCH; i++) { 299 cfg->sections[i].entries = calloc(CFG_ALLOC_ENTRY_BATCH, 300 sizeof(struct rte_cfgfile_entry)); 301 302 if (cfg->sections[i].entries == NULL) 303 goto error1; 304 305 cfg->sections[i].num_entries = 0; 306 cfg->sections[i].allocated_entries = CFG_ALLOC_ENTRY_BATCH; 307 } 308 309 if (flags & CFG_FLAG_GLOBAL_SECTION) 310 rte_cfgfile_add_section(cfg, "GLOBAL"); 311 312 return cfg; 313 error1: 314 if (cfg->sections != NULL) { 315 for (i = 0; i < cfg->allocated_sections; i++) { 316 if (cfg->sections[i].entries != NULL) { 317 free(cfg->sections[i].entries); 318 cfg->sections[i].entries = NULL; 319 } 320 } 321 free(cfg->sections); 322 cfg->sections = NULL; 323 } 324 free(cfg); 325 return NULL; 326 } 327 328 int 329 rte_cfgfile_add_section(struct rte_cfgfile *cfg, const char *sectionname) 330 { 331 int i; 332 333 if (cfg == NULL) 334 return -EINVAL; 335 336 if (sectionname == NULL) 337 return -EINVAL; 338 339 /* resize overall struct if we don't have room for more sections */ 340 if (cfg->num_sections == cfg->allocated_sections) { 341 342 struct rte_cfgfile_section *n_sections = 343 realloc(cfg->sections, 344 sizeof(struct rte_cfgfile_section) * 345 ((cfg->allocated_sections) + 346 CFG_ALLOC_SECTION_BATCH)); 347 348 if (n_sections == NULL) 349 return -ENOMEM; 350 351 for (i = 0; i < CFG_ALLOC_SECTION_BATCH; i++) { 352 n_sections[i + cfg->allocated_sections].num_entries = 0; 353 n_sections[i + 354 cfg->allocated_sections].allocated_entries = 0; 355 n_sections[i + cfg->allocated_sections].entries = NULL; 356 } 357 cfg->sections = n_sections; 358 cfg->allocated_sections += CFG_ALLOC_SECTION_BATCH; 359 } 360 361 strlcpy(cfg->sections[cfg->num_sections].name, sectionname, 362 sizeof(cfg->sections[0].name)); 363 cfg->sections[cfg->num_sections].num_entries = 0; 364 cfg->num_sections++; 365 366 return 0; 367 } 368 369 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 if ((cfg == NULL) || (sectionname == NULL) || (entryname == NULL) 376 || (entryvalue == NULL)) 377 return -EINVAL; 378 379 if (rte_cfgfile_has_entry(cfg, sectionname, entryname) != 0) 380 return -EEXIST; 381 382 /* search for section pointer by sectionname */ 383 struct rte_cfgfile_section *curr_section = _get_section(cfg, 384 sectionname); 385 if (curr_section == NULL) 386 return -EINVAL; 387 388 ret = _add_entry(curr_section, entryname, entryvalue); 389 390 return ret; 391 } 392 393 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 if ((cfg == NULL) || (sectionname == NULL) || (entryname == NULL)) 399 return -EINVAL; 400 401 /* search for section pointer by sectionname */ 402 struct rte_cfgfile_section *curr_section = _get_section(cfg, 403 sectionname); 404 if (curr_section == NULL) 405 return -EINVAL; 406 407 if (entryvalue == NULL) 408 entryvalue = ""; 409 410 for (i = 0; i < curr_section->num_entries; i++) 411 if (!strcmp(curr_section->entries[i].name, entryname)) { 412 strlcpy(curr_section->entries[i].value, entryvalue, 413 sizeof(curr_section->entries[i].value)); 414 return 0; 415 } 416 417 CFG_LOG(ERR, "entry name doesn't exist"); 418 return -EINVAL; 419 } 420 421 int rte_cfgfile_save(struct rte_cfgfile *cfg, const char *filename) 422 { 423 int i, j; 424 425 if ((cfg == NULL) || (filename == NULL)) 426 return -EINVAL; 427 428 FILE *f = fopen(filename, "w"); 429 430 if (f == NULL) 431 return -EINVAL; 432 433 for (i = 0; i < cfg->num_sections; i++) { 434 fprintf(f, "[%s]\n", cfg->sections[i].name); 435 436 for (j = 0; j < cfg->sections[i].num_entries; j++) { 437 fprintf(f, "%s=%s\n", 438 cfg->sections[i].entries[j].name, 439 cfg->sections[i].entries[j].value); 440 } 441 } 442 return fclose(f); 443 } 444 445 int rte_cfgfile_close(struct rte_cfgfile *cfg) 446 { 447 int i; 448 449 if (cfg == NULL) 450 return -1; 451 452 if (cfg->sections != NULL) { 453 for (i = 0; i < cfg->allocated_sections; i++) { 454 if (cfg->sections[i].entries != NULL) { 455 free(cfg->sections[i].entries); 456 cfg->sections[i].entries = NULL; 457 } 458 } 459 free(cfg->sections); 460 cfg->sections = NULL; 461 } 462 free(cfg); 463 cfg = NULL; 464 465 return 0; 466 } 467 468 int 469 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 for (i = 0; i < cfg->num_sections; i++) { 475 if (strncmp(cfg->sections[i].name, sectionname, length) == 0) 476 num_sections++; 477 } 478 return num_sections; 479 } 480 481 int 482 rte_cfgfile_sections(struct rte_cfgfile *cfg, char *sections[], 483 int max_sections) 484 { 485 int i; 486 487 for (i = 0; i < cfg->num_sections && i < max_sections; i++) 488 strlcpy(sections[i], cfg->sections[i].name, CFG_NAME_LEN); 489 490 return i; 491 } 492 493 int 494 rte_cfgfile_has_section(struct rte_cfgfile *cfg, const char *sectionname) 495 { 496 return _get_section(cfg, sectionname) != NULL; 497 } 498 499 int 500 rte_cfgfile_section_num_entries(struct rte_cfgfile *cfg, 501 const char *sectionname) 502 { 503 const struct rte_cfgfile_section *s = _get_section(cfg, sectionname); 504 if (s == NULL) 505 return -1; 506 return s->num_entries; 507 } 508 509 int 510 rte_cfgfile_section_num_entries_by_index(struct rte_cfgfile *cfg, 511 char *sectionname, int index) 512 { 513 if (index < 0 || index >= cfg->num_sections) 514 return -1; 515 516 const struct rte_cfgfile_section *sect = &(cfg->sections[index]); 517 518 strlcpy(sectionname, sect->name, CFG_NAME_LEN); 519 return sect->num_entries; 520 } 521 int 522 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 const struct rte_cfgfile_section *sect = _get_section(cfg, sectionname); 527 if (sect == NULL) 528 return -1; 529 for (i = 0; i < max_entries && i < sect->num_entries; i++) 530 entries[i] = sect->entries[i]; 531 return i; 532 } 533 534 int 535 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 if (index < 0 || index >= cfg->num_sections) 543 return -1; 544 sect = &cfg->sections[index]; 545 strlcpy(sectionname, sect->name, CFG_NAME_LEN); 546 for (i = 0; i < max_entries && i < sect->num_entries; i++) 547 entries[i] = sect->entries[i]; 548 return i; 549 } 550 551 const char * 552 rte_cfgfile_get_entry(struct rte_cfgfile *cfg, const char *sectionname, 553 const char *entryname) 554 { 555 int i; 556 const struct rte_cfgfile_section *sect = _get_section(cfg, sectionname); 557 if (sect == NULL) 558 return NULL; 559 for (i = 0; i < sect->num_entries; i++) 560 if (strncmp(sect->entries[i].name, entryname, CFG_NAME_LEN) 561 == 0) 562 return sect->entries[i].value; 563 return NULL; 564 } 565 566 int 567 rte_cfgfile_has_entry(struct rte_cfgfile *cfg, const char *sectionname, 568 const char *entryname) 569 { 570 return rte_cfgfile_get_entry(cfg, sectionname, entryname) != NULL; 571 } 572