initial commit
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I6a6a6964ee9e6ebe436ca8328c6e4a7ec7c9d8d4
This commit is contained in:
commit
fcc080871a
11 changed files with 12536 additions and 0 deletions
416
src/utils.c
Normal file
416
src/utils.c
Normal file
|
|
@ -0,0 +1,416 @@
|
|||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../include/chroma.h"
|
||||
|
||||
// Global state pointer for signal handling
|
||||
static chroma_state_t *g_state = NULL;
|
||||
static char *g_config_file = NULL;
|
||||
|
||||
// Signal handler implementation
|
||||
static void signal_handler_impl(int sig) {
|
||||
switch (sig) {
|
||||
case SIGTERM:
|
||||
case SIGINT:
|
||||
chroma_log("INFO", "Received signal %d (%s), shutting down gracefully", sig,
|
||||
(sig == SIGTERM) ? "SIGTERM" : "SIGINT");
|
||||
chroma_should_quit = 1;
|
||||
if (g_state) {
|
||||
g_state->running = false;
|
||||
}
|
||||
break;
|
||||
case SIGHUP:
|
||||
chroma_log("INFO", "Received SIGHUP, reloading configuration");
|
||||
if (g_state && g_config_file) {
|
||||
chroma_reload_config(g_state, g_config_file);
|
||||
}
|
||||
break;
|
||||
case SIGPIPE:
|
||||
// Ignore SIGPIPE - we'll handle broken pipes in read/write calls
|
||||
break;
|
||||
default:
|
||||
chroma_log("WARN", "Received unexpected signal: %d", sig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set up signal handlers
|
||||
void chroma_handle_signals(void) {
|
||||
struct sigaction sa;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = signal_handler_impl;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
|
||||
// Install signal handlers
|
||||
if (sigaction(SIGTERM, &sa, NULL) == -1) {
|
||||
chroma_log("ERROR", "Failed to install SIGTERM handler: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
if (sigaction(SIGINT, &sa, NULL) == -1) {
|
||||
chroma_log("ERROR", "Failed to install SIGINT handler: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
if (sigaction(SIGHUP, &sa, NULL) == -1) {
|
||||
chroma_log("ERROR", "Failed to install SIGHUP handler: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
// Ignore SIGPIPE
|
||||
sa.sa_handler = SIG_IGN;
|
||||
if (sigaction(SIGPIPE, &sa, NULL) == -1) {
|
||||
chroma_log("ERROR", "Failed to ignore SIGPIPE: %s", strerror(errno));
|
||||
}
|
||||
|
||||
chroma_log("DEBUG", "Signal handlers installed");
|
||||
}
|
||||
|
||||
// Set global state for signal handling
|
||||
void chroma_set_signal_state(chroma_state_t *state, const char *config_file) {
|
||||
g_state = state;
|
||||
|
||||
free(g_config_file);
|
||||
g_config_file = config_file ? strdup(config_file) : NULL;
|
||||
}
|
||||
|
||||
// Clean up signal handling resources
|
||||
void chroma_cleanup_signals(void) {
|
||||
g_state = NULL;
|
||||
free(g_config_file);
|
||||
g_config_file = NULL;
|
||||
}
|
||||
|
||||
// Expand tilde in path
|
||||
char *chroma_expand_path(const char *path) {
|
||||
if (!path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (path[0] != '~') {
|
||||
return strdup(path);
|
||||
}
|
||||
|
||||
const char *home;
|
||||
if (path[1] == '/' || path[1] == '\0') {
|
||||
// ~/... or just ~
|
||||
home = getenv("HOME");
|
||||
if (!home) {
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
if (pw) {
|
||||
home = pw->pw_dir;
|
||||
}
|
||||
}
|
||||
if (!home) {
|
||||
chroma_log("ERROR", "Could not determine home directory");
|
||||
return strdup(path); // Return original path as fallback
|
||||
}
|
||||
|
||||
size_t home_len = strlen(home);
|
||||
size_t path_len = strlen(path);
|
||||
char *expanded = malloc(home_len + path_len); // -1 for ~ +1 for \0
|
||||
if (!expanded) {
|
||||
chroma_log("ERROR", "Failed to allocate memory for path expansion");
|
||||
return strdup(path);
|
||||
}
|
||||
|
||||
strcpy(expanded, home);
|
||||
if (path[1] == '/') {
|
||||
strcat(expanded, path + 1);
|
||||
}
|
||||
|
||||
return expanded;
|
||||
} else {
|
||||
// ~user/...
|
||||
const char *slash = strchr(path, '/');
|
||||
size_t user_len = slash ? (size_t)(slash - path - 1) : strlen(path) - 1;
|
||||
|
||||
char *username = malloc(user_len + 1);
|
||||
if (!username) {
|
||||
return strdup(path);
|
||||
}
|
||||
|
||||
strncpy(username, path + 1, user_len);
|
||||
username[user_len] = '\0';
|
||||
|
||||
struct passwd *pw = getpwnam(username);
|
||||
|
||||
if (!pw) {
|
||||
chroma_log("ERROR", "User not found: %s", username);
|
||||
free(username);
|
||||
return strdup(path);
|
||||
}
|
||||
|
||||
free(username);
|
||||
|
||||
size_t home_len = strlen(pw->pw_dir);
|
||||
size_t remaining_len = slash ? strlen(slash) : 0;
|
||||
char *expanded = malloc(home_len + remaining_len + 1);
|
||||
if (!expanded) {
|
||||
return strdup(path);
|
||||
}
|
||||
|
||||
strcpy(expanded, pw->pw_dir);
|
||||
if (slash) {
|
||||
strcat(expanded, slash);
|
||||
}
|
||||
|
||||
return expanded;
|
||||
}
|
||||
}
|
||||
|
||||
// Create directory recursively
|
||||
int chroma_mkdir_recursive(const char *path, mode_t mode) {
|
||||
if (!path) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *path_copy = strdup(path);
|
||||
if (!path_copy) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *p = path_copy;
|
||||
|
||||
// Skip leading slashes
|
||||
while (*p == '/') {
|
||||
p++;
|
||||
}
|
||||
|
||||
while (*p) {
|
||||
// Find next slash
|
||||
while (*p && *p != '/') {
|
||||
p++;
|
||||
}
|
||||
|
||||
if (*p) {
|
||||
*p = '\0';
|
||||
|
||||
// Create directory
|
||||
if (mkdir(path_copy, mode) == -1 && errno != EEXIST) {
|
||||
chroma_log("ERROR", "Failed to create directory %s: %s", path_copy,
|
||||
strerror(errno));
|
||||
free(path_copy);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*p = '/';
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
// Create final directory
|
||||
if (mkdir(path_copy, mode) == -1 && errno != EEXIST) {
|
||||
chroma_log("ERROR", "Failed to create directory %s: %s", path_copy,
|
||||
strerror(errno));
|
||||
free(path_copy);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(path_copy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get configuration directory
|
||||
char *chroma_get_config_dir(void) {
|
||||
const char *xdg_config = getenv("XDG_CONFIG_HOME");
|
||||
|
||||
if (xdg_config) {
|
||||
char *config_dir = malloc(strlen(xdg_config) + strlen("/chroma") + 1);
|
||||
if (config_dir) {
|
||||
sprintf(config_dir, "%s/chroma", xdg_config);
|
||||
return config_dir;
|
||||
}
|
||||
}
|
||||
|
||||
const char *home = getenv("HOME");
|
||||
if (home) {
|
||||
char *config_dir = malloc(strlen(home) + strlen("/.config/chroma") + 1);
|
||||
if (config_dir) {
|
||||
sprintf(config_dir, "%s/.config/chroma", home);
|
||||
return config_dir;
|
||||
}
|
||||
}
|
||||
|
||||
return strdup("/etc/chroma"); // Fallback
|
||||
}
|
||||
|
||||
// Check if path exists
|
||||
bool chroma_path_exists(const char *path) {
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
return (stat(path, &st) == 0);
|
||||
}
|
||||
|
||||
// Check if path is a regular file
|
||||
bool chroma_is_regular_file(const char *path) {
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(path, &st) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return S_ISREG(st.st_mode);
|
||||
}
|
||||
|
||||
// Check if path is a directory
|
||||
bool chroma_is_directory(const char *path) {
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(path, &st) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
// Get file size
|
||||
long chroma_get_file_size(const char *path) {
|
||||
if (!path) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(path, &st) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
// Get file extension
|
||||
const char *chroma_get_file_extension(const char *path) {
|
||||
if (!path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *last_dot = strrchr(path, '.');
|
||||
if (!last_dot || last_dot == path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return last_dot + 1;
|
||||
}
|
||||
|
||||
// Case-insensitive string comparison
|
||||
int chroma_strcasecmp(const char *s1, const char *s2) {
|
||||
if (!s1 || !s2) {
|
||||
return (s1 == s2) ? 0 : (s1 ? 1 : -1);
|
||||
}
|
||||
|
||||
while (*s1 && *s2) {
|
||||
int c1 = tolower((unsigned char)*s1);
|
||||
int c2 = tolower((unsigned char)*s2);
|
||||
|
||||
if (c1 != c2) {
|
||||
return c1 - c2;
|
||||
}
|
||||
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
|
||||
return tolower((unsigned char)*s1) - tolower((unsigned char)*s2);
|
||||
}
|
||||
|
||||
// Safe string copy
|
||||
size_t chroma_strlcpy(char *dst, const char *src, size_t size) {
|
||||
size_t src_len = strlen(src);
|
||||
|
||||
if (size > 0) {
|
||||
size_t copy_len = (src_len < size - 1) ? src_len : size - 1;
|
||||
memcpy(dst, src, copy_len);
|
||||
dst[copy_len] = '\0';
|
||||
}
|
||||
|
||||
return src_len;
|
||||
}
|
||||
|
||||
// Safe string concatenation
|
||||
size_t chroma_strlcat(char *dst, const char *src, size_t size) {
|
||||
size_t dst_len = strnlen(dst, size);
|
||||
size_t src_len = strlen(src);
|
||||
|
||||
if (dst_len < size) {
|
||||
size_t copy_len = size - dst_len - 1;
|
||||
if (src_len < copy_len) {
|
||||
copy_len = src_len;
|
||||
}
|
||||
memcpy(dst + dst_len, src, copy_len);
|
||||
dst[dst_len + copy_len] = '\0';
|
||||
}
|
||||
|
||||
return dst_len + src_len;
|
||||
}
|
||||
|
||||
// Get current time in milliseconds
|
||||
long long chroma_get_time_ms(void) {
|
||||
struct timeval tv;
|
||||
if (gettimeofday(&tv, NULL) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (long long)tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
}
|
||||
|
||||
// Sleep for specified milliseconds
|
||||
void chroma_sleep_ms(long ms) {
|
||||
if (ms <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct timespec ts;
|
||||
ts.tv_sec = ms / 1000;
|
||||
ts.tv_nsec = (ms % 1000) * 1000000;
|
||||
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
// Format memory size in human readable format
|
||||
void chroma_format_memory_size(size_t bytes, char *buffer, size_t buffer_size) {
|
||||
if (!buffer || buffer_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char *units[] = {"B", "KB", "MB", "GB", "TB"};
|
||||
const int num_units = sizeof(units) / sizeof(units[0]);
|
||||
|
||||
double size = (double)bytes;
|
||||
int unit_index = 0;
|
||||
|
||||
while (size >= 1024.0 && unit_index < num_units - 1) {
|
||||
size /= 1024.0;
|
||||
unit_index++;
|
||||
}
|
||||
|
||||
if (unit_index == 0) {
|
||||
snprintf(buffer, buffer_size, "%.0f %s", size, units[unit_index]);
|
||||
} else {
|
||||
snprintf(buffer, buffer_size, "%.2f %s", size, units[unit_index]);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup utility functions
|
||||
void chroma_utils_cleanup(void) { chroma_cleanup_signals(); }
|
||||
Loading…
Add table
Add a link
Reference in a new issue