aboutsummaryrefslogtreecommitdiff
path: root/src/lib.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib.c104
1 files changed, 104 insertions, 0 deletions
diff --git a/src/lib.c b/src/lib.c
index 39a462b..3c017d8 100644
--- a/src/lib.c
+++ b/src/lib.c
@@ -597,6 +597,109 @@ static void test_strjoin(void) {
}
#endif
+static char *strsjoin(const char *const strs[]) {
+ size_t size = sizeof('\0');
+ for (size_t i = 0; strs[i]; i++) {
+ const size_t curr_len = strnlen(strs[i], SIZE_MAX);
+ if (SIZE_MAX - size < curr_len) {
+ errno = EOVERFLOW;
+ logerr("strsjoin()", strerror(errno), __LINE__);
+ return NULL;
+ }
+ if (SIZE_MAX == i) {
+ errno = EOVERFLOW;
+ logerr("strsjoin()", strerror(errno), __LINE__);
+ return NULL;
+ }
+ size += curr_len;
+ }
+
+ char *const s = malloc(size);
+ if (!s) {
+ logerrl("malloc(", size, ")", strerror(errno), __LINE__);
+ return NULL;
+ }
+ *s = '\0';
+
+ // "i" can't overflow now, as it was already checked above
+ for (size_t i = 0; strs[i]; i++) {
+ strcat(s, strs[i]);
+ }
+
+ return s;
+}
+
+#ifdef TEST
+static void test_strsjoin(void) {
+ test_start("test_strsjoin");
+ {
+ testing("joining empty strings");
+ char *const s = strsjoin((const char *const [])
+ { "", "", NULL });
+ assert(s);
+ assert(strcmp(s, "") == 0);
+ free(s);
+ test_ok();
+ }
+ {
+ testing("joining empty strs array");
+ char *const s = strsjoin((const char *const [])
+ { NULL });
+ assert(s);
+ assert(strcmp(s, "") == 0);
+ free(s);
+ test_ok();
+ }
+ {
+ testing("first string is empty");
+ char *const s = strsjoin((const char *const [])
+ { "", "second not empty", NULL });
+ assert(s);
+ assert(strcmp(s, "second not empty") == 0);
+ free(s);
+ test_ok();
+ }
+ {
+ testing("third string is empty");
+ char *const s = strsjoin((const char *const [])
+ { "first not empty", "second not empty", "", NULL });
+ assert(s);
+ assert(strcmp(s, "first not emptysecond not empty") == 0);
+ free(s);
+ test_ok();
+ }
+ {
+ testing("four non-empty strings");
+ char *const s = strsjoin((const char *const [])
+ { "abc", "def", "ghi", "jkl", NULL });
+ assert(s);
+ assert(strcmp(s, "abcdefghijkl") == 0);
+ free(s);
+ test_ok();
+ }
+ {
+ testing("example usage: with file names");
+ char *const s = strsjoin((const char *const [])
+ { "../repository.git", "/description", NULL });
+ assert(s);
+ assert(strcmp(s, "../repository.git/description") == 0);
+ free(s);
+ test_ok();
+ }
+ {
+ testing("example usage: with file name parts");
+ char *const s = strsjoin((const char *const []){
+ "/tmp/gistatic.ABCDEF", "/", "project",
+ "-", "main", NULL
+ });
+ assert(s);
+ assert(strcmp(s, "/tmp/gistatic.ABCDEF/project-main") == 0);
+ free(s);
+ test_ok();
+ }
+}
+#endif
+
static char *formatted_date(const time_t time_sec) {
const struct tm *const time_utc = gmtime(&time_sec);
if (!time_utc) {
@@ -2058,6 +2161,7 @@ void unit_tests_gistatic(void) {
test_underscore();
test_remove_suffix();
test_strjoin();
+ test_strsjoin();
test_formatted_date();
test_max();
test_escape_html();