]> git.ipfire.org Git - pakfire.git/blobdiff - src/libpakfire/util.c
util: Add test for pakfire_string_endswith
[pakfire.git] / src / libpakfire / util.c
index 9956fc867a1b31cae70cd260061654848a0453bc..553c83af65ec79a0a9c061961e9af723c6ab7210 100644 (file)
@@ -28,6 +28,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <time.h>
@@ -41,7 +42,7 @@
 
 #include <pakfire/constants.h>
 #include <pakfire/logging.h>
-#include <pakfire/types.h>
+#include <pakfire/package.h>
 #include <pakfire/util.h>
 
 #define NSEC_PER_SEC 1000000000
@@ -65,7 +66,7 @@ static Id pakfire_parse_namespace(Pool* pool, const char* s) {
        return pool_rel2id(pool, namespace, id, REL_NAMESPACE, 1);
 }
 
-Id pakfire_parse_dep(Pakfire pakfire, const char* s) {
+Id pakfire_parse_dep(struct pakfire* pakfire, const char* s) {
        Id id;
 
        if (!s) {
@@ -73,6 +74,10 @@ Id pakfire_parse_dep(Pakfire pakfire, const char* s) {
                return 0;
        }
 
+       // Ignore empty strings
+       if (!*s)
+               return 0;
+
        Pool* pool = pakfire_get_solv_pool(pakfire);
 
        // Consume any leading space
@@ -128,8 +133,8 @@ Id pakfire_parse_dep(Pakfire pakfire, const char* s) {
        return id;
 }
 
-void pakfire_parse_deps(Pakfire pakfire, PakfirePackage pkg,
-               void (*func)(PakfirePackage pkg, const char* dep), const char* deps) {
+void pakfire_parse_deps(struct pakfire* pakfire, struct pakfire_package* pkg,
+               void (*func)(struct pakfire_package* pkg, const char* dep), const char* deps) {
        char* p = strdupa(deps);
 
        while (*p) {
@@ -152,13 +157,29 @@ void pakfire_parse_deps(Pakfire pakfire, PakfirePackage pkg,
 }
 
 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) {
+       return !!strstr(s, pattern);
+}
+
 char* pakfire_unquote_in_place(char* s) {
        if (!s || !*s)
                return s;
@@ -300,37 +321,59 @@ ERROR:
        return result;
 }
 
-char** pakfire_split_string(const char* s, char delim) {
-       // Copy string to stack and count spaces
-       char buffer[strlen(s) + 2];
+static unsigned int pakfire_chrcnt(const char* s, char delim) {
+       size_t length = strlen(s);
 
-       size_t count = 1;
-       for (unsigned int i = 0; i < strlen(s) + 1; i++) {
-               buffer[i] = s[i];
+       unsigned int count = 0;
 
-               if (s[i] == delim) {
-                       buffer[i] = '\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;
        }
 
-       // Allocate an array of sufficient size
-       char** ret = malloc(sizeof(*ret) * (count + 1));
+       // 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;
 
-       // Copy strings to heap one by one
        unsigned int i = 0;
-       char* p = buffer;
        while (*p) {
-               ret[i++] = strdup(p);
+               char* e = strchr(p, delim);
 
-               // Move pointer to the next string
-               p += strlen(p) + 1;
-       }
+               // Terminate the string
+               if (e)
+                       *e = '\0';
 
-       // Terminate array
-       ret[count] = NULL;
+               // Add string to the array
+               array[i++] = strdup(p);
 
-       return ret;
+               // 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) {
@@ -442,6 +485,27 @@ 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)
@@ -572,7 +636,23 @@ char* pakfire_generate_uuid() {
        return ret;
 }
 
-char* pakfire_hexlify(const char* digest, const size_t length) {
+int pakfire_tty_is_noninteractive(void) {
+       const int fds[] = {
+               STDIN_FILENO,
+               STDOUT_FILENO,
+               STDERR_FILENO,
+               -1,
+       };
+
+       for (const int* fd = fds; *fd >= 0; fd++) {
+               if (!isatty(*fd))
+                       return 1;
+       }
+
+       return 0;
+}
+
+char* __pakfire_hexlify(const unsigned char* digest, const size_t length) {
        const char* hexdigits = "0123456789abcdef";
 
        char* s = malloc((length * 2) + 1);
@@ -592,7 +672,50 @@ char* pakfire_hexlify(const char* digest, const size_t length) {
        return s;
 }
 
-static int pakfire_mkparentdir(const char* path, mode_t mode) {
+static int parse_nibble(char nibble) {
+       // Handle digits
+       if (nibble >= '0' && nibble <= '9')
+               return nibble - '0';
+
+       // Handle lowercase letters
+       if (nibble >= 'a' && nibble <= 'f')
+               return 10 + nibble - 'a';
+
+       // Handle uppercase letters
+       if (nibble >= 'A' && nibble <= 'F')
+               return 10 + nibble - 'A';
+
+       // Error
+       return -1;
+}
+
+int __pakfire_unhexlify(unsigned char* dst, const size_t l, const char* src) {
+       if (!src) {
+               errno = EINVAL;
+               return 1;
+       }
+
+       // Determine input length
+       const size_t length = strlen(src);
+
+       for (unsigned int i = 0, j = 0; i < length && j < l; i += 2, j++) {
+               int h = parse_nibble(src[i]);
+               int l = parse_nibble(src[i+1]);
+
+               // Check for invalid input
+               if (h < 0 || l < 0) {
+                       errno = EINVAL;
+                       return 1;
+               }
+
+               // Store result
+               dst[j] = h << 4 | l;
+       }
+
+       return 0;
+}
+
+int pakfire_mkparentdir(const char* path, mode_t mode) {
        int r;
 
        char* dirname = pakfire_dirname(path);
@@ -666,6 +789,26 @@ int pakfire_rmtree(const char* path, int flags) {
        return r;
 }
 
+// Digests
+
+size_t pakfire_digest_length(enum pakfire_digests digest) {
+       switch (digest) {
+               case PAKFIRE_DIGEST_SHA512:
+                       return 64;
+
+               case PAKFIRE_DIGEST_SHA256:
+                       return 32;
+
+               case PAKFIRE_DIGEST_SHA1:
+                       return 20;
+
+               case PAKFIRE_DIGEST_NONE:
+                       return 0;
+       }
+
+       return 0;
+}
+
 // Archive Stuff
 
 int pakfire_archive_copy_data(struct archive* src, struct archive* dst,
@@ -690,7 +833,7 @@ int pakfire_archive_copy_data(struct archive* src, struct archive* dst,
        }
 }
 
-int pakfire_archive_copy_data_to_buffer(Pakfire pakfire, struct archive* a,
+int pakfire_archive_copy_data_to_buffer(struct pakfire* pakfire, struct archive* a,
                struct archive_entry* entry, char** data, size_t* data_size) {
        *data = NULL;
        *data_size = 0;
@@ -700,7 +843,7 @@ int pakfire_archive_copy_data_to_buffer(Pakfire pakfire, struct archive* a,
                return 0;
 
        // Allocate a block of the required size
-       *data = malloc(required_size);
+       *data = calloc(1, required_size + 1);
        if (!*data)
                return ENOMEM;
 
@@ -718,7 +861,7 @@ int pakfire_archive_copy_data_to_buffer(Pakfire pakfire, struct archive* a,
 
 // JSON Stuff
 
-static struct json_object* pakfire_json_parse(Pakfire pakfire, FILE* f) {
+static struct json_object* pakfire_json_parse(struct pakfire* pakfire, FILE* f) {
        struct json_tokener* tokener = NULL;
        struct json_object* json = NULL;
        char* buffer = NULL;
@@ -732,7 +875,7 @@ static struct json_object* pakfire_json_parse(Pakfire pakfire, FILE* f) {
        // Create tokener
        tokener = json_tokener_new();
        if (!tokener) {
-               ERROR(pakfire, "Could not allocate JSON tokener: %s\n", strerror(errno));
+               ERROR(pakfire, "Could not allocate JSON tokener: %m\n");
                goto ERROR;
        }
 
@@ -761,7 +904,7 @@ ERROR:
        return json;
 }
 
-struct json_object* pakfire_json_parse_from_file(Pakfire pakfire, const char* path) {
+struct json_object* pakfire_json_parse_from_file(struct pakfire* pakfire, const char* path) {
        FILE* f = fopen(path, "r");
        if (!f)
                return NULL;
@@ -813,3 +956,45 @@ int timespec_lt(struct timespec* t1, struct timespec* t2) {
                (t1->tv_sec == t2->tv_sec && t1->tv_nsec < t2->tv_nsec)
        );
 }
+
+// Resource Limits
+
+int pakfire_rlimit_set(struct pakfire* pakfire, int limit) {
+       struct rlimit rl;
+
+       // Sanity check
+       if (limit < 3) {
+               errno = EINVAL;
+               return 1;
+       }
+
+       // Fetch current configuration
+       if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
+               ERROR(pakfire, "Could not read RLIMIT_NOFILE: %m\n");
+               return 1;
+       }
+
+       // Do not attempt to set higher than maximum
+       if ((long unsigned int)limit > rl.rlim_max)
+               limit = rl.rlim_max;
+
+       rl.rlim_cur = limit;
+
+       // Set the new limit
+       if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
+               ERROR(pakfire, "Could not set RLIMIT_NOFILE to %lu: %m\n", rl.rlim_cur);
+               return 1;
+       }
+
+       DEBUG(pakfire, "RLIMIT_NOFILE set to %d\n", limit);
+
+       return 0;
+}
+
+/*
+       Resets RLIMIT_NOFILE to FD_SETSIZE (e.g. 1024)
+       for compatibility with software that uses select()
+*/
+int pakfire_rlimit_reset_nofile(struct pakfire* pakfire) {
+       return pakfire_rlimit_set(pakfire, FD_SETSIZE);
+}