brewery
notashelf /
48fa1cc8525bbb05a245b13dba7ec49afa48077a

chroma

public

Lightweight wallpaper daemon for Wayland

Star0tarball
clone
ssh://git@git.notashelf.dev:33/notashelf/chroma.git
src/utils.cC416 lines8.9 KB
1
#include <ctype.h>
2
#include <errno.h>
3
#include <libgen.h>
4
#include <pwd.h>
5
#include <signal.h>
6
#include <stdio.h>
7
#include <stdlib.h>
8
#include <string.h>
9
#include <sys/stat.h>
10
#include <sys/time.h>
11
#include <sys/types.h>
12
#include <time.h>
13
#include <unistd.h>
14
15
#include "../include/chroma.h"
16
17
// Global state pointer for signal handling
18
static chroma_state_t *g_state = NULL;
19
static char *g_config_file = NULL;
20
21
// Signal handler implementation
22
static void signal_handler_impl(int sig) {
23
  switch (sig) {
24
  case SIGTERM:
25
  case SIGINT:
26
    chroma_log("INFO", "Received signal %d (%s), shutting down gracefully", sig,
27
               (sig == SIGTERM) ? "SIGTERM" : "SIGINT");
28
    chroma_should_quit = 1;
29
    if (g_state) {
30
      g_state->running = false;
31
    }
32
    break;
33
  case SIGHUP:
34
    chroma_log("INFO", "Received SIGHUP, reloading configuration");
35
    if (g_state && g_config_file) {
36
      chroma_reload_config(g_state, g_config_file);
37
    }
38
    break;
39
  case SIGPIPE:
40
    // Ignore SIGPIPE - we'll handle broken pipes in read/write calls
41
    break;
42
  default:
43
    chroma_log("WARN", "Received unexpected signal: %d", sig);
44
    break;
45
  }
46
}
47
48
// Set up signal handlers
49
void chroma_handle_signals(void) {
50
  struct sigaction sa;
51
52
  memset(&sa, 0, sizeof(sa));
53
  sa.sa_handler = signal_handler_impl;
54
  sigemptyset(&sa.sa_mask);
55
  sa.sa_flags = SA_RESTART;
56
57
  // Install signal handlers
58
  if (sigaction(SIGTERM, &sa, NULL) == -1) {
59
    chroma_log("ERROR", "Failed to install SIGTERM handler: %s",
60
               strerror(errno));
61
  }
62
63
  if (sigaction(SIGINT, &sa, NULL) == -1) {
64
    chroma_log("ERROR", "Failed to install SIGINT handler: %s",
65
               strerror(errno));
66
  }
67
68
  if (sigaction(SIGHUP, &sa, NULL) == -1) {
69
    chroma_log("ERROR", "Failed to install SIGHUP handler: %s",
70
               strerror(errno));
71
  }
72
73
  // Ignore SIGPIPE
74
  sa.sa_handler = SIG_IGN;
75
  if (sigaction(SIGPIPE, &sa, NULL) == -1) {
76
    chroma_log("ERROR", "Failed to ignore SIGPIPE: %s", strerror(errno));
77
  }
78
79
  chroma_log("DEBUG", "Signal handlers installed");
80
}
81
82
// Set global state for signal handling
83
void chroma_set_signal_state(chroma_state_t *state, const char *config_file) {
84
  g_state = state;
85
86
  free(g_config_file);
87
  g_config_file = config_file ? strdup(config_file) : NULL;
88
}
89
90
// Clean up signal handling resources
91
void chroma_cleanup_signals(void) {
92
  g_state = NULL;
93
  free(g_config_file);
94
  g_config_file = NULL;
95
}
96
97
// Expand tilde in path
98
char *chroma_expand_path(const char *path) {
99
  if (!path) {
100
    return NULL;
101
  }
102
103
  if (path[0] != '~') {
104
    return strdup(path);
105
  }
106
107
  const char *home;
108
  if (path[1] == '/' || path[1] == '\0') {
109
    // ~/... or just ~
110
    home = getenv("HOME");
111
    if (!home) {
112
      struct passwd *pw = getpwuid(getuid());
113
      if (pw) {
114
        home = pw->pw_dir;
115
      }
116
    }
117
    if (!home) {
118
      chroma_log("ERROR", "Could not determine home directory");
119
      return strdup(path); // Return original path as fallback
120
    }
121
122
    size_t home_len = strlen(home);
123
    size_t path_len = strlen(path);
124
    char *expanded = malloc(home_len + path_len); // -1 for ~ +1 for \0
125
    if (!expanded) {
126
      chroma_log("ERROR", "Failed to allocate memory for path expansion");
127
      return strdup(path);
128
    }
129
130
    strcpy(expanded, home);
131
    if (path[1] == '/') {
132
      strcat(expanded, path + 1);
133
    }
134
135
    return expanded;
136
  } else {
137
    // ~user/...
138
    const char *slash = strchr(path, '/');
139
    size_t user_len = slash ? (size_t)(slash - path - 1) : strlen(path) - 1;
140
141
    char *username = malloc(user_len + 1);
142
    if (!username) {
143
      return strdup(path);
144
    }
145
146
    strncpy(username, path + 1, user_len);
147
    username[user_len] = '\0';
148
149
    struct passwd *pw = getpwnam(username);
150
151
    if (!pw) {
152
      chroma_log("ERROR", "User not found: %s", username);
153
      free(username);
154
      return strdup(path);
155
    }
156
157
    free(username);
158
159
    size_t home_len = strlen(pw->pw_dir);
160
    size_t remaining_len = slash ? strlen(slash) : 0;
161
    char *expanded = malloc(home_len + remaining_len + 1);
162
    if (!expanded) {
163
      return strdup(path);
164
    }
165
166
    strcpy(expanded, pw->pw_dir);
167
    if (slash) {
168
      strcat(expanded, slash);
169
    }
170
171
    return expanded;
172
  }
173
}
174
175
// Create directory recursively
176
int chroma_mkdir_recursive(const char *path, mode_t mode) {
177
  if (!path) {
178
    return -1;
179
  }
180
181
  char *path_copy = strdup(path);
182
  if (!path_copy) {
183
    return -1;
184
  }
185
186
  char *p = path_copy;
187
188
  // Skip leading slashes
189
  while (*p == '/') {
190
    p++;
191
  }
192
193
  while (*p) {
194
    // Find next slash
195
    while (*p && *p != '/') {
196
      p++;
197
    }
198
199
    if (*p) {
200
      *p = '\0';
201
202
      // Create directory
203
      if (mkdir(path_copy, mode) == -1 && errno != EEXIST) {
204
        chroma_log("ERROR", "Failed to create directory %s: %s", path_copy,
205
                   strerror(errno));
206
        free(path_copy);
207
        return -1;
208
      }
209
210
      *p = '/';
211
      p++;
212
    }
213
  }
214
215
  // Create final directory
216
  if (mkdir(path_copy, mode) == -1 && errno != EEXIST) {
217
    chroma_log("ERROR", "Failed to create directory %s: %s", path_copy,
218
               strerror(errno));
219
    free(path_copy);
220
    return -1;
221
  }
222
223
  free(path_copy);
224
  return 0;
225
}
226
227
// Get configuration directory
228
char *chroma_get_config_dir(void) {
229
  const char *xdg_config = getenv("XDG_CONFIG_HOME");
230
231
  if (xdg_config) {
232
    char *config_dir = malloc(strlen(xdg_config) + strlen("/chroma") + 1);
233
    if (config_dir) {
234
      sprintf(config_dir, "%s/chroma", xdg_config);
235
      return config_dir;
236
    }
237
  }
238
239
  const char *home = getenv("HOME");
240
  if (home) {
241
    char *config_dir = malloc(strlen(home) + strlen("/.config/chroma") + 1);
242
    if (config_dir) {
243
      sprintf(config_dir, "%s/.config/chroma", home);
244
      return config_dir;
245
    }
246
  }
247
248
  return strdup("/etc/chroma"); // Fallback
249
}
250
251
// Check if path exists
252
bool chroma_path_exists(const char *path) {
253
  if (!path) {
254
    return false;
255
  }
256
257
  struct stat st;
258
  return (stat(path, &st) == 0);
259
}
260
261
// Check if path is a regular file
262
bool chroma_is_regular_file(const char *path) {
263
  if (!path) {
264
    return false;
265
  }
266
267
  struct stat st;
268
  if (stat(path, &st) != 0) {
269
    return false;
270
  }
271
272
  return S_ISREG(st.st_mode);
273
}
274
275
// Check if path is a directory
276
bool chroma_is_directory(const char *path) {
277
  if (!path) {
278
    return false;
279
  }
280
281
  struct stat st;
282
  if (stat(path, &st) != 0) {
283
    return false;
284
  }
285
286
  return S_ISDIR(st.st_mode);
287
}
288
289
// Get file size
290
long chroma_get_file_size(const char *path) {
291
  if (!path) {
292
    return -1;
293
  }
294
295
  struct stat st;
296
  if (stat(path, &st) != 0) {
297
    return -1;
298
  }
299
300
  return st.st_size;
301
}
302
303
// Get file extension
304
const char *chroma_get_file_extension(const char *path) {
305
  if (!path) {
306
    return NULL;
307
  }
308
309
  const char *last_dot = strrchr(path, '.');
310
  if (!last_dot || last_dot == path) {
311
    return NULL;
312
  }
313
314
  return last_dot + 1;
315
}
316
317
// Case-insensitive string comparison
318
int chroma_strcasecmp(const char *s1, const char *s2) {
319
  if (!s1 || !s2) {
320
    return (s1 == s2) ? 0 : (s1 ? 1 : -1);
321
  }
322
323
  while (*s1 && *s2) {
324
    int c1 = tolower((unsigned char)*s1);
325
    int c2 = tolower((unsigned char)*s2);
326
327
    if (c1 != c2) {
328
      return c1 - c2;
329
    }
330
331
    s1++;
332
    s2++;
333
  }
334
335
  return tolower((unsigned char)*s1) - tolower((unsigned char)*s2);
336
}
337
338
// Safe string copy
339
size_t chroma_strlcpy(char *dst, const char *src, size_t size) {
340
  size_t src_len = strlen(src);
341
342
  if (size > 0) {
343
    size_t copy_len = (src_len < size - 1) ? src_len : size - 1;
344
    memcpy(dst, src, copy_len);
345
    dst[copy_len] = '\0';
346
  }
347
348
  return src_len;
349
}
350
351
// Safe string concatenation
352
size_t chroma_strlcat(char *dst, const char *src, size_t size) {
353
  size_t dst_len = strnlen(dst, size);
354
  size_t src_len = strlen(src);
355
356
  if (dst_len < size) {
357
    size_t copy_len = size - dst_len - 1;
358
    if (src_len < copy_len) {
359
      copy_len = src_len;
360
    }
361
    memcpy(dst + dst_len, src, copy_len);
362
    dst[dst_len + copy_len] = '\0';
363
  }
364
365
  return dst_len + src_len;
366
}
367
368
// Get current time in milliseconds
369
long long chroma_get_time_ms(void) {
370
  struct timeval tv;
371
  if (gettimeofday(&tv, NULL) != 0) {
372
    return 0;
373
  }
374
375
  return (long long)tv.tv_sec * 1000 + tv.tv_usec / 1000;
376
}
377
378
// Sleep for specified milliseconds
379
void chroma_sleep_ms(long ms) {
380
  if (ms <= 0) {
381
    return;
382
  }
383
384
  struct timespec ts;
385
  ts.tv_sec = ms / 1000;
386
  ts.tv_nsec = (ms % 1000) * 1000000;
387
388
  nanosleep(&ts, NULL);
389
}
390
391
// Format memory size in human readable format
392
void chroma_format_memory_size(size_t bytes, char *buffer, size_t buffer_size) {
393
  if (!buffer || buffer_size == 0) {
394
    return;
395
  }
396
397
  const char *units[] = {"B", "KB", "MB", "GB", "TB"};
398
  const int num_units = sizeof(units) / sizeof(units[0]);
399
400
  double size = (double)bytes;
401
  int unit_index = 0;
402
403
  while (size >= 1024.0 && unit_index < num_units - 1) {
404
    size /= 1024.0;
405
    unit_index++;
406
  }
407
408
  if (unit_index == 0) {
409
    snprintf(buffer, buffer_size, "%.0f %s", size, units[unit_index]);
410
  } else {
411
    snprintf(buffer, buffer_size, "%.2f %s", size, units[unit_index]);
412
  }
413
}
414
415
// Cleanup utility functions
416
void chroma_utils_cleanup(void) { chroma_cleanup_signals(); }