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

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

commit 16b119bdaec1270602573cc9393ea5d589d0da61
Author: Pepijn Noltes <[email protected]>
AuthorDate: Tue Jan 31 21:18:32 2023 +0100

    Improve utils lib with string convert, file touch and file modified 
functions.
    
    Also moves some cpputest to gtests
---
 libs/framework/src/celix_errorcodes.c         |   2 +
 libs/utils/CMakeLists.txt                     |   7 +-
 libs/utils/gtest/CMakeLists.txt               |   2 +
 libs/utils/gtest/src/CelixUtilsTestSuite.cc   | 317 +++++++++++++++++++++
 libs/utils/gtest/src/ConvertUtilsTestSuite.cc | 159 +++++++++++
 libs/utils/include/celix_convert_utils.h      |  80 ++++++
 libs/utils/include/celix_file_utils.h         |  18 ++
 libs/utils/include/celix_threads.h            |   6 +
 libs/utils/include/celix_utils.h              |  42 +++
 libs/utils/private/test/utils_test.cpp        | 383 --------------------------
 libs/utils/src/celix_convert_utils.c          |  99 +++++++
 libs/utils/src/celix_file_utils.c             |  27 +-
 libs/utils/src/utils.c                        |  63 ++++-
 13 files changed, 809 insertions(+), 396 deletions(-)

diff --git a/libs/framework/src/celix_errorcodes.c 
b/libs/framework/src/celix_errorcodes.c
index 9879a829..d3a01eb6 100644
--- a/libs/framework/src/celix_errorcodes.c
+++ b/libs/framework/src/celix_errorcodes.c
@@ -42,6 +42,8 @@ const char* celix_strerror(celix_status_t status) {
             return "File I/O exception";
         case CELIX_SERVICE_EXCEPTION:
             return "Service exception";
+        case CELIX_ENOMEM:
+            return "Out of memory";
         default:
             return "Unknown code";
        }
diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt
index d1aa4bdc..ff498e66 100644
--- a/libs/utils/CMakeLists.txt
+++ b/libs/utils/CMakeLists.txt
@@ -43,6 +43,7 @@ add_library(utils SHARED
     src/celix_log_utils.c
     src/celix_hash_map.c
     src/celix_file_utils.c
+    src/celix_convert_utils.c
     ${MEMSTREAM_SOURCES}
 )
 set_target_properties(utils PROPERTIES OUTPUT_NAME "celix_utils")
@@ -118,10 +119,6 @@ if (ENABLE_TESTING)
         target_include_directories(properties_test PRIVATE include_deprecated)
         target_link_libraries(properties_test CppUTest::CppUTest 
CppUTest::CppUTestExt  Celix::utils pthread)
 
-        add_executable(utils_test private/test/utils_test.cpp)
-        target_include_directories(utils_test PRIVATE include_deprecated)
-        target_link_libraries(utils_test CppUTest::CppUTest  Celix::utils 
pthread)
-
         add_executable(ip_utils_test private/test/ip_utils_test.cpp)
         target_include_directories(ip_utils_test PRIVATE include_deprecated)
         target_link_libraries(ip_utils_test CppUTest::CppUTest  Celix::utils 
pthread)
@@ -141,7 +138,6 @@ if (ENABLE_TESTING)
         add_test(NAME run_celix_threads_test COMMAND celix_threads_test)
         add_test(NAME run_linked_list_test COMMAND linked_list_test)
         add_test(NAME run_properties_test COMMAND properties_test)
-        add_test(NAME run_utils_test COMMAND utils_test)
         add_test(NAME run_ip_utils_test COMMAND ip_utils_test)
         add_test(NAME filter_test COMMAND filter_test)
         add_test(NAME version_test COMMAND version_test)
@@ -151,7 +147,6 @@ if (ENABLE_TESTING)
         setup_target_for_coverage(celix_threads_test)
         setup_target_for_coverage(linked_list_test)
         setup_target_for_coverage(properties_test)
-        setup_target_for_coverage(utils_test)
         setup_target_for_coverage(ip_utils_test)
         setup_target_for_coverage(filter_test)
         setup_target_for_coverage(version_test)
diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt
index 7440357c..4b871129 100644
--- a/libs/utils/gtest/CMakeLists.txt
+++ b/libs/utils/gtest/CMakeLists.txt
@@ -30,6 +30,8 @@ add_executable(test_utils
         src/HashMapTestSuite.cc
         src/ArrayListTestSuite.cc
         src/FileUtilsTestSuite.cc
+        src/CelixUtilsTestSuite.cc
+        src/ConvertUtilsTestSuite.cc
         ${CELIX_UTIL_TEST_SOURCES_FOR_CXX_HEADERS}
 )
 
diff --git a/libs/utils/gtest/src/CelixUtilsTestSuite.cc 
b/libs/utils/gtest/src/CelixUtilsTestSuite.cc
new file mode 100644
index 00000000..de2a78a5
--- /dev/null
+++ b/libs/utils/gtest/src/CelixUtilsTestSuite.cc
@@ -0,0 +1,317 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "celix_utils.h"
+#include "utils.h"
+
+class UtilsTestSuite : public ::testing::Test {
+public:
+};
+
+
+TEST_F(UtilsTestSuite, CompareServiceIdsAndRankingTest){
+    int ret;
+    //service 1 is higher ranked and has a irrelevant ID, so result is -1 
(smaller -> sorted before service 2)
+    ret = celix_utils_compareServiceIdsAndRanking(2,2,1,1);
+    EXPECT_EQ(-1, ret);
+
+    //service 1 is equally ranked and has a lower ID. so result is -1 (smaller 
-> sorted before service 2)
+    ret = celix_utils_compareServiceIdsAndRanking(1,1,2,1);
+    EXPECT_EQ(-1, ret);
+
+    //service 1 is equally ranked and has a higher ID, so result is 1 (larger 
-> sorted after service 2)
+    ret = celix_utils_compareServiceIdsAndRanking(2,1,1,1);
+    EXPECT_EQ(1, ret);
+
+    //service 1 is lower ranked and has a irrelevant ID, so result is -1 
(larger -> sorted after service 2)
+    ret = celix_utils_compareServiceIdsAndRanking(1,1,2,2);
+    EXPECT_EQ(1, ret);
+
+    //service 1 is equal in ID and irrelevantly ranked
+    //note ensure that also the call without celix_ is tested
+    ret = celix_utils_compareServiceIdsAndRanking(1,1,1,1);
+    EXPECT_EQ(0, ret);
+
+
+}
+
+TEST_F(UtilsTestSuite, ExtractLocalNameAndNamespaceTest) {
+    const char *input = "lb";
+    char* name = nullptr;
+    char *ns = nullptr;
+    celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(input, 
"::", &name, &ns);
+    EXPECT_STREQ("lb", name);
+    EXPECT_TRUE(ns == nullptr);
+    free(name);
+    free(ns);
+
+    input = "celix::lb";
+    name = nullptr;
+    ns = nullptr;
+    celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(input, 
"::", &name, &ns);
+    EXPECT_STREQ("lb", name);
+    EXPECT_STREQ("celix", ns);
+    free(name);
+    free(ns);
+
+    input = "celix::extra::namespace::entries::lb";
+    name = nullptr;
+    ns = nullptr;
+    celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(input, 
"::", &name, &ns);
+    EXPECT_STREQ("lb", name);
+    EXPECT_STREQ("celix::extra::namespace::entries", ns);
+    free(name);
+    free(ns);
+
+    input = "celix.extra.namespace.entries.lb";
+    name = nullptr;
+    ns = nullptr;
+    celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(input, ".", 
&name, &ns);
+    EXPECT_STREQ("lb", name);
+    EXPECT_STREQ("celix.extra.namespace.entries", ns);
+    free(name);
+    free(ns);
+
+    //testing with non existing namespace
+    input = "celix.extra.namespace.entries.lb";
+    name = nullptr;
+    ns = nullptr;
+    celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(input, 
"??", &name, &ns);
+    EXPECT_STREQ("celix.extra.namespace.entries.lb", name);
+    EXPECT_TRUE(ns == nullptr);
+    free(name);
+    free(ns);
+
+    //wrong input check
+    input = nullptr;
+    name = nullptr;
+    ns = nullptr;
+    celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(input, 
"??", &name, &ns);
+    EXPECT_TRUE(name == nullptr);
+    EXPECT_TRUE(ns == nullptr);
+    free(name);
+    free(ns);
+
+
+    //empty namespace check
+    input = "celix.extra.namespace.entries.lb";
+    name = nullptr;
+    ns = nullptr;
+    celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(input, "", 
&name, &ns);
+    EXPECT_STREQ("celix.extra.namespace.entries.lb", name);
+    EXPECT_TRUE(ns == nullptr);
+    free(name);
+    free(ns);
+}
+
+TEST_F(UtilsTestSuite, IsStringnullptrOrEmptyTest) {
+    bool empty = celix_utils_isStringNullOrEmpty(nullptr);
+    EXPECT_TRUE(empty);
+    empty = celix_utils_isStringNullOrEmpty("");
+    EXPECT_TRUE(empty);
+    empty = celix_utils_isStringNullOrEmpty(" ");
+    EXPECT_FALSE(empty);
+    empty = celix_utils_isStringNullOrEmpty("foo");
+    EXPECT_FALSE(empty);
+}
+
+TEST_F(UtilsTestSuite, ContainsWhitespaceTest) {
+    EXPECT_FALSE(celix_utils_containsWhitespace(nullptr));
+    EXPECT_FALSE(celix_utils_containsWhitespace(""));
+    EXPECT_TRUE(celix_utils_containsWhitespace(" "));
+    EXPECT_FALSE(celix_utils_containsWhitespace("abcd"));
+    EXPECT_TRUE(celix_utils_containsWhitespace("ab cd"));
+    EXPECT_TRUE(celix_utils_containsWhitespace("ab\tcd"));
+    EXPECT_TRUE(celix_utils_containsWhitespace("ab\ncd"));
+    EXPECT_TRUE(celix_utils_containsWhitespace("abcd "));
+}
+
+TEST_F(UtilsTestSuite, MakeCIdentifierTest) {
+    //When a string is already a c identifier the result is equal to the input
+    char* id = celix_utils_makeCIdentifier("abcd");
+    EXPECT_STREQ(id, "abcd");
+    free(id);
+
+    //When a string is already a c identifier the result is equal to the input
+    id = celix_utils_makeCIdentifier("abcdABCD1234");
+    EXPECT_STREQ(id, "abcdABCD1234");
+    free(id);
+
+    //When a string start with a digit a _ is prefixed
+    id = celix_utils_makeCIdentifier("1234abcdABCD");
+    EXPECT_STREQ(id, "_1234abcdABCD");
+    free(id);
+
+    //When a string contains non isalnum characters, those are replaced with _
+    id = celix_utils_makeCIdentifier("$%ab \tcd^&");
+    EXPECT_STREQ(id, "__ab__cd__");
+    free(id);
+
+    //When a string is empty or null, null will be returned
+    EXPECT_EQ(celix_utils_makeCIdentifier(nullptr), nullptr);
+    EXPECT_EQ(celix_utils_makeCIdentifier(""), nullptr);
+}
+
+
+TEST_F(UtilsTestSuite, StringHashTest) {
+
+
+    unsigned long hash = celix_utils_stringHash("abc");
+    EXPECT_EQ(193485963, hash);
+
+    hash = 
celix_utils_stringHash("abc123def456ghi789jkl012mno345pqr678stu901vwx234yz");
+    EXPECT_EQ(1532304168, hash);
+
+    hash = celix_utils_stringHash(nullptr);
+    EXPECT_EQ(0, hash);
+
+    //test deprecated api
+    hash = utils_stringHash("abc");
+    EXPECT_EQ(193485963, hash);
+}
+
+TEST_F(UtilsTestSuite, StringEqualsTest) {
+    //refactor StringEqualsTest to use gtest methods instead of cpputest
+    EXPECT_TRUE(celix_utils_stringEquals("abc", "abc"));
+    EXPECT_FALSE(celix_utils_stringEquals("abc", "def"));
+    EXPECT_FALSE(celix_utils_stringEquals("abc", nullptr));
+    EXPECT_FALSE(celix_utils_stringEquals(nullptr, "abc"));
+    EXPECT_TRUE(celix_utils_stringEquals(nullptr, nullptr));
+    EXPECT_TRUE(celix_utils_stringEquals("", ""));
+    EXPECT_FALSE(celix_utils_stringEquals("", nullptr));
+
+    //test deprecated api
+    EXPECT_TRUE(utils_stringEquals("abc", "abc"));
+}
+
+TEST_F(UtilsTestSuite, IsNumericTest) {
+    //test deprecated api
+
+    // Check numeric string
+    bool result;
+    celix_status_t status = utils_isNumeric("42", &result);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(result);
+
+    // Check non numeric string
+    status = utils_isNumeric("42b", &result);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_FALSE(result);
+}
+
+TEST_F(UtilsTestSuite, ThreadEqualSelfTest) {
+    celix_thread thread = celixThread_self();
+    bool equal;
+
+    celix_status_t status = thread_equalsSelf(thread, &equal);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_TRUE(equal);
+
+
+    thread.thread = (pthread_t) 0x42;
+    status = thread_equalsSelf(thread, &equal);
+    EXPECT_EQ(CELIX_SUCCESS, status);
+    EXPECT_FALSE(equal);
+}
+
+TEST_F(UtilsTestSuite, StringTrimTest) {
+    // Multiple whitespaces, before, after and in between
+    auto* trimmed = celix_utils_trim(" a b c ");
+    EXPECT_STREQ("a b c", trimmed);
+    free(trimmed);
+
+    // No whitespaces
+    trimmed = celix_utils_trim("abc");
+    EXPECT_STREQ("abc", trimmed);
+    free(trimmed);
+
+    // Only whitespace before
+    trimmed = celix_utils_trim(" \tabc");
+    EXPECT_STREQ("abc", trimmed);
+    free(trimmed);
+
+    // Only whitespace after
+    trimmed = celix_utils_trim("abc \t");
+    EXPECT_STREQ("abc", trimmed);
+    free(trimmed);
+
+    // Whitespace other than space (tab, cr..).
+    trimmed = celix_utils_trim("\tabc  \n asdf  \n");
+    EXPECT_STREQ("abc  \n asdf", trimmed);
+    free(trimmed);
+
+    // Empty string
+    trimmed = utils_stringTrim(celix_utils_strdup("  abc   "));
+    EXPECT_STREQ("abc", trimmed);
+    free(trimmed);
+}
+
+TEST_F(UtilsTestSuite, StringNBdupTest){
+    // test deprecated api
+
+    // Compare with equal strings
+    const char * org = "abc";
+    char * cmp = nullptr;
+
+    cmp = string_ndup(org, 3);
+    EXPECT_STREQ(org, cmp);
+    free(cmp);
+
+    org = "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz";
+    cmp = string_ndup(org, 50);
+    EXPECT_STREQ(org, cmp);
+    free(cmp);
+
+    cmp = string_ndup(org, 25);
+    EXPECT_EQ(25, strlen(cmp));
+    free(cmp);
+}
+
+TEST_F(UtilsTestSuite, WriteOrCreateStringTest) {
+    //Buffer big enough, write to stack buffer
+    char buffer[16];
+    char* out = celix_utils_writeOrCreateString(buffer, sizeof(buffer), "abc");
+    EXPECT_EQ(buffer, out);
+    EXPECT_STREQ("abc", out);
+    celix_utils_freeStringIfNeeded(buffer, out);
+
+    //Buffer not big enough, malloc new string
+    out = celix_utils_writeOrCreateString(buffer, sizeof(buffer), 
"abc123def456ghi789jkl012mno345pqr678stu901vwx234yz");
+    EXPECT_NE(buffer, out);
+    EXPECT_STREQ("abc123def456ghi789jkl012mno345pqr678stu901vwx234yz", out);
+    celix_utils_freeStringIfNeeded(buffer, out);
+
+    //same test, but with long, int and short format args
+    //Buffer big enough, write to stack buffer
+    char buffer2[64];
+    char* out2 = celix_utils_writeOrCreateString(buffer2, sizeof(buffer2), 
"long %ld, int %d, short %hd", 1234567890L, 123456789, (short)12345);
+    EXPECT_EQ(buffer2, out2);
+    EXPECT_STREQ("long 1234567890, int 123456789, short 12345", out2);
+    celix_utils_freeStringIfNeeded(buffer2, out2);
+
+    //Buffer not big enough, malloc new string
+    out2 = celix_utils_writeOrCreateString(buffer2, sizeof(buffer2), "long 
%ld, int %d, short %hd. abc123def456ghi789jkl012mno345pqr678stu901vwx234yz", 
1234567890123456789L, 123456789, (short)12345);
+    EXPECT_NE(buffer2, out2);
+    EXPECT_STREQ("long 1234567890123456789, int 123456789, short 12345. 
abc123def456ghi789jkl012mno345pqr678stu901vwx234yz", out2);
+    celix_utils_freeStringIfNeeded(buffer2, out2);
+}
+
+
diff --git a/libs/utils/gtest/src/ConvertUtilsTestSuite.cc 
b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc
new file mode 100644
index 00000000..5754d1af
--- /dev/null
+++ b/libs/utils/gtest/src/ConvertUtilsTestSuite.cc
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "celix_convert_utils.h"
+
+class ConvertUtilsTestSuite : public ::testing::Test {
+public:
+    void checkVersion(const celix_version_t* version, int major, int minor, 
int micro, const char* qualifier) {
+        EXPECT_TRUE(version != nullptr);
+        if (version) {
+            EXPECT_EQ(major, celix_version_getMajor(version));
+            EXPECT_EQ(minor, celix_version_getMinor(version));
+            EXPECT_EQ(micro, celix_version_getMicro(version));
+            if (qualifier) {
+                EXPECT_STREQ(qualifier, celix_version_getQualifier(version));
+            } else {
+                EXPECT_STREQ("", celix_version_getQualifier(version));
+            }
+        }
+    }
+};
+
+TEST_F(ConvertUtilsTestSuite, ConvertToLongTest) {
+    bool converted;
+    //test for a valid string
+    long result = celix_utils_convertStringToLong("10", 0, &converted);
+    EXPECT_EQ(10, result);
+    EXPECT_TRUE(converted);
+
+    //test for an invalid string
+    result = celix_utils_convertStringToLong("A", 0, &converted);
+    EXPECT_EQ(0, result);
+    EXPECT_FALSE(converted);
+
+    //test for a string with a number
+    result = celix_utils_convertStringToLong("10A", 0, &converted);
+    EXPECT_EQ(10, result);
+    EXPECT_TRUE(converted);
+
+    //test for a string with a number and a negative sign
+    result = celix_utils_convertStringToLong("-10", 0, &converted);
+    EXPECT_EQ(-10, result);
+    EXPECT_TRUE(converted);
+
+    //test for a string with a number and a positive sign
+    result = celix_utils_convertStringToLong("+10", 0, &converted);
+    EXPECT_EQ(10, result);
+    EXPECT_TRUE(converted);
+
+    //test for a convert with a nullptr for the converted parameter
+    result = celix_utils_convertStringToLong("10", 0, nullptr);
+    EXPECT_EQ(10, result);
+}
+
+TEST_F(ConvertUtilsTestSuite, ConvertToDoubleTest) {
+    bool converted;
+    //test for a valid string
+    double result = celix_utils_convertStringToDouble("10.5", 0, &converted);
+    EXPECT_EQ(10.5, result);
+    EXPECT_TRUE(converted);
+
+    //test for an invalid string
+    result = celix_utils_convertStringToDouble("A", 0, &converted);
+    EXPECT_EQ(0, result);
+    EXPECT_FALSE(converted);
+
+    //test for a string with a number
+    result = celix_utils_convertStringToDouble("10.5A", 0, &converted);
+    EXPECT_EQ(10.5, result);
+    EXPECT_TRUE(converted);
+
+    //test for a string with a number and a negative sign
+    result = celix_utils_convertStringToDouble("-10.5", 0, &converted);
+    EXPECT_EQ(-10.5, result);
+    EXPECT_TRUE(converted);
+
+    //test for a string with a number and a positive sign
+    result = celix_utils_convertStringToDouble("+10.5", 0, &converted);
+    EXPECT_EQ(10.5, result);
+    EXPECT_TRUE(converted);
+
+    //test for a string with a scientific notation
+    result = celix_utils_convertStringToDouble("1.0e-10", 0, &converted);
+    EXPECT_EQ(1.0e-10, result);
+    EXPECT_TRUE(converted);
+
+    //test for a convert with a nullptr for the converted parameter
+    result = celix_utils_convertStringToDouble("10.5", 0, nullptr);
+    EXPECT_EQ(10.5, result);
+}
+
+TEST_F(ConvertUtilsTestSuite, ConvertToBoolTest) {
+    bool converted;
+    //test for a valid string
+    bool result = celix_utils_convertStringToBool("true", false, &converted);
+    EXPECT_EQ(true, result);
+    EXPECT_TRUE(converted);
+
+    //test for an invalid string
+    result = celix_utils_convertStringToBool("A", false, &converted);
+    EXPECT_EQ(false, result);
+    EXPECT_FALSE(converted);
+
+    //test for a almost valid string
+    result = celix_utils_convertStringToBool("trueA", false, &converted);
+    EXPECT_EQ(false, result);
+    EXPECT_FALSE(converted);
+
+    //test for a almost valid string
+    result = celix_utils_convertStringToBool("falseA", true, &converted);
+    EXPECT_EQ(true, result);
+    EXPECT_FALSE(converted);
+
+    //test for a convert with a nullptr for the converted parameter
+    result = celix_utils_convertStringToBool("true", false, nullptr);
+    EXPECT_EQ(true, result);
+}
+
+TEST_F(ConvertUtilsTestSuite, ConvertToVersionTest) {
+    //test for a valid string
+    celix_version_t* result = celix_utils_convertStringToVersion("1.2.3");
+    checkVersion(result, 1, 2, 3, nullptr);
+    celix_version_destroy(result);
+
+    //test for an invalid string
+    result = celix_utils_convertStringToVersion("A");
+    EXPECT_EQ(nullptr, result);
+
+    //test for a string with a number
+    result = celix_utils_convertStringToVersion("1.2.3.A");
+    checkVersion(result, 1, 2, 3, "A");
+    celix_version_destroy(result);
+
+    //test for a string with a partly (strict) version
+    result = celix_utils_convertStringToVersion("1");
+    EXPECT_EQ(nullptr, result);
+
+    //test for a string with a partly (strict) version
+    result = celix_utils_convertStringToVersion("1.2");
+    EXPECT_EQ(nullptr, result);
+}
\ No newline at end of file
diff --git a/libs/utils/include/celix_convert_utils.h 
b/libs/utils/include/celix_convert_utils.h
new file mode 100644
index 00000000..3d5101b0
--- /dev/null
+++ b/libs/utils/include/celix_convert_utils.h
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef CELIX_CELIX_CONVERT_UTILS_H
+#define CELIX_CELIX_CONVERT_UTILS_H
+
+#include <stdbool.h>
+#include "celix_version.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file celix_convert_utils.h
+ * @brief The celix_convert_utils.h file contains utility functions to convert 
strings to other types.
+ */
+
+/**
+ * @brief Convert a string to a boolean.
+ *
+ * @param[in] val The string to convert.
+ * @param[in] defaultValue The default value if the string is not a valid 
boolean.
+ * @param[out] converted If not NULL, will be set to true if the string is a 
valid boolean, otherwise false.
+ * @return The boolean value.
+ */
+bool celix_utils_convertStringToBool(const char* val, bool defaultValue, bool* 
converted);
+
+/**
+ * @brief Convert a string to a double.
+ *
+ * @param[in] val The string to convert.
+ * @param[in] defaultValue The default value if the string is not a valid 
double.
+ * @param[out] converted If not NULL, will be set to true if the string is a 
valid double, otherwise false.
+ * @return The double value.
+ */
+double celix_utils_convertStringToDouble(const char* val, double defaultValue, 
bool* converted);
+
+/**
+ * @brief Convert a string to a long.
+ *
+ * @param[in] val The string to convert.
+ * @param[in] defaultValue The default value if the string is not a valid long.
+ * @param[out] converted If not NULL, will be set to true if the string is a 
valid long, otherwise false.
+ * @return The long value.
+ */
+long celix_utils_convertStringToLong(const char* val, long defaultValue, bool* 
converted);
+
+/**
+ * @brief Convert a string to a celix_version_t.
+ *
+ * @note This convert function will only convert version strings in the format 
major.minor.micro(.qualifier)?.
+ * So the major, minor and micro are required, the qualifier is optional.
+ *
+ * @param val The string to convert.
+ * @return A new celix_version_t* if the string is a valid version, otherwise 
NULL.
+ */
+celix_version_t* celix_utils_convertStringToVersion(const char* val);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //CELIX_CELIX_CONVERT_UTILS_H
diff --git a/libs/utils/include/celix_file_utils.h 
b/libs/utils/include/celix_file_utils.h
index 6524ed3d..4f4dc84e 100644
--- a/libs/utils/include/celix_file_utils.h
+++ b/libs/utils/include/celix_file_utils.h
@@ -21,6 +21,7 @@
 #define CELIX_FILE_UTILS_H
 
 #include <stdbool.h>
+#include <sys/time.h>
 #include "celix_errno.h"
 
 #ifdef __cplusplus
@@ -81,6 +82,23 @@ celix_status_t celix_utils_extractZipFile(const char* 
zipPath, const char* extra
  */
 celix_status_t celix_utils_extractZipData(const void *zipData, size_t 
zipDataSize, const char* extractToDir, const char** errorOut);
 
+/**
+ * @brief Returns the last modified time of the file at path.
+ *
+ * @param[in] path The path to the file.
+ * @param[out] lastModified The last modified time of the file.
+ * @return CELIX_SUCCESS if the last modified time was successfully retrieved.
+ */
+celix_status_t celix_utils_getLastModified(const char* path, struct timespec* 
lastModified);
+
+/**
+ * @brief Touch the file at path and thus update the last modified time.
+ * @param path The path to the file.
+ * @return CELIX_SUCCESS if the last modified time was successfully updated.
+ */
+celix_status_t celix_utils_touch(const char* path);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/utils/include/celix_threads.h 
b/libs/utils/include/celix_threads.h
index ba02bfd0..f9458cba 100644
--- a/libs/utils/include/celix_threads.h
+++ b/libs/utils/include/celix_threads.h
@@ -69,6 +69,12 @@ celix_status_t celixThread_kill(celix_thread_t thread, int 
sig);
 
 celix_thread_t celixThread_self(void);
 
+/**
+ * Return true - as int - if the threads are equals
+ * @param[in] thread1
+ * @param[in] thread2
+ * @return non-zero if the thread IDs t1 and t2 correspond to the same thread, 
otherwise it will return zero.
+ */
 int celixThread_equals(celix_thread_t thread1, celix_thread_t thread2);
 
 bool celixThread_initialized(celix_thread_t thread);
diff --git a/libs/utils/include/celix_utils.h b/libs/utils/include/celix_utils.h
index e0db2731..14ac0307 100644
--- a/libs/utils/include/celix_utils.h
+++ b/libs/utils/include/celix_utils.h
@@ -46,11 +46,42 @@ char* celix_utils_strdup(const char *str);
  */
 unsigned int celix_utils_stringHash(const char* string);
 
+/**
+ * The proposed buffer size to use for celix_utils_writeOrCreateString with a 
buffer on the stcck.
+ */
+#define CELIX_DEFAULT_STRING_CREATE_BUFFER_SIZE 512
+
+/**`
+ * @brief Format a string to the provided buffer or a newly allocated buffer 
if the provided buffer is to small.
+ * @param[in,out] buffer The buffer to write the formatted string to.
+ * @param[in] bufferSize The size of the buffer.
+ * @param[in] format The format string.
+ * @param[in] ... The arguments for the format string.
+ * @return The formatted string in the provided buffer or a newly allocated 
buffer if the provided buffer is to small.
+ */
+char* celix_utils_writeOrCreateString(char* buffer, size_t bufferSize, const 
char* format, ...)
+    __attribute__((format(printf, 3, 4)));
+
+/**
+ * @brief Free the provided str if the str is not equal to the provided buffer.
+ * @note This function is useful combined with celix_utils_writeOrCreateString.
+ * @param buffer The buffer to compare the str to.
+ * @param str The string to free if it is not equal to the buffer.
+ */
+void celix_utils_freeStringIfNeeded(const char* buffer, char* str);
+
+
 /**
  * @brief Compares two strings and returns true if the strings are equal.
  */
 bool celix_utils_stringEquals(const char* a, const char* b);
 
+/**
+ * Check if the provided string contains a whitespace (spaces, tabs, etc).
+ * The check is based on `isspace`.
+ */
+bool celix_utils_containsWhitespace(const char* s);
+
 /**
  * @brief Returns a trimmed string.
  *
@@ -64,6 +95,17 @@ char* celix_utils_trim(const char* string);
  */
 bool celix_utils_isStringNullOrEmpty(const char* s);
 
+/** @brief create a C identifier from the provided string by replacing each 
non-alphanumeric character with a
+ * underscore.
+ *
+ * If the first character is a digit, a prefix underscore will also be added.
+ * Will return NULL if the input is NULL or an empty string.
+ *
+ * @param string the input string to make a C identifier for.
+ * @return new newly allocated string or NULL if the input was wrong. The 
caller is owner of the returned string.
+ */
+char* celix_utils_makeCIdentifier(const char* s);
+
 
 /**
  * @brief Extract a local name and namespace from a fully qualified name using 
the provided namespace separator.
diff --git a/libs/utils/private/test/utils_test.cpp 
b/libs/utils/private/test/utils_test.cpp
deleted file mode 100644
index 0bd37172..00000000
--- a/libs/utils/private/test/utils_test.cpp
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#include "string.h"
-#include <stdlib.h>
-#include <string.h>
-
-#include "CppUTest/TestHarness.h"
-#include "CppUTest/TestHarness_c.h"
-#include "CppUTest/CommandLineTestRunner.h"
-#include "celix_utils.h"
-
-extern "C"
-{
-#include "utils.h"
-}
-
-int main(int argc, char** argv) {
-    MemoryLeakWarningPlugin::turnOffNewDeleteOverloads();
-    return RUN_ALL_TESTS(argc, argv);
-}
-
-TEST_GROUP(utils) {
-
-    void setup(void) {
-    }
-
-    void teardown() {
-    }
-};
-
-static char* my_strdup(const char* s){
-    if(s==NULL){
-        return NULL;
-    }
-
-    size_t len = strlen(s);
-
-    char *d = (char*) calloc (len + 1,sizeof(char));
-
-    if (d == NULL){
-        return NULL;
-    }
-
-    strncpy (d,s,len);
-    return d;
-}
-
-TEST(utils, stringHash) {
-    char * toHash = my_strdup("abc");
-    unsigned int hash;
-    hash = utils_stringHash((void *) toHash);
-    LONGS_EQUAL(193485963, hash);
-
-    free(toHash);
-    toHash = my_strdup("abc123def456ghi789jkl012mno345pqr678stu901vwx234yz");
-    hash = utils_stringHash((void *) toHash);
-    LONGS_EQUAL(1532304168, hash);
-
-    free(toHash);
-    toHash = my_strdup("abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz");
-    hash = utils_stringHash((void *) toHash);
-    LONGS_EQUAL(3721605959, hash);
-    free(toHash);
-
-    hash = utils_stringHash(NULL);
-    LONGS_EQUAL(0, hash);
-}
-
-TEST(utils, stringEquals) {
-    // Compare with equal strings
-    char * org = my_strdup("abc");
-    char * cmp = my_strdup("abc");
-
-    int result = utils_stringEquals((void *) org, (void *) cmp);
-    CHECK(result);
-
-    // Compare with no equal strings
-    free(cmp);
-    cmp = my_strdup("abcd");
-
-    result = utils_stringEquals((void *) org, (void *) cmp);
-    CHECK_FALSE(result);
-
-    // Compare with numeric strings
-    free(org);
-    free(cmp);
-    org = my_strdup("123");
-    cmp = my_strdup("123");
-
-    result = utils_stringEquals((void *) org, (void *) cmp);
-    CHECK(result);
-
-    // Compare with long strings
-    free(org);
-    free(cmp);
-    org = my_strdup("abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz");
-    cmp = my_strdup("abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz"
-            "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz");
-
-    result = utils_stringEquals((void *) org, (void *) cmp);
-    CHECK(result);
-
-    free(org);
-    free(cmp);
-}
-
-TEST(utils, string_ndup){
-    // Compare with equal strings
-    const char * org = "abc";
-    char * cmp = NULL;
-
-    cmp = string_ndup(org, 3);
-    STRCMP_EQUAL(org, cmp);
-    free(cmp);
-
-    org = "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz";
-    cmp = string_ndup(org, 50);
-    STRCMP_EQUAL(org, cmp);
-    free(cmp);
-
-    cmp = string_ndup(org, 25);
-    LONGS_EQUAL(25, strlen(cmp));
-    free(cmp);
-}
-
-TEST(utils, stringTrim) {
-    // Multiple whitespaces, before, after and in between
-    char * toTrim = my_strdup(" a b c ");
-    char * result = utils_stringTrim(toTrim);
-
-    STRCMP_EQUAL("a b c", result);
-
-    // No whitespaces
-    free(toTrim);
-    toTrim = my_strdup("abc");
-    result = utils_stringTrim(toTrim);
-
-    STRCMP_EQUAL("abc", result);
-
-    // Only whitespace before
-    free(toTrim);
-    toTrim = my_strdup("               abc");
-    result = utils_stringTrim(toTrim);
-
-    STRCMP_EQUAL("abc", result);
-
-    // Only whitespace after
-    free(toTrim);
-    toTrim = my_strdup("abc         ");
-    result = utils_stringTrim(toTrim);
-
-    STRCMP_EQUAL("abc", result);
-
-    // Whitespace other than space (tab, cr..).
-    free(toTrim);
-    toTrim = my_strdup("\tabc  \n asdf  \n");
-    result = utils_stringTrim(toTrim);
-
-    STRCMP_EQUAL("abc  \n asdf", result);
-
-    free(toTrim);
-
-    char* trimmed = celix_utils_trim("  abc   ");
-    STRCMP_EQUAL("abc", trimmed);
-    free(trimmed);
-}
-
-TEST(utils, thread_equalsSelf){
-    celix_thread thread = celixThread_self();
-    bool get;
-
-    LONGS_EQUAL(CELIX_SUCCESS, thread_equalsSelf(thread, &get));
-    CHECK(get);
-
-    thread.thread = (pthread_t) 0x42;
-    LONGS_EQUAL(CELIX_SUCCESS, thread_equalsSelf(thread, &get));
-    CHECK_FALSE(get);
-}
-
-TEST(utils, isNumeric) {
-    // Check numeric string
-    char * toCheck = my_strdup("42");
-
-    bool result;
-    celix_status_t status = utils_isNumeric(toCheck, &result);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(result);
-
-    // Check non numeric string
-    free(toCheck);
-    toCheck = my_strdup("42b");
-    status = utils_isNumeric(toCheck, &result);
-    LONGS_EQUAL(CELIX_SUCCESS, status);
-    CHECK_C(!result);
-
-    free(toCheck);
-}
-
-TEST(utils, compareServiceIdsAndRanking){
-    int ret;
-    //service 1 is higher ranked and has a irrelevant ID, so result is -1 
(smaller -> sorted before service 2)
-    ret = celix_utils_compareServiceIdsAndRanking(2,2,1,1);
-    LONGS_EQUAL(-1, ret);
-
-    //service 1 is equally ranked and has a lower ID. so result is -1 (smaller 
-> sorted before service 2)
-    ret = celix_utils_compareServiceIdsAndRanking(1,1,2,1);
-    LONGS_EQUAL(-1, ret);
-
-    //service 1 is equally ranked and has a higher ID, so result is 1 (larger 
-> sorted after service 2)
-    ret = celix_utils_compareServiceIdsAndRanking(2,1,1,1);
-    LONGS_EQUAL(1, ret);
-
-    //service 1 is lower ranked and has a irrelevant ID, so result is -1 
(larger -> sorted after service 2)
-    ret = celix_utils_compareServiceIdsAndRanking(1,1,2,2);
-    LONGS_EQUAL(1, ret);
-
-    //service 1 is equal in ID and irrelevantly ranked
-    //note ensure that also the call without celix_ is tested
-    ret = utils_compareServiceIdsAndRanking(1,1,1,1);
-    LONGS_EQUAL(0, ret);
-
-
-}
-
-TEST(utils, extractLocalNameAndNamespaceTest) {
-    const char *input = "lb";
-    char* name = NULL;
-    char *ns = NULL;
-    celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(input, 
"::", &name, &ns);
-    CHECK_EQUAL_C_STRING("lb", name);
-    CHECK_TRUE(ns == NULL);
-    free(name);
-    free(ns);
-
-    input = "celix::lb";
-    name = NULL;
-    ns = NULL;
-    celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(input, 
"::", &name, &ns);
-    CHECK_EQUAL_C_STRING("lb", name);
-    CHECK_EQUAL_C_STRING("celix", ns);
-    free(name);
-    free(ns);
-
-    input = "celix::extra::namespace::entries::lb";
-    name = NULL;
-    ns = NULL;
-    celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(input, 
"::", &name, &ns);
-    CHECK_EQUAL_C_STRING("lb", name);
-    CHECK_EQUAL_C_STRING("celix::extra::namespace::entries", ns);
-    free(name);
-    free(ns);
-
-    input = "celix.extra.namespace.entries.lb";
-    name = NULL;
-    ns = NULL;
-    celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(input, ".", 
&name, &ns);
-    CHECK_EQUAL_C_STRING("lb", name);
-    CHECK_EQUAL_C_STRING("celix.extra.namespace.entries", ns);
-    free(name);
-    free(ns);
-
-    //testing with non existing namespace
-    input = "celix.extra.namespace.entries.lb";
-    name = NULL;
-    ns = NULL;
-    celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(input, 
"??", &name, &ns);
-    CHECK_EQUAL_C_STRING("celix.extra.namespace.entries.lb", name);
-    CHECK_TRUE(ns == NULL);
-    free(name);
-    free(ns);
-
-    //wrong input check
-    input = NULL;
-    name = NULL;
-    ns = NULL;
-    celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(input, 
"??", &name, &ns);
-    CHECK_TRUE(name == NULL);
-    CHECK_TRUE(ns == NULL);
-    free(name);
-    free(ns);
-
-
-    //empty namespace check
-    input = "celix.extra.namespace.entries.lb";
-    name = NULL;
-    ns = NULL;
-    celix_utils_extractLocalNameAndNamespaceFromFullyQualifiedName(input, "", 
&name, &ns);
-    CHECK_EQUAL_C_STRING("celix.extra.namespace.entries.lb", name);
-    CHECK_TRUE(ns == NULL);
-    free(name);
-    free(ns);
-}
-
-TEST(utils, isStringNullOrEmpty) {
-    bool empty = celix_utils_isStringNullOrEmpty(nullptr);
-    CHECK_TRUE(empty);
-    empty = celix_utils_isStringNullOrEmpty("");
-    CHECK_TRUE(empty);
-    empty = celix_utils_isStringNullOrEmpty(" ");
-    CHECK_FALSE(empty);
-    empty = celix_utils_isStringNullOrEmpty("foo");
-    CHECK_FALSE(empty);
-}
\ No newline at end of file
diff --git a/libs/utils/src/celix_convert_utils.c 
b/libs/utils/src/celix_convert_utils.c
new file mode 100644
index 00000000..de945c33
--- /dev/null
+++ b/libs/utils/src/celix_convert_utils.c
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "utils.h"
+#include "celix_utils.h"
+#include "celix_version.h"
+
+bool celix_utils_convertStringToBool(const char* val, bool defaultValue, bool* 
converted) {
+    bool result = defaultValue;
+    if (converted != NULL) {
+        *converted = false;
+    }
+    if (val != NULL) {
+        char buf[32];
+        snprintf(buf, 32, "%s", val);
+        char *trimmed = utils_stringTrim(buf);
+        if (strncasecmp("true", trimmed, 5) == 0) {
+            result = true;
+            if (converted) {
+                *converted = true;
+            }
+        } else if (strncasecmp("false", trimmed, 6) == 0) {
+            result = false;
+            if (converted) {
+                *converted = true;
+            }
+        }
+    }
+    return result;
+}
+
+double celix_utils_convertStringToDouble(const char* val, double defaultValue, 
bool* converted) {
+    double result = defaultValue;
+    if (converted != NULL) {
+        *converted = false;
+    }
+    if (val != NULL) {
+        char *endptr;
+        double d = strtod(val, &endptr);
+        if (endptr != val) {
+            result = d;
+            if (converted) {
+                *converted = true;
+            }
+        }
+    }
+    return result;
+}
+
+long celix_utils_convertStringToLong(const char* val, long defaultValue, bool* 
converted) {
+    long result = defaultValue;
+    if (converted != NULL) {
+        *converted = false;
+    }
+    if (val != NULL) {
+        char *endptr;
+        long l = strtol(val, &endptr, 10);
+        if (endptr != val) {
+            result = l;
+            if (converted) {
+                *converted = true;
+            }
+        }
+    }
+    return result;
+}
+
+celix_version_t* celix_utils_convertStringToVersion(const char* val) {
+    celix_version_t* result = NULL;
+    if (val != NULL) {
+        //check if string has two dots ('.'), and only try to create string if 
it has two dots
+        char* firstDot = strchr(val, '.');
+        char* lastDot = strrchr(val, '.');
+        if (firstDot != NULL && lastDot != NULL && firstDot != lastDot) {
+            result = celix_version_createVersionFromString(val);
+        }
+    }
+    return result;
+}
diff --git a/libs/utils/src/celix_file_utils.c 
b/libs/utils/src/celix_file_utils.c
index d66d06ba..ac0b2224 100644
--- a/libs/utils/src/celix_file_utils.c
+++ b/libs/utils/src/celix_file_utils.c
@@ -25,6 +25,7 @@
 #include <dirent.h>
 #include <stdlib.h>
 #include <zip.h>
+#include <sys/time.h>
 
 #include "celix_utils.h"
 
@@ -235,7 +236,7 @@ celix_status_t celix_utils_extractZipFile(const char* 
zipPath, const char* extra
         status = celix_utils_extractZipInternal(zip, extractToDir, errorOut);
         zip_close(zip);
     } else {
-        //note libzip can give more info with zip_error_to_str if needed (but 
this requires a allocated string buf).
+        //note libzip can give more info with zip_error_to_str if needed (but 
this requires an allocated string buf).
         status = CELIX_FILE_IO_EXCEPTION;
         *errorOut = ERROR_OPENING_ZIP;
     }
@@ -277,4 +278,28 @@ celix_status_t celix_utils_extractZipData(const void 
*zipData, size_t zipDataSiz
     return status;
 }
 
+celix_status_t celix_utils_getLastModified(const char* path, struct timespec* 
lastModified) {
+    celix_status_t status = CELIX_SUCCESS;
+    struct stat st;
+    if (stat(path, &st) == 0) {
+#ifdef __APPLE__
+        *lastModified = st.st_mtimespec;
+#else
+        *lastModified = st.st_mtim;
+#endif
+    } else {
+        lastModified->tv_sec = 0;
+        lastModified->tv_nsec = 0;
+        status = CELIX_FILE_IO_EXCEPTION;
+    }
+    return status;
+}
 
+celix_status_t celix_utils_touch(const char* path) {
+    celix_status_t status = CELIX_SUCCESS;
+    int rc = utimes(path, NULL);
+    if (rc != 0) {
+        status = CELIX_FILE_IO_EXCEPTION;
+    }
+    return status;
+}
diff --git a/libs/utils/src/utils.c b/libs/utils/src/utils.c
index 4ebb938c..292daa1b 100644
--- a/libs/utils/src/utils.c
+++ b/libs/utils/src/utils.c
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <stdarg.h>
 
 #include "utils.h"
 #include "celix_utils.h"
@@ -64,10 +65,43 @@ bool celix_utils_stringEquals(const char* a, const char* b) 
{
     }
 }
 
+bool celix_utils_containsWhitespace(const char* s) {
+    if (!celix_utils_isStringNullOrEmpty(s)) {
+        for (int i = 0; s[i] != '\0'; ++i) {
+            if (isspace(s[i])) {
+                return true;
+            }
+        }
+    }
+    return false;
+};
+
 bool celix_utils_isStringNullOrEmpty(const char* s) {
     return s == NULL || s[0] == '\0';
 }
 
+char* celix_utils_makeCIdentifier(const char* s) {
+    if (celix_utils_isStringNullOrEmpty(s)) {
+        return NULL;
+    }
+    size_t len = strnlen(s, CELIX_UTILS_MAX_STRLEN);
+    char* ns = malloc(len + 2); //+2 for '\0' and an extra _ prefix if needed.
+    int i = 0;
+    if (isdigit(s[0])) {
+        ns[i++] = '_';
+    }
+    for (size_t j = 0; j < len; j++) {
+        if (isalnum(s[j])) {
+            ns[i++] = s[j];
+        } else {
+            ns[i++] = '_';
+        }
+    }
+    ns[i] = '\0';
+    return ns;
+}
+
+
 char * string_ndup(const char *s, size_t n) {
     size_t len = strlen(s);
     char *ret;
@@ -138,15 +172,32 @@ bool utils_isStringEmptyOrNull(const char * const str) {
     return empty;
 }
 
-celix_status_t thread_equalsSelf(celix_thread_t thread, bool *equals) {
-    celix_status_t status = CELIX_SUCCESS;
+char* celix_utils_writeOrCreateString(char* buffer, size_t bufferSize, const 
char* format, ...) {
+    va_list args;
+    va_start(args, format);
+    int written = vsnprintf(buffer, bufferSize, format, args);
+    va_end(args);
+    if (written < 0 || written >= bufferSize) {
+        //buffer to small, create new string
+        char* newStr = NULL;
+        va_start(args, format);
+        vasprintf(&newStr, format, args);
+        va_end(args);
+        return newStr;
+    }
+    return buffer;
+}
 
-    celix_thread_t self = celixThread_self();
-    if (status == CELIX_SUCCESS) {
-        *equals = celixThread_equals(self, thread);
+void celix_utils_freeStringIfNeeded(const char* buffer, char* str) {
+    if (str != buffer) {
+        free(str);
     }
+}
 
-    return status;
+celix_status_t thread_equalsSelf(celix_thread_t thread, bool *equals) {
+    celix_thread_t self = celixThread_self();
+    *equals = celixThread_equals(self, thread);
+    return CELIX_SUCCESS;
 }
 
 celix_status_t utils_isNumeric(const char *number, bool *ret) {

Reply via email to