diff --git a/makefiles/defaultmodules_regular.inc.mk b/makefiles/defaultmodules_regular.inc.mk index 5dc2fa6e9c..6f5f46584c 100644 --- a/makefiles/defaultmodules_regular.inc.mk +++ b/makefiles/defaultmodules_regular.inc.mk @@ -6,7 +6,7 @@ DEFAULT_MODULE += board board_common_init \ cpu \ core core_init core_lib core_msg core_panic core_thread \ - sys + sys libc # Include potentially added default modules by the board -include $(BOARDDIR)/Makefile.default diff --git a/sys/Kconfig b/sys/Kconfig index 4a6c84c21e..73354e3f5a 100644 --- a/sys/Kconfig +++ b/sys/Kconfig @@ -37,6 +37,7 @@ rsource "fs/Kconfig" rsource "hashes/Kconfig" rsource "iolist/Kconfig" rsource "isrpipe/Kconfig" +rsource "libc/Kconfig" menu "Libc" diff --git a/sys/include/string_utils.h b/sys/include/string_utils.h index eb0ec8641a..46bfb3def4 100644 --- a/sys/include/string_utils.h +++ b/sys/include/string_utils.h @@ -22,11 +22,13 @@ * @author Marian Buschsieweke */ +#include +#include /* if explicit_bzero() is provided by standard C lib, it may be defined in * either `string.h` or `strings.h`, so just include both here */ -#include #include #include +#include #include "kernel_defines.h" @@ -45,7 +47,7 @@ extern "C" { * * for all other cases, we provide it here */ -#if !defined(BOARD_NATIVE) \ +#if !defined(CPU_NATIVE) \ && !(IS_USED(MODULE_PICOLIBC) && __BSD_VISIBLE) \ && !(IS_USED(MODULE_NEWLIB) && __BSD_VISIBLE && !defined(MCU_ESP8266)) @@ -70,6 +72,26 @@ static inline void explicit_bzero(void *dest, size_t n_bytes) } #endif +/** + * @brief Copy the string, or as much of it as fits, into the dest buffer. + * + * Preferred to `strncpy` since it always returns a valid string, and doesn't + * unnecessarily force the tail of the destination buffer to be zeroed. + * If the zeroing is desired, it's likely cleaner to use `strscpy` with an + * overflow test, then just memset the tail of the dest buffer. + * + * @param[out] dest Where to copy the string to + * @param[in] src Where to copy the string from + * @param[in] count Size of destination buffer + * + * @pre The destination buffer is at least one byte large, as + * otherwise the terminating zero byte won't fit + * + * @return the number of characters copied (not including the trailing zero) + * @retval -E2BIG the destination buffer wasn't big enough + */ +ssize_t strscpy(char *dest, const char *src, size_t count); + #ifdef __cplusplus } #endif diff --git a/sys/libc/Kconfig b/sys/libc/Kconfig new file mode 100644 index 0000000000..eaa4895518 --- /dev/null +++ b/sys/libc/Kconfig @@ -0,0 +1,11 @@ +# Copyright (c) 2021 HAW Hamburg +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +config MODULE_LIBC + bool "C Library helper functions" + default y + depends on TEST_KCONFIG diff --git a/sys/libc/Makefile b/sys/libc/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/libc/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/libc/string.c b/sys/libc/string.c new file mode 100644 index 0000000000..c3c1cc661a --- /dev/null +++ b/sys/libc/string.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * + * @author Benjamin Valentin + */ + +#include +#include "string_utils.h" + +ssize_t strscpy(char *dest, const char *src, size_t count) +{ + const char *start = dest; + + if (!count) { + return -E2BIG; + } + + while (--count && *src) { + *dest++ = *src++; + } + + *dest = 0; + + if (*src == 0) { + return dest - start; + } else { + return -E2BIG; + } +} +/** @} */ diff --git a/tests/unittests/tests-libc/Makefile b/tests/unittests/tests-libc/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/tests/unittests/tests-libc/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-libc/tests-libc.c b/tests/unittests/tests-libc/tests-libc.c new file mode 100644 index 0000000000..3d4a714ed3 --- /dev/null +++ b/tests/unittests/tests-libc/tests-libc.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include +#include +#include "string_utils.h" + +#include "tests-libc.h" + +static void test_libc_strscpy(void) +{ + char buffer[8]; + + TEST_ASSERT_EQUAL_INT(strscpy(buffer, "Hello", sizeof(buffer)), 5); + TEST_ASSERT_EQUAL_INT(strcmp(buffer, "Hello"), 0); + TEST_ASSERT_EQUAL_INT(strscpy(buffer, "012345678", sizeof(buffer)), -E2BIG); + TEST_ASSERT_EQUAL_INT(strcmp(buffer, "0123456"), 0); + memset(buffer, 0, sizeof(buffer)); + TEST_ASSERT_EQUAL_INT(strscpy(buffer, "01234567", sizeof(buffer)), -E2BIG); + TEST_ASSERT_EQUAL_INT(strcmp(buffer, "0123456"), 0); + memset(buffer, 0, sizeof(buffer)); + TEST_ASSERT_EQUAL_INT(strscpy(buffer, "0123456", sizeof(buffer)), 7); + TEST_ASSERT_EQUAL_INT(strcmp(buffer, "0123456"), 0); + TEST_ASSERT_EQUAL_INT(strscpy(buffer, "empty", 0), -E2BIG); +} + +Test *tests_libc_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_libc_strscpy), + }; + + EMB_UNIT_TESTCALLER(libc_tests, NULL, NULL, fixtures); + + return (Test *)&libc_tests; +} + +void tests_libc(void) +{ + TESTS_RUN(tests_libc_tests()); +} diff --git a/tests/unittests/tests-libc/tests-libc.h b/tests/unittests/tests-libc/tests-libc.h new file mode 100644 index 0000000000..619d92dccf --- /dev/null +++ b/tests/unittests/tests-libc/tests-libc.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file + * @brief Unittests for the ``libc`` module + * + * @author Benjamin Valentin + */ +#ifndef TESTS_LIBC_H +#define TESTS_LIBC_H + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_libc(void); + +/** + * @brief Generates tests for libc + * + * @return embUnit tests if successful, NULL if not. + */ +Test *tests_libc_tests(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_LIBC_H */ +/** @} */