1 /* 2 INI LIBRARY 3 4 Reading configuration from INI file 5 and storing as a collection. 6 7 Copyright (C) Dmitri Pal <dpal@redhat.com> 2009 8 9 INI Library is free software: you can redistribute it and/or modify 10 it under the terms of the GNU Lesser General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 INI Library is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU Lesser General Public License for more details. 18 19 You should have received a copy of the GNU Lesser General Public License 20 along with INI Library. If not, see <http://www.gnu.org/licenses/>. 21 */ 22 23 #define _GNU_SOURCE 24 #include <stdio.h> 25 #include <errno.h> 26 #include <string.h> 27 #include <ctype.h> 28 #include <stdlib.h> 29 #include <locale.h> 30 #include <fcntl.h> 31 #include "config.h" 32 /* For error text */ 33 #include <libintl.h> 34 #define _(String) gettext (String) 35 /* INI file is used as a collection */ 36 #include "collection.h" 37 #include "collection_tools.h" 38 #include "trace.h" 39 #include "ini_config.h" 40 41 #define NAME_OVERHEAD 10 42 43 #define SLASH "/" 44 45 #define EXCLUDE_EMPTY 0 46 #define INCLUDE_EMPTY 1 47 48 /* Name of the special collection used to store parsing errors */ 49 #define FILE_ERROR_SET "ini_file_error_set" 50 51 /* Text error strings used when errors are printed out */ 52 #define WARNING_TXT _("Warning") 53 #define ERROR_TXT _("Error") 54 /* For parse errors */ 55 #define WRONG_COLLECTION _("Passed in list is not a list of parse errors.\n") 56 #define FAILED_TO_PROCCESS _("Internal Error. Failed to process error list.\n") 57 #define ERROR_HEADER _("Parsing errors and warnings in file: %s\n") 58 /* For grammar errors */ 59 #define WRONG_GRAMMAR _("Passed in list is not a list of grammar errors.\n") 60 #define FAILED_TO_PROC_G _("Internal Error. Failed to process list of grammar errors.\n") 61 #define ERROR_HEADER_G _("Logical errors and warnings in file: %s\n") 62 /* For validation errors */ 63 #define WRONG_VALIDATION _("Passed in list is not a list of validation errors.\n") 64 #define FAILED_TO_PROC_V _("Internal Error. Failed to process list of validation errors.\n") 65 #define ERROR_HEADER_V _("Validation errors and warnings in file: %s\n") 66 67 #define LINE_FORMAT _("%s (%d) on line %d: %s\n") 68 69 70 /* Codes that parsing function can return */ 71 #define RET_PAIR 0 72 #define RET_COMMENT 1 73 #define RET_SECTION 2 74 #define RET_INVALID 3 75 #define RET_EMPTY 4 76 #define RET_EOF 5 77 #define RET_ERROR 6 78 79 #define INI_ERROR "errors" 80 #define INI_ERROR_NAME "errname" 81 82 83 /* Internal sizes. MAX_KEY is defined in config.h */ 84 #define MAX_VALUE PATH_MAX 85 #define BUFFER_SIZE MAX_KEY + MAX_VALUE + 3 86 87 /* Internally used functions */ 88 static int config_with_lines(const char *application, 89 FILE *config_file, 90 const char *config_source, 91 struct collection_item **ini_config, 92 int error_level, 93 struct collection_item **error_list, 94 struct collection_item **lines); 95 96 97 98 /* Different error string functions can be passed as callbacks */ 99 typedef const char * (*error_fn)(int error); 100 101 /* Function to return parsing error */ 102 const char *parsing_error_str(int parsing_error) 103 { 104 const char *placeholder= _("Unknown pasing error."); 105 const char *str_error[] = { _("Data is too long."), 106 _("No closing bracket."), 107 _("Section name is missing."), 108 _("Section name is too long."), 109 _("Equal sign is missing."), 110 _("Property name is missing."), 111 _("Property name is too long.") 112 }; 113 114 /* Check the range */ 115 if ((parsing_error < 1) || (parsing_error > ERR_MAXPARSE)) 116 return placeholder; 117 else 118 return str_error[parsing_error-1]; 119 } 120 121 /* Function to return grammar error. 122 * This function is currently not used. 123 * It is planned to be used by the INI 124 * file grammar parser. 125 */ 126 const char *grammar_error_str(int grammar_error) 127 { 128 const char *placeholder= _("Unknown grammar error."); 129 /* THIS IS A TEMPORARY PLACEHOLDER !!!! */ 130 const char *str_error[] = { _(""), 131 _(""), 132 _(""), 133 _(""), 134 _(""), 135 _(""), 136 _("") 137 }; 138 139 /* Check the range */ 140 if ((grammar_error < 1) || (grammar_error > ERR_MAXGRAMMAR)) 141 return placeholder; 142 else 143 return str_error[grammar_error-1]; 144 } 145 146 /* Function to return validation error. 147 * This function is currently not used. 148 * It is planned to be used by the INI 149 * file grammar validator. 150 */ 151 const char *validation_error_str(int validation_error) 152 { 153 const char *placeholder= _("Unknown validation error."); 154 /* THIS IS A TEMPORARY PLACEHOLDER !!!! */ 155 const char *str_error[] = { _(""), 156 _(""), 157 _(""), 158 _(""), 159 _(""), 160 _(""), 161 _("") 162 }; 163 164 /* Check the range */
Event dead_error_condition: On this path, the condition "validation_error > 0" cannot be false. Event at_least: After this line, the value of "validation_error" is at least 1. Event new_values: Noticing condition "validation_error < 1". Also see events: [dead_error_line] | |
165 if ((validation_error < 1) || (validation_error > ERR_MAXVALID)) 166 return placeholder; 167 else
Event dead_error_line: Execution cannot reach this statement "return str_error[validation...".Also see events: [dead_error_condition][at_least][new_values] | |
168 return str_error[validation_error-1]; 169 } 170 171 172 /* Internal function to read line from INI file */ 173 int read_line(FILE *file, 174 char *buf, 175 int read_size, 176 char **key, 177 char **value, 178 int *length, 179 int *ext_error); 180 181 /***************************************************************************/ 182 /* Function to read single ini file and pupulate 183 * the provided collection with subcollcetions from the file */ 184 static int ini_to_collection(FILE *file, 185 const char *config_filename, 186 struct collection_item *ini_config, 187 int error_level, 188 struct collection_item **error_list, 189 struct collection_item **lines) 190 { 191 int error; 192 int status; 193 int section_count = 0; 194 char *key = NULL; 195 char *value = NULL; 196 struct collection_item *current_section = NULL; 197 int length; 198 int ext_err = -1; 199 struct parse_error pe; 200 int line = 0; 201 int created = 0; 202 char buf[BUFFER_SIZE+1]; 203 204 205 TRACE_FLOW_STRING("ini_to_collection", "Entry"); 206 207 if (file == NULL) { 208 TRACE_ERROR_NUMBER("No file handle", EINVAL); 209 return EINVAL; 210 } 211 212 /* Open the collection of errors */ 213 if (error_list != NULL) { 214 *error_list = NULL; 215 error = col_create_collection(error_list, INI_ERROR, COL_CLASS_INI_PERROR); 216 if (error) { 217 TRACE_ERROR_NUMBER("Failed to create error collection", error); 218 fclose(file); 219 return error; 220 } 221 /* Add file name as the first item */ 222 error = col_add_str_property(*error_list, NULL, INI_ERROR_NAME, config_filename, 0); 223 if (error) { 224 TRACE_ERROR_NUMBER("Failed to and name to collection", error); 225 fclose(file); 226 col_destroy_collection(*error_list); 227 return error; 228 } 229 created = 1; 230 } 231 232 /* Read file lines */ 233 while (1) { 234 /* Always read one less than the buffer */ 235 status = read_line(file, buf, BUFFER_SIZE+1, &key, &value, &length, &ext_err); 236 if (status == RET_EOF) break; 237 238 line++; 239 240 switch (status) { 241 case RET_PAIR: 242 /* Add line to the collection of lines. 243 * It is pretty safe in this case to just type cast the value to 244 * int32_t since it is unrealistic that ini file will ever have 245 * so many lines. 246 */ 247 if (lines) { 248 error = col_add_int_property(*lines, NULL, key, (int32_t)line); 249 if (error) { 250 TRACE_ERROR_NUMBER("Failed to add line to line collection", error); 251 fclose(file); 252 col_destroy_collection(current_section); 253 if (created) { 254 col_destroy_collection(*error_list); 255 *error_list = NULL; 256 } 257 return error; 258 } 259 } 260 261 /* Do we have a section at the top of the file ? */ 262 if (section_count == 0) { 263 /* Check if collection already exists */ 264 error = col_get_collection_reference(ini_config, ¤t_section, 265 INI_DEFAULT_SECTION); 266 if (error != EOK) { 267 /* Create default collection */ 268 if ((error = col_create_collection(¤t_section, 269 INI_DEFAULT_SECTION, 270 COL_CLASS_INI_SECTION)) || 271 (error = col_add_collection_to_collection(ini_config, 272 NULL,NULL, 273 current_section, 274 COL_ADD_MODE_REFERENCE))) { 275 TRACE_ERROR_NUMBER("Failed to create collection", error); 276 fclose(file); 277 col_destroy_collection(current_section); 278 if (created) { 279 col_destroy_collection(*error_list); 280 *error_list = NULL; 281 } 282 return error; 283 } 284 } 285 section_count++; 286 } 287 288 /* Put value into the collection */ 289 error = col_insert_str_property(current_section, 290 NULL, 291 COL_DSP_END, 292 NULL, 293 0, 294 COL_INSERT_DUPOVER, 295 key, 296 value, 297 length); 298 if (error != EOK) { 299 TRACE_ERROR_NUMBER("Failed to add pair to collection", error); 300 fclose(file); 301 col_destroy_collection(current_section); 302 if (created) { 303 col_destroy_collection(*error_list); 304 *error_list = NULL; 305 } 306 return error; 307 } 308 break; 309 310 case RET_SECTION: 311 /* Add line to the collection of lines */ 312 if (lines) { 313 /* For easier search make line numbers for the sections negative. 314 * This would allow differentiating sections and attributes. 315 * It is pretty safe in this case to just type cast the value to 316 * int32_t since it is unrealistic that ini file will ever have 317 * so many lines. 318 */ 319 error = col_add_int_property(*lines, NULL, key, (int32_t)(-1 * line)); 320 if (error) { 321 TRACE_ERROR_NUMBER("Failed to add line to line collection", error); 322 fclose(file); 323 col_destroy_collection(current_section); 324 if (created) { 325 col_destroy_collection(*error_list); 326 *error_list = NULL; 327 } 328 return error; 329 } 330 } 331 332 /* Read a new section */ 333 col_destroy_collection(current_section); 334 current_section = NULL; 335 336 error = col_get_collection_reference(ini_config, ¤t_section, key); 337 if (error != EOK) { 338 /* Create default collection */ 339 if ((error = col_create_collection(¤t_section, key, 340 COL_CLASS_INI_SECTION)) || 341 (error = col_add_collection_to_collection(ini_config, 342 NULL, NULL, 343 current_section, 344 COL_ADD_MODE_REFERENCE))) { 345 TRACE_ERROR_NUMBER("Failed to add collection", error); 346 fclose(file); 347 col_destroy_collection(current_section); 348 if (created) { 349 col_destroy_collection(*error_list); 350 *error_list = NULL; 351 } 352 return error; 353 } 354 } 355 section_count++; 356 break; 357 358 case RET_EMPTY: 359 TRACE_INFO_STRING("Empty string", ""); 360 break; 361 362 case RET_COMMENT: 363 TRACE_INFO_STRING("Comment", ""); 364 break; 365 366 case RET_ERROR: 367 pe.line = line; 368 pe.error = ext_err; 369 error = col_add_binary_property(*error_list, NULL, 370 ERROR_TXT, &pe, sizeof(pe)); 371 if (error) { 372 TRACE_ERROR_NUMBER("Failed to add error to collection", error); 373 fclose(file); 374 col_destroy_collection(current_section); 375 if (created) { 376 col_destroy_collection(*error_list); 377 *error_list = NULL; 378 } 379 return error; 380 } 381 /* Exit if there was an error parsing file */ 382 if (error_level != INI_STOP_ON_NONE) { 383 TRACE_ERROR_STRING("Invalid format of the file", ""); 384 col_destroy_collection(current_section); 385 fclose(file); 386 return EIO; 387 } 388 break; 389 390 case RET_INVALID: 391 default: 392 pe.line = line; 393 pe.error = ext_err; 394 error = col_add_binary_property(*error_list, NULL, 395 WARNING_TXT, &pe, sizeof(pe)); 396 if (error) { 397 TRACE_ERROR_NUMBER("Failed to add warning to collection", error); 398 fclose(file); 399 col_destroy_collection(current_section); 400 if (created) { 401 col_destroy_collection(*error_list); 402 *error_list = NULL; 403 } 404 return error; 405 } 406 /* Exit if we are told to exit on warnings */ 407 if (error_level == INI_STOP_ON_ANY) { 408 TRACE_ERROR_STRING("Invalid format of the file", ""); 409 if (created) col_destroy_collection(current_section); 410 fclose(file); 411 return EIO; 412 } 413 TRACE_ERROR_STRING("Invalid string", ""); 414 break; 415 } 416 ext_err = -1; 417 } 418 419 /* Close file */ 420 fclose(file); 421 422 COL_DEBUG_COLLECTION(ini_config); 423 424 col_destroy_collection(current_section); 425 426 COL_DEBUG_COLLECTION(ini_config); 427 428 TRACE_FLOW_STRING("ini_to_collection", "Success Exit"); 429 430 return EOK; 431 } 432 433 /*********************************************************************/ 434 /* Function to free configuration */ 435 void free_ini_config(struct collection_item *ini_config) 436 { 437 TRACE_FLOW_STRING("free_ini_config", "Entry"); 438 col_destroy_collection(ini_config); 439 TRACE_FLOW_STRING("free_ini_config", "Exit"); 440 } 441 442 /* Function to free configuration error list */ 443 void free_ini_config_errors(struct collection_item *error_set) 444 { 445 TRACE_FLOW_STRING("free_ini_config_errors", "Entry"); 446 col_destroy_collection(error_set); 447 TRACE_FLOW_STRING("free_ini_config_errors", "Exit"); 448 } 449 450 /* Function to free configuration lines list */ 451 void free_ini_config_lines(struct collection_item *lines) 452 { 453 TRACE_FLOW_STRING("free_ini_config_lines", "Entry"); 454 col_destroy_collection(lines); 455 TRACE_FLOW_STRING("free_ini_config_lines", "Exit"); 456 } 457 458 459 /* Read configuration information from a file */ 460 int config_from_file(const char *application, 461 const char *config_filename, 462 struct collection_item **ini_config, 463 int error_level, 464 struct collection_item **error_list) 465 { 466 int error; 467 468 TRACE_FLOW_STRING("config_from_file", "Entry"); 469 error = config_from_file_with_lines(application, 470 config_filename, 471 ini_config, 472 error_level, 473 error_list, 474 NULL); 475 TRACE_FLOW_NUMBER("config_from_file. Returns", error); 476 return error; 477 } 478 479 /* Read configuration information from a file descriptor */ 480 int config_from_fd(const char *application, 481 int fd, 482 const char *config_source, 483 struct collection_item **ini_config, 484 int error_level, 485 struct collection_item **error_list) 486 { 487 int error; 488 489 TRACE_FLOW_STRING("config_from_fd", "Entry"); 490 error = config_from_fd_with_lines(application, fd, config_source, 491 ini_config, error_level, 492 error_list, NULL); 493 TRACE_FLOW_NUMBER("config_from_fd. Returns", error); 494 return error; 495 } 496 497 /* Function to read the ini file and have a collection 498 * of which item appers on which line 499 */ 500 int config_from_file_with_lines(const char *application, 501 const char *config_filename, 502 struct collection_item **ini_config, 503 int error_level, 504 struct collection_item **error_list, 505 struct collection_item **lines) 506 { 507 int error = EOK; 508 FILE *config_file = NULL; 509 510 TRACE_FLOW_STRING("config_from_file_with_lines", "Entry"); 511 512 config_file = fopen(config_filename, "r"); 513 if(!config_file) { 514 error = errno; 515 TRACE_ERROR_NUMBER("Failed to open file", error); 516 return error; 517 } 518 519 error = config_with_lines(application, config_file, 520 config_filename, ini_config, 521 error_level, error_list, 522 lines); 523 TRACE_FLOW_NUMBER("config_from_file_with_lines. Returns", error); 524 return error; 525 } 526 527 /* Function to read the ini file and have a collection 528 * of which item appers on which line 529 */ 530 int config_from_fd_with_lines(const char *application, 531 int fd, 532 const char *config_source, 533 struct collection_item **ini_config, 534 int error_level, 535 struct collection_item **error_list, 536 struct collection_item **lines) 537 { 538 int error = EOK; 539 FILE *config_file; 540 541 TRACE_FLOW_STRING("config_from_fd_with_lines", "Entry"); 542 543 config_file = fdopen(fd, "r"); 544 if (!config_file) { 545 error = errno; 546 TRACE_ERROR_NUMBER("Failed to dup file", error); 547 return error; 548 } 549 550 error = config_with_lines(application, config_file, 551 config_source, ini_config, 552 error_level, error_list, 553 lines); 554 TRACE_FLOW_NUMBER("config_from_fd_with_lines. Returns", error); 555 556 return error; 557 } 558 559 /* Low level function that prepares the collection 560 * and calls parser. 561 */ 562 static int config_with_lines(const char *application, 563 FILE *config_file, 564 const char *config_source, 565 struct collection_item **ini_config, 566 int error_level, 567 struct collection_item **error_list, 568 struct collection_item **lines) 569 { 570 int error; 571 int created = 0; 572 int created_lines = 0; 573 574 TRACE_FLOW_STRING("config_from_file", "Entry"); 575 576 if ((ini_config == NULL) || 577 (application == NULL)) { 578 TRACE_ERROR_NUMBER("Invalid argument", EINVAL); 579 return EINVAL; 580 } 581 582 /* Create collection if needed */ 583 if (*ini_config == NULL) { 584 error = col_create_collection(ini_config, 585 application, 586 COL_CLASS_INI_CONFIG); 587 if (error != EOK) { 588 TRACE_ERROR_NUMBER("Failed to create collection", error); 589 return error; 590 } 591 created = 1; 592 } 593 /* Is the collection of the right class? */ 594 else if (col_is_of_class(*ini_config, COL_CLASS_INI_CONFIG)) { 595 TRACE_ERROR_NUMBER("Wrong collection type", EINVAL); 596 return EINVAL; 597 } 598 599 600 /* Create collection if needed */ 601 if (lines) { 602 603 /* Make sure that the lines collection is empty */ 604 if (*lines) { 605 TRACE_ERROR_NUMBER("Collection of lines is not empty", EINVAL); 606 if (created) { 607 col_destroy_collection(*ini_config); 608 *ini_config = NULL; 609 } 610 return EINVAL; 611 } 612 613 error = col_create_collection(lines, 614 application, 615 COL_CLASS_INI_LINES); 616 if (error != EOK) { 617 TRACE_ERROR_NUMBER("Failed to create collection", error); 618 if (created) { 619 col_destroy_collection(*ini_config); 620 *ini_config = NULL; 621 } 622 return error; 623 } 624 created_lines = 1; 625 } 626 627 /* Do the actual work */ 628 error = ini_to_collection(config_file, config_source, 629 *ini_config, error_level, 630 error_list, lines); 631 /* In case of error when we created collection - delete it */ 632 if (error && created) { 633 col_destroy_collection(*ini_config); 634 *ini_config = NULL; 635 } 636 /* Also create collection of lines if we created it */ 637 if (error && created_lines) { 638 col_destroy_collection(*lines); 639 *lines = NULL; 640 } 641 642 TRACE_FLOW_NUMBER("config_from_file. Returns", error); 643 return error; 644 } 645 646 /* Special wrapper around the inernal parser 647 * to open the file first. 648 * Used in conf_for_app function. 649 */ 650 static int ini_to_col_from_file(const char *config_filename, 651 struct collection_item *ini_config, 652 int error_level, 653 struct collection_item **error_list, 654 struct collection_item **lines) 655 { 656 int error = EOK; 657 FILE *config_file = NULL; 658 659 TRACE_FLOW_STRING("ini_to_col_from_file", "Entry"); 660 661 config_file = fopen(config_filename, "r"); 662 if(!config_file) { 663 error = errno; 664 TRACE_ERROR_NUMBER("ini_to_col_from_file. Returns", error); 665 return ENOENT; 666 } 667 668 error = ini_to_collection(config_file, 669 config_filename, 670 ini_config, 671 error_level, 672 error_list, 673 lines); 674 TRACE_FLOW_NUMBER("ini_to_col_from_file. Returns", error); 675 return error; 676 } 677 678 679 /* Read default config file and then overwrite it with a specific one 680 * from the directory */ 681 int config_for_app(const char *application, 682 const char *config_file, 683 const char *config_dir, 684 struct collection_item **ini_config, 685 int error_level, 686 struct collection_item **error_set) 687 { 688 int error = EOK; 689 char *file_name; 690 struct collection_item *error_list_common = NULL; 691 struct collection_item *error_list_specific = NULL; 692 struct collection_item **pass_common = NULL; 693 struct collection_item **pass_specific = NULL; 694 int created = 0; 695 int tried = 0; 696 int noents = 0; 697 698 TRACE_FLOW_STRING("config_to_collection", "Entry"); 699 700 if (ini_config == NULL) { 701 TRACE_ERROR_NUMBER("Invalid parameter", EINVAL); 702 return EINVAL; 703 } 704 705 if ((config_file == NULL) && (config_dir == NULL)) { 706 TRACE_ERROR_NUMBER("Noop call of the function is invalid", EINVAL); 707 return EINVAL; 708 } 709 710 /* Prepare error collection pointers */ 711 if (error_set != NULL) { 712 TRACE_INFO_STRING("Error set is not NULL", "preparing error set"); 713 pass_common = &error_list_common; 714 pass_specific = &error_list_specific; 715 *error_set = NULL; 716 /* Construct the overarching error collection */ 717 error = col_create_collection(error_set, 718 FILE_ERROR_SET, 719 COL_CLASS_INI_PESET); 720 if (error != EOK) { 721 TRACE_ERROR_NUMBER("Failed to create collection", error); 722 return error; 723 } 724 } 725 else { 726 TRACE_INFO_STRING("No error set. Errors will not be captured", ""); 727 pass_common = NULL; 728 pass_specific = NULL; 729 } 730 731 /* Create collection if needed */ 732 if (*ini_config == NULL) { 733 TRACE_INFO_STRING("New config collection. Allocate.", ""); 734 error = col_create_collection(ini_config, 735 application, 736 COL_CLASS_INI_CONFIG); 737 if (error != EOK) { 738 TRACE_ERROR_NUMBER("Failed to create collection", error); 739 if (error_set) { 740 col_destroy_collection(*error_set); 741 *error_set = NULL; 742 } 743 return error; 744 } 745 created = 1; 746 } 747 /* Is the collection of the right class? */ 748 else if (col_is_of_class(*ini_config, COL_CLASS_INI_CONFIG)) { 749 TRACE_ERROR_NUMBER("Wrong collection type", EINVAL); 750 return EINVAL; 751 } 752 753 /* Read master file */ 754 if (config_file != NULL) { 755 TRACE_INFO_STRING("Reading master file:", config_file); 756 error = ini_to_col_from_file(config_file, *ini_config, 757 error_level, pass_common, NULL); 758 tried++; 759 /* ENOENT and EOK are Ok */ 760 if (error) { 761 if (error != ENOENT) { 762 TRACE_ERROR_NUMBER("Failed to read master file", error); 763 /* In case of error when we created collection - delete it */ 764 if(error && created) { 765 col_destroy_collection(*ini_config); 766 *ini_config = NULL; 767 } 768 /* We do not clear the error_set here */ 769 return error; 770 } 771 else noents++; 772 } 773 /* Add error results if any to the overarching error collection */ 774 if ((pass_common != NULL) && (*pass_common != NULL)) { 775 TRACE_INFO_STRING("Process errors resulting from file:", config_file); 776 error = col_add_collection_to_collection(*error_set, NULL, NULL, 777 *pass_common, 778 COL_ADD_MODE_EMBED); 779 if (error) { 780 if (created) { 781 col_destroy_collection(*ini_config); 782 *ini_config = NULL; 783 } 784 if (error_set) { 785 col_destroy_collection(*error_set); 786 *error_set = NULL; 787 } 788 TRACE_ERROR_NUMBER("Failed to add error collection to another error collection", error); 789 return error; 790 } 791 } 792 } 793 794 if (config_dir != NULL) { 795 /* Get specific application file */ 796 file_name = malloc(strlen(config_dir) + strlen(application) + NAME_OVERHEAD); 797 if (file_name == NULL) { 798 error = ENOMEM; 799 TRACE_ERROR_NUMBER("Failed to allocate memory for file name", error); 800 /* In case of error when we created collection - delete it */ 801 if(created) { 802 col_destroy_collection(*ini_config); 803 *ini_config = NULL; 804 } 805 if (error_set) { 806 col_destroy_collection(*error_set); 807 *error_set = NULL; 808 } 809 return error; 810 } 811 812 sprintf(file_name, "%s%s%s.conf", config_dir, SLASH, application); 813 TRACE_INFO_STRING("Opening file:", file_name); 814 /* Read specific file */ 815 error = ini_to_col_from_file(file_name, *ini_config, 816 error_level, pass_specific, NULL); 817 tried++; 818 free(file_name); 819 /* ENOENT and EOK are Ok */ 820 if (error) { 821 if (error != ENOENT) { 822 TRACE_ERROR_NUMBER("Failed to read specific application file", error); 823 /* In case of error when we created collection - delete it */ 824 if (error && created) { 825 col_destroy_collection(*ini_config); 826 *ini_config = NULL; 827 } 828 /* We do not clear the error_set here */ 829 return error; 830 } 831 else noents++; 832 } 833 /* Add error results if any to the overarching error collection */ 834 if ((pass_specific != NULL) && (*pass_specific != NULL)) { 835 error = col_add_collection_to_collection(*error_set, NULL, NULL, 836 *pass_specific, 837 COL_ADD_MODE_EMBED); 838 if (error) { 839 if (created) { 840 col_destroy_collection(*ini_config); 841 *ini_config = NULL; 842 } 843 if (error_set) { 844 col_destroy_collection(*error_set); 845 *error_set = NULL; 846 } 847 TRACE_ERROR_NUMBER("Failed to add error collection to another error collection", error); 848 return error; 849 } 850 } 851 } 852 853 /* If we failed to read or access file as many 854 * times as we tried and we told to stop on any errors 855 * we should report an error. 856 */ 857 TRACE_INFO_NUMBER("Tried:", tried); 858 TRACE_INFO_NUMBER("Noents:", noents); 859 860 if ((tried == noents) && (error_level == INI_STOP_ON_ANY)) { 861 TRACE_ERROR_NUMBER("Fail to read or access all the files tried", ENOENT); 862 if (created) { 863 col_destroy_collection(*ini_config); 864 *ini_config = NULL; 865 } 866 if (error_set) { 867 col_destroy_collection(*error_set); 868 *error_set = NULL; 869 } 870 return ENOENT; 871 } 872 873 TRACE_FLOW_STRING("config_to_collection", "Exit"); 874 return EOK; 875 } 876 877 /* Reads a line from the file */ 878 int read_line(FILE *file, 879 char *buf, 880 int read_size, 881 char **key, char **value, 882 int *length, 883 int *ext_error) 884 { 885 886 char *res; 887 int len; 888 char *buffer; 889 int i; 890 char *eq; 891 892 TRACE_FLOW_STRING("read_line", "Entry"); 893 894 *ext_error = 0; 895 896 buffer = buf; 897 898 /* Get data from file */ 899 res = fgets(buffer, read_size - 1, file); 900 if (res == NULL) { 901 TRACE_ERROR_STRING("Read nothing", ""); 902 return RET_EOF; 903 } 904 905 /* Make sure the buffer is NULL terminated */ 906 buffer[read_size - 1] = '\0'; 907 908 len = strlen(buffer); 909 if (len == 0) { 910 TRACE_ERROR_STRING("Nothing was read.", ""); 911 return RET_EMPTY; 912 } 913 914 /* Added \r just in case we deal with Windows in future */ 915 if ((buffer[len - 1] != '\n') && (buffer[len - 1] != '\r')) { 916 TRACE_ERROR_STRING("String it too big!", ""); 917 *ext_error = ERR_LONGDATA; 918 return RET_ERROR; 919 } 920 921 /* Ingnore comments */ 922 if ((*buffer == ';') || (*buffer == '#')) { 923 TRACE_FLOW_STRING("Comment", buf); 924 return RET_COMMENT; 925 } 926 927 TRACE_INFO_STRING("BUFFER before trimming:", buffer); 928 929 /* Trucate trailing spaces and CRs */ 930 /* Make sure not to step before the beginning */ 931 while (len && isspace(buffer[len - 1])) { 932 buffer[len - 1] = '\0'; 933 len--; 934 } 935 936 TRACE_INFO_STRING("BUFFER after trimming trailing spaces:", buffer); 937 938 /* Trucate leading spaces */ 939 while (isspace(*buffer)) { 940 buffer++; 941 len--; 942 } 943 944 TRACE_INFO_STRING("BUFFER after trimming leading spaces:", buffer); 945 TRACE_INFO_NUMBER("BUFFER length:", len); 946 947 /* Empty line */ 948 if (len == 0) { 949 TRACE_FLOW_STRING("Empty line", buf); 950 return RET_EMPTY; 951 } 952 953 /* Section */ 954 if (*buffer == '[') { 955 if (buffer[len-1] != ']') { 956 TRACE_ERROR_STRING("Invalid format for section", buf); 957 *ext_error = ERR_NOCLOSESEC; 958 return RET_ERROR; 959 } 960 buffer++; 961 len--; 962 while (isspace(*buffer)) { 963 buffer++; 964 len--; 965 } 966 if (len == 0) { 967 TRACE_ERROR_STRING("Invalid format for section", buf); 968 *ext_error = ERR_NOSECTION; 969 return RET_ERROR; 970 } 971 972 buffer[len - 1] = '\0'; 973 len--; 974 while (isspace(buffer[len - 1])) { 975 buffer[len - 1] = '\0'; 976 len--; 977 } 978 if (len >= MAX_KEY) { 979 TRACE_ERROR_STRING("Section name is too long", buf); 980 *ext_error = ERR_SECTIONLONG; 981 return RET_ERROR; 982 } 983 984 *key = buffer; 985 return RET_SECTION; 986 } 987 988 /* Assume we are dealing with the K-V here */ 989 /* Find "=" */ 990 eq = strchr(buffer, '='); 991 if (eq == NULL) { 992 TRACE_ERROR_STRING("No equal sign", buf); 993 *ext_error = ERR_NOEQUAL; 994 return RET_INVALID; 995 } 996 997 len -= eq-buffer; 998 999 /* Strip spaces around "=" */ 1000 i = eq - buffer - 1; 1001 while ((i >= 0) && isspace(buffer[i])) i--; 1002 if (i < 0) { 1003 TRACE_ERROR_STRING("No key", buf); 1004 *ext_error = ERR_NOKEY; 1005 return RET_INVALID; 1006 } 1007 1008 /* Copy key into provided buffer */ 1009 if(i >= MAX_KEY) { 1010 TRACE_ERROR_STRING("Section name is too long", buf); 1011 *ext_error = ERR_LONGKEY; 1012 return RET_INVALID; 1013 } 1014 *key = buffer; 1015 buffer[i + 1] = '\0'; 1016 TRACE_INFO_STRING("KEY:", *key); 1017 1018 eq++; 1019 len--; 1020 while (isspace(*eq)) { 1021 eq++; 1022 len--; 1023 } 1024 1025 *value = eq; 1026 /* Make sure we include trailing 0 into data */ 1027 *length = len + 1; 1028 1029 TRACE_INFO_STRING("VALUE:", *value); 1030 TRACE_INFO_NUMBER("LENGTH:", *length); 1031 1032 TRACE_FLOW_STRING("read_line", "Exit"); 1033 return RET_PAIR; 1034 } 1035 1036 1037 1038 /* Internal function that prints errors */ 1039 static void print_error_list(FILE *file, 1040 struct collection_item *error_list, 1041 int cclass, 1042 char *wrong_col_error, 1043 char *failed_to_process, 1044 char *error_header, 1045 char *line_format, 1046 error_fn error_function) 1047 { 1048 struct collection_iterator *iterator; 1049 int error; 1050 struct collection_item *item = NULL; 1051 struct parse_error *pe; 1052 unsigned int count; 1053 1054 TRACE_FLOW_STRING("print_error_list", "Entry"); 1055 1056 /* If we have something to print print it */ 1057 if (error_list == NULL) { 1058 TRACE_ERROR_STRING("No error list",""); 1059 return; 1060 } 1061 1062 /* Make sure we go the right collection */ 1063 if (!col_is_of_class(error_list, cclass)) { 1064 TRACE_ERROR_STRING("Wrong collection class:", wrong_col_error); 1065 fprintf(file,"%s\n", wrong_col_error); 1066 return; 1067 } 1068 1069 /* Bind iterator */ 1070 error = col_bind_iterator(&iterator, error_list, COL_TRAVERSE_DEFAULT); 1071 if (error) { 1072 TRACE_ERROR_STRING("Error (bind):", failed_to_process); 1073 fprintf(file, "%s\n", failed_to_process); 1074 return; 1075 } 1076 1077 while(1) { 1078 /* Loop through a collection */ 1079 error = col_iterate_collection(iterator, &item); 1080 if (error) { 1081 TRACE_ERROR_STRING("Error (iterate):", failed_to_process); 1082 fprintf(file, "%s\n", failed_to_process); 1083 col_unbind_iterator(iterator); 1084 return; 1085 } 1086 1087 /* Are we done ? */ 1088 if (item == NULL) break; 1089 1090 /* Process collection header */ 1091 if (col_get_item_type(item) == COL_TYPE_COLLECTION) { 1092 col_get_collection_count(item, &count); 1093 if (count <= 2) break; 1094 } else if (col_get_item_type(item) == COL_TYPE_STRING) { 1095 fprintf(file, error_header, (char *)col_get_item_data(item)); 1096 } 1097 else { 1098 /* Put error into provided format */ 1099 pe = (struct parse_error *)(col_get_item_data(item)); 1100 fprintf(file, line_format, 1101 col_get_item_property(item, NULL), /* Error or warning */ 1102 pe->error, /* Error */ 1103 pe->line, /* Line */ 1104 error_function(pe->error)); /* Error str */ 1105 } 1106 1107 } 1108 1109 /* Do not forget to unbind iterator - otherwise there will be a leak */ 1110 col_unbind_iterator(iterator); 1111 1112 TRACE_FLOW_STRING("print_error_list", "Exit"); 1113 } 1114 1115 /* Print errors and warnings that were detected while parsing one file */ 1116 void print_file_parsing_errors(FILE *file, 1117 struct collection_item *error_list) 1118 { 1119 print_error_list(file, 1120 error_list, 1121 COL_CLASS_INI_PERROR, 1122 WRONG_COLLECTION, 1123 FAILED_TO_PROCCESS, 1124 ERROR_HEADER, 1125 LINE_FORMAT, 1126 parsing_error_str); 1127 } 1128 1129 1130 /* Print errors and warnings that were detected while processing grammar */ 1131 void print_grammar_errors(FILE *file, 1132 struct collection_item *error_list) 1133 { 1134 print_error_list(file, 1135 error_list, 1136 COL_CLASS_INI_GERROR, 1137 WRONG_GRAMMAR, 1138 FAILED_TO_PROC_G, 1139 ERROR_HEADER_G, 1140 LINE_FORMAT, 1141 grammar_error_str); 1142 } 1143 1144 /* Print errors and warnings that were detected while validating INI file. */ 1145 void print_validation_errors(FILE *file, 1146 struct collection_item *error_list) 1147 { 1148 print_error_list(file, 1149 error_list, 1150 COL_CLASS_INI_VERROR, 1151 WRONG_VALIDATION, 1152 FAILED_TO_PROC_V, 1153 ERROR_HEADER_V, 1154 LINE_FORMAT, 1155 validation_error_str); 1156 } 1157 1158 /* Print errors and warnings that were detected while parsing 1159 * the whole configuration */ 1160 void print_config_parsing_errors(FILE *file, 1161 struct collection_item *error_list) 1162 { 1163 struct collection_iterator *iterator; 1164 int error; 1165 struct collection_item *item = NULL; 1166 struct collection_item *file_errors = NULL; 1167 1168 TRACE_FLOW_STRING("print_config_parsing_errors", "Entry"); 1169 1170 /* If we have something to print print it */ 1171 if (error_list == NULL) { 1172 TRACE_ERROR_STRING("No error list", ""); 1173 return; 1174 } 1175 1176 /* Make sure we go the right collection */ 1177 if (!col_is_of_class(error_list, COL_CLASS_INI_PESET)) { 1178 TRACE_ERROR_STRING("Wrong collection class:", WRONG_COLLECTION); 1179 fprintf(file, "%s\n", WRONG_COLLECTION); 1180 return; 1181 } 1182 1183 /* Bind iterator */ 1184 error = col_bind_iterator(&iterator, error_list, COL_TRAVERSE_DEFAULT); 1185 if (error) { 1186 TRACE_ERROR_STRING("Error (bind):", FAILED_TO_PROCCESS); 1187 fprintf(file,"%s\n", FAILED_TO_PROCCESS); 1188 return; 1189 } 1190 1191 while(1) { 1192 /* Loop through a collection */ 1193 error = col_iterate_collection(iterator, &item); 1194 if (error) { 1195 TRACE_ERROR_STRING("Error (iterate):", FAILED_TO_PROCCESS); 1196 fprintf(file, "%s\n", FAILED_TO_PROCCESS); 1197 col_unbind_iterator(iterator); 1198 return; 1199 } 1200 1201 /* Are we done ? */ 1202 if (item == NULL) break; 1203 1204 /* Print per file sets of errors */ 1205 if (col_get_item_type(item) == COL_TYPE_COLLECTIONREF) { 1206 /* Extract a sub collection */ 1207 error = col_get_reference_from_item(item, &file_errors); 1208 if (error) { 1209 TRACE_ERROR_STRING("Error (extract):", FAILED_TO_PROCCESS); 1210 fprintf(file, "%s\n", FAILED_TO_PROCCESS); 1211 col_unbind_iterator(iterator); 1212 return; 1213 } 1214 print_file_parsing_errors(file, file_errors); 1215 col_destroy_collection(file_errors); 1216 } 1217 } 1218 1219 /* Do not forget to unbind iterator - otherwise there will be a leak */ 1220 col_unbind_iterator(iterator); 1221 1222 TRACE_FLOW_STRING("print_config_parsing_errors", "Exit"); 1223 } 1224 1225 1226 /* Function to get value from the configration handle */ 1227 int get_config_item(const char *section, 1228 const char *name, 1229 struct collection_item *ini_config, 1230 struct collection_item **item) 1231 { 1232 int error = EOK; 1233 struct collection_item *section_handle = NULL; 1234 const char *to_find; 1235 char default_section[] = INI_DEFAULT_SECTION; 1236 1237 TRACE_FLOW_STRING("get_config_item", "Entry"); 1238 1239 /* Do we have the accepting memory ? */ 1240 if (item == NULL) { 1241 TRACE_ERROR_NUMBER("No buffer - invalid argument.", EINVAL); 1242 return EINVAL; 1243 } 1244 1245 /* Is the collection of a right type */ 1246 if (!col_is_of_class(ini_config, COL_CLASS_INI_CONFIG)) { 1247 TRACE_ERROR_NUMBER("Wrong collection type", EINVAL); 1248 return EINVAL; 1249 } 1250 1251 *item = NULL; 1252 1253 if (section == NULL) to_find = default_section; 1254 else to_find = section; 1255 1256 TRACE_INFO_STRING("Getting Name:", name); 1257 TRACE_INFO_STRING("In Section:", section); 1258 1259 /* Get Subcollection */ 1260 error = col_get_collection_reference(ini_config, §ion_handle, to_find); 1261 /* Check error */ 1262 if (error && (error != ENOENT)) { 1263 TRACE_ERROR_NUMBER("Failed to get section", error); 1264 return error; 1265 } 1266 1267 /* Did we find a section */ 1268 if ((error == ENOENT) || (section_handle == NULL)) { 1269 /* We have not found section - return success */ 1270 TRACE_FLOW_STRING("get_value_from_config", "No such section"); 1271 return EOK; 1272 } 1273 1274 /* Get item */ 1275 error = col_get_item(section_handle, name, 1276 COL_TYPE_STRING, COL_TRAVERSE_ONELEVEL, item); 1277 1278 /* Make sure we free the section we found */ 1279 col_destroy_collection(section_handle); 1280 1281 TRACE_FLOW_NUMBER("get_config_item returning", error); 1282 return error; 1283 } 1284 1285 /* Get long long value from config item */ 1286 static long long get_llong_config_value(struct collection_item *item, 1287 int strict, 1288 long long def, 1289 int *error) 1290 { 1291 int err; 1292 const char *str; 1293 char *endptr; 1294 long long val = 0; 1295 1296 TRACE_FLOW_STRING("get_long_config_value", "Entry"); 1297 1298 /* Do we have the item ? */ 1299 if ((item == NULL) || 1300 (col_get_item_type(item) != COL_TYPE_STRING)) { 1301 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); 1302 if (error) *error = EINVAL; 1303 return def; 1304 } 1305 1306 if (error) *error = EOK; 1307 1308 /* Try to parse the value */ 1309 str = (const char *)col_get_item_data(item); 1310 errno = 0; 1311 val = strtoll(str, &endptr, 10); 1312 err = errno; 1313 1314 /* Check for various possible errors */ 1315 if (err != 0) { 1316 TRACE_ERROR_NUMBER("Conversion failed", err); 1317 if (error) *error = err; 1318 return def; 1319 } 1320 1321 /* Other error cases */ 1322 if ((endptr == str) || (strict && (*endptr != '\0'))) { 1323 TRACE_ERROR_NUMBER("More characters or nothing processed", EIO); 1324 if (error) *error = EIO; 1325 return def; 1326 } 1327 1328 TRACE_FLOW_NUMBER("get_long_config_value returning", (long)val); 1329 return val; 1330 } 1331 1332 /* Get unsigned long long value from config item */ 1333 static unsigned long long get_ullong_config_value(struct collection_item *item, 1334 int strict, 1335 unsigned long long def, 1336 int *error) 1337 { 1338 int err; 1339 const char *str; 1340 char *endptr; 1341 unsigned long long val = 0; 1342 1343 TRACE_FLOW_STRING("get_long_config_value", "Entry"); 1344 1345 /* Do we have the item ? */ 1346 if ((item == NULL) || 1347 (col_get_item_type(item) != COL_TYPE_STRING)) { 1348 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); 1349 if (error) *error = EINVAL; 1350 return def; 1351 } 1352 1353 if (error) *error = EOK; 1354 1355 /* Try to parse the value */ 1356 str = (const char *)col_get_item_data(item); 1357 errno = 0; 1358 val = strtoull(str, &endptr, 10); 1359 err = errno; 1360 1361 /* Check for various possible errors */ 1362 if (err != 0) { 1363 TRACE_ERROR_NUMBER("Conversion failed", err); 1364 if (error) *error = err; 1365 return def; 1366 } 1367 1368 /* Other error cases */ 1369 if ((endptr == str) || (strict && (*endptr != '\0'))) { 1370 TRACE_ERROR_NUMBER("More characters or nothing processed", EIO); 1371 if (error) *error = EIO; 1372 return def; 1373 } 1374 1375 TRACE_FLOW_NUMBER("get_long_config_value returning", (long)val); 1376 return val; 1377 } 1378 1379 1380 /* Get integer value from config item */ 1381 int get_int_config_value(struct collection_item *item, 1382 int strict, 1383 int def, 1384 int *error) 1385 { 1386 long long val = 0; 1387 int err = 0; 1388 1389 TRACE_FLOW_STRING("get_int_config_value", "Entry"); 1390 1391 val = get_llong_config_value(item, strict, def, &err); 1392 if (err == 0) { 1393 if ((val > INT_MAX) || (val < INT_MIN)) { 1394 val = def; 1395 err = ERANGE; 1396 } 1397 } 1398 1399 if (error) *error = err; 1400 1401 TRACE_FLOW_NUMBER("get_int_config_value returning", (int)val); 1402 return (int)val; 1403 } 1404 1405 /* Get unsigned integer value from config item */ 1406 unsigned get_unsigned_config_value(struct collection_item *item, 1407 int strict, 1408 unsigned def, 1409 int *error) 1410 { 1411 unsigned long long val = 0; 1412 int err = 0; 1413 1414 TRACE_FLOW_STRING("get_unsigned_config_value", "Entry"); 1415 1416 val = get_ullong_config_value(item, strict, def, &err); 1417 if (err == 0) { 1418 if (val > UINT_MAX) { 1419 val = def; 1420 err = ERANGE; 1421 } 1422 } 1423 1424 if (error) *error = err; 1425 1426 TRACE_FLOW_NUMBER("get_unsigned_config_value returning", 1427 (unsigned)val); 1428 return (unsigned)val; 1429 } 1430 1431 /* Get long value from config item */ 1432 long get_long_config_value(struct collection_item *item, 1433 int strict, 1434 long def, 1435 int *error) 1436 { 1437 long long val = 0; 1438 int err = 0; 1439 1440 TRACE_FLOW_STRING("get_long_config_value", "Entry"); 1441 1442 val = get_llong_config_value(item, strict, def, &err); 1443 if (err == 0) { 1444 if ((val > LONG_MAX) || (val < LONG_MIN)) { 1445 val = def; 1446 err = ERANGE; 1447 } 1448 } 1449 1450 if (error) *error = err; 1451 1452 TRACE_FLOW_NUMBER("get_long_config_value returning", 1453 (long)val); 1454 return (long)val; 1455 } 1456 1457 /* Get unsigned long value from config item */ 1458 unsigned long get_ulong_config_value(struct collection_item *item, 1459 int strict, 1460 unsigned long def, 1461 int *error) 1462 { 1463 unsigned long long val = 0; 1464 int err = 0; 1465 1466 TRACE_FLOW_STRING("get_ulong_config_value", "Entry"); 1467 1468 val = get_ullong_config_value(item, strict, def, &err); 1469 if (err == 0) { 1470 if (val > ULONG_MAX) { 1471 val = def; 1472 err = ERANGE; 1473 } 1474 } 1475 1476 if (error) *error = err; 1477 1478 TRACE_FLOW_NUMBER("get_ulong_config_value returning", 1479 (unsigned long)val); 1480 return (unsigned long)val; 1481 } 1482 1483 1484 /* Get double value */ 1485 double get_double_config_value(struct collection_item *item, 1486 int strict, double def, int *error) 1487 { 1488 const char *str; 1489 char *endptr; 1490 double val = 0; 1491 1492 TRACE_FLOW_STRING("get_double_config_value", "Entry"); 1493 1494 /* Do we have the item ? */ 1495 if ((item == NULL) || 1496 (col_get_item_type(item) != COL_TYPE_STRING)) { 1497 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); 1498 if (error) *error = EINVAL; 1499 return def; 1500 } 1501 1502 if (error) *error = EOK; 1503 1504 /* Try to parse the value */ 1505 str = (const char *)col_get_item_data(item); 1506 errno = 0; 1507 val = strtod(str, &endptr); 1508 1509 /* Check for various possible errors */ 1510 if ((errno == ERANGE) || 1511 ((errno != 0) && (val == 0)) || 1512 (endptr == str)) { 1513 TRACE_ERROR_NUMBER("Conversion failed", EIO); 1514 if (error) *error = EIO; 1515 return def; 1516 } 1517 1518 if (strict && (*endptr != '\0')) { 1519 TRACE_ERROR_NUMBER("More characters than expected", EIO); 1520 if (error) *error = EIO; 1521 val = def; 1522 } 1523 1524 TRACE_FLOW_NUMBER("get_double_config_value returning", val); 1525 return val; 1526 } 1527 1528 /* Get boolean value */ 1529 unsigned char get_bool_config_value(struct collection_item *item, 1530 unsigned char def, int *error) 1531 { 1532 const char *str; 1533 int len; 1534 1535 TRACE_FLOW_STRING("get_bool_config_value", "Entry"); 1536 1537 /* Do we have the item ? */ 1538 if ((item == NULL) || 1539 (col_get_item_type(item) != COL_TYPE_STRING)) { 1540 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); 1541 if (error) *error = EINVAL; 1542 return def; 1543 } 1544 1545 if (error) *error = EOK; 1546 1547 str = (const char *)col_get_item_data(item); 1548 len = col_get_item_length(item); 1549 1550 /* Try to parse the value */ 1551 if ((strncasecmp(str, "true", len) == 0) || 1552 (strncasecmp(str, "yes", len) == 0)) { 1553 TRACE_FLOW_STRING("Returning", "true"); 1554 return '\1'; 1555 } 1556 else if ((strncasecmp(str, "false", len) == 0) || 1557 (strncasecmp(str, "no", len) == 0)) { 1558 TRACE_FLOW_STRING("Returning", "false"); 1559 return '\0'; 1560 } 1561 1562 TRACE_ERROR_STRING("Returning", "error"); 1563 if (error) *error = EIO; 1564 return def; 1565 } 1566 1567 /* Return a string out of the value */ 1568 char *get_string_config_value(struct collection_item *item, 1569 int *error) 1570 { 1571 char *str = NULL; 1572 1573 TRACE_FLOW_STRING("get_string_config_value", "Entry"); 1574 1575 /* Do we have the item ? */ 1576 if ((item == NULL) || 1577 (col_get_item_type(item) != COL_TYPE_STRING)) { 1578 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); 1579 if (error) *error = EINVAL; 1580 return NULL; 1581 } 1582 1583 str = strdup((const char *)col_get_item_data(item)); 1584 if (str == NULL) { 1585 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); 1586 if (error) *error = ENOMEM; 1587 return NULL; 1588 } 1589 1590 if (error) *error = EOK; 1591 1592 TRACE_FLOW_STRING("get_string_config_value returning", str); 1593 return str; 1594 } 1595 1596 /* Get string from item */ 1597 const char *get_const_string_config_value(struct collection_item *item, int *error) 1598 { 1599 const char *str; 1600 1601 TRACE_FLOW_STRING("get_const_string_config_value", "Entry"); 1602 1603 /* Do we have the item ? */ 1604 if ((item == NULL) || 1605 (col_get_item_type(item) != COL_TYPE_STRING)) { 1606 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); 1607 if (error) *error = EINVAL; 1608 return NULL; 1609 } 1610 1611 str = (const char *)col_get_item_data(item); 1612 1613 if (error) *error = EOK; 1614 1615 TRACE_FLOW_STRING("get_const_string_config_value returning", str); 1616 return str; 1617 } 1618 1619 /* A special hex format is assumed. 1620 * The string should be taken in single quotes 1621 * and consist of hex encoded value two hex digits per byte. 1622 * Example: '0A2BFECC' 1623 * Case does not matter. 1624 */ 1625 char *get_bin_config_value(struct collection_item *item, 1626 int *length, int *error) 1627 { 1628 int i; 1629 char *value = NULL; 1630 const char *buff; 1631 int size = 0; 1632 unsigned char hex; 1633 int len; 1634 const char *str; 1635 1636 TRACE_FLOW_STRING("get_bin_config_value", "Entry"); 1637 1638 /* Do we have the item ? */ 1639 if ((item == NULL) || 1640 (col_get_item_type(item) != COL_TYPE_STRING)) { 1641 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); 1642 if (error) *error = EINVAL; 1643 return NULL; 1644 } 1645 1646 /* Check the length */ 1647 len = col_get_item_length(item)-1; 1648 if ((len%2) != 0) { 1649 TRACE_ERROR_STRING("Invalid length for binary data", ""); 1650 if (error) *error = EINVAL; 1651 return NULL; 1652 } 1653 1654 str = (const char *)col_get_item_data(item); 1655 1656 /* Is the format correct ? */ 1657 if ((*str != '\'') || 1658 (str[len -1] != '\'')) { 1659 TRACE_ERROR_STRING("String is not escaped",""); 1660 if (error) *error = EIO; 1661 return NULL; 1662 } 1663 1664 /* Check that all the symbols are ok */ 1665 buff = str + 1; 1666 len -= 2; 1667 for (i = 0; i < len; i += 2) { 1668 if (!isxdigit(buff[i]) || !isxdigit(buff[i + 1])) { 1669 TRACE_ERROR_STRING("Invalid encoding for binary data", buff + i); 1670 if (error) *error = EIO; 1671 return NULL; 1672 } 1673 } 1674 1675 /* The value is good so we can allocate memory for it */ 1676 value = malloc(len / 2); 1677 if (value == NULL) { 1678 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); 1679 if (error) *error = ENOMEM; 1680 return NULL; 1681 } 1682 1683 /* Convert the value */ 1684 for (i = 0; i < len; i += 2) { 1685 if (isdigit(buff[i])) { 1686 if (isdigit(buff[i+1])) 1687 hex = 16 * (buff[i] - '0') + (buff[i+1] - '0'); 1688 else 1689 hex = 16 * (buff[i] - '0') + (tolower(buff[i+1]) - 'a' + 10); 1690 } 1691 else { 1692 if (isdigit(buff[i+1])) 1693 hex = 16 * (tolower(buff[i]) - 'a') + (buff[i+1] - '0'); 1694 else 1695 hex = 16 * (tolower(buff[i]) - 'a' + 10) + (tolower(buff[i+1]) - 'a' + 10); 1696 } 1697 1698 value[size] = (char)(hex); 1699 size++; 1700 } 1701 1702 if (error) *error = EOK; 1703 if (length) *length = size; 1704 TRACE_FLOW_STRING("get_bin_config_value", "Exit"); 1705 return value; 1706 } 1707 1708 /* Function to free binary configuration value */ 1709 void free_bin_config_value(char *value) 1710 { 1711 if (value) free(value); 1712 } 1713 1714 /* Arrays of stings */ 1715 static char **get_str_cfg_array(struct collection_item *item, 1716 int include, 1717 const char *sep, 1718 int *size, 1719 int *error) 1720 { 1721 char *copy = NULL; 1722 char *dest = NULL; 1723 char locsep[4]; 1724 int lensep; 1725 char *buff; 1726 int count = 0; 1727 int len = 0; 1728 int resume_len; 1729 char **array; 1730 char *start; 1731 int i, j; 1732 int dlen; 1733 1734 TRACE_FLOW_STRING("get_str_cfg_array", "Entry"); 1735 1736 /* Do we have the item ? */ 1737 if ((item == NULL) || 1738 (col_get_item_type(item) != COL_TYPE_STRING)) { 1739 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); 1740 if (error) *error = EINVAL; 1741 return NULL; 1742 } 1743 1744 /* Handle the separators */ 1745 if (sep == NULL) { 1746 locsep[0] = ','; 1747 locsep[1] = '\0'; 1748 lensep = 2; 1749 } 1750 else { 1751 strncpy(locsep, sep, 3); 1752 locsep[3] = '\0'; 1753 lensep = strlen(locsep) + 1; 1754 } 1755 1756 /* Allocate memory for the copy of the string */ 1757 copy = malloc(col_get_item_length(item)); 1758 if (copy == NULL) { 1759 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); 1760 if (error) *error = ENOMEM; 1761 return NULL; 1762 } 1763 1764 /* Loop through the string */ 1765 dest = copy; 1766 buff = col_get_item_data(item); 1767 start = buff; 1768 dlen = col_get_item_length(item); 1769 for(i = 0; i < dlen; i++) { 1770 for(j = 0; j < lensep; j++) { 1771 if(buff[i] == locsep[j]) { 1772 /* If we found one of the separators trim spaces around */ 1773 resume_len = len; 1774 while (len > 0) { 1775 if (isspace(start[len - 1])) len--; 1776 else break; 1777 } 1778 TRACE_INFO_STRING("Current:", start); 1779 TRACE_INFO_NUMBER("Length:", len); 1780 if (len > 0) { 1781 /* Save block aside */ 1782 memcpy(dest, start, len); 1783 count++; 1784 dest += len; 1785 *dest = '\0'; 1786 dest++; 1787 } 1788 else if(include) { 1789 count++; 1790 *dest = '\0'; 1791 dest++; 1792 } 1793 if (locsep[j] == '\0') break; /* We are done */ 1794 1795 /* Move forward and trim spaces if any */ 1796 start += resume_len + 1; 1797 i++; 1798 TRACE_INFO_STRING("Other pointer :", buff + i); 1799 while ((i < dlen) && (isspace(*start))) { 1800 i++; 1801 start++; 1802 } 1803 len = -1; /* Len will be increased in the loop */ 1804 i--; /* i will be increas so we need to step back */ 1805 TRACE_INFO_STRING("Remaining buffer after triming spaces:", start); 1806 break; 1807 } 1808 } 1809 len++; 1810 } 1811 1812 /* Now we know how many items are there in the list */ 1813 array = malloc((count + 1) * sizeof(char *)); 1814 if (array == NULL) { 1815 free(copy); 1816 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); 1817 if (error) *error = ENOMEM; 1818 return NULL; 1819 } 1820 1821 /* Loop again to fill in the pointers */ 1822 start = copy; 1823 for (i = 0; i < count; i++) { 1824 TRACE_INFO_STRING("Token :", start); 1825 TRACE_INFO_NUMBER("Item :", i); 1826 array[i] = start; 1827 /* Move to next item */ 1828 while(*start) start++; 1829 start++; 1830 } 1831 array[count] = NULL; 1832 1833 if (error) *error = EOK; 1834 if (size) *size = count; 1835 TRACE_FLOW_STRING("get_str_cfg_array", "Exit"); 1836 return array; 1837 } 1838 1839 /* Get array of strings from item eliminating empty tokens */ 1840 char **get_string_config_array(struct collection_item *item, 1841 const char *sep, int *size, int *error) 1842 { 1843 TRACE_FLOW_STRING("get_string_config_array", "Called."); 1844 return get_str_cfg_array(item, EXCLUDE_EMPTY, sep, size, error); 1845 } 1846 /* Get array of strings from item preserving empty tokens */ 1847 char **get_raw_string_config_array(struct collection_item *item, 1848 const char *sep, int *size, int *error) 1849 { 1850 TRACE_FLOW_STRING("get_raw_string_config_array", "Called."); 1851 return get_str_cfg_array(item, INCLUDE_EMPTY, sep, size, error); 1852 } 1853 1854 /* Special function to free string config array */ 1855 void free_string_config_array(char **str_config) 1856 { 1857 TRACE_FLOW_STRING("free_string_config_array", "Entry"); 1858 1859 if (str_config != NULL) { 1860 if (*str_config != NULL) free(*str_config); 1861 free(str_config); 1862 } 1863 1864 TRACE_FLOW_STRING("free_string_config_array", "Exit"); 1865 } 1866 1867 /* Get an array of long values. 1868 * NOTE: For now I leave just one function that returns numeric arrays. 1869 * In future if we need other numeric types we can change it to do strtoll 1870 * internally and wrap it for backward compatibility. 1871 */ 1872 long *get_long_config_array(struct collection_item *item, int *size, int *error) 1873 { 1874 const char *str; 1875 char *endptr; 1876 long val = 0; 1877 long *array; 1878 int count = 0; 1879 int err; 1880 1881 TRACE_FLOW_STRING("get_long_config_array", "Entry"); 1882 1883 /* Do we have the item ? */ 1884 if ((item == NULL) || 1885 (col_get_item_type(item) != COL_TYPE_STRING) || 1886 (size == NULL)) { 1887 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); 1888 if (error) *error = EINVAL; 1889 return NULL; 1890 } 1891 1892 /* Assume that we have maximum number of different numbers */ 1893 array = (long *)malloc(sizeof(long) * col_get_item_length(item)/2); 1894 if (array == NULL) { 1895 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); 1896 if (error) *error = ENOMEM; 1897 return NULL; 1898 } 1899 1900 /* Now parse the string */ 1901 str = (const char *)col_get_item_data(item); 1902 while (*str) { 1903 1904 errno = 0; 1905 val = strtol(str, &endptr, 10); 1906 err = errno; 1907 1908 if (err) { 1909 TRACE_ERROR_NUMBER("Conversion failed", err); 1910 free(array); 1911 if (error) *error = err; 1912 return NULL; 1913 } 1914 1915 if (endptr == str) { 1916 TRACE_ERROR_NUMBER("Nothing processed", EIO); 1917 free(array); 1918 if (error) *error = EIO; 1919 return NULL; 1920 } 1921 1922 /* Save value */ 1923 array[count] = val; 1924 count++; 1925 /* Are we done? */ 1926 if (*endptr == 0) break; 1927 /* Advance to the next valid number */ 1928 for (str = endptr; *str; str++) { 1929 if (isdigit(*str) || (*str == '-') || (*str == '+')) break; 1930 } 1931 } 1932 1933 *size = count; 1934 if (error) *error = EOK; 1935 1936 TRACE_FLOW_NUMBER("get_long_config_value returning", val); 1937 return array; 1938 1939 } 1940 1941 /* Get an array of double values */ 1942 double *get_double_config_array(struct collection_item *item, int *size, int *error) 1943 { 1944 const char *str; 1945 char *endptr; 1946 double val = 0; 1947 double *array; 1948 int count = 0; 1949 struct lconv *loc; 1950 1951 TRACE_FLOW_STRING("get_double_config_array", "Entry"); 1952 1953 /* Do we have the item ? */ 1954 if ((item == NULL) || 1955 (col_get_item_type(item) != COL_TYPE_STRING) || 1956 (size == NULL)) { 1957 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); 1958 if (error) *error = EINVAL; 1959 return NULL; 1960 } 1961 1962 /* Assume that we have maximum number of different numbers */ 1963 array = (double *)malloc(sizeof(double) * col_get_item_length(item)/2); 1964 if (array == NULL) { 1965 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); 1966 if (error) *error = ENOMEM; 1967 return NULL; 1968 } 1969 1970 /* Get locale information so that we can check for decimal point character. 1971 * Based on the man pages it is unclear if this is an allocated memory or not. 1972 * Seems like it is a static thread or process local structure so 1973 * I will not try to free it after use. 1974 */ 1975 loc = localeconv(); 1976 1977 /* Now parse the string */ 1978 str = (const char *)col_get_item_data(item); 1979 while (*str) { 1980 TRACE_INFO_STRING("String to convert",str); 1981 errno = 0; 1982 val = strtod(str, &endptr); 1983 if ((errno == ERANGE) || 1984 ((errno != 0) && (val == 0)) || 1985 (endptr == str)) { 1986 TRACE_ERROR_NUMBER("Conversion failed", EIO); 1987 free(array); 1988 if (error) *error = EIO; 1989 return NULL; 1990 } 1991 /* Save value */ 1992 array[count] = val; 1993 count++; 1994 /* Are we done? */ 1995 if (*endptr == 0) break; 1996 TRACE_INFO_STRING("End pointer after conversion",endptr); 1997 /* Advance to the next valid number */ 1998 for (str = endptr; *str; str++) { 1999 if (isdigit(*str) || (*str == '-') || (*str == '+') || 2000 /* It is ok to do this since the string is null terminated */ 2001 ((*str == *(loc->decimal_point)) && isdigit(str[1]))) break; 2002 } 2003 } 2004 2005 *size = count; 2006 if (error) *error = EOK; 2007 2008 TRACE_FLOW_NUMBER("get_double_config_value returning", val); 2009 return array; 2010 2011 } 2012 2013 2014 /* Special function to free long config array */ 2015 void free_long_config_array(long *array) 2016 { 2017 if (array != NULL) free(array); 2018 } 2019 2020 /* Special function to free double config array */ 2021 void free_double_config_array(double *array) 2022 { 2023 if (array != NULL) free(array); 2024 } 2025 2026 /* The section array should be freed using this function */ 2027 void free_section_list(char **section_list) 2028 { 2029 TRACE_FLOW_STRING("free_section_list","Entry"); 2030 2031 col_free_property_list(section_list); 2032 2033 TRACE_FLOW_STRING("free_section_list","Exit"); 2034 } 2035 2036 /* The section array should be freed using this function */ 2037 void free_attribute_list(char **section_list) 2038 { 2039 TRACE_FLOW_STRING("free_section_list","Entry"); 2040 2041 col_free_property_list(section_list); 2042 2043 TRACE_FLOW_STRING("free_section_list","Exit"); 2044 } 2045 2046 2047 /* Get list of sections as an array of strings. 2048 * Function allocates memory for the array of the sections. 2049 */ 2050 char **get_section_list(struct collection_item *ini_config, int *size, int *error) 2051 { 2052 char **list; 2053 2054 TRACE_FLOW_STRING("get_section_list","Entry"); 2055 /* Do we have the item ? */ 2056 if ((ini_config == NULL) || 2057 !col_is_of_class(ini_config, COL_CLASS_INI_CONFIG)) { 2058 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); 2059 if (error) *error = EINVAL; 2060 return NULL; 2061 } 2062 2063 /* Pass it to the function from collection API */ 2064 list = col_collection_to_list(ini_config, size, error); 2065 2066 TRACE_FLOW_STRING("get_section_list returning", ((list == NULL) ? "NULL" : list[0])); 2067 return list; 2068 } 2069 2070 /* Get list of attributes in a section as an array of strings. 2071 * Function allocates memory for the array of the strings. 2072 */ 2073 char **get_attribute_list(struct collection_item *ini_config, const char *section, int *size, int *error) 2074 { 2075 struct collection_item *subcollection = NULL; 2076 char **list; 2077 int err; 2078 2079 TRACE_FLOW_STRING("get_attribute_list","Entry"); 2080 /* Do we have the item ? */ 2081 if ((ini_config == NULL) || 2082 !col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) || 2083 (section == NULL)) { 2084 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); 2085 if (error) *error = EINVAL; 2086 return NULL; 2087 } 2088 2089 /* Fetch section */ 2090 err = col_get_collection_reference(ini_config, &subcollection, section); 2091 /* Check error */ 2092 if (err && (subcollection == NULL)) { 2093 TRACE_ERROR_NUMBER("Failed to get section", err); 2094 if (error) *error = EINVAL; 2095 return NULL; 2096 } 2097 2098 /* Pass it to the function from collection API */ 2099 list = col_collection_to_list(subcollection, size, error); 2100 2101 col_destroy_collection(subcollection); 2102 2103 TRACE_FLOW_STRING("get_attribute_list returning", ((list == NULL) ? "NULL" : list[0])); 2104 return list; 2105 }