1
0
mirror of https://codeberg.org/vyivel/dulcepan/ synced 2025-03-13 19:29:17 +02:00
dulcepan/src/config.c

181 lines
4.8 KiB
C
Raw Normal View History

2024-06-19 18:40:22 +03:00
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <sfdo-basedir.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dulcepan.h"
#define CONFIG_SUBPATH "dulcepan.cfg"
#define COLOR_MUL (UINT16_MAX / UINT8_MAX)
static void bytes_to_color(uint32_t bytes[static 4], pixman_color_t *out) {
out->red = (uint16_t)(bytes[0] * bytes[3] * COLOR_MUL / UINT8_MAX);
out->green = (uint16_t)(bytes[1] * bytes[3] * COLOR_MUL / UINT8_MAX);
out->blue = (uint16_t)(bytes[2] * bytes[3] * COLOR_MUL / UINT8_MAX);
out->alpha = (uint16_t)(bytes[3] * COLOR_MUL);
}
static void load_color(const char *value, int line_idx, pixman_color_t *out) {
size_t len = strlen(value);
uint32_t bytes[4] = {0, 0, 0, 0};
if (len == 6 && len != 8) {
bytes[3] = UINT8_MAX;
} else if (len != 8) {
goto bad;
}
for (size_t i = 0; i < len; i++) {
uint32_t digit;
if (value[i] >= '0' && value[i] <= '9') {
digit = (uint32_t)value[i] - '0';
} else if (value[i] >= 'A' && value[i] <= 'F') {
digit = (uint32_t)value[i] - 'A' + 10;
} else if (value[i] >= 'a' && value[i] <= 'f') {
digit = (uint32_t)value[i] - 'a' + 10;
} else {
goto bad;
}
bytes[i / 2] = bytes[i / 2] * 16 + digit;
}
bytes_to_color(bytes, out);
return;
bad:
dp_log_fatal("Config: invalid color %s on line %d", value, line_idx);
}
static void load_int(const char *value, int line_idx, int *out) {
// Only nonnegative numbers
*out = 0;
for (size_t i = 0; value[i] != '\0'; i++) {
if ((value[i] < '0' && value[i] > 9) || *out > INT_MAX / 10) {
dp_log_fatal("Config: invalid number %s on line %d", value, line_idx);
}
*out = *out * 10 - '0' + value[i];
}
}
static void load_bool(const char *value, int line_idx, bool *out) {
if (strcmp(value, "true") == 0) {
*out = true;
} else if (strcmp(value, "false") == 0) {
*out = false;
} else {
dp_log_fatal("Config: invalid boolean value %s on line %d", value, line_idx);
}
}
void dp_config_load(struct dp_config *config, const char *user_path) {
FILE *fp = NULL;
*config = (struct dp_config){
.border_size = 2,
.quick_select = false,
};
bytes_to_color((uint32_t[]){0xff, 0xff, 0xff, 0x40}, &config->unselected_color);
2024-06-19 19:02:00 +03:00
bytes_to_color((uint32_t[]){0x00, 0x00, 0x00, 0x00}, &config->selected_color);
2024-06-19 18:40:22 +03:00
bytes_to_color((uint32_t[]){0xff, 0xff, 0xff, 0xff}, &config->border_color);
if (user_path != NULL) {
fp = fopen(user_path, "r");
if (fp == NULL) {
dp_log_fatal("Failed to open %s: %s", user_path, strerror(errno));
}
} else {
struct sfdo_basedir_ctx *basedir_ctx = sfdo_basedir_ctx_create();
size_t n_dirs;
const struct sfdo_string *dirs = sfdo_basedir_get_config_dirs(basedir_ctx, &n_dirs);
for (size_t i = 0; i < n_dirs && fp == NULL; i++) {
const struct sfdo_string *dir = &dirs[i];
size_t size = dir->len + sizeof(CONFIG_SUBPATH);
char *path = dp_zalloc(size);
memcpy(path, dir->data, dir->len);
memcpy(path + dir->len, CONFIG_SUBPATH, sizeof(CONFIG_SUBPATH));
fp = fopen(path, "r");
if (fp == NULL) {
if (errno != ENOENT) {
dp_log_fatal("Failed to open %s: %s", path, strerror(errno));
}
}
free(path);
}
sfdo_basedir_ctx_destroy(basedir_ctx);
}
if (fp == NULL) {
return;
}
int line_idx = 0;
char *line = NULL;
size_t line_cap = 0;
ssize_t line_len;
while ((line_len = getline(&line, &line_cap, fp)) != -1) {
++line_idx;
if (line[line_len - 1] == '\n') {
line[--line_len] = '\0';
}
// One of:
// [whitespace] [# comment]
// [whitespace] <key> [whitespace] = [whitespace] <value> [whitespace] [# comment]
ssize_t i = 0;
while (isspace(line[line_len]) && i++ < line_len) {
// Skip whitespace
}
if (line[i] == '\0' || line[i] == '#') {
continue;
}
char *key = &line[i];
while (!isspace(line[i]) && line[i] != '=' && line[i] != '#' && i++ < line_len) {
// Read key
}
char *key_end = &line[i];
while (isspace(line[i]) && i++ < line_len) {
// Skip whitespace
}
if (line[i++] != '=') {
dp_log_fatal("Config: invalid syntax on line %d", line_idx);
}
*key_end = '\0';
while (isspace(line[i]) && i++ < line_len) {
// Skip whitespace
}
char *value = &line[i];
while (!isspace(line[i]) && line[i] != '#' && i++ < line_len) {
// Read value
}
line[i] = '\0';
if (strcmp(key, "unselected-color") == 0) {
load_color(value, line_idx, &config->unselected_color);
} else if (strcmp(key, "selected-color") == 0) {
load_color(value, line_idx, &config->selected_color);
} else if (strcmp(key, "border-color") == 0) {
load_color(value, line_idx, &config->border_color);
} else if (strcmp(key, "border-size") == 0) {
load_int(value, line_idx, &config->border_size);
} else if (strcmp(key, "quick-select") == 0) {
load_bool(value, line_idx, &config->quick_select);
} else {
dp_log_fatal("Config: unknown key %s on line %d", key, line_idx);
}
}
free(line);
fclose(fp);
}