/* * pkg - Utility functions * https://rawnix.org * * Copyright (c) 2025 zorz@gmx.com * SPDX-License-Identifier: BSD-2-Clause */ #include "pkg.h" #include #include #include #include #include #include /* ---- Shared hash function (djb2) ---- */ unsigned long hash_djb2(const char *str) { unsigned long hash = 5381; int c; while ((c = *str++)) hash = ((hash << 5) + hash) + c; return hash; } /* ---- Allocators ---- */ void *xmalloc(size_t size) { void *p = malloc(size); if (!p) err(1, "malloc"); return p; } void *xrealloc(void *ptr, size_t size) { void *p = realloc(ptr, size); if (!p) err(1, "realloc"); return p; } char *xstrdup(const char *str) { char *p; if (!str) return NULL; p = strdup(str); if (!p) err(1, "strdup"); return p; } /* ---- String utilities ---- */ char *str_trim(char *str) { char *end; if (!str) return NULL; while (isspace((unsigned char)*str)) str++; if (*str == '\0') return str; end = str + strlen(str) - 1; while (end > str && isspace((unsigned char)*end)) end--; end[1] = '\0'; return str; } /* ---- String array ---- */ void strarray_init(strarray_t *arr) { arr->data = NULL; arr->size = 0; arr->capacity = 0; } void strarray_free(strarray_t *arr) { size_t i; if (!arr) return; for (i = 0; i < arr->size; i++) free(arr->data[i]); free(arr->data); arr->data = NULL; arr->size = 0; arr->capacity = 0; } int strarray_add(strarray_t *arr, const char *str) { if (arr->size >= arr->capacity) { size_t newcap = arr->capacity == 0 ? 16 : arr->capacity * 2; arr->data = xrealloc(arr->data, newcap * sizeof(char *)); arr->capacity = newcap; } arr->data[arr->size++] = xstrdup(str); return 0; } bool strarray_contains(strarray_t *arr, const char *str) { size_t i; if (!arr || !str) return false; for (i = 0; i < arr->size; i++) if (strcmp(arr->data[i], str) == 0) return true; return false; } /* ---- String list ---- */ strlist_t *strlist_new(const char *data) { strlist_t *node = xmalloc(sizeof(strlist_t)); node->data = xstrdup(data); node->next = NULL; return node; } void strlist_free(strlist_t *list) { strlist_t *next; while (list) { next = list->next; free(list->data); free(list); list = next; } } int strlist_add(strlist_t **list, const char *data) { strlist_t *node = strlist_new(data); node->next = *list; *list = node; return 0; } /* ---- Filesystem helpers ---- */ int mkdirp(const char *path) { char *tmp, *p; tmp = xstrdup(path); for (p = tmp + 1; *p; p++) { if (*p == '/') { *p = '\0'; mkdir(tmp, 0755); *p = '/'; } } mkdir(tmp, 0755); free(tmp); return 0; } void ensure_parent_dir(const char *path) { char *tmp, *slash; tmp = xstrdup(path); slash = strrchr(tmp, '/'); if (slash) { *slash = '\0'; mkdirp(tmp); } free(tmp); } /* * Build an absolute path from root + relative. * root="/" relative="var/lib/pkg/db" → "/var/lib/pkg/db" * root="/mnt/tux" relative="var/lib/pkg/db" → "/mnt/tux/var/lib/pkg/db" * Caller must free() the result. */ char *build_root_path(const char *root, const char *relative) { size_t len; char *path; if (root && strcmp(root, "/") != 0) { len = strlen(root) + 1 + strlen(relative) + 1; path = xmalloc(len); snprintf(path, len, "%s/%s", root, relative); } else { len = 1 + strlen(relative) + 1; path = xmalloc(len); snprintf(path, len, "/%s", relative); } return path; } /* ---- Version helpers ---- */ void version_build(char *buf, size_t len, const char *version, const char *release) { if (release && *release) snprintf(buf, len, "%s-%s", version, release); else snprintf(buf, len, "%s-1", version); } /* ---- Safe file close ---- */ int file_close_checked(FILE *fp) { int ret = 0; if (fflush(fp) != 0) ret = -1; if (ferror(fp)) ret = -1; if (ret == 0 && fsync(fileno(fp)) != 0) ret = -1; /* make data durable before the caller's rename */ if (fclose(fp) != 0) ret = -1; return ret; } /* ---- Process execution ---- */ int run_cmd(const char *cmd, char *const argv[], const char *dir) { pid_t pid; int status; pid = fork(); if (pid == 0) { if (dir && chdir(dir) != 0) { fprintf(stderr, "chdir %s: %s\n", dir, strerror(errno)); _exit(1); } execvp(cmd, argv); fprintf(stderr, "%s: %s\n", cmd, strerror(errno)); _exit(127); } else if (pid < 0) { warn("fork"); return -1; } if (waitpid(pid, &status, 0) < 0) return -1; return (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? 0 : -1; }