diff options
author | Dmitri Pal <dpal@redhat.com> | 2010-04-15 17:27:04 -0400 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2010-04-26 09:54:59 -0400 |
commit | 5a5eb2619fed6b37178d3b2c58970788c55cb529 (patch) | |
tree | 4328cef0d08e87d08a37e692cd860bee34c4352e | |
parent | d1de8d73252e973018cb985e6aedffd76cddbbe4 (diff) | |
download | sssd-5a5eb2619fed6b37178d3b2c58970788c55cb529.tar.gz sssd-5a5eb2619fed6b37178d3b2c58970788c55cb529.tar.bz2 sssd-5a5eb2619fed6b37178d3b2c58970788c55cb529.zip |
Code restructuring
Time came to split ini_config.c
into many much smaller pieces.
1) ini_parse.c - will have parsing functions
2) ini_get_value.c - will have single value
interpretation functions
3) ini_get_array.c - will have array interpretation
functions.
4) ini_print.c - error printing
5) ini_defines.h - common constants
6) ini_parse.h header for parsing functions
7) ini_list.c - will have list processing functions
-rw-r--r-- | common/ini/Makefile.am | 11 | ||||
-rw-r--r-- | common/ini/ini_config.c | 1476 | ||||
-rw-r--r-- | common/ini/ini_config.h | 3 | ||||
-rw-r--r-- | common/ini/ini_defines.h | 74 | ||||
-rw-r--r-- | common/ini/ini_get_array.c | 354 | ||||
-rw-r--r-- | common/ini/ini_get_value.c | 523 | ||||
-rw-r--r-- | common/ini/ini_list.c | 123 | ||||
-rw-r--r-- | common/ini/ini_parse.c | 190 | ||||
-rw-r--r-- | common/ini/ini_parse.h | 36 | ||||
-rw-r--r-- | common/ini/ini_print.c | 387 |
10 files changed, 1705 insertions, 1472 deletions
diff --git a/common/ini/Makefile.am b/common/ini/Makefile.am index a72ce3a0..1dc55d9d 100644 --- a/common/ini/Makefile.am +++ b/common/ini/Makefile.am @@ -34,8 +34,17 @@ dist_include_HEADERS = \ lib_LTLIBRARIES = libini_config.la libini_config_la_SOURCES = \ ini_config.c \ + ini_config.h \ + ini_get_value.c \ + ini_get_array.c \ + ini_list.c \ + ini_print.c \ + ini_parse.c \ + ini_parse.h \ ini_metadata.c \ - ini_metadata.h + ini_metadata.h \ + ini_defines.h + libini_config_la_LIBADD = \ -L$(topbuilddir)/collection \ diff --git a/common/ini/ini_config.c b/common/ini/ini_config.c index 1c8e4aef..b9cd6eb0 100644 --- a/common/ini/ini_config.c +++ b/common/ini/ini_config.c @@ -21,220 +21,19 @@ */ #define _GNU_SOURCE -#include <stdio.h> #include <errno.h> #include <string.h> -#include <ctype.h> #include <stdlib.h> -#include <locale.h> -#include <fcntl.h> -#include <unistd.h> -#include <limits.h> #include "config.h" -/* For error text */ -#include <libintl.h> -#define _(String) gettext (String) -/* INI file is used as a collection */ +#include "trace.h" #include "collection.h" #include "collection_tools.h" -#include "trace.h" -#include "ini_config.h" -#include "ini_metadata.h" #include "path_utils.h" +#include "ini_defines.h" +#include "ini_parse.h" +#include "ini_metadata.h" +#include "ini_config.h" -#define NAME_OVERHEAD 10 - -#define SLASH "/" - -#define EXCLUDE_EMPTY 0 -#define INCLUDE_EMPTY 1 - -/* Name of the special collection used to store parsing errors */ -#define FILE_ERROR_SET "ini_file_error_set" - -/* Text error strings used when errors are printed out */ -#define WARNING_TXT _("Warning") -#define ERROR_TXT _("Error") -/* For parse errors */ -#define WRONG_COLLECTION _("Passed in list is not a list of parse errors.\n") -#define FAILED_TO_PROCCESS _("Internal Error. Failed to process error list.\n") -#define ERROR_HEADER _("Parsing errors and warnings in file: %s\n") -/* For grammar errors */ -#define WRONG_GRAMMAR _("Passed in list is not a list of grammar errors.\n") -#define FAILED_TO_PROC_G _("Internal Error. Failed to process list of grammar errors.\n") -#define ERROR_HEADER_G _("Logical errors and warnings in file: %s\n") -/* For validation errors */ -#define WRONG_VALIDATION _("Passed in list is not a list of validation errors.\n") -#define FAILED_TO_PROC_V _("Internal Error. Failed to process list of validation errors.\n") -#define ERROR_HEADER_V _("Validation errors and warnings in file: %s\n") - -#define LINE_FORMAT _("%s (%d) on line %d: %s\n") - - -/* Codes that parsing function can return */ -#define RET_PAIR 0 -#define RET_COMMENT 1 -#define RET_SECTION 2 -#define RET_INVALID 3 -#define RET_EMPTY 4 -#define RET_EOF 5 -#define RET_ERROR 6 - -#define INI_ERROR "errors" -#define INI_ERROR_NAME "errname" - -/* Internal sizes. MAX_KEY is defined in config.h */ -#define MAX_VALUE PATH_MAX -#define BUFFER_SIZE MAX_KEY + MAX_VALUE + 3 - -/* Beffer length used for int to string conversions */ -#define CONVERSION_BUFFER 80 - -/*============================================================*/ -/* The following classes moved here from the public header - * They are reserved for future use. - * - * NOTE: before exposing these constants again in the common header - * check that the class IDs did not get reused over time by - * other classes. - */ -/** @brief Collection of grammar errors. - * - * Reserved for future use. - */ -#define COL_CLASS_INI_GERROR COL_CLASS_INI_BASE + 5 -/** @brief Collection of validation errors. - * - * Reserved for future use. - */ -#define COL_CLASS_INI_VERROR COL_CLASS_INI_BASE + 6 - -#ifdef HAVE_VALIDATION - -/** @brief Collection of lines from the INI file. - * - * Reserved for future use - */ -#define COL_CLASS_INI_LINES COL_CLASS_INI_BASE + 7 - -#endif /* HAVE_VALIDATION */ -/*============================================================*/ - - -/* Different error string functions can be passed as callbacks */ -typedef const char * (*error_fn)(int error); - -/* Function to return parsing error */ -const char *parsing_error_str(int parsing_error) -{ - const char *placeholder= _("Unknown pasing error."); - const char *str_error[] = { _("Data is too long."), - _("No closing bracket."), - _("Section name is missing."), - _("Section name is too long."), - _("Equal sign is missing."), - _("Property name is missing."), - _("Property name is too long.") - }; - - /* Check the range */ - if ((parsing_error < 1) || (parsing_error > ERR_MAXPARSE)) - return placeholder; - else - return str_error[parsing_error-1]; -} - -/* Function to return grammar error. - * This function is currently not used. - * It is planned to be used by the INI - * file grammar parser. - * - * The following doxygen description is moved here. - * When the function gets exposed move it into - * the header file. - */ -/** @brief Function to return a grammar error in template. - * - * EXPERIMENTAL. Reserved for future use. - * - * This error is returned when the template - * is translated into the grammar object. - * - * @param[in] parsing_error Error code for the grammar error. - * - * @return Error string. - */ - -const char *grammar_error_str(int grammar_error) -{ - const char *placeholder= _("Unknown grammar error."); - /* THIS IS A TEMPORARY PLACEHOLDER !!!! */ - const char *str_error[] = { _(""), - _(""), - _(""), - _(""), - _(""), - _(""), - _("") - }; - - /* Check the range */ - if ((grammar_error < 1) || (grammar_error > ERR_MAXGRAMMAR)) - return placeholder; - else - return str_error[grammar_error-1]; -} - -/* Function to return validation error. - * This function is currently not used. - * It is planned to be used by the INI - * file grammar validator. - * - * The following doxygen description is moved here. - * When the function gets exposed move it into - * the header file. - */ -/** @brief Function to return a validation error. - * - * EXPERIMENTAL. Reserved for future use. - * - * This is the error that it is returned when - * the INI file is validated against the - * grammar object. - * - * @param[in] parsing_error Error code for the validation error. - * - * @return Error string. - */ -const char *validation_error_str(int validation_error) -{ - const char *placeholder= _("Unknown validation error."); - /* THIS IS A TEMPORARY PLACEHOLDER !!!! */ - const char *str_error[] = { _(""), - _(""), - _(""), - _(""), - _(""), - _(""), - _("") - }; - - /* Check the range */ - if ((validation_error < 1) || (validation_error > ERR_MAXVALID)) - return placeholder; - else - return str_error[validation_error-1]; -} - - -/* Internal function to read line from INI file */ -int read_line(FILE *file, - char *buf, - int read_size, - char **key, - char **value, - int *length, - int *ext_error); /***************************************************************************/ /* Function to read single ini file and pupulate @@ -1035,1268 +834,3 @@ int config_for_app(const char *application, TRACE_FLOW_NUMBER("config_for_app. Returning", error); return error; } - - - -/* Reads a line from the file */ -int read_line(FILE *file, - char *buf, - int read_size, - char **key, char **value, - int *length, - int *ext_error) -{ - - char *res; - int len; - char *buffer; - int i; - char *eq; - - TRACE_FLOW_STRING("read_line", "Entry"); - - *ext_error = 0; - - buffer = buf; - - /* Get data from file */ - res = fgets(buffer, read_size - 1, file); - if (res == NULL) { - TRACE_ERROR_STRING("Read nothing", ""); - return RET_EOF; - } - - /* Make sure the buffer is NULL terminated */ - buffer[read_size - 1] = '\0'; - - len = strlen(buffer); - if (len == 0) { - TRACE_ERROR_STRING("Nothing was read.", ""); - return RET_EMPTY; - } - - /* Added \r just in case we deal with Windows in future */ - if ((buffer[len - 1] != '\n') && (buffer[len - 1] != '\r')) { - TRACE_ERROR_STRING("String it too big!", ""); - *ext_error = ERR_LONGDATA; - return RET_ERROR; - } - - /* Ingnore comments */ - if ((*buffer == ';') || (*buffer == '#')) { - TRACE_FLOW_STRING("Comment", buf); - return RET_COMMENT; - } - - TRACE_INFO_STRING("BUFFER before trimming:", buffer); - - /* Trucate trailing spaces and CRs */ - /* Make sure not to step before the beginning */ - while (len && isspace(buffer[len - 1])) { - buffer[len - 1] = '\0'; - len--; - } - - TRACE_INFO_STRING("BUFFER after trimming trailing spaces:", buffer); - - /* Trucate leading spaces */ - while (isspace(*buffer)) { - buffer++; - len--; - } - - TRACE_INFO_STRING("BUFFER after trimming leading spaces:", buffer); - TRACE_INFO_NUMBER("BUFFER length:", len); - - /* Empty line */ - if (len == 0) { - TRACE_FLOW_STRING("Empty line", buf); - return RET_EMPTY; - } - - /* Section */ - if (*buffer == '[') { - if (buffer[len-1] != ']') { - TRACE_ERROR_STRING("Invalid format for section", buf); - *ext_error = ERR_NOCLOSESEC; - return RET_ERROR; - } - buffer++; - len--; - while (isspace(*buffer)) { - buffer++; - len--; - } - if (len == 0) { - TRACE_ERROR_STRING("Invalid format for section", buf); - *ext_error = ERR_NOSECTION; - return RET_ERROR; - } - - buffer[len - 1] = '\0'; - len--; - while (isspace(buffer[len - 1])) { - buffer[len - 1] = '\0'; - len--; - } - if (len >= MAX_KEY) { - TRACE_ERROR_STRING("Section name is too long", buf); - *ext_error = ERR_SECTIONLONG; - return RET_ERROR; - } - - *key = buffer; - return RET_SECTION; - } - - /* Assume we are dealing with the K-V here */ - /* Find "=" */ - eq = strchr(buffer, '='); - if (eq == NULL) { - TRACE_ERROR_STRING("No equal sign", buf); - *ext_error = ERR_NOEQUAL; - return RET_INVALID; - } - - len -= eq-buffer; - - /* Strip spaces around "=" */ - i = eq - buffer - 1; - while ((i >= 0) && isspace(buffer[i])) i--; - if (i < 0) { - TRACE_ERROR_STRING("No key", buf); - *ext_error = ERR_NOKEY; - return RET_INVALID; - } - - /* Copy key into provided buffer */ - if(i >= MAX_KEY) { - TRACE_ERROR_STRING("Section name is too long", buf); - *ext_error = ERR_LONGKEY; - return RET_INVALID; - } - *key = buffer; - buffer[i + 1] = '\0'; - TRACE_INFO_STRING("KEY:", *key); - - eq++; - len--; - while (isspace(*eq)) { - eq++; - len--; - } - - *value = eq; - /* Make sure we include trailing 0 into data */ - *length = len + 1; - - TRACE_INFO_STRING("VALUE:", *value); - TRACE_INFO_NUMBER("LENGTH:", *length); - - TRACE_FLOW_STRING("read_line", "Exit"); - return RET_PAIR; -} - - - -/* Internal function that prints errors */ -static void print_error_list(FILE *file, - struct collection_item *error_list, - int cclass, - char *wrong_col_error, - char *failed_to_process, - char *error_header, - char *line_format, - error_fn error_function) -{ - struct collection_iterator *iterator; - int error; - struct collection_item *item = NULL; - struct parse_error *pe; - unsigned int count; - - TRACE_FLOW_STRING("print_error_list", "Entry"); - - /* If we have something to print print it */ - if (error_list == NULL) { - TRACE_ERROR_STRING("No error list",""); - return; - } - - /* Make sure we go the right collection */ - if (!col_is_of_class(error_list, cclass)) { - TRACE_ERROR_STRING("Wrong collection class:", wrong_col_error); - fprintf(file,"%s\n", wrong_col_error); - return; - } - - /* Bind iterator */ - error = col_bind_iterator(&iterator, error_list, COL_TRAVERSE_DEFAULT); - if (error) { - TRACE_ERROR_STRING("Error (bind):", failed_to_process); - fprintf(file, "%s\n", failed_to_process); - return; - } - - while(1) { - /* Loop through a collection */ - error = col_iterate_collection(iterator, &item); - if (error) { - TRACE_ERROR_STRING("Error (iterate):", failed_to_process); - fprintf(file, "%s\n", failed_to_process); - col_unbind_iterator(iterator); - return; - } - - /* Are we done ? */ - if (item == NULL) break; - - /* Process collection header */ - if (col_get_item_type(item) == COL_TYPE_COLLECTION) { - col_get_collection_count(item, &count); - if (count <= 2) break; - } else if (col_get_item_type(item) == COL_TYPE_STRING) { - fprintf(file, error_header, (char *)col_get_item_data(item)); - } - else { - /* Put error into provided format */ - pe = (struct parse_error *)(col_get_item_data(item)); - fprintf(file, line_format, - col_get_item_property(item, NULL), /* Error or warning */ - pe->error, /* Error */ - pe->line, /* Line */ - error_function(pe->error)); /* Error str */ - } - - } - - /* Do not forget to unbind iterator - otherwise there will be a leak */ - col_unbind_iterator(iterator); - - TRACE_FLOW_STRING("print_error_list", "Exit"); -} - -/* Print errors and warnings that were detected while parsing one file */ -void print_file_parsing_errors(FILE *file, - struct collection_item *error_list) -{ - print_error_list(file, - error_list, - COL_CLASS_INI_PERROR, - WRONG_COLLECTION, - FAILED_TO_PROCCESS, - ERROR_HEADER, - LINE_FORMAT, - parsing_error_str); -} - - -/* Print errors and warnings that were detected while processing grammar. - * - * The following doxygen description is moved here. - * When the function gets exposed move it into - * the header file. - */ -/** - * @brief Print errors and warnings that were detected while - * checking grammar of the template. - * - * EXPERIMENTAL. Reserved for future use. - * - * @param[in] file File descriptor. - * @param[in] error_list List of the parsing errors. - * - */ -void print_grammar_errors(FILE *file, - struct collection_item *error_list) -{ - print_error_list(file, - error_list, - COL_CLASS_INI_GERROR, - WRONG_GRAMMAR, - FAILED_TO_PROC_G, - ERROR_HEADER_G, - LINE_FORMAT, - grammar_error_str); -} - -/* Print errors and warnings that were detected while validating INI file. - * - * The following doxygen description is moved here. - * When the function gets exposed move it into - * the header file. - */ -/** - * @brief Print errors and warnings that were detected while - * checking INI file against the grammar object. - * - * EXPERIMENTAL. Reserved for future use. - * - * @param[in] file File descriptor. - * @param[in] error_list List of the parsing errors. - * - */ -void print_validation_errors(FILE *file, - struct collection_item *error_list) -{ - print_error_list(file, - error_list, - COL_CLASS_INI_VERROR, - WRONG_VALIDATION, - FAILED_TO_PROC_V, - ERROR_HEADER_V, - LINE_FORMAT, - validation_error_str); -} - -/* Print errors and warnings that were detected while parsing - * the whole configuration */ -void print_config_parsing_errors(FILE *file, - struct collection_item *error_list) -{ - struct collection_iterator *iterator; - int error; - struct collection_item *item = NULL; - struct collection_item *file_errors = NULL; - - TRACE_FLOW_STRING("print_config_parsing_errors", "Entry"); - - /* If we have something to print print it */ - if (error_list == NULL) { - TRACE_ERROR_STRING("No error list", ""); - return; - } - - /* Make sure we go the right collection */ - if (!col_is_of_class(error_list, COL_CLASS_INI_PESET)) { - TRACE_ERROR_STRING("Wrong collection class:", WRONG_COLLECTION); - fprintf(file, "%s\n", WRONG_COLLECTION); - return; - } - - /* Bind iterator */ - error = col_bind_iterator(&iterator, error_list, COL_TRAVERSE_DEFAULT); - if (error) { - TRACE_ERROR_STRING("Error (bind):", FAILED_TO_PROCCESS); - fprintf(file,"%s\n", FAILED_TO_PROCCESS); - return; - } - - while(1) { - /* Loop through a collection */ - error = col_iterate_collection(iterator, &item); - if (error) { - TRACE_ERROR_STRING("Error (iterate):", FAILED_TO_PROCCESS); - fprintf(file, "%s\n", FAILED_TO_PROCCESS); - col_unbind_iterator(iterator); - return; - } - - /* Are we done ? */ - if (item == NULL) break; - - /* Print per file sets of errors */ - if (col_get_item_type(item) == COL_TYPE_COLLECTIONREF) { - /* Extract a sub collection */ - error = col_get_reference_from_item(item, &file_errors); - if (error) { - TRACE_ERROR_STRING("Error (extract):", FAILED_TO_PROCCESS); - fprintf(file, "%s\n", FAILED_TO_PROCCESS); - col_unbind_iterator(iterator); - return; - } - print_file_parsing_errors(file, file_errors); - col_destroy_collection(file_errors); - } - } - - /* Do not forget to unbind iterator - otherwise there will be a leak */ - col_unbind_iterator(iterator); - - TRACE_FLOW_STRING("print_config_parsing_errors", "Exit"); -} - - -/* Function to get value from the configration handle */ -int get_config_item(const char *section, - const char *name, - struct collection_item *ini_config, - struct collection_item **item) -{ - int error = EOK; - struct collection_item *section_handle = NULL; - const char *to_find; - char default_section[] = INI_DEFAULT_SECTION; - - TRACE_FLOW_STRING("get_config_item", "Entry"); - - /* Do we have the accepting memory ? */ - if (item == NULL) { - TRACE_ERROR_NUMBER("No buffer - invalid argument.", EINVAL); - return EINVAL; - } - - /* Is the collection of a right type */ - if ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) && - (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0)) { - TRACE_ERROR_NUMBER("Wrong collection type", EINVAL); - return EINVAL; - } - - *item = NULL; - - if (section == NULL) to_find = default_section; - else to_find = section; - - TRACE_INFO_STRING("Getting Name:", name); - TRACE_INFO_STRING("In Section:", section); - - /* Get Subcollection */ - error = col_get_collection_reference(ini_config, §ion_handle, to_find); - /* Check error */ - if (error && (error != ENOENT)) { - TRACE_ERROR_NUMBER("Failed to get section", error); - return error; - } - - /* Did we find a section */ - if ((error == ENOENT) || (section_handle == NULL)) { - /* We have not found section - return success */ - TRACE_FLOW_STRING("get_value_from_config", "No such section"); - return EOK; - } - - /* Get item */ - error = col_get_item(section_handle, name, - COL_TYPE_STRING, COL_TRAVERSE_ONELEVEL, item); - - /* Make sure we free the section we found */ - col_destroy_collection(section_handle); - - TRACE_FLOW_NUMBER("get_config_item returning", error); - return error; -} - -/* Get long long value from config item */ -static long long get_llong_config_value(struct collection_item *item, - int strict, - long long def, - int *error) -{ - int err; - const char *str; - char *endptr; - long long val = 0; - - TRACE_FLOW_STRING("get_long_config_value", "Entry"); - - /* Do we have the item ? */ - if ((item == NULL) || - (col_get_item_type(item) != COL_TYPE_STRING)) { - TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); - if (error) *error = EINVAL; - return def; - } - - if (error) *error = EOK; - - /* Try to parse the value */ - str = (const char *)col_get_item_data(item); - errno = 0; - val = strtoll(str, &endptr, 10); - err = errno; - - /* Check for various possible errors */ - if (err != 0) { - TRACE_ERROR_NUMBER("Conversion failed", err); - if (error) *error = err; - return def; - } - - /* Other error cases */ - if ((endptr == str) || (strict && (*endptr != '\0'))) { - TRACE_ERROR_NUMBER("More characters or nothing processed", EIO); - if (error) *error = EIO; - return def; - } - - TRACE_FLOW_NUMBER("get_long_config_value returning", (long)val); - return val; -} - -/* Get unsigned long long value from config item */ -static unsigned long long get_ullong_config_value(struct collection_item *item, - int strict, - unsigned long long def, - int *error) -{ - int err; - const char *str; - char *endptr; - unsigned long long val = 0; - - TRACE_FLOW_STRING("get_ullong_config_value", "Entry"); - - /* Do we have the item ? */ - if ((item == NULL) || - (col_get_item_type(item) != COL_TYPE_STRING)) { - TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); - if (error) *error = EINVAL; - return def; - } - - if (error) *error = EOK; - - /* Try to parse the value */ - str = (const char *)col_get_item_data(item); - errno = 0; - val = strtoull(str, &endptr, 10); - err = errno; - - /* Check for various possible errors */ - if (err != 0) { - TRACE_ERROR_NUMBER("Conversion failed", err); - if (error) *error = err; - return def; - } - - /* Other error cases */ - if ((endptr == str) || (strict && (*endptr != '\0'))) { - TRACE_ERROR_NUMBER("More characters or nothing processed", EIO); - if (error) *error = EIO; - return def; - } - - TRACE_FLOW_NUMBER("get_ullong_config_value returning", (long)val); - return val; -} - - -/* Get integer value from config item */ -int get_int_config_value(struct collection_item *item, - int strict, - int def, - int *error) -{ - long long val = 0; - int err = 0; - - TRACE_FLOW_STRING("get_int_config_value", "Entry"); - - val = get_llong_config_value(item, strict, def, &err); - if (err == 0) { - if ((val > INT_MAX) || (val < INT_MIN)) { - val = def; - err = ERANGE; - } - } - - if (error) *error = err; - - TRACE_FLOW_NUMBER("get_int_config_value returning", (int)val); - return (int)val; -} - -/* Get unsigned integer value from config item */ -unsigned get_unsigned_config_value(struct collection_item *item, - int strict, - unsigned def, - int *error) -{ - unsigned long long val = 0; - int err = 0; - - TRACE_FLOW_STRING("get_unsigned_config_value", "Entry"); - - val = get_ullong_config_value(item, strict, def, &err); - if (err == 0) { - if (val > UINT_MAX) { - val = def; - err = ERANGE; - } - } - - if (error) *error = err; - - TRACE_FLOW_NUMBER("get_unsigned_config_value returning", - (unsigned)val); - return (unsigned)val; -} - -/* Get long value from config item */ -long get_long_config_value(struct collection_item *item, - int strict, - long def, - int *error) -{ - long long val = 0; - int err = 0; - - TRACE_FLOW_STRING("get_long_config_value", "Entry"); - - val = get_llong_config_value(item, strict, def, &err); - if (err == 0) { - if ((val > LONG_MAX) || (val < LONG_MIN)) { - val = def; - err = ERANGE; - } - } - - if (error) *error = err; - - TRACE_FLOW_NUMBER("get_long_config_value returning", - (long)val); - return (long)val; -} - -/* Get unsigned long value from config item */ -unsigned long get_ulong_config_value(struct collection_item *item, - int strict, - unsigned long def, - int *error) -{ - unsigned long long val = 0; - int err = 0; - - TRACE_FLOW_STRING("get_ulong_config_value", "Entry"); - - val = get_ullong_config_value(item, strict, def, &err); - if (err == 0) { - if (val > ULONG_MAX) { - val = def; - err = ERANGE; - } - } - - if (error) *error = err; - - TRACE_FLOW_NUMBER("get_ulong_config_value returning", - (unsigned long)val); - return (unsigned long)val; -} - - -/* Get double value */ -double get_double_config_value(struct collection_item *item, - int strict, double def, int *error) -{ - const char *str; - char *endptr; - double val = 0; - - TRACE_FLOW_STRING("get_double_config_value", "Entry"); - - /* Do we have the item ? */ - if ((item == NULL) || - (col_get_item_type(item) != COL_TYPE_STRING)) { - TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); - if (error) *error = EINVAL; - return def; - } - - if (error) *error = EOK; - - /* Try to parse the value */ - str = (const char *)col_get_item_data(item); - errno = 0; - val = strtod(str, &endptr); - - /* Check for various possible errors */ - if ((errno == ERANGE) || - ((errno != 0) && (val == 0)) || - (endptr == str)) { - TRACE_ERROR_NUMBER("Conversion failed", EIO); - if (error) *error = EIO; - return def; - } - - if (strict && (*endptr != '\0')) { - TRACE_ERROR_NUMBER("More characters than expected", EIO); - if (error) *error = EIO; - val = def; - } - - TRACE_FLOW_NUMBER("get_double_config_value returning", val); - return val; -} - -/* Get boolean value */ -unsigned char get_bool_config_value(struct collection_item *item, - unsigned char def, int *error) -{ - const char *str; - int len; - - TRACE_FLOW_STRING("get_bool_config_value", "Entry"); - - /* Do we have the item ? */ - if ((item == NULL) || - (col_get_item_type(item) != COL_TYPE_STRING)) { - TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); - if (error) *error = EINVAL; - return def; - } - - if (error) *error = EOK; - - str = (const char *)col_get_item_data(item); - len = col_get_item_length(item); - - /* Try to parse the value */ - if ((strncasecmp(str, "true", len) == 0) || - (strncasecmp(str, "yes", len) == 0)) { - TRACE_FLOW_STRING("Returning", "true"); - return '\1'; - } - else if ((strncasecmp(str, "false", len) == 0) || - (strncasecmp(str, "no", len) == 0)) { - TRACE_FLOW_STRING("Returning", "false"); - return '\0'; - } - - TRACE_ERROR_STRING("Returning", "error"); - if (error) *error = EIO; - return def; -} - -/* Return a string out of the value */ -char *get_string_config_value(struct collection_item *item, - int *error) -{ - char *str = NULL; - - TRACE_FLOW_STRING("get_string_config_value", "Entry"); - - /* Do we have the item ? */ - if ((item == NULL) || - (col_get_item_type(item) != COL_TYPE_STRING)) { - TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); - if (error) *error = EINVAL; - return NULL; - } - - str = strdup((const char *)col_get_item_data(item)); - if (str == NULL) { - TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); - if (error) *error = ENOMEM; - return NULL; - } - - if (error) *error = EOK; - - TRACE_FLOW_STRING("get_string_config_value returning", str); - return str; -} - -/* Get string from item */ -const char *get_const_string_config_value(struct collection_item *item, int *error) -{ - const char *str; - - TRACE_FLOW_STRING("get_const_string_config_value", "Entry"); - - /* Do we have the item ? */ - if ((item == NULL) || - (col_get_item_type(item) != COL_TYPE_STRING)) { - TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); - if (error) *error = EINVAL; - return NULL; - } - - str = (const char *)col_get_item_data(item); - - if (error) *error = EOK; - - TRACE_FLOW_STRING("get_const_string_config_value returning", str); - return str; -} - -/* A special hex format is assumed. - * The string should be taken in single quotes - * and consist of hex encoded value two hex digits per byte. - * Example: '0A2BFECC' - * Case does not matter. - */ -char *get_bin_config_value(struct collection_item *item, - int *length, int *error) -{ - int i; - char *value = NULL; - const char *buff; - int size = 0; - unsigned char hex; - int len; - const char *str; - - TRACE_FLOW_STRING("get_bin_config_value", "Entry"); - - /* Do we have the item ? */ - if ((item == NULL) || - (col_get_item_type(item) != COL_TYPE_STRING)) { - TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); - if (error) *error = EINVAL; - return NULL; - } - - /* Check the length */ - len = col_get_item_length(item)-1; - if ((len%2) != 0) { - TRACE_ERROR_STRING("Invalid length for binary data", ""); - if (error) *error = EINVAL; - return NULL; - } - - str = (const char *)col_get_item_data(item); - - /* Is the format correct ? */ - if ((*str != '\'') || - (str[len -1] != '\'')) { - TRACE_ERROR_STRING("String is not escaped",""); - if (error) *error = EIO; - return NULL; - } - - /* Check that all the symbols are ok */ - buff = str + 1; - len -= 2; - for (i = 0; i < len; i += 2) { - if (!isxdigit(buff[i]) || !isxdigit(buff[i + 1])) { - TRACE_ERROR_STRING("Invalid encoding for binary data", buff + i); - if (error) *error = EIO; - return NULL; - } - } - - /* The value is good so we can allocate memory for it */ - value = malloc(len / 2); - if (value == NULL) { - TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); - if (error) *error = ENOMEM; - return NULL; - } - - /* Convert the value */ - for (i = 0; i < len; i += 2) { - if (isdigit(buff[i])) { - if (isdigit(buff[i+1])) - hex = 16 * (buff[i] - '0') + (buff[i+1] - '0'); - else - hex = 16 * (buff[i] - '0') + (tolower(buff[i+1]) - 'a' + 10); - } - else { - if (isdigit(buff[i+1])) - hex = 16 * (tolower(buff[i]) - 'a') + (buff[i+1] - '0'); - else - hex = 16 * (tolower(buff[i]) - 'a' + 10) + (tolower(buff[i+1]) - 'a' + 10); - } - - value[size] = (char)(hex); - size++; - } - - if (error) *error = EOK; - if (length) *length = size; - TRACE_FLOW_STRING("get_bin_config_value", "Exit"); - return value; -} - -/* Function to free binary configuration value */ -void free_bin_config_value(char *value) -{ - if (value) free(value); -} - -/* Arrays of stings */ -static char **get_str_cfg_array(struct collection_item *item, - int include, - const char *sep, - int *size, - int *error) -{ - char *copy = NULL; - char *dest = NULL; - char locsep[4]; - int lensep; - char *buff; - int count = 0; - int len = 0; - int resume_len; - char **array; - char *start; - int i, j; - int dlen; - - TRACE_FLOW_STRING("get_str_cfg_array", "Entry"); - - /* Do we have the item ? */ - if ((item == NULL) || - (col_get_item_type(item) != COL_TYPE_STRING)) { - TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); - if (error) *error = EINVAL; - return NULL; - } - - /* Handle the separators */ - if (sep == NULL) { - locsep[0] = ','; - locsep[1] = '\0'; - lensep = 2; - } - else { - strncpy(locsep, sep, 3); - locsep[3] = '\0'; - lensep = strlen(locsep) + 1; - } - - /* Allocate memory for the copy of the string */ - copy = malloc(col_get_item_length(item)); - if (copy == NULL) { - TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); - if (error) *error = ENOMEM; - return NULL; - } - - /* Loop through the string */ - dest = copy; - buff = col_get_item_data(item); - start = buff; - dlen = col_get_item_length(item); - for(i = 0; i < dlen; i++) { - for(j = 0; j < lensep; j++) { - if(buff[i] == locsep[j]) { - /* If we found one of the separators trim spaces around */ - resume_len = len; - while (len > 0) { - if (isspace(start[len - 1])) len--; - else break; - } - TRACE_INFO_STRING("Current:", start); - TRACE_INFO_NUMBER("Length:", len); - if (len > 0) { - /* Save block aside */ - memcpy(dest, start, len); - count++; - dest += len; - *dest = '\0'; - dest++; - } - else if(include) { - count++; - *dest = '\0'; - dest++; - } - if (locsep[j] == '\0') break; /* We are done */ - - /* Move forward and trim spaces if any */ - start += resume_len + 1; - i++; - TRACE_INFO_STRING("Other pointer :", buff + i); - while ((i < dlen) && (isspace(*start))) { - i++; - start++; - } - len = -1; /* Len will be increased in the loop */ - i--; /* i will be increas so we need to step back */ - TRACE_INFO_STRING("Remaining buffer after triming spaces:", start); - break; - } - } - len++; - } - - /* Now we know how many items are there in the list */ - array = malloc((count + 1) * sizeof(char *)); - if (array == NULL) { - free(copy); - TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); - if (error) *error = ENOMEM; - return NULL; - } - - /* Loop again to fill in the pointers */ - start = copy; - for (i = 0; i < count; i++) { - TRACE_INFO_STRING("Token :", start); - TRACE_INFO_NUMBER("Item :", i); - array[i] = start; - /* Move to next item */ - while(*start) start++; - start++; - } - array[count] = NULL; - - if (error) *error = EOK; - if (size) *size = count; - TRACE_FLOW_STRING("get_str_cfg_array", "Exit"); - return array; -} - -/* Get array of strings from item eliminating empty tokens */ -char **get_string_config_array(struct collection_item *item, - const char *sep, int *size, int *error) -{ - TRACE_FLOW_STRING("get_string_config_array", "Called."); - return get_str_cfg_array(item, EXCLUDE_EMPTY, sep, size, error); -} -/* Get array of strings from item preserving empty tokens */ -char **get_raw_string_config_array(struct collection_item *item, - const char *sep, int *size, int *error) -{ - TRACE_FLOW_STRING("get_raw_string_config_array", "Called."); - return get_str_cfg_array(item, INCLUDE_EMPTY, sep, size, error); -} - -/* Special function to free string config array */ -void free_string_config_array(char **str_config) -{ - TRACE_FLOW_STRING("free_string_config_array", "Entry"); - - if (str_config != NULL) { - if (*str_config != NULL) free(*str_config); - free(str_config); - } - - TRACE_FLOW_STRING("free_string_config_array", "Exit"); -} - -/* Get an array of long values. - * NOTE: For now I leave just one function that returns numeric arrays. - * In future if we need other numeric types we can change it to do strtoll - * internally and wrap it for backward compatibility. - */ -long *get_long_config_array(struct collection_item *item, int *size, int *error) -{ - const char *str; - char *endptr; - long val = 0; - long *array; - int count = 0; - int err; - - TRACE_FLOW_STRING("get_long_config_array", "Entry"); - - /* Do we have the item ? */ - if ((item == NULL) || - (col_get_item_type(item) != COL_TYPE_STRING) || - (size == NULL)) { - TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); - if (error) *error = EINVAL; - return NULL; - } - - /* Assume that we have maximum number of different numbers */ - array = (long *)malloc(sizeof(long) * col_get_item_length(item)/2); - if (array == NULL) { - TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); - if (error) *error = ENOMEM; - return NULL; - } - - /* Now parse the string */ - str = (const char *)col_get_item_data(item); - while (*str) { - - errno = 0; - val = strtol(str, &endptr, 10); - err = errno; - - if (err) { - TRACE_ERROR_NUMBER("Conversion failed", err); - free(array); - if (error) *error = err; - return NULL; - } - - if (endptr == str) { - TRACE_ERROR_NUMBER("Nothing processed", EIO); - free(array); - if (error) *error = EIO; - return NULL; - } - - /* Save value */ - array[count] = val; - count++; - /* Are we done? */ - if (*endptr == 0) break; - /* Advance to the next valid number */ - for (str = endptr; *str; str++) { - if (isdigit(*str) || (*str == '-') || (*str == '+')) break; - } - } - - *size = count; - if (error) *error = EOK; - - TRACE_FLOW_NUMBER("get_long_config_value returning", val); - return array; - -} - -/* Get an array of double values */ -double *get_double_config_array(struct collection_item *item, int *size, int *error) -{ - const char *str; - char *endptr; - double val = 0; - double *array; - int count = 0; - struct lconv *loc; - - TRACE_FLOW_STRING("get_double_config_array", "Entry"); - - /* Do we have the item ? */ - if ((item == NULL) || - (col_get_item_type(item) != COL_TYPE_STRING) || - (size == NULL)) { - TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); - if (error) *error = EINVAL; - return NULL; - } - - /* Assume that we have maximum number of different numbers */ - array = (double *)malloc(sizeof(double) * col_get_item_length(item)/2); - if (array == NULL) { - TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); - if (error) *error = ENOMEM; - return NULL; - } - - /* Get locale information so that we can check for decimal point character. - * Based on the man pages it is unclear if this is an allocated memory or not. - * Seems like it is a static thread or process local structure so - * I will not try to free it after use. - */ - loc = localeconv(); - - /* Now parse the string */ - str = (const char *)col_get_item_data(item); - while (*str) { - TRACE_INFO_STRING("String to convert",str); - errno = 0; - val = strtod(str, &endptr); - if ((errno == ERANGE) || - ((errno != 0) && (val == 0)) || - (endptr == str)) { - TRACE_ERROR_NUMBER("Conversion failed", EIO); - free(array); - if (error) *error = EIO; - return NULL; - } - /* Save value */ - array[count] = val; - count++; - /* Are we done? */ - if (*endptr == 0) break; - TRACE_INFO_STRING("End pointer after conversion",endptr); - /* Advance to the next valid number */ - for (str = endptr; *str; str++) { - if (isdigit(*str) || (*str == '-') || (*str == '+') || - /* It is ok to do this since the string is null terminated */ - ((*str == *(loc->decimal_point)) && isdigit(str[1]))) break; - } - } - - *size = count; - if (error) *error = EOK; - - TRACE_FLOW_NUMBER("get_double_config_value returning", val); - return array; - -} - - -/* Special function to free long config array */ -void free_long_config_array(long *array) -{ - if (array != NULL) free(array); -} - -/* Special function to free double config array */ -void free_double_config_array(double *array) -{ - if (array != NULL) free(array); -} - -/* The section array should be freed using this function */ -void free_section_list(char **section_list) -{ - TRACE_FLOW_STRING("free_section_list","Entry"); - - col_free_property_list(section_list); - - TRACE_FLOW_STRING("free_section_list","Exit"); -} - -/* The section array should be freed using this function */ -void free_attribute_list(char **section_list) -{ - TRACE_FLOW_STRING("free_section_list","Entry"); - - col_free_property_list(section_list); - - TRACE_FLOW_STRING("free_section_list","Exit"); -} - - -/* Get list of sections as an array of strings. - * Function allocates memory for the array of the sections. - */ -char **get_section_list(struct collection_item *ini_config, int *size, int *error) -{ - char **list; - - TRACE_FLOW_STRING("get_section_list","Entry"); - /* Do we have the item ? */ - if ((ini_config == NULL) || - ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) && - (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0))) { - TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); - if (error) *error = EINVAL; - return NULL; - } - - /* Pass it to the function from collection API */ - list = col_collection_to_list(ini_config, size, error); - - TRACE_FLOW_STRING("get_section_list returning", ((list == NULL) ? "NULL" : list[0])); - return list; -} - -/* Get list of attributes in a section as an array of strings. - * Function allocates memory for the array of the strings. - */ -char **get_attribute_list(struct collection_item *ini_config, const char *section, int *size, int *error) -{ - struct collection_item *subcollection = NULL; - char **list; - int err; - - TRACE_FLOW_STRING("get_attribute_list","Entry"); - /* Do we have the item ? */ - if ((ini_config == NULL) || - ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) && - (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0)) || - (section == NULL)) { - TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); - if (error) *error = EINVAL; - return NULL; - } - - /* Fetch section */ - err = col_get_collection_reference(ini_config, &subcollection, section); - /* Check error */ - if (err && (subcollection == NULL)) { - TRACE_ERROR_NUMBER("Failed to get section", err); - if (error) *error = EINVAL; - return NULL; - } - - /* Pass it to the function from collection API */ - list = col_collection_to_list(subcollection, size, error); - - col_destroy_collection(subcollection); - - TRACE_FLOW_STRING("get_attribute_list returning", ((list == NULL) ? "NULL" : list[0])); - return list; -} diff --git a/common/ini/ini_config.h b/common/ini/ini_config.h index a5fd4a93..3b58a116 100644 --- a/common/ini/ini_config.h +++ b/common/ini/ini_config.h @@ -23,6 +23,9 @@ #ifndef INI_CONFIG_H #define INI_CONFIG_H +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> #include <limits.h> #include <stdio.h> #include "collection.h" diff --git a/common/ini/ini_defines.h b/common/ini/ini_defines.h new file mode 100644 index 00000000..46ed00f4 --- /dev/null +++ b/common/ini/ini_defines.h @@ -0,0 +1,74 @@ +/* + INI LIBRARY + + Header file for the internal constants for the INI interface. + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2010 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef INI_DEFINES_H +#define INI_DEFINES_H + +#define NAME_OVERHEAD 10 + +#define SLASH "/" + + +/* Name of the special collection used to store parsing errors */ +#define FILE_ERROR_SET "ini_file_error_set" + +/* Text error strings used when errors are printed out */ +#define WARNING_TXT _("Warning") +#define ERROR_TXT _("Error") +/* For parse errors */ +#define WRONG_COLLECTION _("Passed in list is not a list of parse errors.\n") +#define FAILED_TO_PROCCESS _("Internal Error. Failed to process error list.\n") +#define ERROR_HEADER _("Parsing errors and warnings in file: %s\n") +/* For grammar errors */ +#define WRONG_GRAMMAR _("Passed in list is not a list of grammar errors.\n") +#define FAILED_TO_PROC_G _("Internal Error. Failed to process list of grammar errors.\n") +#define ERROR_HEADER_G _("Logical errors and warnings in file: %s\n") +/* For validation errors */ +#define WRONG_VALIDATION _("Passed in list is not a list of validation errors.\n") +#define FAILED_TO_PROC_V _("Internal Error. Failed to process list of validation errors.\n") +#define ERROR_HEADER_V _("Validation errors and warnings in file: %s\n") + +#define LINE_FORMAT _("%s (%d) on line %d: %s\n") + + +/* Codes that parsing function can return */ +#define RET_PAIR 0 +#define RET_COMMENT 1 +#define RET_SECTION 2 +#define RET_INVALID 3 +#define RET_EMPTY 4 +#define RET_EOF 5 +#define RET_ERROR 6 + +#define INI_ERROR "errors" +#define INI_ERROR_NAME "errname" + +/* Internal sizes. MAX_KEY is defined in config.h */ +#define MAX_VALUE PATH_MAX +#define BUFFER_SIZE MAX_KEY + MAX_VALUE + 3 + +/* Beffer length used for int to string conversions */ +#define CONVERSION_BUFFER 80 + +/* Different error string functions can be passed as callbacks */ +typedef const char * (*error_fn)(int error); + +#endif diff --git a/common/ini/ini_get_array.c b/common/ini/ini_get_array.c new file mode 100644 index 00000000..c2c6362a --- /dev/null +++ b/common/ini/ini_get_array.c @@ -0,0 +1,354 @@ +/* + INI LIBRARY + + Value interpretation functions for arrays of values + and corresponding memory cleanup functions. + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2010 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define _GNU_SOURCE +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <locale.h> +#include "config.h" +#include "trace.h" +#include "collection.h" +#include "collection_tools.h" +#include "ini_defines.h" +#include "ini_config.h" + +/* + * Internal contants to indicate how + * to process the lists of strings. + */ +#define EXCLUDE_EMPTY 0 +#define INCLUDE_EMPTY 1 + +/* Arrays of stings */ +static char **get_str_cfg_array(struct collection_item *item, + int include, + const char *sep, + int *size, + int *error) +{ + char *copy = NULL; + char *dest = NULL; + char locsep[4]; + int lensep; + char *buff; + int count = 0; + int len = 0; + int resume_len; + char **array; + char *start; + int i, j; + int dlen; + + TRACE_FLOW_STRING("get_str_cfg_array", "Entry"); + + /* Do we have the item ? */ + if ((item == NULL) || + (col_get_item_type(item) != COL_TYPE_STRING)) { + TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); + if (error) *error = EINVAL; + return NULL; + } + + /* Handle the separators */ + if (sep == NULL) { + locsep[0] = ','; + locsep[1] = '\0'; + lensep = 2; + } + else { + strncpy(locsep, sep, 3); + locsep[3] = '\0'; + lensep = strlen(locsep) + 1; + } + + /* Allocate memory for the copy of the string */ + copy = malloc(col_get_item_length(item)); + if (copy == NULL) { + TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); + if (error) *error = ENOMEM; + return NULL; + } + + /* Loop through the string */ + dest = copy; + buff = col_get_item_data(item); + start = buff; + dlen = col_get_item_length(item); + for(i = 0; i < dlen; i++) { + for(j = 0; j < lensep; j++) { + if(buff[i] == locsep[j]) { + /* If we found one of the separators trim spaces around */ + resume_len = len; + while (len > 0) { + if (isspace(start[len - 1])) len--; + else break; + } + TRACE_INFO_STRING("Current:", start); + TRACE_INFO_NUMBER("Length:", len); + if (len > 0) { + /* Save block aside */ + memcpy(dest, start, len); + count++; + dest += len; + *dest = '\0'; + dest++; + } + else if(include) { + count++; + *dest = '\0'; + dest++; + } + if (locsep[j] == '\0') break; /* We are done */ + + /* Move forward and trim spaces if any */ + start += resume_len + 1; + i++; + TRACE_INFO_STRING("Other pointer :", buff + i); + while ((i < dlen) && (isspace(*start))) { + i++; + start++; + } + len = -1; /* Len will be increased in the loop */ + i--; /* i will be increas so we need to step back */ + TRACE_INFO_STRING("Remaining buffer after triming spaces:", start); + break; + } + } + len++; + } + + /* Now we know how many items are there in the list */ + array = malloc((count + 1) * sizeof(char *)); + if (array == NULL) { + free(copy); + TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); + if (error) *error = ENOMEM; + return NULL; + } + + /* Loop again to fill in the pointers */ + start = copy; + for (i = 0; i < count; i++) { + TRACE_INFO_STRING("Token :", start); + TRACE_INFO_NUMBER("Item :", i); + array[i] = start; + /* Move to next item */ + while(*start) start++; + start++; + } + array[count] = NULL; + + if (error) *error = EOK; + if (size) *size = count; + TRACE_FLOW_STRING("get_str_cfg_array", "Exit"); + return array; +} + +/* Get array of strings from item eliminating empty tokens */ +char **get_string_config_array(struct collection_item *item, + const char *sep, int *size, int *error) +{ + TRACE_FLOW_STRING("get_string_config_array", "Called."); + return get_str_cfg_array(item, EXCLUDE_EMPTY, sep, size, error); +} +/* Get array of strings from item preserving empty tokens */ +char **get_raw_string_config_array(struct collection_item *item, + const char *sep, int *size, int *error) +{ + TRACE_FLOW_STRING("get_raw_string_config_array", "Called."); + return get_str_cfg_array(item, INCLUDE_EMPTY, sep, size, error); +} + +/* Special function to free string config array */ +void free_string_config_array(char **str_config) +{ + TRACE_FLOW_STRING("free_string_config_array", "Entry"); + + if (str_config != NULL) { + if (*str_config != NULL) free(*str_config); + free(str_config); + } + + TRACE_FLOW_STRING("free_string_config_array", "Exit"); +} + +/* Get an array of long values. + * NOTE: For now I leave just one function that returns numeric arrays. + * In future if we need other numeric types we can change it to do strtoll + * internally and wrap it for backward compatibility. + */ +long *get_long_config_array(struct collection_item *item, int *size, int *error) +{ + const char *str; + char *endptr; + long val = 0; + long *array; + int count = 0; + int err; + + TRACE_FLOW_STRING("get_long_config_array", "Entry"); + + /* Do we have the item ? */ + if ((item == NULL) || + (col_get_item_type(item) != COL_TYPE_STRING) || + (size == NULL)) { + TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); + if (error) *error = EINVAL; + return NULL; + } + + /* Assume that we have maximum number of different numbers */ + array = (long *)malloc(sizeof(long) * col_get_item_length(item)/2); + if (array == NULL) { + TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); + if (error) *error = ENOMEM; + return NULL; + } + + /* Now parse the string */ + str = (const char *)col_get_item_data(item); + while (*str) { + + errno = 0; + val = strtol(str, &endptr, 10); + err = errno; + + if (err) { + TRACE_ERROR_NUMBER("Conversion failed", err); + free(array); + if (error) *error = err; + return NULL; + } + + if (endptr == str) { + TRACE_ERROR_NUMBER("Nothing processed", EIO); + free(array); + if (error) *error = EIO; + return NULL; + } + + /* Save value */ + array[count] = val; + count++; + /* Are we done? */ + if (*endptr == 0) break; + /* Advance to the next valid number */ + for (str = endptr; *str; str++) { + if (isdigit(*str) || (*str == '-') || (*str == '+')) break; + } + } + + *size = count; + if (error) *error = EOK; + + TRACE_FLOW_NUMBER("get_long_config_value returning", val); + return array; + +} + +/* Get an array of double values */ +double *get_double_config_array(struct collection_item *item, int *size, int *error) +{ + const char *str; + char *endptr; + double val = 0; + double *array; + int count = 0; + struct lconv *loc; + + TRACE_FLOW_STRING("get_double_config_array", "Entry"); + + /* Do we have the item ? */ + if ((item == NULL) || + (col_get_item_type(item) != COL_TYPE_STRING) || + (size == NULL)) { + TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); + if (error) *error = EINVAL; + return NULL; + } + + /* Assume that we have maximum number of different numbers */ + array = (double *)malloc(sizeof(double) * col_get_item_length(item)/2); + if (array == NULL) { + TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); + if (error) *error = ENOMEM; + return NULL; + } + + /* Get locale information so that we can check for decimal point character. + * Based on the man pages it is unclear if this is an allocated memory or not. + * Seems like it is a static thread or process local structure so + * I will not try to free it after use. + */ + loc = localeconv(); + + /* Now parse the string */ + str = (const char *)col_get_item_data(item); + while (*str) { + TRACE_INFO_STRING("String to convert",str); + errno = 0; + val = strtod(str, &endptr); + if ((errno == ERANGE) || + ((errno != 0) && (val == 0)) || + (endptr == str)) { + TRACE_ERROR_NUMBER("Conversion failed", EIO); + free(array); + if (error) *error = EIO; + return NULL; + } + /* Save value */ + array[count] = val; + count++; + /* Are we done? */ + if (*endptr == 0) break; + TRACE_INFO_STRING("End pointer after conversion",endptr); + /* Advance to the next valid number */ + for (str = endptr; *str; str++) { + if (isdigit(*str) || (*str == '-') || (*str == '+') || + /* It is ok to do this since the string is null terminated */ + ((*str == *(loc->decimal_point)) && isdigit(str[1]))) break; + } + } + + *size = count; + if (error) *error = EOK; + + TRACE_FLOW_NUMBER("get_double_config_value returning", val); + return array; + +} + + +/* Special function to free long config array */ +void free_long_config_array(long *array) +{ + if (array != NULL) free(array); +} + +/* Special function to free double config array */ +void free_double_config_array(double *array) +{ + if (array != NULL) free(array); +} diff --git a/common/ini/ini_get_value.c b/common/ini/ini_get_value.c new file mode 100644 index 00000000..70d940fa --- /dev/null +++ b/common/ini/ini_get_value.c @@ -0,0 +1,523 @@ +/* + INI LIBRARY + + Value interpretation functions for single values + and corresponding memory cleanup functions. + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2010 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define _GNU_SOURCE +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include "config.h" +#include "trace.h" +#include "collection.h" +#include "collection_tools.h" +#include "ini_defines.h" +#include "ini_config.h" + +/* Function to get value from the configuration handle */ +int get_config_item(const char *section, + const char *name, + struct collection_item *ini_config, + struct collection_item **item) +{ + int error = EOK; + struct collection_item *section_handle = NULL; + const char *to_find; + char default_section[] = INI_DEFAULT_SECTION; + + TRACE_FLOW_STRING("get_config_item", "Entry"); + + /* Do we have the accepting memory ? */ + if (item == NULL) { + TRACE_ERROR_NUMBER("No buffer - invalid argument.", EINVAL); + return EINVAL; + } + + /* Is the collection of a right type */ + if ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) && + (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0)) { + TRACE_ERROR_NUMBER("Wrong collection type", EINVAL); + return EINVAL; + } + + *item = NULL; + + if (section == NULL) to_find = default_section; + else to_find = section; + + TRACE_INFO_STRING("Getting Name:", name); + TRACE_INFO_STRING("In Section:", section); + + /* Get Subcollection */ + error = col_get_collection_reference(ini_config, §ion_handle, to_find); + /* Check error */ + if (error && (error != ENOENT)) { + TRACE_ERROR_NUMBER("Failed to get section", error); + return error; + } + + /* Did we find a section */ + if ((error == ENOENT) || (section_handle == NULL)) { + /* We have not found section - return success */ + TRACE_FLOW_STRING("get_value_from_config", "No such section"); + return EOK; + } + + /* Get item */ + error = col_get_item(section_handle, name, + COL_TYPE_STRING, COL_TRAVERSE_ONELEVEL, item); + + /* Make sure we free the section we found */ + col_destroy_collection(section_handle); + + TRACE_FLOW_NUMBER("get_config_item returning", error); + return error; +} + +/* Get long long value from config item */ +static long long get_llong_config_value(struct collection_item *item, + int strict, + long long def, + int *error) +{ + int err; + const char *str; + char *endptr; + long long val = 0; + + TRACE_FLOW_STRING("get_long_config_value", "Entry"); + + /* Do we have the item ? */ + if ((item == NULL) || + (col_get_item_type(item) != COL_TYPE_STRING)) { + TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); + if (error) *error = EINVAL; + return def; + } + + if (error) *error = EOK; + + /* Try to parse the value */ + str = (const char *)col_get_item_data(item); + errno = 0; + val = strtoll(str, &endptr, 10); + err = errno; + + /* Check for various possible errors */ + if (err != 0) { + TRACE_ERROR_NUMBER("Conversion failed", err); + if (error) *error = err; + return def; + } + + /* Other error cases */ + if ((endptr == str) || (strict && (*endptr != '\0'))) { + TRACE_ERROR_NUMBER("More characters or nothing processed", EIO); + if (error) *error = EIO; + return def; + } + + TRACE_FLOW_NUMBER("get_long_config_value returning", (long)val); + return val; +} + +/* Get unsigned long long value from config item */ +static unsigned long long get_ullong_config_value(struct collection_item *item, + int strict, + unsigned long long def, + int *error) +{ + int err; + const char *str; + char *endptr; + unsigned long long val = 0; + + TRACE_FLOW_STRING("get_ullong_config_value", "Entry"); + + /* Do we have the item ? */ + if ((item == NULL) || + (col_get_item_type(item) != COL_TYPE_STRING)) { + TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); + if (error) *error = EINVAL; + return def; + } + + if (error) *error = EOK; + + /* Try to parse the value */ + str = (const char *)col_get_item_data(item); + errno = 0; + val = strtoull(str, &endptr, 10); + err = errno; + + /* Check for various possible errors */ + if (err != 0) { + TRACE_ERROR_NUMBER("Conversion failed", err); + if (error) *error = err; + return def; + } + + /* Other error cases */ + if ((endptr == str) || (strict && (*endptr != '\0'))) { + TRACE_ERROR_NUMBER("More characters or nothing processed", EIO); + if (error) *error = EIO; + return def; + } + + TRACE_FLOW_NUMBER("get_ullong_config_value returning", (long)val); + return val; +} + + +/* Get integer value from config item */ +int get_int_config_value(struct collection_item *item, + int strict, + int def, + int *error) +{ + long long val = 0; + int err = 0; + + TRACE_FLOW_STRING("get_int_config_value", "Entry"); + + val = get_llong_config_value(item, strict, def, &err); + if (err == 0) { + if ((val > INT_MAX) || (val < INT_MIN)) { + val = def; + err = ERANGE; + } + } + + if (error) *error = err; + + TRACE_FLOW_NUMBER("get_int_config_value returning", (int)val); + return (int)val; +} + +/* Get unsigned integer value from config item */ +unsigned get_unsigned_config_value(struct collection_item *item, + int strict, + unsigned def, + int *error) +{ + unsigned long long val = 0; + int err = 0; + + TRACE_FLOW_STRING("get_unsigned_config_value", "Entry"); + + val = get_ullong_config_value(item, strict, def, &err); + if (err == 0) { + if (val > UINT_MAX) { + val = def; + err = ERANGE; + } + } + + if (error) *error = err; + + TRACE_FLOW_NUMBER("get_unsigned_config_value returning", + (unsigned)val); + return (unsigned)val; +} + +/* Get long value from config item */ +long get_long_config_value(struct collection_item *item, + int strict, + long def, + int *error) +{ + long long val = 0; + int err = 0; + + TRACE_FLOW_STRING("get_long_config_value", "Entry"); + + val = get_llong_config_value(item, strict, def, &err); + if (err == 0) { + if ((val > LONG_MAX) || (val < LONG_MIN)) { + val = def; + err = ERANGE; + } + } + + if (error) *error = err; + + TRACE_FLOW_NUMBER("get_long_config_value returning", + (long)val); + return (long)val; +} + +/* Get unsigned long value from config item */ +unsigned long get_ulong_config_value(struct collection_item *item, + int strict, + unsigned long def, + int *error) +{ + unsigned long long val = 0; + int err = 0; + + TRACE_FLOW_STRING("get_ulong_config_value", "Entry"); + + val = get_ullong_config_value(item, strict, def, &err); + if (err == 0) { + if (val > ULONG_MAX) { + val = def; + err = ERANGE; + } + } + + if (error) *error = err; + + TRACE_FLOW_NUMBER("get_ulong_config_value returning", + (unsigned long)val); + return (unsigned long)val; +} + + +/* Get double value */ +double get_double_config_value(struct collection_item *item, + int strict, double def, int *error) +{ + const char *str; + char *endptr; + double val = 0; + + TRACE_FLOW_STRING("get_double_config_value", "Entry"); + + /* Do we have the item ? */ + if ((item == NULL) || + (col_get_item_type(item) != COL_TYPE_STRING)) { + TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); + if (error) *error = EINVAL; + return def; + } + + if (error) *error = EOK; + + /* Try to parse the value */ + str = (const char *)col_get_item_data(item); + errno = 0; + val = strtod(str, &endptr); + + /* Check for various possible errors */ + if ((errno == ERANGE) || + ((errno != 0) && (val == 0)) || + (endptr == str)) { + TRACE_ERROR_NUMBER("Conversion failed", EIO); + if (error) *error = EIO; + return def; + } + + if (strict && (*endptr != '\0')) { + TRACE_ERROR_NUMBER("More characters than expected", EIO); + if (error) *error = EIO; + val = def; + } + + TRACE_FLOW_NUMBER("get_double_config_value returning", val); + return val; +} + +/* Get boolean value */ +unsigned char get_bool_config_value(struct collection_item *item, + unsigned char def, int *error) +{ + const char *str; + int len; + + TRACE_FLOW_STRING("get_bool_config_value", "Entry"); + + /* Do we have the item ? */ + if ((item == NULL) || + (col_get_item_type(item) != COL_TYPE_STRING)) { + TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); + if (error) *error = EINVAL; + return def; + } + + if (error) *error = EOK; + + str = (const char *)col_get_item_data(item); + len = col_get_item_length(item); + + /* Try to parse the value */ + if ((strncasecmp(str, "true", len) == 0) || + (strncasecmp(str, "yes", len) == 0)) { + TRACE_FLOW_STRING("Returning", "true"); + return '\1'; + } + else if ((strncasecmp(str, "false", len) == 0) || + (strncasecmp(str, "no", len) == 0)) { + TRACE_FLOW_STRING("Returning", "false"); + return '\0'; + } + + TRACE_ERROR_STRING("Returning", "error"); + if (error) *error = EIO; + return def; +} + +/* Return a string out of the value */ +char *get_string_config_value(struct collection_item *item, + int *error) +{ + char *str = NULL; + + TRACE_FLOW_STRING("get_string_config_value", "Entry"); + + /* Do we have the item ? */ + if ((item == NULL) || + (col_get_item_type(item) != COL_TYPE_STRING)) { + TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); + if (error) *error = EINVAL; + return NULL; + } + + str = strdup((const char *)col_get_item_data(item)); + if (str == NULL) { + TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); + if (error) *error = ENOMEM; + return NULL; + } + + if (error) *error = EOK; + + TRACE_FLOW_STRING("get_string_config_value returning", str); + return str; +} + +/* Get string from item */ +const char *get_const_string_config_value(struct collection_item *item, int *error) +{ + const char *str; + + TRACE_FLOW_STRING("get_const_string_config_value", "Entry"); + + /* Do we have the item ? */ + if ((item == NULL) || + (col_get_item_type(item) != COL_TYPE_STRING)) { + TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); + if (error) *error = EINVAL; + return NULL; + } + + str = (const char *)col_get_item_data(item); + + if (error) *error = EOK; + + TRACE_FLOW_STRING("get_const_string_config_value returning", str); + return str; +} + +/* A special hex format is assumed. + * The string should be taken in single quotes + * and consist of hex encoded value two hex digits per byte. + * Example: '0A2BFECC' + * Case does not matter. + */ +char *get_bin_config_value(struct collection_item *item, + int *length, int *error) +{ + int i; + char *value = NULL; + const char *buff; + int size = 0; + unsigned char hex; + int len; + const char *str; + + TRACE_FLOW_STRING("get_bin_config_value", "Entry"); + + /* Do we have the item ? */ + if ((item == NULL) || + (col_get_item_type(item) != COL_TYPE_STRING)) { + TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); + if (error) *error = EINVAL; + return NULL; + } + + /* Check the length */ + len = col_get_item_length(item)-1; + if ((len%2) != 0) { + TRACE_ERROR_STRING("Invalid length for binary data", ""); + if (error) *error = EINVAL; + return NULL; + } + + str = (const char *)col_get_item_data(item); + + /* Is the format correct ? */ + if ((*str != '\'') || + (str[len -1] != '\'')) { + TRACE_ERROR_STRING("String is not escaped",""); + if (error) *error = EIO; + return NULL; + } + + /* Check that all the symbols are ok */ + buff = str + 1; + len -= 2; + for (i = 0; i < len; i += 2) { + if (!isxdigit(buff[i]) || !isxdigit(buff[i + 1])) { + TRACE_ERROR_STRING("Invalid encoding for binary data", buff + i); + if (error) *error = EIO; + return NULL; + } + } + + /* The value is good so we can allocate memory for it */ + value = malloc(len / 2); + if (value == NULL) { + TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM); + if (error) *error = ENOMEM; + return NULL; + } + + /* Convert the value */ + for (i = 0; i < len; i += 2) { + if (isdigit(buff[i])) { + if (isdigit(buff[i+1])) + hex = 16 * (buff[i] - '0') + (buff[i+1] - '0'); + else + hex = 16 * (buff[i] - '0') + (tolower(buff[i+1]) - 'a' + 10); + } + else { + if (isdigit(buff[i+1])) + hex = 16 * (tolower(buff[i]) - 'a') + (buff[i+1] - '0'); + else + hex = 16 * (tolower(buff[i]) - 'a' + 10) + (tolower(buff[i+1]) - 'a' + 10); + } + + value[size] = (char)(hex); + size++; + } + + if (error) *error = EOK; + if (length) *length = size; + TRACE_FLOW_STRING("get_bin_config_value", "Exit"); + return value; +} + +/* Function to free binary configuration value */ +void free_bin_config_value(char *value) +{ + if (value) free(value); +} diff --git a/common/ini/ini_list.c b/common/ini/ini_list.c new file mode 100644 index 00000000..95894a15 --- /dev/null +++ b/common/ini/ini_list.c @@ -0,0 +1,123 @@ +/* + INI LIBRARY + + Value interpretation functions for single values + and corresponding memory cleanup functions. + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2010 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define _GNU_SOURCE +#include <stdio.h> +#include <errno.h> +/* +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <locale.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> +*/ +#include "config.h" +#include "trace.h" +#include "collection.h" +#include "collection_tools.h" +#include "ini_config.h" + + +/* The section array should be freed using this function */ +void free_section_list(char **section_list) +{ + TRACE_FLOW_STRING("free_section_list","Entry"); + + col_free_property_list(section_list); + + TRACE_FLOW_STRING("free_section_list","Exit"); +} + +/* The section array should be freed using this function */ +void free_attribute_list(char **section_list) +{ + TRACE_FLOW_STRING("free_attribute_list","Entry"); + + col_free_property_list(section_list); + + TRACE_FLOW_STRING("free_attribute_list","Exit"); +} + + +/* Get list of sections as an array of strings. + * Function allocates memory for the array of the sections. + */ +char **get_section_list(struct collection_item *ini_config, int *size, int *error) +{ + char **list; + + TRACE_FLOW_STRING("get_section_list","Entry"); + /* Do we have the item ? */ + if ((ini_config == NULL) || + ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) && + (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0))) { + TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); + if (error) *error = EINVAL; + return NULL; + } + + /* Pass it to the function from collection API */ + list = col_collection_to_list(ini_config, size, error); + + TRACE_FLOW_STRING("get_section_list returning", ((list == NULL) ? "NULL" : list[0])); + return list; +} + +/* Get list of attributes in a section as an array of strings. + * Function allocates memory for the array of the strings. + */ +char **get_attribute_list(struct collection_item *ini_config, const char *section, int *size, int *error) +{ + struct collection_item *subcollection = NULL; + char **list; + int err; + + TRACE_FLOW_STRING("get_attribute_list","Entry"); + /* Do we have the item ? */ + if ((ini_config == NULL) || + ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) && + (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0)) || + (section == NULL)) { + TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); + if (error) *error = EINVAL; + return NULL; + } + + /* Fetch section */ + err = col_get_collection_reference(ini_config, &subcollection, section); + /* Check error */ + if (err && (subcollection == NULL)) { + TRACE_ERROR_NUMBER("Failed to get section", err); + if (error) *error = EINVAL; + return NULL; + } + + /* Pass it to the function from collection API */ + list = col_collection_to_list(subcollection, size, error); + + col_destroy_collection(subcollection); + + TRACE_FLOW_STRING("get_attribute_list returning", ((list == NULL) ? "NULL" : list[0])); + return list; +} diff --git a/common/ini/ini_parse.c b/common/ini/ini_parse.c new file mode 100644 index 00000000..be733d61 --- /dev/null +++ b/common/ini/ini_parse.c @@ -0,0 +1,190 @@ +/* + INI LIBRARY + + Low level parsing functions + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2010 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define _GNU_SOURCE +#include <errno.h> +#include <string.h> +#include <ctype.h> +#include "config.h" +#include "trace.h" +#include "ini_parse.h" +#include "ini_defines.h" +#include "ini_config.h" + + +/* Reads a line from the file */ +int read_line(FILE *file, + char *buf, + int read_size, + char **key, char **value, + int *length, + int *ext_error) +{ + + char *res; + int len; + char *buffer; + int i; + char *eq; + + TRACE_FLOW_STRING("read_line", "Entry"); + + *ext_error = 0; + + buffer = buf; + + /* Get data from file */ + res = fgets(buffer, read_size - 1, file); + if (res == NULL) { + TRACE_ERROR_STRING("Read nothing", ""); + return RET_EOF; + } + + /* Make sure the buffer is NULL terminated */ + buffer[read_size - 1] = '\0'; + + len = strlen(buffer); + if (len == 0) { + TRACE_ERROR_STRING("Nothing was read.", ""); + return RET_EMPTY; + } + + /* Added \r just in case we deal with Windows in future */ + if ((buffer[len - 1] != '\n') && (buffer[len - 1] != '\r')) { + TRACE_ERROR_STRING("String it too big!", ""); + *ext_error = ERR_LONGDATA; + return RET_ERROR; + } + + /* Ingnore comments */ + if ((*buffer == ';') || (*buffer == '#')) { + TRACE_FLOW_STRING("Comment", buf); + return RET_COMMENT; + } + + TRACE_INFO_STRING("BUFFER before trimming:", buffer); + + /* Trucate trailing spaces and CRs */ + /* Make sure not to step before the beginning */ + while (len && isspace(buffer[len - 1])) { + buffer[len - 1] = '\0'; + len--; + } + + TRACE_INFO_STRING("BUFFER after trimming trailing spaces:", buffer); + + /* Trucate leading spaces */ + while (isspace(*buffer)) { + buffer++; + len--; + } + + TRACE_INFO_STRING("BUFFER after trimming leading spaces:", buffer); + TRACE_INFO_NUMBER("BUFFER length:", len); + + /* Empty line */ + if (len == 0) { + TRACE_FLOW_STRING("Empty line", buf); + return RET_EMPTY; + } + + /* Section */ + if (*buffer == '[') { + if (buffer[len-1] != ']') { + TRACE_ERROR_STRING("Invalid format for section", buf); + *ext_error = ERR_NOCLOSESEC; + return RET_ERROR; + } + buffer++; + len--; + while (isspace(*buffer)) { + buffer++; + len--; + } + if (len == 0) { + TRACE_ERROR_STRING("Invalid format for section", buf); + *ext_error = ERR_NOSECTION; + return RET_ERROR; + } + + buffer[len - 1] = '\0'; + len--; + while (isspace(buffer[len - 1])) { + buffer[len - 1] = '\0'; + len--; + } + if (len >= MAX_KEY) { + TRACE_ERROR_STRING("Section name is too long", buf); + *ext_error = ERR_SECTIONLONG; + return RET_ERROR; + } + + *key = buffer; + return RET_SECTION; + } + + /* Assume we are dealing with the K-V here */ + /* Find "=" */ + eq = strchr(buffer, '='); + if (eq == NULL) { + TRACE_ERROR_STRING("No equal sign", buf); + *ext_error = ERR_NOEQUAL; + return RET_INVALID; + } + + len -= eq-buffer; + + /* Strip spaces around "=" */ + i = eq - buffer - 1; + while ((i >= 0) && isspace(buffer[i])) i--; + if (i < 0) { + TRACE_ERROR_STRING("No key", buf); + *ext_error = ERR_NOKEY; + return RET_INVALID; + } + + /* Copy key into provided buffer */ + if(i >= MAX_KEY) { + TRACE_ERROR_STRING("Section name is too long", buf); + *ext_error = ERR_LONGKEY; + return RET_INVALID; + } + *key = buffer; + buffer[i + 1] = '\0'; + TRACE_INFO_STRING("KEY:", *key); + + eq++; + len--; + while (isspace(*eq)) { + eq++; + len--; + } + + *value = eq; + /* Make sure we include trailing 0 into data */ + *length = len + 1; + + TRACE_INFO_STRING("VALUE:", *value); + TRACE_INFO_NUMBER("LENGTH:", *length); + + TRACE_FLOW_STRING("read_line", "Exit"); + return RET_PAIR; +} diff --git a/common/ini/ini_parse.h b/common/ini/ini_parse.h new file mode 100644 index 00000000..56a0db92 --- /dev/null +++ b/common/ini/ini_parse.h @@ -0,0 +1,36 @@ +/* + INI LIBRARY + + Header file for the internal parsing functions. + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2010 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef INI_PARSE_H +#define INI_PARSE_H + +#include <stdio.h> + +/* Internal function to read line from INI file */ +int read_line(FILE *file, + char *buf, + int read_size, + char **key, + char **value, + int *length, + int *ext_error); + +#endif diff --git a/common/ini/ini_print.c b/common/ini/ini_print.c new file mode 100644 index 00000000..e8f9186b --- /dev/null +++ b/common/ini/ini_print.c @@ -0,0 +1,387 @@ +/* + INI LIBRARY + + Parsing functions of the INI interface + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2009 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define _GNU_SOURCE +#include <stdio.h> +#include <errno.h> +#include "config.h" +/* For error text */ +#include <libintl.h> +#define _(String) gettext (String) +/* INI file is used as a collection */ +#include "trace.h" +#include "collection.h" +#include "collection_tools.h" +#include "ini_defines.h" +#include "ini_config.h" + + +/*============================================================*/ +/* The following classes moved here from the public header + * They are reserved for future use. + * + * NOTE: before exposing these constants again in the common header + * check that the class IDs did not get reused over time by + * other classes. + */ +/** @brief Collection of grammar errors. + * + * Reserved for future use. + */ +#define COL_CLASS_INI_GERROR COL_CLASS_INI_BASE + 5 +/** @brief Collection of validation errors. + * + * Reserved for future use. + */ +#define COL_CLASS_INI_VERROR COL_CLASS_INI_BASE + 6 + +#ifdef HAVE_VALIDATION + +/** @brief Collection of lines from the INI file. + * + * Reserved for future use + */ +#define COL_CLASS_INI_LINES COL_CLASS_INI_BASE + 7 + +#endif /* HAVE_VALIDATION */ +/*============================================================*/ + + +/* Function to return parsing error */ +const char *parsing_error_str(int parsing_error) +{ + const char *placeholder= _("Unknown pasing error."); + const char *str_error[] = { _("Data is too long."), + _("No closing bracket."), + _("Section name is missing."), + _("Section name is too long."), + _("Equal sign is missing."), + _("Property name is missing."), + _("Property name is too long.") + }; + + /* Check the range */ + if ((parsing_error < 1) || (parsing_error > ERR_MAXPARSE)) + return placeholder; + else + return str_error[parsing_error-1]; +} + +/* Function to return grammar error. + * This function is currently not used. + * It is planned to be used by the INI + * file grammar parser. + * + * The following doxygen description is moved here. + * When the function gets exposed move it into + * the header file. + */ +/** @brief Function to return a grammar error in template. + * + * EXPERIMENTAL. Reserved for future use. + * + * This error is returned when the template + * is translated into the grammar object. + * + * @param[in] parsing_error Error code for the grammar error. + * + * @return Error string. + */ + +const char *grammar_error_str(int grammar_error) +{ + const char *placeholder= _("Unknown grammar error."); + /* THIS IS A TEMPORARY PLACEHOLDER !!!! */ + const char *str_error[] = { _(""), + _(""), + _(""), + _(""), + _(""), + _(""), + _("") + }; + + /* Check the range */ + if ((grammar_error < 1) || (grammar_error > ERR_MAXGRAMMAR)) + return placeholder; + else + return str_error[grammar_error-1]; +} + +/* Function to return validation error. + * This function is currently not used. + * It is planned to be used by the INI + * file grammar validator. + * + * The following doxygen description is moved here. + * When the function gets exposed move it into + * the header file. + */ +/** @brief Function to return a validation error. + * + * EXPERIMENTAL. Reserved for future use. + * + * This is the error that it is returned when + * the INI file is validated against the + * grammar object. + * + * @param[in] parsing_error Error code for the validation error. + * + * @return Error string. + */ +const char *validation_error_str(int validation_error) +{ + const char *placeholder= _("Unknown validation error."); + /* THIS IS A TEMPORARY PLACEHOLDER !!!! */ + const char *str_error[] = { _(""), + _(""), + _(""), + _(""), + _(""), + _(""), + _("") + }; + + /* Check the range */ + if ((validation_error < 1) || (validation_error > ERR_MAXVALID)) + return placeholder; + else + return str_error[validation_error-1]; +} + + + +/* Internal function that prints errors */ +static void print_error_list(FILE *file, + struct collection_item *error_list, + int cclass, + char *wrong_col_error, + char *failed_to_process, + char *error_header, + char *line_format, + error_fn error_function) +{ + struct collection_iterator *iterator; + int error; + struct collection_item *item = NULL; + struct parse_error *pe; + unsigned int count; + + TRACE_FLOW_STRING("print_error_list", "Entry"); + + /* If we have something to print print it */ + if (error_list == NULL) { + TRACE_ERROR_STRING("No error list",""); + return; + } + + /* Make sure we go the right collection */ + if (!col_is_of_class(error_list, cclass)) { + TRACE_ERROR_STRING("Wrong collection class:", wrong_col_error); + fprintf(file,"%s\n", wrong_col_error); + return; + } + + /* Bind iterator */ + error = col_bind_iterator(&iterator, error_list, COL_TRAVERSE_DEFAULT); + if (error) { + TRACE_ERROR_STRING("Error (bind):", failed_to_process); + fprintf(file, "%s\n", failed_to_process); + return; + } + + while(1) { + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + TRACE_ERROR_STRING("Error (iterate):", failed_to_process); + fprintf(file, "%s\n", failed_to_process); + col_unbind_iterator(iterator); + return; + } + + /* Are we done ? */ + if (item == NULL) break; + + /* Process collection header */ + if (col_get_item_type(item) == COL_TYPE_COLLECTION) { + col_get_collection_count(item, &count); + if (count <= 2) break; + } else if (col_get_item_type(item) == COL_TYPE_STRING) { + fprintf(file, error_header, (char *)col_get_item_data(item)); + } + else { + /* Put error into provided format */ + pe = (struct parse_error *)(col_get_item_data(item)); + fprintf(file, line_format, + col_get_item_property(item, NULL), /* Error or warning */ + pe->error, /* Error */ + pe->line, /* Line */ + error_function(pe->error)); /* Error str */ + } + + } + + /* Do not forget to unbind iterator - otherwise there will be a leak */ + col_unbind_iterator(iterator); + + TRACE_FLOW_STRING("print_error_list", "Exit"); +} + +/* Print errors and warnings that were detected while parsing one file */ +void print_file_parsing_errors(FILE *file, + struct collection_item *error_list) +{ + print_error_list(file, + error_list, + COL_CLASS_INI_PERROR, + WRONG_COLLECTION, + FAILED_TO_PROCCESS, + ERROR_HEADER, + LINE_FORMAT, + parsing_error_str); +} + + +/* Print errors and warnings that were detected while processing grammar. + * + * The following doxygen description is moved here. + * When the function gets exposed move it into + * the header file. + */ +/** + * @brief Print errors and warnings that were detected while + * checking grammar of the template. + * + * EXPERIMENTAL. Reserved for future use. + * + * @param[in] file File descriptor. + * @param[in] error_list List of the parsing errors. + * + */ +void print_grammar_errors(FILE *file, + struct collection_item *error_list) +{ + print_error_list(file, + error_list, + COL_CLASS_INI_GERROR, + WRONG_GRAMMAR, + FAILED_TO_PROC_G, + ERROR_HEADER_G, + LINE_FORMAT, + grammar_error_str); +} + +/* Print errors and warnings that were detected while validating INI file. + * + * The following doxygen description is moved here. + * When the function gets exposed move it into + * the header file. + */ +/** + * @brief Print errors and warnings that were detected while + * checking INI file against the grammar object. + * + * EXPERIMENTAL. Reserved for future use. + * + * @param[in] file File descriptor. + * @param[in] error_list List of the parsing errors. + * + */ +void print_validation_errors(FILE *file, + struct collection_item *error_list) +{ + print_error_list(file, + error_list, + COL_CLASS_INI_VERROR, + WRONG_VALIDATION, + FAILED_TO_PROC_V, + ERROR_HEADER_V, + LINE_FORMAT, + validation_error_str); +} + +/* Print errors and warnings that were detected while parsing + * the whole configuration */ +void print_config_parsing_errors(FILE *file, + struct collection_item *error_list) +{ + struct collection_iterator *iterator; + int error; + struct collection_item *item = NULL; + struct collection_item *file_errors = NULL; + + TRACE_FLOW_STRING("print_config_parsing_errors", "Entry"); + + /* If we have something to print print it */ + if (error_list == NULL) { + TRACE_ERROR_STRING("No error list", ""); + return; + } + + /* Make sure we go the right collection */ + if (!col_is_of_class(error_list, COL_CLASS_INI_PESET)) { + TRACE_ERROR_STRING("Wrong collection class:", WRONG_COLLECTION); + fprintf(file, "%s\n", WRONG_COLLECTION); + return; + } + + /* Bind iterator */ + error = col_bind_iterator(&iterator, error_list, COL_TRAVERSE_DEFAULT); + if (error) { + TRACE_ERROR_STRING("Error (bind):", FAILED_TO_PROCCESS); + fprintf(file,"%s\n", FAILED_TO_PROCCESS); + return; + } + + while(1) { + /* Loop through a collection */ + error = col_iterate_collection(iterator, &item); + if (error) { + TRACE_ERROR_STRING("Error (iterate):", FAILED_TO_PROCCESS); + fprintf(file, "%s\n", FAILED_TO_PROCCESS); + col_unbind_iterator(iterator); + return; + } + + /* Are we done ? */ + if (item == NULL) break; + + /* Print per file sets of errors */ + if (col_get_item_type(item) == COL_TYPE_COLLECTIONREF) { + /* Extract a sub collection */ + error = col_get_reference_from_item(item, &file_errors); + if (error) { + TRACE_ERROR_STRING("Error (extract):", FAILED_TO_PROCCESS); + fprintf(file, "%s\n", FAILED_TO_PROCCESS); + col_unbind_iterator(iterator); + return; + } + print_file_parsing_errors(file, file_errors); + col_destroy_collection(file_errors); + } + } + + /* Do not forget to unbind iterator - otherwise there will be a leak */ + col_unbind_iterator(iterator); + + TRACE_FLOW_STRING("print_config_parsing_errors", "Exit"); +} |