commit 4412ab9f43326f812e48b87c9c75529644b39f93 Author: Ondřej Hruška Date: Sat Sep 19 15:48:13 2020 +0200 a diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76251f5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,63 @@ +*.swp +.lock* +.waf* +*.pyc +.project +.cproject +.settings +~* +pdebug* +*.tar* +*.log +packetlog.db +*.bin +*.dat +*.map +*~ +*.bak + +fsim +*.img + +# ninja files +build.ninja +rules.ninja +.ninja_deps +.ninja_log + +# generated by cmake +CMakeCache.txt +*.cmake +CMakeFiles +vcom +Makefile +*.cbp +*.a + +.idea/ +.DS_Store + +build/ +clion-build/ +cmake-build-*/ + +CMakeLists.txt.user + +# Visual Studio clutter +_ReSharper* +*.sdf +*.suo +*.dir +*.vcxproj* +*.sln +.vs +CMakeSettings.json +Win32 +x64 +Debug +Release +MinSizeRel +RelWithDebInfo +*.opensdf + +*.out diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..cf39f1b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.15) +project(a) + +set(CMAKE_BUILD_TYPE Debug) + +add_executable(a main.c strlcat.c) diff --git a/main.c b/main.c new file mode 100644 index 0000000..ea06396 --- /dev/null +++ b/main.c @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include "strlcat.h" + +int main () { + // Test argument checking + assert(0 == strlcpy(NULL, "", 10)); + assert(0 == strlcpy("", NULL, 10)); + assert(0 == strlcpy(NULL, NULL, 10)); + + assert(0 == strlncpy(NULL, "", 10, 10)); + assert(0 == strlncpy("", NULL, 10, 10)); + assert(0 == strlncpy(NULL, NULL, 10, 10)); + + assert(0 == strlcat(NULL, "", 10)); + assert(0 == strlcat("", NULL, 10)); + assert(0 == strlcat(NULL, NULL, 10)); + + assert(0 == strlncat(NULL, "", 10, 10)); + assert(0 == strlncat("", NULL, 10, 10)); + assert(0 == strlncat(NULL, NULL, 10, 10)); + + + // strlcpy + { + char buf[5] = {0,1,2,3,4}; + assert(9 == strlcpy(buf, "abcdefgh", 4)); + assert(buf[0] == 'a'); + assert(buf[1] == 'b'); + assert(buf[2] == 'c'); + assert(buf[3] == 0); + assert(buf[4] == 4); + } + { + char buf[5] = {0,1,2,3,4}; + assert(3 == strlcpy(buf, "ab", 5)); + assert(buf[0] == 'a'); + assert(buf[1] == 'b'); + assert(buf[2] == 0); + assert(buf[3] == 3); + } + { + char buf[5] = {5,4,3,2,1}; + assert(7 == strlcpy(buf, "abcdef", 1)); + assert(buf[0] == 0); // it puts the zero there + assert(buf[1] == 4); + } + + // strlncpy + { + char buf[5] = {0,1,2,3,4}; + assert(3 == strlncpy(buf, "abcdefgh", 4, 2)); + assert(buf[0] == 'a'); + assert(buf[1] == 'b'); + assert(buf[2] == 0); + assert(buf[3] == 3); + } + { + char buf[5] = {0,1,2,3,4}; + assert(5 == strlncpy(buf, "abcdefgh", 5, 4)); + assert(buf[0] == 'a'); + assert(buf[1] == 'b'); + assert(buf[2] == 'c'); + assert(buf[3] == 'd'); + assert(buf[4] == 0); + } + { + char buf[5] = {5,4,3,2,1}; + assert(2 == strlncpy(buf, "abcdef", 1, 1)); + assert(buf[0] == 0); // it puts the zero there + assert(buf[1] == 4); + } + + // strlcat + { + char buf[8] = {0,1,2,3,4,5,6,7}; + assert(2 == strlcat(buf, "a", 8)); + assert(buf[0] == 'a'); + assert(buf[1] == 0); + assert(buf[2] == 2); + } + { + char buf[8] = {0,1,2,3,4,5,6,7}; + assert(3 == strlcat(buf, "ab", 8)); + assert(buf[0] == 'a'); + assert(buf[1] == 'b'); + assert(buf[2] == 0); + assert(buf[3] == 3); + + assert(5 == strlcat(buf, "xx", 8)); + assert(0==strcmp(buf,"abxx")); + + assert(5 == strlcat(buf, "", 8)); // this just sets the terminator + assert(0==strcmp(buf,"abxx")); + + assert(8 == strlcat(buf, "dog", 8)); + assert(0==strcmp(buf,"abxxdog")); + + assert(10 == strlcat(buf, "ff", 8)); + assert(0==strcmp(buf,"abxxdog")); + } + { + char buf[5] = {5,4,3,2,1}; + assert(7 == strlcat(buf, "abcdef", 1)); + assert(buf[0] == 0); // it puts the zero there + assert(buf[1] == 4); + } + + + // strlncat + { + char buf[8] = {0,1,2,3,4,5,6,7}; + assert(2 == strlncat(buf, "a", 8, 1)); + assert(buf[0] == 'a'); + assert(buf[1] == 0); + assert(buf[2] == 2); + } + { + char buf[8] = {0,1,2,3,4,5,6,7}; + assert(2 == strlncat(buf, "a", 8, 50)); // larger n than string size + assert(buf[0] == 'a'); + assert(buf[1] == 0); + assert(buf[2] == 2); + } + { + char buf[8] = {0,1,2,3,4,5,6,7}; + assert(26 == strlncat(buf, "abcdefghijklmnopqrstuvwyx", 8, 50)); // larger n than string size & larger than capacity + assert(0==strcmp(buf, "abcdefg")); + } + { + char buf[8] = {0,1,2,3,4,5,6,7}; + assert(5 == strlncat(buf, "abcd", 8, 50)); // larger n than string size & larger than capacity + assert(0==strcmp(buf, "abcd")); + assert(buf[4] == 0); + assert(buf[5] == 5); + } + { + char buf[8] = {0,1,2,3,4,5,6,7}; + assert(3 == strlncat(buf, "abcdef", 8, 2)); + assert(buf[0] == 'a'); + assert(buf[1] == 'b'); + assert(buf[2] == 0); + assert(buf[3] == 3); + + assert(5 == strlncat(buf, "xxyyyyy", 8, 2)); + assert(0==strcmp(buf,"abxx")); + + assert(5 == strlncat(buf, "", 8, 100)); // this just sets the terminator + assert(0==strcmp(buf,"abxx")); + + assert(5 == strlncat(buf, "", 8, 0)); + assert(0==strcmp(buf,"abxx")); + + assert(8 == strlncat(buf, "doggg", 8, 3)); + assert(0==strcmp(buf,"abxxdog")); + + assert(10 == strlncat(buf, "ffxxx", 8, 2)); + assert(0==strcmp(buf,"abxxdog")); + } + { + char buf[5] = {5,4,3,2,1}; + assert(2 == strlncat(buf, "abcdef", 1, 1)); + assert(buf[0] == 0); // it puts the zero there + assert(buf[1] == 4); + } + + // this is a test that asserts are evaluated + assert((printf("all ok\n"), 1)); + + return 0; +} + diff --git a/strlcat.c b/strlcat.c new file mode 100644 index 0000000..9d44fa9 --- /dev/null +++ b/strlcat.c @@ -0,0 +1,72 @@ +#include +#include + +size_t strlcat(char *__restrict__ dst, const char *__restrict__ src, size_t capacity) +{ + if (!dst) { return 0; } + if (!src) { return 0; } + size_t used = strnlen(dst, capacity); + if (used >= capacity) { used = capacity - 1; } + size_t to_print = strlen(src); + // to_print does not include the terminator + const size_t needed = used + to_print + 1; + if (needed > capacity) { + to_print = capacity - used - 1; + } + memcpy(dst + used, src, to_print); + *(dst + used + to_print) = '\0'; + return needed; +} + +size_t strlncat(char *__restrict__ dst, const char *__restrict__ src, size_t capacity, size_t num) +{ + if (!dst) { return 0; } + if (!src) { return 0; } + size_t used = strnlen(dst, capacity); + if (used >= capacity) { used = capacity - 1; } + size_t to_print = strlen(src); + if (to_print > num) { + to_print = num; + } + // to_print does not include the terminator + const size_t needed = used + to_print + 1; + if (needed > capacity) { + to_print = capacity - used - 1; + } + memcpy(dst + used, src, to_print); + *(dst + used + to_print) = '\0'; + return needed; +} + +size_t strlcpy(char *__restrict__ dst, const char *__restrict__ src, size_t capacity) +{ + if (!dst) { return 0; } + if (!src) { return 0; } + size_t to_print = strlen(src); + // to_print does not include the terminator + const size_t needed = to_print + 1; + if (needed > capacity) { + to_print = capacity - 1; + } + memcpy(dst, src, to_print); + *(dst + to_print) = '\0'; + return needed; +} + +size_t strlncpy(char *__restrict__ dst, const char *__restrict__ src, size_t capacity, size_t num) +{ + if (!dst) { return 0; } + if (!src) { return 0; } + size_t to_print = strlen(src); + if (to_print > num) { + to_print = num; + } + // to_print does not include the terminator + const size_t needed = to_print + 1; + if (needed > capacity) { + to_print = capacity - 1; + } + memcpy(dst, src, to_print); + *(dst + to_print) = '\0'; + return needed; +} diff --git a/strlcat.h b/strlcat.h new file mode 100644 index 0000000..8d843af --- /dev/null +++ b/strlcat.h @@ -0,0 +1,55 @@ +/** + * Strcat variant respecting buffer capacity. + * Function signatures based on libbsd. + * + * Created by Ondřej Hruška on 2020/09/19. + */ + +#ifndef STRLCAT_H +#define STRLCAT_H + +#include + +/** + * Append a zero-terminated to a zero-terminated buffer. + * + * @param[out] dst - destination buffer + * @param[in] src - source string + * @param[in] capacity - destination buffer capacity + * @return The buffer size needed. If > size, then data loss occurred. Returns 0 if invalid arguments were supplied. + */ +size_t strlcat(char *__restrict__ dst, const char *__restrict__ src, size_t capacity); + +/** + * Append at most N characters of a zero-terminated string to a zero-terminated buffer. + * + * @param[out] dst - destination buffer + * @param[in] src - source string + * @param[in] capacity - destination buffer capacity + * @param[in] num - number of bytes to write + * @return The buffer size needed. If > size, then data loss occurred. Returns 0 if invalid arguments were supplied. + */ +size_t strlncat(char *__restrict__ dst, const char *__restrict__ src, size_t capacity, size_t num); + +/** + * Copy a zero-terminated string to a zero-terminated buffer. + * + * @param[out] dst - destination buffer + * @param[in] src - source string + * @param[in] capacity - destination buffer capacity + * @return The buffer size needed. If > size, then data loss occurred. Returns 0 if invalid arguments were supplied. + */ +size_t strlcpy(char *__restrict__ dst, const char *__restrict__ src, size_t capacity); + +/** + * Copy at most N characters of a zero-terminated string to a zero-terminated buffer. + * + * @param[out] dst - destination buffer + * @param[in] src - source string + * @param[in] capacity - destination buffer capacity + * @param[in] num - number of bytes to write + * @return The buffer size needed. If > size, then data loss occurred. Returns 0 if invalid arguments were supplied. + */ +size_t strlncpy(char *__restrict__ dst, const char *__restrict__ src, size_t capacity, size_t num); + +#endif //STRLCAT_H