@@ -35,6 +35,7 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
+#include <rte_common.h>
#include <rte_string_fns.h>
#include "rte_cfgfile.h"
@@ -58,6 +59,25 @@ struct rte_cfgfile {
* for new entries do we add in */
#define CFG_ALLOC_ENTRY_BATCH 16
+/**
+ * Default cfgfile load parameters.
+ */
+static const struct rte_cfgfile_parameters default_cfgfile_params = {
+ .comment_character = CFG_DEFAULT_COMMENT_CHARACTER,
+};
+
+/**
+ * Defines the list of acceptable comment characters supported by this
+ * library.
+ */
+static const char valid_comment_chars[] = {
+ '!',
+ '#',
+ '%',
+ ';',
+ '@'
+};
+
static unsigned
_strip(char *str, unsigned len)
{
@@ -85,9 +105,45 @@ _strip(char *str, unsigned len)
return newlen;
}
+static int
+rte_cfgfile_check_params(const struct rte_cfgfile_parameters *params)
+{
+ unsigned int valid_comment;
+ unsigned int i;
+
+ if (!params) {
+ printf("Error - missing cfgfile parameters\n");
+ return -EINVAL;
+ }
+
+ valid_comment = 0;
+ for (i = 0; i < RTE_DIM(valid_comment_chars); i++) {
+ if (params->comment_character == valid_comment_chars[i]) {
+ valid_comment = 1;
+ break;
+ }
+ }
+
+ if (valid_comment == 0) {
+ printf("Error - invalid comment characters %c\n",
+ params->comment_character);
+ return -ENOTSUP;
+ }
+
+ return 0;
+}
+
struct rte_cfgfile *
rte_cfgfile_load(const char *filename, int flags)
{
+ return rte_cfgfile_load_with_params(filename, flags,
+ &default_cfgfile_params);
+}
+
+struct rte_cfgfile *
+rte_cfgfile_load_with_params(const char *filename, int flags,
+ const struct rte_cfgfile_parameters *params)
+{
int allocated_sections = CFG_ALLOC_SECTION_BATCH;
int allocated_entries = 0;
int curr_section = -1;
@@ -96,6 +152,9 @@ rte_cfgfile_load(const char *filename, int flags)
int lineno = 0;
struct rte_cfgfile *cfg = NULL;
+ if (rte_cfgfile_check_params(params))
+ return NULL;
+
FILE *f = fopen(filename, "r");
if (f == NULL)
return NULL;
@@ -132,7 +191,7 @@ rte_cfgfile_load(const char *filename, int flags)
"Check if line too long\n", lineno);
goto error1;
}
- pos = memchr(buffer, ';', sizeof(buffer));
+ pos = memchr(buffer, params->comment_character, sizeof(buffer));
if (pos != NULL) {
*pos = '\0';
len = pos - buffer;
@@ -66,6 +66,12 @@ struct rte_cfgfile_entry {
char value[CFG_VALUE_LEN]; /**< Value */
};
+/** Configuration file operation optional arguments */
+struct rte_cfgfile_parameters {
+ char comment_character;
+ /**< Config file comment character; one of '!', '#', '%', ';', '@' */
+};
+
/**@{ cfgfile load operation flags */
/**
* Indicates that the file supports key value entries before the first defined
@@ -74,6 +80,9 @@ struct rte_cfgfile_entry {
#define CFG_FLAG_GLOBAL_SECTION (1 << 0)
/**@} */
+/** Defines the default comment character used for parsing config files. */
+#define CFG_DEFAULT_COMMENT_CHARACTER ';'
+
/**
* Open config file
*
@@ -87,6 +96,26 @@ struct rte_cfgfile_entry {
struct rte_cfgfile *rte_cfgfile_load(const char *filename, int flags);
/**
+ * Open config file with specified optional parameters. Use @see
+ * rte_cfgfile_init_parameters to setup the default parameters.
+ *
+ * @param filename
+ * Config file name
+ * @param flags
+ * Config file flags
+ * @param params
+ * Additional configuration attributes. Must be configured with default
+ * values prior to invoking this API.
+ * @return
+ * Handle to configuration file on success, NULL otherwise
+ * @param
+ *
+ */
+struct rte_cfgfile *rte_cfgfile_load_with_params(const char *filename,
+ int flags, const struct rte_cfgfile_parameters *params);
+
+
+/**
* Get number of sections in config file
*
* @param cfg
@@ -130,6 +130,30 @@ test_cfgfile_sample1(void)
}
static int
+test_cfgfile_sample2(void)
+{
+ struct rte_cfgfile_parameters params;
+ struct rte_cfgfile *cfgfile;
+ int ret;
+
+ /* override comment character */
+ memset(¶ms, 0, sizeof(params));
+ params.comment_character = '#';
+
+ cfgfile = rte_cfgfile_load_with_params(CFG_FILES_ETC "/sample2.ini", 0,
+ ¶ms);
+ TEST_ASSERT_NOT_NULL(cfgfile, "Failed to parse sample2.ini");
+
+ ret = _test_cfgfile_sample(cfgfile);
+ TEST_ASSERT_SUCCESS(ret, "Failed to validate sample file: %d", ret);
+
+ ret = rte_cfgfile_close(cfgfile);
+ TEST_ASSERT_SUCCESS(ret, "Failed to close cfgfile");
+
+ return 0;
+}
+
+static int
test_cfgfile_invalid_section_header(void)
{
struct rte_cfgfile *cfgfile;
@@ -141,6 +165,23 @@ test_cfgfile_invalid_section_header(void)
}
static int
+test_cfgfile_invalid_comment(void)
+{
+ struct rte_cfgfile_parameters params;
+ struct rte_cfgfile *cfgfile;
+
+ /* override comment character with an invalid one */
+ memset(¶ms, 0, sizeof(params));
+ params.comment_character = '$';
+
+ cfgfile = rte_cfgfile_load_with_params(CFG_FILES_ETC "/sample2.ini", 0,
+ ¶ms);
+ TEST_ASSERT_NULL(cfgfile, "Expected failured did not occur");
+
+ return 0;
+}
+
+static int
test_cfgfile_invalid_key_value_pair(void)
{
struct rte_cfgfile *cfgfile;
@@ -219,9 +260,15 @@ test_cfgfile(void)
if (test_cfgfile_sample1())
return -1;
+ if (test_cfgfile_sample2())
+ return -1;
+
if (test_cfgfile_invalid_section_header())
return -1;
+ if (test_cfgfile_invalid_comment())
+ return -1;
+
if (test_cfgfile_invalid_key_value_pair())
return -1;
new file mode 100644
@@ -0,0 +1,12 @@
+# this is a global comment
+
+[section1]
+# this is section 1
+key1=value1
+
+[section2]
+# this is section 2
+#key1=value1
+key2=value2
+key3=value3 # this is key3
+ignore-missing-separator