This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a commit to branch feature/type_support_for_properties
in repository https://gitbox.apache.org/repos/asf/celix.git

commit 2c5194ff1b254d88c8a112c28307233a044ca970
Author: Pepijn Noltes <[email protected]>
AuthorDate: Wed Jan 4 20:21:32 2023 +0100

    Add unit test for celix types properties and fix some mem issues
---
 libs/utils/gtest/src/PropertiesTestSuite.cc |  23 ++-
 libs/utils/gtest/src/VersionTestSuite.cc    |  46 ++---
 libs/utils/include/celix/Properties.h       |  44 +++--
 libs/utils/include/celix_properties.h       |  26 ++-
 libs/utils/include/celix_utils.h            |  16 +-
 libs/utils/include/celix_version.h          |   8 +-
 libs/utils/src/properties.c                 | 281 ++++++++++++++--------------
 libs/utils/src/utils.c                      |  19 +-
 libs/utils/src/version.c                    |   9 +-
 9 files changed, 273 insertions(+), 199 deletions(-)

diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc 
b/libs/utils/gtest/src/PropertiesTestSuite.cc
index 1528ddb2..f0537f75 100644
--- a/libs/utils/gtest/src/PropertiesTestSuite.cc
+++ b/libs/utils/gtest/src/PropertiesTestSuite.cc
@@ -284,6 +284,7 @@ TEST_F(PropertiesTestSuite, getType) {
     EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_VERSION, 
celix_properties_getType(props, "version"));
     EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_UNSET, 
celix_properties_getType(props, "missing"));
 
+    celix_version_destroy(version);
     celix_properties_destroy(props);
 }
 
@@ -326,6 +327,7 @@ TEST_F(PropertiesTestSuite, getEntry) {
     entry = celix_properties_getEntry(props, "key6");
     EXPECT_EQ(CELIX_PROPERTIES_VALUE_TYPE_UNSET, entry.valueType);
 
+    celix_version_destroy(version);
     celix_properties_destroy(props);
 }
 
@@ -403,13 +405,14 @@ TEST_F(PropertiesTestSuite, iterateOverProperties) {
     celix_properties_destroy(props);
 }
 
-TEST_F(PropertiesTestSuite, getAsVersion) {
+TEST_F(PropertiesTestSuite, getVersion) {
     auto* properties = celix_properties_create();
+    auto* emptyVersion = celix_version_createEmptyVersion();
 
     // Test getting a version property
     auto* expected = celix_version_createVersion(1, 2, 3, "test");
     celix_properties_setVersion(properties, "key", expected);
-    const auto* actual = celix_properties_getAsVersion(properties, "key", 
nullptr);
+    const auto* actual = celix_properties_getVersion(properties, "key", 
nullptr);
     EXPECT_EQ(celix_version_getMajor(expected), 
celix_version_getMajor(actual));
     EXPECT_EQ(celix_version_getMinor(expected), 
celix_version_getMinor(actual));
     EXPECT_EQ(celix_version_getMicro(expected), 
celix_version_getMicro(actual));
@@ -417,16 +420,22 @@ TEST_F(PropertiesTestSuite, getAsVersion) {
 
     // Test getting a non-version property
     celix_properties_set(properties, "key2", "value");
-    auto* emptyVersion = celix_version_createEmptyVersion();
-    actual = celix_properties_getAsVersion(properties, "key2", emptyVersion);
+    actual = celix_properties_getVersion(properties, "key2", emptyVersion);
     EXPECT_EQ(celix_version_getMajor(actual), 0);
     EXPECT_EQ(celix_version_getMinor(actual), 0);
     EXPECT_EQ(celix_version_getMicro(actual), 0);
     EXPECT_STREQ(celix_version_getQualifier(actual), "");
+    EXPECT_EQ(celix_properties_getVersion(properties, "non-existent", 
nullptr), nullptr);
+    celix_version_destroy(expected);
 
-    EXPECT_EQ(celix_properties_getAsVersion(properties, "non-existent", 
nullptr), nullptr);
+    // Test setting without copy
+    celix_properties_setVersionWithoutCopy(properties, "key3", 
celix_version_createVersion(3,3,3,""));
+    actual = celix_properties_getVersion(properties, "key3", emptyVersion);
+    EXPECT_EQ(celix_version_getMajor(actual), 3);
+    EXPECT_EQ(celix_version_getMinor(actual), 3);
+    EXPECT_EQ(celix_version_getMicro(actual), 3);
+    EXPECT_STREQ(celix_version_getQualifier(actual), "");
 
-    celix_version_destroy(expected);
     celix_version_destroy(emptyVersion);
     celix_properties_destroy(properties);
-}
\ No newline at end of file
+}
diff --git a/libs/utils/gtest/src/VersionTestSuite.cc 
b/libs/utils/gtest/src/VersionTestSuite.cc
index a0874ecf..90e5e9f1 100644
--- a/libs/utils/gtest/src/VersionTestSuite.cc
+++ b/libs/utils/gtest/src/VersionTestSuite.cc
@@ -68,23 +68,25 @@ TEST_F(VersionTestSuite, create) {
     free(str);
 }
 
-TEST_F(VersionTestSuite, clone) {
-    celix_version_t* version = nullptr;
-    celix_version_t* clone = nullptr;
-    char * str;
-
-    str = celix_utils_strdup("abc");
-    EXPECT_EQ(CELIX_SUCCESS, version_createVersion(1, 2, 3, str, &version));
-    EXPECT_EQ(CELIX_SUCCESS, version_clone(version, &clone));
-    EXPECT_TRUE(version != nullptr);
-    EXPECT_EQ(1, clone->major);
-    EXPECT_EQ(2, clone->minor);
-    EXPECT_EQ(3, clone->micro);
-    EXPECT_STREQ("abc", clone->qualifier);
+TEST_F(VersionTestSuite, copy) {
+    auto* version = celix_version_create(1, 2, 3, "abc");
+    auto* copy = celix_version_copy(version);
+    EXPECT_NE(nullptr, version);
+    EXPECT_NE(nullptr, copy);
+    EXPECT_EQ(1, celix_version_getMajor(copy));
+    EXPECT_EQ(2, celix_version_getMinor(copy));
+    EXPECT_EQ(3, celix_version_getMicro(copy));
+    EXPECT_STREQ("abc", celix_version_getQualifier(copy));
+    celix_version_destroy(copy);
+    celix_version_destroy(version);
 
-    version_destroy(clone);
-    version_destroy(version);
-    free(str);
+    copy = celix_version_copy(nullptr); //returns "empty" version
+    EXPECT_NE(nullptr, copy);
+    EXPECT_EQ(0, celix_version_getMajor(copy));
+    EXPECT_EQ(0, celix_version_getMinor(copy));
+    EXPECT_EQ(0, celix_version_getMicro(copy));
+    EXPECT_STREQ("", celix_version_getQualifier(copy));
+    celix_version_destroy(copy);
 }
 
 TEST_F(VersionTestSuite, createFromString) {
@@ -277,8 +279,8 @@ TEST_F(VersionTestSuite, compare) {
 }
 
 TEST_F(VersionTestSuite, celix_version_compareToMajorMinor) {
-    celix_version_t *version1 = celix_version_createVersion(2, 2, 0, nullptr);
-    celix_version_t *version2 = celix_version_createVersion(2, 2, 4, 
"qualifier");
+    auto* version1 = celix_version_create(2, 2, 0, nullptr);
+    auto* version2 = celix_version_create(2, 2, 4, "qualifier");
 
     EXPECT_EQ(0, celix_version_compareToMajorMinor(version1, 2, 2));
     EXPECT_EQ(0, celix_version_compareToMajorMinor(version2, 2, 2));
@@ -367,8 +369,8 @@ TEST_F(VersionTestSuite,semanticCompatibility) {
 
 TEST_F(VersionTestSuite, compareEmptyAndNullQualifier) {
     //nullptr or "" qualifier should be the same
-    auto* v1 = celix_version_createVersion(0, 0, 0, nullptr);
-    auto* v2 = celix_version_createVersion(0, 0, 0, "");
+    auto* v1 = celix_version_create(0, 0, 0, nullptr);
+    auto* v2 = celix_version_create(0, 0, 0, "");
     EXPECT_EQ(0, celix_version_compareTo(v1, v1));
     EXPECT_EQ(0, celix_version_compareTo(v1, v2));
     EXPECT_EQ(0, celix_version_compareTo(v2, v2));
@@ -379,7 +381,7 @@ TEST_F(VersionTestSuite, compareEmptyAndNullQualifier) {
 
 TEST_F(VersionTestSuite, fillString) {
     // Create a version object
-    celix_version_t* version = celix_version_createVersion(1, 2, 3, "alpha");
+    auto* version = celix_version_create(1, 2, 3, "alpha");
 
     // Test with buffer large enough to hold the formatted string
     char buffer[32];
@@ -392,4 +394,4 @@ TEST_F(VersionTestSuite, fillString) {
     EXPECT_FALSE(success);
 
     celix_version_destroy(version);
-}
\ No newline at end of file
+}
diff --git a/libs/utils/include/celix/Properties.h 
b/libs/utils/include/celix/Properties.h
index 111c7fe4..682fe490 100644
--- a/libs/utils/include/celix/Properties.h
+++ b/libs/utils/include/celix/Properties.h
@@ -36,6 +36,7 @@ namespace celix {
     public:
         explicit PropertiesIterator(const celix_properties_t* props) {
             iter = celix_properties_begin(props);
+            setFields();
         }
 
         PropertiesIterator& operator++() {
@@ -60,27 +61,27 @@ namespace celix {
 
         void next() {
             celix_propertiesIterator_next(&iter);
-            if (celix_propertiesIterator_isEnd(&iter)) {
-                moveToEnd();
-            } else {
-                first = iter.entry.key;
-                second = iter.entry.value;
-                end = false;
-            }
+            setFields();
         }
 
-        //TODO try to remove moveToEnd
         void moveToEnd() {
             first = {};
             second = {};
             end = true;
         }
 
-        //TODO for C++17 try to update first and second to stringview
         std::string first{};
         std::string second{};
-        //TODO iter?
     private:
+        void setFields() {
+            if (celix_propertiesIterator_isEnd(&iter)) {
+                moveToEnd();
+            } else {
+                first = iter.entry.key;
+                second = iter.entry.value;
+            }
+        }
+
         celix_properties_iterator_t iter{.index = -1, .entry = {}, ._data = 
{}};
         bool end{false};
     };
@@ -130,8 +131,6 @@ namespace celix {
             }
 #endif
 
-            //TODO operator= with long, double, boolean and version
-
             [[nodiscard]] const char* getValue() const {
                 if (charKey == nullptr) {
                     return celix_properties_get(props.get(), 
stringKey.c_str(), nullptr);
@@ -140,6 +139,8 @@ namespace celix {
                 }
             }
 
+            //TODO get typed value
+
             operator std::string() const {
                 auto *cstr = getValue();
                 return cstr == nullptr ? std::string{} : std::string{cstr};
@@ -290,6 +291,10 @@ namespace celix {
             return celix_properties_getAsBool(cProps.get(), key.data(), 
defaultValue);
         }
 
+        //TODO getType
+
+        //TODO getAsVersion
+
         /**
          * @brief Sets a T&& property. Will use (std::) to_string to convert 
the value to string.
          */
@@ -367,6 +372,8 @@ namespace celix {
             using namespace std;
             celix_properties_set(cProps.get(), key.c_str(), 
to_string(value).c_str());
         }
+
+        //TODO set long, double, boolean and version
 #endif
 
         /**
@@ -382,7 +389,7 @@ namespace celix {
         [[nodiscard]] std::map<std::string, std::string> convertToMap() const {
             std::map<std::string, std::string> result{};
             for (const auto& pair : *this) {
-                result[pair.first] = pair.second;
+                result[std::string{pair.first}] = pair.second;
             }
             return result;
         }
@@ -393,7 +400,7 @@ namespace celix {
         [[nodiscard]] std::unordered_map<std::string, std::string> 
convertToUnorderedMap() const {
             std::unordered_map<std::string, std::string> result{};
             for (const auto& pair : *this) {
-                result[pair.first] = pair.second;
+                result[std::string{pair.first}] = pair.second;
             }
             return result;
         }
@@ -411,8 +418,13 @@ namespace celix {
 #endif
 
 
-        //TODO save
-        //TODO load
+        //TODO test
+        void store(const std::string& file, const std::string& header = {}) 
const {
+            celix_properties_store(cProps.get(), file.c_str(), header.empty() 
? nullptr : header.c_str());
+        }
+
+        //TODO laod
+
     private:
         explicit Properties(celix_properties_t* props) : cProps{props, 
[](celix_properties_t*) { /*nop*/ }} {}
 
diff --git a/libs/utils/include/celix_properties.h 
b/libs/utils/include/celix_properties.h
index 45846506..e484a436 100644
--- a/libs/utils/include/celix_properties.h
+++ b/libs/utils/include/celix_properties.h
@@ -284,14 +284,30 @@ void celix_properties_setDouble(celix_properties_t* 
properties, const char* key,
 double celix_properties_getAsDouble(const celix_properties_t* properties, 
const char* key, double defaultValue);
 
 /**
- * @brief Sets the value of a property as a Celix version string.
+ * @brief Sets the value of a property as a Celix version.
  *
- * @param properties The property set to modify.
- * @param key The key of the property to set.
- * @param version The value to set.
+ * This function will make a copy of the provided celix_version_t object and 
store it in the property set.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] version The value to set. The function will make a copy of this 
object and store it in the property set.
  */
 void celix_properties_setVersion(celix_properties_t* properties, const char* 
key, const celix_version_t* version);
 
+/**
+ * @brief Sets the value of a property as a Celix version.
+ *
+ * This function will store a reference to the provided celix_version_t object 
in the property set and takes
+ * ownership of the provided version.
+ *
+ * @param[in] properties The property set to modify.
+ * @param[in] key The key of the property to set.
+ * @param[in] version The value to set. The function will store a reference to 
this object in the property set and
+ *                    takes ownership of the provided version.
+ */
+void celix_properties_setVersionWithoutCopy(celix_properties_t* properties, 
const char* key, celix_version_t* version);
+
+
 /**
  * @brief Gets the value of a property as a Celix version.
  *
@@ -303,7 +319,7 @@ void celix_properties_setVersion(celix_properties_t* 
properties, const char* key
  * @return The value of the property if it is a Celix version, or the default 
value if the property is not set or the
  *         value is not a Celix version.
  */
-const celix_version_t* celix_properties_getAsVersion(
+const celix_version_t* celix_properties_getVersion(
         const celix_properties_t* properties,
         const char* key,
         const celix_version_t* defaultValue);
diff --git a/libs/utils/include/celix_utils.h b/libs/utils/include/celix_utils.h
index e0db2731..791d8f89 100644
--- a/libs/utils/include/celix_utils.h
+++ b/libs/utils/include/celix_utils.h
@@ -54,11 +54,23 @@ bool celix_utils_stringEquals(const char* a, const char* b);
 /**
  * @brief Returns a trimmed string.
  *
- * The trim will remove eny leading and trailing whitespaces (' ', '\t', etc 
based on `isspace`)/
- * Caller is owner of the returned string.
+ * This function will remove any leading and trailing whitespaces (' ', '\t', 
etc based on isspace) from the
+ * input string.
+ *
+ * @param[in] string The input string to be trimmed.
+ * @return A trimmed version of the input string. The caller is responsible 
for freeing the memory of this string.
  */
 char* celix_utils_trim(const char* string);
 
+/**
+ * @brief Trims a string in place.
+ *
+ * The trim will remove any leading and trailing whitespaces (' ', '\t', etc 
based on isspace) from the input string.
+ * The input string is modified in place.
+ * @param[in,out] string The string to trim.
+ */
+void celix_utils_trimInPlace(char* string);
+
 /**
  * @brief Check if a string is NULL or empty "".
  */
diff --git a/libs/utils/include/celix_version.h 
b/libs/utils/include/celix_version.h
index be03aeef..f4cea390 100644
--- a/libs/utils/include/celix_version.h
+++ b/libs/utils/include/celix_version.h
@@ -42,7 +42,13 @@ typedef struct celix_version celix_version_t;
  *        the empty string.
  * @return The created version or NULL if the input was incorrect
  */
-celix_version_t* celix_version_createVersion(int major, int minor, int micro, 
const char* qualifier);
+celix_version_t* celix_version_create(int major, int minor, int micro, const 
char* qualifier);
+
+/**
+ * @brief Same as celix_version_create, but deprecated
+ */
+celix_version_t* celix_version_createVersion(int major, int minor, int micro, 
const char* qualifier)
+    __attribute__((deprecated(("celix_version_createVersion is deprecated use 
celix_version_create instead"))));
 
 void celix_version_destroy(celix_version_t* version);
 
diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c
index 9185fa64..7be3e713 100644
--- a/libs/utils/src/properties.c
+++ b/libs/utils/src/properties.c
@@ -137,106 +137,6 @@ static void updateBuffers(char **key, char ** value, char 
**output, int outputPo
     }
 }
 
-static void parseLine(const char* line, celix_properties_t *props) {
-    int linePos = 0;
-    bool precedingCharIsBackslash = false;
-    bool isComment = false;
-    int outputPos = 0;
-    char *output = NULL;
-    int key_len = MALLOC_BLOCK_SIZE;
-    int value_len = MALLOC_BLOCK_SIZE;
-    linePos = 0;
-    precedingCharIsBackslash = false;
-    isComment = false;
-    output = NULL;
-    outputPos = 0;
-
-    //Ignore empty lines
-    if (line[0] == '\n' && line[1] == '\0') {
-        return;
-    }
-
-    char *key = calloc(1, key_len);
-    char *value = calloc(1, value_len);
-    key[0] = '\0';
-    value[0] = '\0';
-
-    while (line[linePos] != '\0') {
-        if (line[linePos] == ' ' || line[linePos] == '\t') {
-            if (output == NULL) {
-                //ignore
-                linePos += 1;
-                continue;
-            }
-        }
-        else {
-            if (output == NULL) {
-                output = key;
-            }
-        }
-        if (line[linePos] == '=' || line[linePos] == ':' || line[linePos] == 
'#' || line[linePos] == '!') {
-            if (precedingCharIsBackslash) {
-                //escaped special character
-                output[outputPos++] = line[linePos];
-                updateBuffers(&key, &value, &output, outputPos, &key_len, 
&value_len);
-                precedingCharIsBackslash = false;
-            }
-            else {
-                if (line[linePos] == '#' || line[linePos] == '!') {
-                    if (outputPos == 0) {
-                        isComment = true;
-                        break;
-                    }
-                    else {
-                        output[outputPos++] = line[linePos];
-                        updateBuffers(&key, &value, &output, outputPos, 
&key_len, &value_len);
-                    }
-                }
-                else { // = or :
-                    if (output == value) { //already have a seperator
-                        output[outputPos++] = line[linePos];
-                        updateBuffers(&key, &value, &output, outputPos, 
&key_len, &value_len);
-                    }
-                    else {
-                        output[outputPos++] = '\0';
-                        updateBuffers(&key, &value, &output, outputPos, 
&key_len, &value_len);
-                        output = value;
-                        outputPos = 0;
-                    }
-                }
-            }
-        }
-        else if (line[linePos] == '\\') {
-            if (precedingCharIsBackslash) { //double backslash -> backslash
-                output[outputPos++] = '\\';
-                updateBuffers(&key, &value, &output, outputPos, &key_len, 
&value_len);
-            }
-            precedingCharIsBackslash = true;
-        }
-        else { //normal character
-            precedingCharIsBackslash = false;
-            output[outputPos++] = line[linePos];
-            updateBuffers(&key, &value, &output, outputPos, &key_len, 
&value_len);
-        }
-        linePos += 1;
-    }
-    if (output != NULL) {
-        output[outputPos] = '\0';
-    }
-
-    if (!isComment) {
-        //printf("putting 'key'/'value' '%s'/'%s' in properties\n", 
utils_stringTrim(key), utils_stringTrim(value));
-        celix_properties_set(props, utils_stringTrim(key), 
utils_stringTrim(value));
-    }
-    if(key) {
-        free(key);
-    }
-    if(value) {
-        free(value);
-    }
-
-}
-
 /**
  * Create a new string from the provided str by either using strup or storing 
the string the short properties
  * optimization string buffer.
@@ -266,7 +166,7 @@ static celix_status_t celix_properties_fillEntry(
         const long* longValue,
         const double* doubleValue,
         const bool* boolValue,
-        const celix_version_t* versionValue) {
+        celix_version_t* versionValue) {
     char convertedValueBuffer[32];
     entry->key = celix_properties_createString(properties, key);
     if (strValue != NULL) {
@@ -302,8 +202,7 @@ static celix_status_t celix_properties_fillEntry(
     } else /*versionValue*/ {
         assert(versionValue != NULL);
         entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_VERSION;
-        entry->typed.versionValue = celix_version_copy(versionValue);
-
+        entry->typed.versionValue = versionValue;
         bool written = celix_version_fillString(versionValue, 
convertedValueBuffer, sizeof(convertedValueBuffer));
         if (written) {
             entry->value = celix_properties_createString(properties, 
convertedValueBuffer);
@@ -360,7 +259,7 @@ static celix_properties_entry_t* 
celix_properties_createEntry(
         const long* longValue,
         const double* doubleValue,
         const bool* boolValue,
-        const celix_version_t* versionValue) {
+        celix_version_t* versionValue) {
     celix_properties_entry_t* entry = celix_properties_allocEntry(properties);
     if (entry == NULL) {
         return NULL;
@@ -386,7 +285,7 @@ static void celix_properties_createAndSetEntry(
         const long* longValue,
         const double* doubleValue,
         const bool* boolValue,
-        const celix_version_t* versionValue) {
+        celix_version_t* versionValue) {
     if (properties == NULL) {
         return;
     }
@@ -415,6 +314,9 @@ static void celix_properties_removeEntryCallback(void* 
handle, const char* key _
     celix_properties_entry_t* entry = val.ptrValue;
     celix_properties_freeString(properties, (char*)entry->key);
     celix_properties_freeString(properties, (char*)entry->value);
+    if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) {
+        celix_version_destroy(entry->typed.versionValue);
+    }
     if (entry >= properties->entriesBuffer &&
             entry <= (properties->entriesBuffer + 
CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE)) {
         //entry is part of the properties entries buffer -> nop.
@@ -423,7 +325,6 @@ static void celix_properties_removeEntryCallback(void* 
handle, const char* key _
     }
 }
 
-
 celix_properties_t* celix_properties_create(void) {
     celix_properties_t* props = malloc(sizeof(*props));
     if (props != NULL) {
@@ -456,38 +357,143 @@ celix_properties_t* celix_properties_load(const char 
*filename) {
     return props;
 }
 
-celix_properties_t* celix_properties_loadWithStream(FILE *file) {
-    celix_properties_t *props = NULL;
-
-    if (file != NULL ) {
-        char *saveptr;
-        char *filebuffer = NULL;
-        char *line = NULL;
-        size_t file_size = 0;
-
-        props = celix_properties_create();
-        fseek(file, 0, SEEK_END);
-        file_size = ftell(file);
-        fseek(file, 0, SEEK_SET);
-
-        if (file_size > 0) {
-            filebuffer = calloc(file_size + 1, sizeof(char));
-            if (filebuffer) {
-                size_t rs = fread(filebuffer, sizeof(char), file_size, file);
-                if (rs != file_size) {
-                    fprintf(stderr,"fread read only %lu bytes out of %lu\n", 
(long unsigned int) rs, (long unsigned int) file_size);
+static void parseLine(const char* line, celix_properties_t *props) {
+    int linePos = 0;
+    bool precedingCharIsBackslash = false;
+    bool isComment = false;
+    int outputPos = 0;
+    char *output = NULL;
+    int key_len = MALLOC_BLOCK_SIZE;
+    int value_len = MALLOC_BLOCK_SIZE;
+    linePos = 0;
+    precedingCharIsBackslash = false;
+    isComment = false;
+    output = NULL;
+    outputPos = 0;
+
+    //Ignore empty lines
+    if (line[0] == '\n' && line[1] == '\0') {
+        return;
+    }
+
+    char *key = calloc(1, key_len);
+    char *value = calloc(1, value_len);
+    key[0] = '\0';
+    value[0] = '\0';
+
+    while (line[linePos] != '\0') {
+        if (line[linePos] == ' ' || line[linePos] == '\t') {
+            if (output == NULL) {
+                //ignore
+                linePos += 1;
+                continue;
+            }
+        }
+        else {
+            if (output == NULL) {
+                output = key;
+            }
+        }
+        if (line[linePos] == '=' || line[linePos] == ':' || line[linePos] == 
'#' || line[linePos] == '!') {
+            if (precedingCharIsBackslash) {
+                //escaped special character
+                output[outputPos++] = line[linePos];
+                updateBuffers(&key, &value, &output, outputPos, &key_len, 
&value_len);
+                precedingCharIsBackslash = false;
+            }
+            else {
+                if (line[linePos] == '#' || line[linePos] == '!') {
+                    if (outputPos == 0) {
+                        isComment = true;
+                        break;
+                    }
+                    else {
+                        output[outputPos++] = line[linePos];
+                        updateBuffers(&key, &value, &output, outputPos, 
&key_len, &value_len);
+                    }
                 }
-                filebuffer[file_size]='\0';
-                line = strtok_r(filebuffer, "\n", &saveptr);
-                while (line != NULL) {
-                    parseLine(line, props);
-                    line = strtok_r(NULL, "\n", &saveptr);
+                else { // = or :
+                    if (output == value) { //already have a seperator
+                        output[outputPos++] = line[linePos];
+                        updateBuffers(&key, &value, &output, outputPos, 
&key_len, &value_len);
+                    }
+                    else {
+                        output[outputPos++] = '\0';
+                        updateBuffers(&key, &value, &output, outputPos, 
&key_len, &value_len);
+                        output = value;
+                        outputPos = 0;
+                    }
                 }
-                free(filebuffer);
             }
         }
+        else if (line[linePos] == '\\') {
+            if (precedingCharIsBackslash) { //double backslash -> backslash
+                output[outputPos++] = '\\';
+                updateBuffers(&key, &value, &output, outputPos, &key_len, 
&value_len);
+            }
+            precedingCharIsBackslash = true;
+        }
+        else { //normal character
+            precedingCharIsBackslash = false;
+            output[outputPos++] = line[linePos];
+            updateBuffers(&key, &value, &output, outputPos, &key_len, 
&value_len);
+        }
+        linePos += 1;
+    }
+    if (output != NULL) {
+        output[outputPos] = '\0';
     }
 
+    if (!isComment) {
+        //printf("putting 'key'/'value' '%s'/'%s' in properties\n", 
utils_stringTrim(key), utils_stringTrim(value));
+        celix_utils_trimInPlace(key);
+        celix_utils_trimInPlace(value);
+        celix_properties_setWithoutCopy(props, key, value);
+    } else {
+        free(key);
+        free(value);
+    }
+
+
+}
+
+celix_properties_t* celix_properties_loadWithStream(FILE *file) {
+    if (file == NULL) {
+        return NULL;
+    }
+
+    celix_properties_t *props = celix_properties_create();
+    if (props == NULL) {
+        return NULL;
+    }
+
+    fseek(file, 0, SEEK_END);
+    size_t fileSize = ftell(file);
+    fseek(file, 0, SEEK_SET);
+    if (fileSize == 0) {
+        return props;
+    }
+
+    char* fileBuffer = malloc(fileSize + 1);
+    if (fileBuffer == NULL) {
+        celix_properties_destroy(props);
+        return NULL;
+    }
+
+    size_t rs = fread(fileBuffer, sizeof(char), fileSize, file);
+    if (rs < fileSize) {
+        fprintf(stderr,"fread read only %zu bytes out of %zu\n", rs, fileSize);
+    }
+    fileBuffer[fileSize]='\0'; //ensure a '\0' at the end of the fileBuffer
+
+    char* savePtr = NULL;
+    char* line = strtok_r(fileBuffer, "\n", &savePtr);
+    while (line != NULL) {
+        parseLine(line, props);
+        line = strtok_r(NULL, "\n", &savePtr);
+    }
+    free(fileBuffer);
+
     return props;
 }
 
@@ -692,7 +698,7 @@ void celix_properties_setBool(celix_properties_t *props, 
const char *key, bool v
     celix_properties_createAndSetEntry(props, key, NULL, NULL, NULL, &val, 
NULL);
 }
 
-const celix_version_t* celix_properties_getAsVersion(
+const celix_version_t* celix_properties_getVersion(
         const celix_properties_t* properties,
         const char* key,
         const celix_version_t* defaultValue) {
@@ -703,10 +709,13 @@ const celix_version_t* celix_properties_getAsVersion(
     return defaultValue;
 }
 
-void celix_properties_setVersion(celix_properties_t *props, const char *key, 
const celix_version_t* version) {
-    celix_properties_createAndSetEntry(props, key, NULL, NULL, NULL, NULL, 
version);
+void celix_properties_setVersion(celix_properties_t *properties, const char 
*key, const celix_version_t* version) {
+    celix_properties_createAndSetEntry(properties, key, NULL, NULL, NULL, 
NULL, celix_version_copy(version));
 }
 
+void celix_properties_setVersionWithoutCopy(celix_properties_t* properties, 
const char* key, celix_version_t* version) {
+    celix_properties_createAndSetEntry(properties, key, NULL, NULL, NULL, 
NULL, version);
+}
 
 int celix_properties_size(const celix_properties_t *properties) {
     return (int)celix_stringHashMap_size(properties->map);
@@ -748,7 +757,7 @@ const char* 
celix_propertiesIterator_nextKey(celix_properties_iterator_t *iter)
     iter->index = (int)internalIter.mapIter.index;
     celix_properties_entry_t* entry = internalIter.mapIter.value.ptrValue;
     if (entry != NULL) {
-        memcpy(&iter->entry, iter, sizeof(iter->entry));
+        memcpy(&iter->entry, entry, sizeof(iter->entry));
     } else {
         memset(&iter->entry, 0, sizeof(iter->entry));
     }
diff --git a/libs/utils/src/utils.c b/libs/utils/src/utils.c
index 7bbe2f4c..6ff882a8 100644
--- a/libs/utils/src/utils.c
+++ b/libs/utils/src/utils.c
@@ -82,12 +82,12 @@ char * string_ndup(const char *s, size_t n) {
     return ret;
 }
 
-static char* celix_utilsTrimInternal(char *string) {
+void celix_utils_trimInPlace(char* string) {
     if (string == NULL) {
-        return NULL;
+        return;
     }
 
-    char* begin = string; //save begin to correctly free in the end.
+    char* begin = string; //save begin to check in the end.
 
     char *end;
     // Trim leading space
@@ -103,7 +103,7 @@ static char* celix_utilsTrimInternal(char *string) {
     }
 
     if (string != begin) {
-        //beginning whitespaces -> move char in copy to to begin string
+        //beginning whitespaces -> move chars to the beginning of string
         //This to ensure free still works on the same pointer.
         char* nstring = begin;
         while(*string != '\0') {
@@ -111,16 +111,17 @@ static char* celix_utilsTrimInternal(char *string) {
         }
         (*nstring) = '\0';
     }
-
-    return begin;
 }
 
 char* celix_utils_trim(const char* string) {
-    return celix_utilsTrimInternal(celix_utils_strdup(string));
+    char* result = celix_utils_strdup(string);
+    celix_utils_trimInPlace(result);
+    return result;
 }
 
 char* utils_stringTrim(char* string) {
-    return celix_utilsTrimInternal(string);
+    celix_utils_trimInPlace(string);
+    return string;
 }
 
 bool utils_isStringEmptyOrNull(const char * const str) {
@@ -264,4 +265,4 @@ void 
celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(const char *
     } else {
         *outNamespace = namespace;
     }
-}
\ No newline at end of file
+}
diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c
index f83fe655..3d0ee1ab 100644
--- a/libs/utils/src/version.c
+++ b/libs/utils/src/version.c
@@ -96,6 +96,10 @@ celix_status_t version_isCompatible(version_pt user, 
version_pt provider, bool*
 }
 
 celix_version_t* celix_version_createVersion(int major, int minor, int micro, 
const char* qualifier) {
+    return celix_version_create(major, minor, micro, qualifier);
+}
+
+celix_version_t* celix_version_create(int major, int minor, int micro, const 
char* qualifier) {
     if (major < 0 || minor < 0 || micro < 0) {
         return NULL;
     }
@@ -141,6 +145,9 @@ void celix_version_destroy(celix_version_t* version) {
 
 
 celix_version_t* celix_version_copy(const celix_version_t* version) {
+    if (version == NULL) {
+        return celix_version_createEmptyVersion();
+    }
     return celix_version_createVersion(version->major, version->minor, 
version->micro, version->qualifier);
 }
 
@@ -322,4 +329,4 @@ int celix_version_compareToMajorMinor(const 
celix_version_t* version, int majorV
         result = version->minor - minorVersionPart;
     }
     return result;
-}
\ No newline at end of file
+}

Reply via email to