From: Michael Tremer Date: Thu, 18 Aug 2022 10:04:54 +0000 (+0000) Subject: util: Split string functions into an extra file X-Git-Tag: 0.9.28~452 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d973a13d7bee98cb4f22db8a7905a6381cfd3921;p=pakfire.git util: Split string functions into an extra file Signed-off-by: Michael Tremer --- diff --git a/.gitignore b/.gitignore index 0945ff6ad..2baa3d327 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ /tests/libpakfire/progressbar /tests/libpakfire/repo /tests/libpakfire/snapshot +/tests/libpakfire/string /tests/libpakfire/util /tmp *.py[co] diff --git a/Makefile.am b/Makefile.am index 79735f6a1..429035545 100644 --- a/Makefile.am +++ b/Makefile.am @@ -248,6 +248,7 @@ libpakfire_la_SOURCES = \ src/libpakfire/scriptlet.c \ src/libpakfire/snapshot.c \ src/libpakfire/solution.c \ + src/libpakfire/string.c \ src/libpakfire/transaction.c \ src/libpakfire/ui.c \ src/libpakfire/util.c @@ -288,6 +289,7 @@ pkginclude_HEADERS += \ src/libpakfire/include/pakfire/scriptlet.h \ src/libpakfire/include/pakfire/snapshot.h \ src/libpakfire/include/pakfire/solution.h \ + src/libpakfire/include/pakfire/string.h \ src/libpakfire/include/pakfire/transaction.h \ src/libpakfire/include/pakfire/ui.h \ src/libpakfire/include/pakfire/util.h @@ -387,6 +389,7 @@ check_PROGRAMS += \ tests/libpakfire/progressbar \ tests/libpakfire/repo \ tests/libpakfire/snapshot \ + tests/libpakfire/string \ tests/libpakfire/util dist_tests_libpakfire_main_SOURCES = \ @@ -606,6 +609,18 @@ tests_libpakfire_snapshot_CFLAGS = \ tests_libpakfire_snapshot_LDADD = \ $(TESTSUITE_LDADD) +dist_tests_libpakfire_string_SOURCES = \ + tests/libpakfire/string.c + +tests_libpakfire_string_CPPFLAGS = \ + $(TESTSUITE_CPPFLAGS) + +tests_libpakfire_string_CFLAGS = \ + $(TESTSUITE_CFLAGS) + +tests_libpakfire_string_LDADD = \ + $(TESTSUITE_LDADD) + dist_tests_libpakfire_util_SOURCES = \ tests/libpakfire/util.c diff --git a/src/libpakfire/arch.c b/src/libpakfire/arch.c index e3a5a20ae..55978a584 100644 --- a/src/libpakfire/arch.c +++ b/src/libpakfire/arch.c @@ -32,6 +32,7 @@ #include #include #include +#include #include struct pakfire_arch { diff --git a/src/libpakfire/archive.c b/src/libpakfire/archive.c index 9a77fc407..3a0c8531b 100644 --- a/src/libpakfire/archive.c +++ b/src/libpakfire/archive.c @@ -56,6 +56,7 @@ #include #include #include +#include #include enum pakfire_archive_verify_flags { diff --git a/src/libpakfire/build.c b/src/libpakfire/build.c index 377d1a19f..044bce819 100644 --- a/src/libpakfire/build.c +++ b/src/libpakfire/build.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #define CCACHE_DIR "/var/cache/ccache" @@ -1036,7 +1037,7 @@ static int pakfire_build_install_packages(struct pakfire_build* build, } // Split requirements into packages - packages = pakfire_split_string(requires, ','); + packages = pakfire_string_split(requires, ','); if (!packages) goto ERROR; diff --git a/src/libpakfire/cgroup.c b/src/libpakfire/cgroup.c index 2497bd21a..14062241a 100644 --- a/src/libpakfire/cgroup.c +++ b/src/libpakfire/cgroup.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #define ROOT "/sys/fs/cgroup" diff --git a/src/libpakfire/config.c b/src/libpakfire/config.c index 92091971c..c9b3b6c78 100644 --- a/src/libpakfire/config.c +++ b/src/libpakfire/config.c @@ -25,6 +25,7 @@ #include #include +#include #include #define KEY_MAX_LENGTH 32 diff --git a/src/libpakfire/db.c b/src/libpakfire/db.c index 1a92d7dcf..587d62b58 100644 --- a/src/libpakfire/db.c +++ b/src/libpakfire/db.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #define DATABASE_PATH PAKFIRE_PRIVATE_DIR "/packages.db" diff --git a/src/libpakfire/dependencies.c b/src/libpakfire/dependencies.c index 904c4c3b7..a2b243c95 100644 --- a/src/libpakfire/dependencies.c +++ b/src/libpakfire/dependencies.c @@ -29,6 +29,7 @@ #include #include #include +#include #include static const struct pakfire_rich_operation { diff --git a/src/libpakfire/dist.c b/src/libpakfire/dist.c index 03160a8d1..dd9ac18df 100644 --- a/src/libpakfire/dist.c +++ b/src/libpakfire/dist.c @@ -35,6 +35,7 @@ #include #include #include +#include #include // XXX for now diff --git a/src/libpakfire/downloader.c b/src/libpakfire/downloader.c index 6954cc2a5..70605950f 100644 --- a/src/libpakfire/downloader.c +++ b/src/libpakfire/downloader.c @@ -34,6 +34,7 @@ #include #include #include +#include #include // Retry a mirror up to N times before marking it as broken diff --git a/src/libpakfire/file.c b/src/libpakfire/file.c index 29723197e..7120ef44c 100644 --- a/src/libpakfire/file.c +++ b/src/libpakfire/file.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #define MAX_DIGESTS 4 diff --git a/src/libpakfire/filelist.c b/src/libpakfire/filelist.c index caad275d5..bef56c03b 100644 --- a/src/libpakfire/filelist.c +++ b/src/libpakfire/filelist.c @@ -31,6 +31,7 @@ #include #include #include +#include #include struct pakfire_filelist { diff --git a/src/libpakfire/include/pakfire/string.h b/src/libpakfire/include/pakfire/string.h new file mode 100644 index 000000000..6baeaee21 --- /dev/null +++ b/src/libpakfire/include/pakfire/string.h @@ -0,0 +1,51 @@ +/*############################################################################# +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2022 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +#############################################################################*/ + +#ifndef PAKFIRE_STRING_H +#define PAKFIRE_STRING_H + +#ifdef PAKFIRE_PRIVATE + +#define pakfire_string_format(s, fmt, ...) snprintf(s, sizeof(s) - 1, fmt, __VA_ARGS__) +#define pakfire_string_set(s, value) pakfire_string_format(s, "%s", value) + +int pakfire_string_startswith(const char* s, const char* prefix); +int pakfire_string_endswith(const char* s, const char* suffix); +int pakfire_string_matches(const char* s, const char* pattern); + +int pakfire_string_partition(const char* s, const char* delim, char** s1, char** s2); +char* pakfire_string_replace(const char* s, const char* pattern, const char* repl); +char** pakfire_string_split(const char* s, char delim); +char* pakfire_string_join(char** list, const char* delim); + +#define pakfire_format_size(dst, value) \ + __pakfire_format_size(dst, sizeof(dst) - 1, value) +int __pakfire_format_size(char* dst, size_t length, double value); +int pakfire_format_speed(char* dst, size_t length, double value); +char* pakfire_format_date(time_t t); + +#define pakfire_strftime_now(dest, format) \ + __pakfire_strftime_now(dest, sizeof(dest) - 1, format) +int __pakfire_strftime_now(char* dest, size_t length, const char* format) + __attribute__((format(strftime, 3, 0)));; + +#endif + +#endif /* PAKFIRE_STRING_H */ diff --git a/src/libpakfire/include/pakfire/util.h b/src/libpakfire/include/pakfire/util.h index d8cbc2cbc..8150f085d 100644 --- a/src/libpakfire/include/pakfire/util.h +++ b/src/libpakfire/include/pakfire/util.h @@ -30,28 +30,7 @@ #include -#define pakfire_string_format(s, fmt, ...) snprintf(s, sizeof(s) - 1, fmt, __VA_ARGS__) -#define pakfire_string_set(s, value) pakfire_string_format(s, "%s", value) - -int pakfire_string_startswith(const char* s, const char* prefix); -int pakfire_string_endswith(const char* s, const char* suffix); -int pakfire_string_matches(const char* s, const char* pattern); char* pakfire_unquote_in_place(char* s); -int pakfire_string_partition(const char* s, const char* delim, char** s1, char** s2); -char* pakfire_string_replace(const char* s, const char* pattern, const char* repl); -char** pakfire_split_string(const char* s, char delim); -char* pakfire_string_join(char** list, const char* delim); - -#define pakfire_format_size(dst, value) \ - __pakfire_format_size(dst, sizeof(dst) - 1, value) -int __pakfire_format_size(char* dst, size_t length, double value); -int pakfire_format_speed(char* dst, size_t length, double value); -char* pakfire_format_date(time_t t); - -#define pakfire_strftime_now(dest, format) \ - __pakfire_strftime_now(dest, sizeof(dest) - 1, format) -int __pakfire_strftime_now(char* dest, size_t length, const char* format) - __attribute__((format(strftime, 3, 0)));; int pakfire_path_exists(const char* path); time_t pakfire_path_age(const char* path); diff --git a/src/libpakfire/jail.c b/src/libpakfire/jail.c index 4321512b0..f2aac5fd6 100644 --- a/src/libpakfire/jail.c +++ b/src/libpakfire/jail.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #define BUFFER_SIZE 1024 * 64 diff --git a/src/libpakfire/key.c b/src/libpakfire/key.c index cb81057b6..66f52ac03 100644 --- a/src/libpakfire/key.c +++ b/src/libpakfire/key.c @@ -32,6 +32,7 @@ #include #include #include +#include #include struct pakfire_key { diff --git a/src/libpakfire/keystore.c b/src/libpakfire/keystore.c index cab2f53fc..38247b5b8 100644 --- a/src/libpakfire/keystore.c +++ b/src/libpakfire/keystore.c @@ -27,6 +27,7 @@ #include #include #include +#include #include static int pakfire_init_gpgme(struct pakfire* pakfire) { diff --git a/src/libpakfire/mount.c b/src/libpakfire/mount.c index c7e019168..025c41d16 100644 --- a/src/libpakfire/mount.c +++ b/src/libpakfire/mount.c @@ -33,6 +33,7 @@ #include #include #include +#include #include static const struct pakfire_mountpoint { diff --git a/src/libpakfire/package.c b/src/libpakfire/package.c index 24a9be74f..2671c5ebc 100644 --- a/src/libpakfire/package.c +++ b/src/libpakfire/package.c @@ -43,6 +43,7 @@ #include #include #include +#include #include struct pakfire_package { @@ -550,7 +551,7 @@ PAKFIRE_EXPORT char* pakfire_package_get_groups(struct pakfire_package* pkg) { } PAKFIRE_EXPORT void pakfire_package_set_groups(struct pakfire_package* pkg, const char* groups) { - char** list = pakfire_split_string(groups, ' '); + char** list = pakfire_string_split(groups, ' '); if (!list) return; @@ -1011,7 +1012,7 @@ static void pakfire_package_dump_add_line(char** str, const char* key, const cha } static void pakfire_package_dump_add_lines(char** str, const char* key, const char* val) { - char** lines = pakfire_split_string(val, '\n'); + char** lines = pakfire_string_split(val, '\n'); if (!lines) return; diff --git a/src/libpakfire/packager.c b/src/libpakfire/packager.c index fc98f4b0c..ad10c1afe 100644 --- a/src/libpakfire/packager.c +++ b/src/libpakfire/packager.c @@ -39,6 +39,7 @@ #include #include #include +#include #include struct pakfire_packager { diff --git a/src/libpakfire/pakfire.c b/src/libpakfire/pakfire.c index d0ecb100b..a13a861e6 100644 --- a/src/libpakfire/pakfire.c +++ b/src/libpakfire/pakfire.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include diff --git a/src/libpakfire/parser.c b/src/libpakfire/parser.c index fbea82c1c..ee1509cd1 100644 --- a/src/libpakfire/parser.c +++ b/src/libpakfire/parser.c @@ -36,6 +36,7 @@ #include #include #include +#include #include struct pakfire_parser { @@ -663,7 +664,7 @@ char** pakfire_parser_get_split(struct pakfire_parser* parser, return NULL; // Split the string - char** list = pakfire_split_string(value, delim); + char** list = pakfire_string_split(value, delim); free(value); return list; diff --git a/src/libpakfire/parser/grammar.y b/src/libpakfire/parser/grammar.y index ff156bf29..d22317e26 100644 --- a/src/libpakfire/parser/grammar.y +++ b/src/libpakfire/parser/grammar.y @@ -44,6 +44,7 @@ #include #include #include +#include #include #define YYERROR_VERBOSE 1 diff --git a/src/libpakfire/parser/scanner.l b/src/libpakfire/parser/scanner.l index cf1fbfedc..d92570673 100644 --- a/src/libpakfire/parser/scanner.l +++ b/src/libpakfire/parser/scanner.l @@ -38,6 +38,7 @@ int num_lines; #include #include +#include #include #include "grammar.h" diff --git a/src/libpakfire/progressbar.c b/src/libpakfire/progressbar.c index a7d2c1e53..01edfcb34 100644 --- a/src/libpakfire/progressbar.c +++ b/src/libpakfire/progressbar.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #define DRAWS_PER_SECOND 20 diff --git a/src/libpakfire/pwd.c b/src/libpakfire/pwd.c index 33d4b5f47..b3ce65635 100644 --- a/src/libpakfire/pwd.c +++ b/src/libpakfire/pwd.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #define ETC_SUBUID "/etc/subuid" diff --git a/src/libpakfire/repo.c b/src/libpakfire/repo.c index f12ead727..138aa5427 100644 --- a/src/libpakfire/repo.c +++ b/src/libpakfire/repo.c @@ -45,6 +45,7 @@ #include #include #include +#include #include // Refresh mirrorlists once a day diff --git a/src/libpakfire/request.c b/src/libpakfire/request.c index 79b618603..4642db657 100644 --- a/src/libpakfire/request.c +++ b/src/libpakfire/request.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include diff --git a/src/libpakfire/scriptlet.c b/src/libpakfire/scriptlet.c index 8b41646f6..e9c2aae97 100644 --- a/src/libpakfire/scriptlet.c +++ b/src/libpakfire/scriptlet.c @@ -25,6 +25,7 @@ #include #include #include +#include #include const char* pakfire_scriptlet_types[] = { diff --git a/src/libpakfire/solution.c b/src/libpakfire/solution.c index 3eb825bd2..cad749a34 100644 --- a/src/libpakfire/solution.c +++ b/src/libpakfire/solution.c @@ -29,6 +29,7 @@ #include #include #include +#include #include struct pakfire_solution { diff --git a/src/libpakfire/string.c b/src/libpakfire/string.c new file mode 100644 index 000000000..60b78ab68 --- /dev/null +++ b/src/libpakfire/string.c @@ -0,0 +1,362 @@ +/*############################################################################# +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2022 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +#############################################################################*/ + +#include +#include +#include +#include +#include + +#include + +int pakfire_string_startswith(const char* s, const char* prefix) { + // Validate input + if (!s || !prefix) { + errno = EINVAL; + return 1; + } + + return !strncmp(s, prefix, strlen(prefix)); +} + +int pakfire_string_endswith(const char* s, const char* suffix) { + // Validate input + if (!s || !suffix) { + errno = EINVAL; + return 1; + } + + return !strcmp(s + strlen(s) - strlen(suffix), suffix); +} + +int pakfire_string_matches(const char* s, const char* pattern) { + // Validate input + if (!s || !pattern) { + errno = EINVAL; + return 1; + } + + return !!strstr(s, pattern); +} + +int pakfire_string_partition(const char* s, const char* delim, char** s1, char** s2) { + char* p = strstr(s, delim); + + // Delim was not found + if (!p) { + *s1 = NULL; + *s2 = NULL; + return 1; + } + + // Length of string before delim + size_t l = p - s; + + char* buffer = malloc(l + 1); + if (!buffer) + return 1; + + // Copy first part + *s1 = memcpy(buffer, s, l); + buffer[l] = '\0'; + + // Copy second part + *s2 = strdup(p + strlen(delim)); + + return 0; +} + +char* pakfire_string_replace(const char* s, const char* pattern, const char* repl) { + // Return NULL on no input or no pattern + if (!s || !pattern) { + errno = EINVAL; + return NULL; + } + + // Replace with nothing when repl is NULL + if (!repl) + repl = ""; + + char* result = NULL; + const char** cache = NULL; + unsigned int count = 0; + + const size_t pattern_length = strlen(pattern); + + // Working pointer + const char* p = s; + + // Find all occurrences of pattern and store their location + while (1) { + const char* needle = strstr(p, pattern); + if (!needle) + break; + + // Make space in the cache + cache = reallocarray(cache, sizeof(*cache), count + 1); + cache[count++] = needle; + + // Move p forward + p = needle + pattern_length; + } + + // Copy the string if no occurence was found + if (count == 0) { + result = strdup(s); + goto ERROR; + } + + // Store the end pointer + cache = reallocarray(cache, sizeof(*cache), count + 1); + cache[count] = s + strlen(s); + + const size_t repl_length = strlen(repl); + + // Determine the length of the final string + const size_t length = strlen(s) + ((repl_length - pattern_length) * count); + + // Allocate enough memory for the result + result = malloc(length + 1); + if (!result) + goto ERROR; + + // Reset p + p = s; + + // Working pointer for the result + char* r = result; + + // Copy everything up to the first match + ssize_t l = cache[0] - s; + memcpy(r, p, l); + r += l; + p += l; + + for (unsigned int i = 0; i < count; i++) { + // Put replacement here + memcpy(r, repl, repl_length); + r += repl_length; + p += pattern_length; + + // Determine the length between two matches + l = cache[i+1] - (cache[i] + pattern_length); + + memcpy(r, p, l); + r += l; + p += l; + } + + // Terminate the string + result[length] = '\0'; + +ERROR: + if (cache) + free(cache); + + return result; +} + +static unsigned int pakfire_chrcnt(const char* s, char delim) { + size_t length = strlen(s); + + unsigned int count = 0; + + for (unsigned int i = 0; i < length; i++) + if (s[i] == delim) + count++; + + return count; +} + +char** pakfire_string_split(const char* s, char delim) { + char** array = NULL; + + if (!s) { + errno = EINVAL; + return NULL; + } + + // Count how often we need to split + unsigned int count = pakfire_chrcnt(s, delim) + 1; + + // Allocate array + array = calloc(count + 1, sizeof(*array)); + if (!array) + return NULL; + + // Copy string to stack + char* p = strdupa(s); + if (!p) + return NULL; + + unsigned int i = 0; + while (*p) { + char* e = strchr(p, delim); + + // Terminate the string + if (e) + *e = '\0'; + + // Add string to the array + array[i++] = strdup(p); + + // End loop when we reached the end + if (!e) + break; + + // Or continue at the next line + p = e + 1; + } + + return array; +} + +char* pakfire_string_join(char** list, const char* delim) { + // Validate input + if (!list || !delim) { + errno = EINVAL; + return NULL; + } + + size_t length = 0; + unsigned int elements = 0; + + // Count the number of elements and the total length + for (char** item = list; *item; item++) { + length += strlen(*item); + elements++; + } + + // Empty list? + if (!elements) + return NULL; + + // Add the delimiters + length += strlen(delim) * (elements - 1); + + // Allocate the result string + char* string = malloc(length + 1); + if (!string) + return NULL; + + // Pointer to where we are writing + char* p = string; + + size_t bytes_left = length + 1; + size_t bytes_written; + + for (char** item = list; *item; item++) { + bytes_written = snprintf(p, bytes_left, "%s", *item); + + bytes_left -= bytes_written; + p += bytes_written; + + // Write the delimiter + if (bytes_left) { + bytes_written = snprintf(p, bytes_left, "%s", delim); + + bytes_left -= bytes_written; + p += bytes_written; + } + } + + return string; +} + +int __pakfire_format_size(char* dst, size_t length, double value) { + const char* units[] = { + "%.0f ", + "%.0fk", + "%.1fM", + "%.1fG", + "%.1fT", + NULL + }; + const char** unit = units; + + while (*(unit + 1) && value >= 1024.0) { + value /= 1024.0; + unit++; + } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + return snprintf(dst, length, *unit, value); +#pragma GCC diagnostic pop +} + +int pakfire_format_speed(char* dst, size_t length, double value) { + const char* units[] = { + "%4.0fB/s", + "%4.0fkB/s", + "%4.1fMB/s", + "%4.1fGB/s", + "%4.1fTB/s", + NULL + }; + const char** unit = units; + + while (*(unit + 1) && value >= 1024.0) { + value /= 1024.0; + unit++; + } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + return snprintf(dst, length, *unit, value); +#pragma GCC diagnostic pop +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +static char* pakfire_strftime(const char* format, time_t t) { + char string[128]; + struct tm* tm = gmtime(&t); + + strftime(string, sizeof(string) - 1, format, tm); + + return strdup(string); +} +#pragma GCC diagnostic pop + +char* pakfire_format_date(time_t t) { + return pakfire_strftime("%Y-%m-%d", t); +} + +int __pakfire_strftime_now(char* dest, size_t length, const char* format) { + struct tm tm; + + // Fetch the current time + const time_t t = time(NULL); + if (t < 0) + return 1; + + // Convert to struct tm + struct tm* now = gmtime_r(&t, &tm); + if (!now) + return 1; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + strftime(dest, length, format, now); +#pragma GCC diagnostic pop + + return 0; +} diff --git a/src/libpakfire/transaction.c b/src/libpakfire/transaction.c index 373efe451..fb47e5a47 100644 --- a/src/libpakfire/transaction.c +++ b/src/libpakfire/transaction.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include diff --git a/src/libpakfire/util.c b/src/libpakfire/util.c index 0ff1107bd..436b31623 100644 --- a/src/libpakfire/util.c +++ b/src/libpakfire/util.c @@ -41,41 +41,12 @@ #include #include #include +#include #include #define BUFFER_SIZE 64 * 1024 #define NSEC_PER_SEC 1000000000 -int pakfire_string_startswith(const char* s, const char* prefix) { - // Validate input - if (!s || !prefix) { - errno = EINVAL; - return 1; - } - - return !strncmp(s, prefix, strlen(prefix)); -} - -int pakfire_string_endswith(const char* s, const char* suffix) { - // Validate input - if (!s || !suffix) { - errno = EINVAL; - return 1; - } - - return !strcmp(s + strlen(s) - strlen(suffix), suffix); -} - -int pakfire_string_matches(const char* s, const char* pattern) { - // Validate input - if (!s || !pattern) { - errno = EINVAL; - return 1; - } - - return !!strstr(s, pattern); -} - char* pakfire_unquote_in_place(char* s) { if (!s || !*s) return s; @@ -100,311 +71,6 @@ char* pakfire_unquote_in_place(char* s) { return s; } -int pakfire_string_partition(const char* s, const char* delim, char** s1, char** s2) { - char* p = strstr(s, delim); - - // Delim was not found - if (!p) { - *s1 = NULL; - *s2 = NULL; - return 1; - } - - // Length of string before delim - size_t l = p - s; - - char* buffer = malloc(l + 1); - if (!buffer) - return -ENOMEM; - - // Copy first part - *s1 = memcpy(buffer, s, l); - buffer[l] = '\0'; - - // Copy second part - *s2 = strdup(p + strlen(delim)); - - return 0; -} - -char* pakfire_string_replace(const char* s, const char* pattern, const char* repl) { - // Return NULL on no input or no pattern - if (!s || !pattern) { - errno = EINVAL; - return NULL; - } - - // Replace with nothing when repl is NULL - if (!repl) - repl = ""; - - char* result = NULL; - const char** cache = NULL; - unsigned int count = 0; - - const size_t pattern_length = strlen(pattern); - - // Working pointer - const char* p = s; - - // Find all occurrences of pattern and store their location - while (1) { - const char* needle = strstr(p, pattern); - if (!needle) - break; - - // Make space in the cache - cache = reallocarray(cache, sizeof(*cache), count + 1); - cache[count++] = needle; - - // Move p forward - p = needle + pattern_length; - } - - // Copy the string if no occurence was found - if (count == 0) { - result = strdup(s); - goto ERROR; - } - - // Store the end pointer - cache = reallocarray(cache, sizeof(*cache), count + 1); - cache[count] = s + strlen(s); - - const size_t repl_length = strlen(repl); - - // Determine the length of the final string - const size_t length = strlen(s) + ((repl_length - pattern_length) * count); - - // Allocate enough memory for the result - result = malloc(length + 1); - if (!result) - goto ERROR; - - // Reset p - p = s; - - // Working pointer for the result - char* r = result; - - // Copy everything up to the first match - ssize_t l = cache[0] - s; - memcpy(r, p, l); - r += l; - p += l; - - for (unsigned int i = 0; i < count; i++) { - // Put replacement here - memcpy(r, repl, repl_length); - r += repl_length; - p += pattern_length; - - // Determine the length between two matches - l = cache[i+1] - (cache[i] + pattern_length); - - memcpy(r, p, l); - r += l; - p += l; - } - - // Terminate the string - result[length] = '\0'; - -ERROR: - if (cache) - free(cache); - - return result; -} - -static unsigned int pakfire_chrcnt(const char* s, char delim) { - size_t length = strlen(s); - - unsigned int count = 0; - - for (unsigned int i = 0; i < length; i++) - if (s[i] == delim) - count++; - - return count; -} - -char** pakfire_split_string(const char* s, char delim) { - char** array = NULL; - - if (!s) { - errno = EINVAL; - return NULL; - } - - // Count how often we need to split - unsigned int count = pakfire_chrcnt(s, delim) + 1; - - // Allocate array - array = calloc(count + 1, sizeof(*array)); - if (!array) - return NULL; - - // Copy string to stack - char* p = strdupa(s); - if (!p) - return NULL; - - unsigned int i = 0; - while (*p) { - char* e = strchr(p, delim); - - // Terminate the string - if (e) - *e = '\0'; - - // Add string to the array - array[i++] = strdup(p); - - // End loop when we reached the end - if (!e) - break; - - // Or continue at the next line - p = e + 1; - } - - return array; -} - -char* pakfire_string_join(char** list, const char* delim) { - // Validate input - if (!list || !delim) { - errno = EINVAL; - return NULL; - } - - size_t length = 0; - unsigned int elements = 0; - - // Count the number of elements and the total length - for (char** item = list; *item; item++) { - length += strlen(*item); - elements++; - } - - // Empty list? - if (!elements) - return NULL; - - // Add the delimiters - length += strlen(delim) * (elements - 1); - - // Allocate the result string - char* string = malloc(length + 1); - if (!string) - return NULL; - - // Pointer to where we are writing - char* p = string; - - size_t bytes_left = length + 1; - size_t bytes_written; - - for (char** item = list; *item; item++) { - bytes_written = snprintf(p, bytes_left, "%s", *item); - - bytes_left -= bytes_written; - p += bytes_written; - - // Write the delimiter - if (bytes_left) { - bytes_written = snprintf(p, bytes_left, "%s", delim); - - bytes_left -= bytes_written; - p += bytes_written; - } - } - - return string; -} - -int __pakfire_format_size(char* dst, size_t length, double value) { - const char* units[] = { - "%.0f ", - "%.0fk", - "%.1fM", - "%.1fG", - "%.1fT", - NULL - }; - const char** unit = units; - - while (*(unit + 1) && value >= 1024.0) { - value /= 1024.0; - unit++; - } - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" - return snprintf(dst, length, *unit, value); -#pragma GCC diagnostic pop -} - -int pakfire_format_speed(char* dst, size_t length, double value) { - const char* units[] = { - "%4.0fB/s", - "%4.0fkB/s", - "%4.1fMB/s", - "%4.1fGB/s", - "%4.1fTB/s", - NULL - }; - const char** unit = units; - - while (*(unit + 1) && value >= 1024.0) { - value /= 1024.0; - unit++; - } - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" - return snprintf(dst, length, *unit, value); -#pragma GCC diagnostic pop -} - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" -static char* pakfire_strftime(const char* format, time_t t) { - char string[128]; - struct tm* tm = gmtime(&t); - - strftime(string, sizeof(string) - 1, format, tm); - - return strdup(string); -} -#pragma GCC diagnostic pop - -char* pakfire_format_date(time_t t) { - return pakfire_strftime("%Y-%m-%d", t); -} - -int __pakfire_strftime_now(char* dest, size_t length, const char* format) { - struct tm tm; - - // Fetch the current time - const time_t t = time(NULL); - if (t < 0) - return 1; - - // Convert to struct tm - struct tm* now = gmtime_r(&t, &tm); - if (!now) - return 1; - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" - strftime(dest, length, format, now); -#pragma GCC diagnostic pop - - return 0; -} - int __pakfire_path_join(char* dest, size_t length, const char* first, const char* second) { if (!first) diff --git a/tests/libpakfire/compress.c b/tests/libpakfire/compress.c index a55c6aaaa..880f085d3 100644 --- a/tests/libpakfire/compress.c +++ b/tests/libpakfire/compress.c @@ -23,6 +23,7 @@ #include #include +#include #include #include "../testsuite.h" diff --git a/tests/libpakfire/repo.c b/tests/libpakfire/repo.c index 0582d71c6..b536e1da0 100644 --- a/tests/libpakfire/repo.c +++ b/tests/libpakfire/repo.c @@ -19,6 +19,7 @@ #############################################################################*/ #include +#include #include #include "../testsuite.h" diff --git a/tests/libpakfire/string.c b/tests/libpakfire/string.c new file mode 100644 index 000000000..238e6669a --- /dev/null +++ b/tests/libpakfire/string.c @@ -0,0 +1,287 @@ +/*############################################################################# +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2022 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +#############################################################################*/ + +#include +#include +#include + +#include + +#include "../testsuite.h" + +static int test_string_startswith(const struct test* t) { + ASSERT_TRUE(pakfire_string_startswith("ABC", "A")); + ASSERT_FALSE(pakfire_string_startswith("ABC", "B")); + + // Check for invalid inputs + ASSERT_ERRNO(pakfire_string_startswith("ABC", NULL), EINVAL); + ASSERT_ERRNO(pakfire_string_startswith(NULL, "ABC"), EINVAL); + ASSERT_ERRNO(pakfire_string_startswith(NULL, NULL), EINVAL); + + return EXIT_SUCCESS; + +FAIL: + return EXIT_FAILURE; +} + +static int test_string_endswith(const struct test* t) { + ASSERT_TRUE(pakfire_string_endswith("ABC", "C")); + ASSERT_FALSE(pakfire_string_endswith("ABC", "B")); + + // Check for invalid inputs + ASSERT_ERRNO(pakfire_string_endswith("ABC", NULL), EINVAL); + ASSERT_ERRNO(pakfire_string_endswith(NULL, "ABC"), EINVAL); + ASSERT_ERRNO(pakfire_string_endswith(NULL, NULL), EINVAL); + + return EXIT_SUCCESS; + +FAIL: + return EXIT_FAILURE; +} + +static int test_string_matches(const struct test* t) { + ASSERT_TRUE(pakfire_string_matches("ABC", "A")); + ASSERT_TRUE(pakfire_string_matches("ABC", "B")); + ASSERT_TRUE(pakfire_string_matches("ABC", "C")); + ASSERT_TRUE(pakfire_string_matches("ABC", "AB")); + ASSERT_TRUE(pakfire_string_matches("ABC", "BC")); + ASSERT_TRUE(pakfire_string_matches("ABC", "ABC")); + ASSERT_FALSE(pakfire_string_matches("ABC", "D")); + ASSERT_FALSE(pakfire_string_matches("ABC", "ABCD")); + + // Check for invalid inputs + ASSERT_ERRNO(pakfire_string_matches("ABC", NULL), EINVAL); + ASSERT_ERRNO(pakfire_string_matches(NULL, "ABC"), EINVAL); + ASSERT_ERRNO(pakfire_string_matches(NULL, NULL), EINVAL); + + return EXIT_SUCCESS; + +FAIL: + return EXIT_FAILURE; +} + +static int test_string_partition(const struct test* t) { + char* part1; + char* part2; + + // Regular case + int r = pakfire_string_partition("ABC:DEF", ":", &part1, &part2); + ASSERT(r == 0); + ASSERT_STRING_EQUALS(part1, "ABC"); + ASSERT_STRING_EQUALS(part2, "DEF"); + + free(part1); + free(part2); + + // No delimiter + r = pakfire_string_partition("ABCDEF", ":", &part1, &part2); + ASSERT(r == 1); + ASSERT(part1 == NULL); + ASSERT(part2 == NULL); + + // Nothing after the delimiter + r = pakfire_string_partition("ABC:", ":", &part1, &part2); + ASSERT(r == 0); + ASSERT_STRING_EQUALS(part1, "ABC"); + ASSERT_STRING_EQUALS(part2, ""); + + free(part1); + free(part2); + + // Nothing before the delimiter + r = pakfire_string_partition(":ABC", ":", &part1, &part2); + ASSERT(r == 0); + ASSERT_STRING_EQUALS(part1, ""); + ASSERT_STRING_EQUALS(part2, "ABC"); + + free(part1); + free(part2); + + // Multi-character delimiter + r = pakfire_string_partition("ABC:-:DEF", ":-:", &part1, &part2); + ASSERT(r == 0); + ASSERT_STRING_EQUALS(part1, "ABC"); + ASSERT_STRING_EQUALS(part2, "DEF"); + + free(part1); + free(part2); + + return EXIT_SUCCESS; + +FAIL: + return EXIT_FAILURE; +} + +static int test_string_replace(const struct test* t) { + const char* result = pakfire_string_replace( + "ABCABCABCABC", "AB", "CC" + ); + ASSERT_STRING_EQUALS(result, "CCCCCCCCCCCC"); + + return EXIT_SUCCESS; + +FAIL: + return EXIT_FAILURE; +} + +static int test_string_split(const struct test* t) { + char** result = pakfire_string_split(NULL, 'X'); + + // Must return on invalid input + ASSERT_ERRNO(!result, EINVAL); + + // Split a string + result = pakfire_string_split("ABCXABCXABC", 'X'); + ASSERT(result); + + ASSERT_STRING_EQUALS(result[0], "ABC"); + ASSERT_STRING_EQUALS(result[1], "ABC"); + ASSERT_STRING_EQUALS(result[2], "ABC"); + ASSERT_NULL(result[3]); + + // Split a string withtout the delimiter + result = pakfire_string_split("ABCABC", 'X'); + ASSERT(result); + + ASSERT_STRING_EQUALS(result[0], "ABCABC"); + ASSERT_NULL(result[1]); + + // String with only delimiters + result = pakfire_string_split("XXXX", 'X'); + ASSERT(result); + + ASSERT_STRING_EQUALS(result[0], ""); + ASSERT_STRING_EQUALS(result[1], ""); + ASSERT_STRING_EQUALS(result[2], ""); + ASSERT_STRING_EQUALS(result[3], ""); + ASSERT_NULL(result[4]); + + return EXIT_SUCCESS; + +FAIL: + return EXIT_FAILURE; +} + +static int test_string_join(const struct test* t) { + char* s = NULL; + + // Some test elements + char* elements1[] = { + "A", + "B", + "C", + NULL, + }; + + // Join with newline + s = pakfire_string_join(elements1, "\n"); + ASSERT_STRING_EQUALS(s, "A\nB\nC"); + + if (s) { + free(s); + s = NULL; + } + + // Join with empty delimiter + s = pakfire_string_join(elements1, ""); + ASSERT_STRING_EQUALS(s, "ABC"); + + if (s) { + free(s); + s = NULL; + } + + char* elements2[] = { + "", + "", + "", + NULL, + }; + + // Join list with empty elements + s = pakfire_string_join(elements2, "X"); + ASSERT_STRING_EQUALS(s, "XX"); + + if (s) { + free(s); + s = NULL; + } + + // Invalid inputs + s = pakfire_string_join(NULL, NULL); + ASSERT_ERRNO(!s, EINVAL); + + s = pakfire_string_join(elements1, NULL); + ASSERT_ERRNO(!s, EINVAL); + + s = pakfire_string_join(NULL, "\n"); + ASSERT_ERRNO(!s, EINVAL); + + char* elements3[] = { + NULL, + }; + + // Join empty elements + ASSERT_NULL(pakfire_string_join(elements3, "\n")); + + return EXIT_SUCCESS; + +FAIL: + if (s) + free(s); + + return EXIT_FAILURE; +} + +static int test_format_size(const struct test* t) { + char buffer[128]; + char small_buffer[2]; + int r; + + ASSERT(pakfire_format_size(buffer, 0) == 2); + ASSERT_STRING_EQUALS(buffer, "0 "); + + ASSERT(pakfire_format_size(buffer, 1024) == 2); + ASSERT_STRING_EQUALS(buffer, "1k"); + + ASSERT(pakfire_format_size(buffer, 1024 * 1024) == 4); + ASSERT_STRING_EQUALS(buffer, "1.0M"); + + ASSERT(pakfire_format_size(small_buffer, 0) == 2); + ASSERT_STRING_EQUALS(small_buffer, ""); + + return EXIT_SUCCESS; + +FAIL: + return EXIT_FAILURE; +} + +int main(int argc, const char* argv[]) { + testsuite_add_test(test_string_startswith); + testsuite_add_test(test_string_endswith); + testsuite_add_test(test_string_matches); + testsuite_add_test(test_string_partition); + testsuite_add_test(test_string_replace); + testsuite_add_test(test_string_split); + testsuite_add_test(test_string_join); + testsuite_add_test(test_format_size); + + return testsuite_run(argc, argv); +} diff --git a/tests/libpakfire/util.c b/tests/libpakfire/util.c index 8f8ec1f2a..d6543ecb8 100644 --- a/tests/libpakfire/util.c +++ b/tests/libpakfire/util.c @@ -18,11 +18,8 @@ # # #############################################################################*/ -#include #include -#include -#include #include #include "../testsuite.h" @@ -53,264 +50,9 @@ FAIL: return EXIT_FAILURE; } -static int test_string_startswith(const struct test* t) { - ASSERT_TRUE(pakfire_string_startswith("ABC", "A")); - ASSERT_FALSE(pakfire_string_startswith("ABC", "B")); - - // Check for invalid inputs - ASSERT_ERRNO(pakfire_string_startswith("ABC", NULL), EINVAL); - ASSERT_ERRNO(pakfire_string_startswith(NULL, "ABC"), EINVAL); - ASSERT_ERRNO(pakfire_string_startswith(NULL, NULL), EINVAL); - - return EXIT_SUCCESS; - -FAIL: - return EXIT_FAILURE; -} - -static int test_string_endswith(const struct test* t) { - ASSERT_TRUE(pakfire_string_endswith("ABC", "C")); - ASSERT_FALSE(pakfire_string_endswith("ABC", "B")); - - // Check for invalid inputs - ASSERT_ERRNO(pakfire_string_endswith("ABC", NULL), EINVAL); - ASSERT_ERRNO(pakfire_string_endswith(NULL, "ABC"), EINVAL); - ASSERT_ERRNO(pakfire_string_endswith(NULL, NULL), EINVAL); - - return EXIT_SUCCESS; - -FAIL: - return EXIT_FAILURE; -} - -static int test_string_matches(const struct test* t) { - ASSERT_TRUE(pakfire_string_matches("ABC", "A")); - ASSERT_TRUE(pakfire_string_matches("ABC", "B")); - ASSERT_TRUE(pakfire_string_matches("ABC", "C")); - ASSERT_TRUE(pakfire_string_matches("ABC", "AB")); - ASSERT_TRUE(pakfire_string_matches("ABC", "BC")); - ASSERT_TRUE(pakfire_string_matches("ABC", "ABC")); - ASSERT_FALSE(pakfire_string_matches("ABC", "D")); - ASSERT_FALSE(pakfire_string_matches("ABC", "ABCD")); - - // Check for invalid inputs - ASSERT_ERRNO(pakfire_string_matches("ABC", NULL), EINVAL); - ASSERT_ERRNO(pakfire_string_matches(NULL, "ABC"), EINVAL); - ASSERT_ERRNO(pakfire_string_matches(NULL, NULL), EINVAL); - - return EXIT_SUCCESS; - -FAIL: - return EXIT_FAILURE; -} - -static int test_string_partition(const struct test* t) { - char* part1; - char* part2; - - // Regular case - int r = pakfire_string_partition("ABC:DEF", ":", &part1, &part2); - ASSERT(r == 0); - ASSERT_STRING_EQUALS(part1, "ABC"); - ASSERT_STRING_EQUALS(part2, "DEF"); - - free(part1); - free(part2); - - // No delimiter - r = pakfire_string_partition("ABCDEF", ":", &part1, &part2); - ASSERT(r == 1); - ASSERT(part1 == NULL); - ASSERT(part2 == NULL); - - // Nothing after the delimiter - r = pakfire_string_partition("ABC:", ":", &part1, &part2); - ASSERT(r == 0); - ASSERT_STRING_EQUALS(part1, "ABC"); - ASSERT_STRING_EQUALS(part2, ""); - - free(part1); - free(part2); - - // Nothing before the delimiter - r = pakfire_string_partition(":ABC", ":", &part1, &part2); - ASSERT(r == 0); - ASSERT_STRING_EQUALS(part1, ""); - ASSERT_STRING_EQUALS(part2, "ABC"); - - free(part1); - free(part2); - - // Multi-character delimiter - r = pakfire_string_partition("ABC:-:DEF", ":-:", &part1, &part2); - ASSERT(r == 0); - ASSERT_STRING_EQUALS(part1, "ABC"); - ASSERT_STRING_EQUALS(part2, "DEF"); - - free(part1); - free(part2); - - return EXIT_SUCCESS; - -FAIL: - return EXIT_FAILURE; -} - -static int test_string_replace(const struct test* t) { - const char* result = pakfire_string_replace( - "ABCABCABCABC", "AB", "CC" - ); - ASSERT_STRING_EQUALS(result, "CCCCCCCCCCCC"); - - return EXIT_SUCCESS; - -FAIL: - return EXIT_FAILURE; -} - -static int test_string_split(const struct test* t) { - char** result = pakfire_split_string(NULL, 'X'); - - // Must return on invalid input - ASSERT_ERRNO(!result, EINVAL); - - // Split a string - result = pakfire_split_string("ABCXABCXABC", 'X'); - ASSERT(result); - - ASSERT_STRING_EQUALS(result[0], "ABC"); - ASSERT_STRING_EQUALS(result[1], "ABC"); - ASSERT_STRING_EQUALS(result[2], "ABC"); - ASSERT_NULL(result[3]); - - // Split a string withtout the delimiter - result = pakfire_split_string("ABCABC", 'X'); - ASSERT(result); - - ASSERT_STRING_EQUALS(result[0], "ABCABC"); - ASSERT_NULL(result[1]); - - // String with only delimiters - result = pakfire_split_string("XXXX", 'X'); - ASSERT(result); - - ASSERT_STRING_EQUALS(result[0], ""); - ASSERT_STRING_EQUALS(result[1], ""); - ASSERT_STRING_EQUALS(result[2], ""); - ASSERT_STRING_EQUALS(result[3], ""); - ASSERT_NULL(result[4]); - - return EXIT_SUCCESS; - -FAIL: - return EXIT_FAILURE; -} - -static int test_string_join(const struct test* t) { - char* s = NULL; - - // Some test elements - char* elements1[] = { - "A", - "B", - "C", - NULL, - }; - - // Join with newline - s = pakfire_string_join(elements1, "\n"); - ASSERT_STRING_EQUALS(s, "A\nB\nC"); - - if (s) { - free(s); - s = NULL; - } - - // Join with empty delimiter - s = pakfire_string_join(elements1, ""); - ASSERT_STRING_EQUALS(s, "ABC"); - - if (s) { - free(s); - s = NULL; - } - - char* elements2[] = { - "", - "", - "", - NULL, - }; - - // Join list with empty elements - s = pakfire_string_join(elements2, "X"); - ASSERT_STRING_EQUALS(s, "XX"); - - if (s) { - free(s); - s = NULL; - } - - // Invalid inputs - s = pakfire_string_join(NULL, NULL); - ASSERT_ERRNO(!s, EINVAL); - - s = pakfire_string_join(elements1, NULL); - ASSERT_ERRNO(!s, EINVAL); - - s = pakfire_string_join(NULL, "\n"); - ASSERT_ERRNO(!s, EINVAL); - - char* elements3[] = { - NULL, - }; - - // Join empty elements - ASSERT_NULL(pakfire_string_join(elements3, "\n")); - - return EXIT_SUCCESS; - -FAIL: - if (s) - free(s); - - return EXIT_FAILURE; -} - -static int test_format_size(const struct test* t) { - char buffer[128]; - char small_buffer[2]; - int r; - - ASSERT(pakfire_format_size(buffer, 0) == 2); - ASSERT_STRING_EQUALS(buffer, "0 "); - - ASSERT(pakfire_format_size(buffer, 1024) == 2); - ASSERT_STRING_EQUALS(buffer, "1k"); - - ASSERT(pakfire_format_size(buffer, 1024 * 1024) == 4); - ASSERT_STRING_EQUALS(buffer, "1.0M"); - - ASSERT(pakfire_format_size(small_buffer, 0) == 2); - ASSERT_STRING_EQUALS(small_buffer, ""); - - return EXIT_SUCCESS; - -FAIL: - return EXIT_FAILURE; -} - int main(int argc, const char* argv[]) { testsuite_add_test(test_basename); testsuite_add_test(test_dirname); - testsuite_add_test(test_string_startswith); - testsuite_add_test(test_string_endswith); - testsuite_add_test(test_string_matches); - testsuite_add_test(test_string_partition); - testsuite_add_test(test_string_replace); - testsuite_add_test(test_string_split); - testsuite_add_test(test_string_join); - testsuite_add_test(test_format_size); return testsuite_run(argc, argv); }