feat: modernize config

- config now is more toml like
- no need to declare all fields, it implements defaults
- no yanderedev code, introspection babbyyy 😎
- desktop and config files parser semi-unification
- misc tweaks all over (mainly allocation failures handling)
This commit is contained in:
javalsai 2025-06-13 14:05:19 +02:00
parent 3480393a66
commit 5174f0b2bf
Signed by: javalsai
SSH Key Fingerprint: SHA256:3G83yKhBUWVABVX/vPWH88xnK4+ptMtHkZGCRXD4Mk8
13 changed files with 480 additions and 304 deletions

View File

@ -7,6 +7,6 @@
#include "sessions.h" #include "sessions.h"
bool launch(char* user, char* passwd, struct session session, void (*cb)(void), bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
struct behavior* behavior); struct config* config);
#endif #endif

View File

@ -2,77 +2,166 @@
#define CONFIGH_ #define CONFIGH_
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stddef.h>
#include "keys.h" #include "keys.h"
#include "macros.h"
#include "util.h" #include "util.h"
enum introspection_type {
STRING,
BOOL,
KEY,
STRING_ARRAY,
};
static const char* const intros_tys_names[] = {
[STRING] = "STRING",
[BOOL] = "BOOL",
[KEY] = "KEY",
[STRING_ARRAY] = "STRING_ARRAY",
};
union introspection_variant {
char* string;
bool boolean;
enum keys key;
char** string_array;
};
struct introspection_item {
char* NNULLABLE name;
size_t offset;
enum introspection_type typ;
};
#define INTROS_ITEM(key, table, ty) \
{ \
.name = #key, \
.offset = offsetof(table, key), \
.typ = (ty), \
}
#define STRUCT_BUILDER(cty, name, ty, def, tname) cty name;
#define DEFAULT_BUILDER(cty, name, ty, def, tname) .name = (def),
#define INTROS_BUILDER(cty, name, ty, def, tname) \
INTROS_ITEM(name, struct table_##tname, ty),
#define BUILD_TABLE(table, TABLE) \
struct table_##table { \
TABLE(STRUCT_BUILDER, table) \
}
#define BUILD_DEFAULT(table, TABLE) \
static const struct table_##table default_table_##table = { \
TABLE(DEFAULT_BUILDER, table)}
#define BUILD_INTROS(table, TABLE) \
static const struct introspection_item intros_table_##table[] = { \
TABLE(INTROS_BUILDER, table)}
#define BUILD(table, TABLE) \
BUILD_TABLE(table, TABLE); \
BUILD_DEFAULT(table, TABLE); \
BUILD_INTROS(table, TABLE);
// should be ansi escape codes under \x1b[...m // should be ansi escape codes under \x1b[...m
// if not prepared accordingly, it might break // if not prepared accordingly, it might break
struct theme_colors { #define TABLE_COLORS(F, name) \
char* bg; F(char*, bg, STRING, "48;2;38;28;28", name) \
char* fg; F(char*, fg, STRING, "22;24;38;2;245;245;245", name) \
char* err; F(char*, err, STRING, "1;31", name) \
char* s_wayland; F(char*, s_wayland, STRING, "38;2;255;174;66", name) \
char* s_xorg; F(char*, s_xorg, STRING, "38;2;37;175;255", name) \
char* s_shell; F(char*, s_shell, STRING, "38;2;34;140;34", name) \
char* e_hostname; F(char*, e_hostname, STRING, "38;2;255;64;64", name) \
char* e_date; F(char*, e_date, STRING, "38;2;144;144;144", name) \
char* e_box; F(char*, e_box, STRING, "38;2;122;122;122", name) \
char* e_header; F(char*, e_header, STRING, "4;38;2;0;255;0", name) \
char* e_user; F(char*, e_user, STRING, "36", name) \
char* e_passwd; F(char*, e_passwd, STRING, "4;38;2;245;245;205", name) \
char* e_badpasswd; F(char*, e_badpasswd, STRING, "3;4;31", name) \
char* e_key; F(char*, e_key, STRING, "38;2;255;174;66", name)
};
BUILD(colors, TABLE_COLORS);
// even if they're multiple bytes long // even if they're multiple bytes long
// they should only take up 1 char size on display // they should only take up 1 char size on display
struct theme_chars { #define TABLE_CHARS(F, name) \
char* hb; F(char*, hb, STRING, "", name) \
char* vb; F(char*, vb, STRING, "", name) \
F(char*, ctl, STRING, "", name) \
F(char*, ctr, STRING, "", name) \
F(char*, cbl, STRING, "", name) \
F(char*, cbr, STRING, "", name)
char* ctl; BUILD(chars, TABLE_CHARS);
char* ctr;
char* cbl;
char* cbr;
};
struct theme { #define TABLE_FUNCTIONS(F, name) \
struct theme_colors colors; F(enum keys, poweroff, KEY, F1, name) \
struct theme_chars chars; F(enum keys, reboot, KEY, F2, name) \
}; F(enum keys, refresh, KEY, F5, name)
struct functions { BUILD(functions, TABLE_FUNCTIONS);
enum keys poweroff;
enum keys reboot;
enum keys refresh;
};
struct strings { #define TABLE_STRINGS(F, name) \
char* f_poweroff; F(char*, f_poweroff, STRING, "poweroff", name) \
char* f_reboot; F(char*, f_reboot, STRING, "reboot", name) \
char* f_refresh; F(char*, f_refresh, STRING, "refresh", name) \
char* e_user; F(char*, e_user, STRING, "user", name) \
char* e_passwd; F(char*, e_passwd, STRING, "password", name) \
char* s_wayland; F(char*, s_wayland, STRING, "wayland", name) \
char* s_xorg; F(char*, s_xorg, STRING, "xorg", name) \
char* s_shell; F(char*, s_shell, STRING, "shell", name)
};
struct behavior { BUILD(strings, TABLE_STRINGS);
bool include_defshell;
struct Vector source;
struct Vector user_source;
};
#define NULL_VEC \
(struct Vector) { \
0, 0, NULL \
}
#define TABLE_BEHAVIOR(F, name) \
F(bool, include_defshell, BOOL, true, name) \
F(struct Vector, source, STRING_ARRAY, NULL_VEC, name) \
F(struct Vector, user_source, STRING_ARRAY, NULL_VEC, name)
BUILD(behavior, TABLE_BEHAVIOR);
//// CONFIG
struct config { struct config {
struct theme theme; struct table_colors colors;
struct functions functions; struct table_chars chars;
struct strings strings; struct table_functions functions;
struct behavior behavior; struct table_strings strings;
struct table_behavior behavior;
}; };
struct config* parse_config(char* path); static const struct config default_config = {
.colors = default_table_colors,
.chars = default_table_chars,
.functions = default_table_functions,
.strings = default_table_strings,
.behavior = default_table_behavior,
};
struct introspection_table {
char* NNULLABLE tname;
size_t offset;
const struct introspection_item* NNULLABLE intros;
size_t len;
};
static const struct introspection_table config_instrospection[] = {
{"colors", offsetof(struct config, colors), intros_table_colors,
sizeof(intros_table_colors) / sizeof(intros_table_colors[0])},
{"chars", offsetof(struct config, chars), intros_table_chars,
sizeof(intros_table_chars) / sizeof(intros_table_chars[0])},
{"functions", offsetof(struct config, functions), intros_table_functions,
sizeof(intros_table_functions) / sizeof(intros_table_functions[0])},
{"strings", offsetof(struct config, strings), intros_table_strings,
sizeof(intros_table_strings) / sizeof(intros_table_strings[0])},
{"behavior", offsetof(struct config, behavior), intros_table_behavior,
sizeof(intros_table_behavior) / sizeof(intros_table_behavior[0])},
};
//// FUNCTIONS
int parse_config(struct config* NNULLABLE config, char* NNULLABLE path);
#endif #endif

View File

@ -20,4 +20,6 @@
#define UNULLABLE #define UNULLABLE
#endif #endif
#define LEN(X) (sizeof(X) / sizeof((X)[0]))
#endif #endif

View File

@ -4,7 +4,7 @@
#include "config.h" #include "config.h"
#include "util.h" #include "util.h"
void setup(struct config); void setup(struct config* config);
int load(struct Vector* users, struct Vector* sessions); int load(struct Vector* users, struct Vector* sessions);
void print_err(const char*); void print_err(const char*);
void print_errno(const char*); void print_errno(const char*);

View File

@ -2,13 +2,14 @@
#define UTILH_ #define UTILH_
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <sys/types.h> #include <sys/types.h>
#include "keys.h" #include "keys.h"
enum keys find_keyname(char*); int find_keyname(enum keys* at, char* name);
enum keys find_ansi(char*); enum keys find_ansi(char*);
void read_press(u_char*, char*); void read_press(u_char*, char*);
@ -18,7 +19,7 @@ struct Vector {
void** pages; void** pages;
}; };
struct Vector vec_new(); extern const struct Vector VEC_NEW;
int vec_resize(struct Vector*, size_t size); int vec_resize(struct Vector*, size_t size);
int vec_reserve(struct Vector*, size_t size); int vec_reserve(struct Vector*, size_t size);
int vec_reserve_exact(struct Vector*, size_t size); int vec_reserve_exact(struct Vector*, size_t size);

View File

@ -94,7 +94,7 @@ void sourceFileTry(char* file) {
} }
void moarEnv(char* user, struct session session, struct passwd* pw, void moarEnv(char* user, struct session session, struct passwd* pw,
struct behavior* behavior) { struct config* config) {
if (chdir(pw->pw_dir) == -1) print_errno("can't chdir to user home"); if (chdir(pw->pw_dir) == -1) print_errno("can't chdir to user home");
setenv("HOME", pw->pw_dir, true); setenv("HOME", pw->pw_dir, true);
@ -113,16 +113,16 @@ void moarEnv(char* user, struct session session, struct passwd* pw,
setenv("XDG_SESSION_TYPE", xdg_session_type, true); setenv("XDG_SESSION_TYPE", xdg_session_type, true);
printf("\n\n\n\n\x1b[1m"); printf("\n\n\n\n\x1b[1m");
for (size_t i = 0; i < behavior->source.length; i++) { for (size_t i = 0; i < config->behavior.source.length; i++) {
/* printf("DEBUG(source)!!!! %d %s\n", i, (char*)vec_get(&behavior->source, /* printf("DEBUG(source)!!!! %d %s\n", i, (char*)vec_get(&behavior->source,
* i)); */ * i)); */
sourceFileTry((char*)vec_get(&behavior->source, i)); sourceFileTry((char*)vec_get(&config->behavior.source, i));
} }
/* printf("\n"); */ /* printf("\n"); */
if (pw->pw_dir) { if (pw->pw_dir) {
uint home_len = strlen(pw->pw_dir); uint home_len = strlen(pw->pw_dir);
for (size_t i = 0; i < behavior->user_source.length; i++) { for (size_t i = 0; i < config->behavior.user_source.length; i++) {
char* file2sourcepath = (char*)vec_get(&behavior->user_source, i); char* file2sourcepath = (char*)vec_get(&config->behavior.user_source, i);
size_t newbuf_len = home_len + strlen(file2sourcepath) + 2; size_t newbuf_len = home_len + strlen(file2sourcepath) + 2;
char* newbuf = malloc(newbuf_len); // nullbyte and slash char* newbuf = malloc(newbuf_len); // nullbyte and slash
if (newbuf == NULL) continue; // can't bother if (newbuf == NULL) continue; // can't bother
@ -150,7 +150,7 @@ void moarEnv(char* user, struct session session, struct passwd* pw,
// NOLINTBEGIN(readability-function-cognitive-complexity) // NOLINTBEGIN(readability-function-cognitive-complexity)
bool launch(char* user, char* passwd, struct session session, void (*cb)(void), bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
struct behavior* behavior) { struct config* config) {
struct passwd* pw = getpwnam(user); struct passwd* pw = getpwnam(user);
if (pw == NULL) { if (pw == NULL) {
print_err("could not get user info"); print_err("could not get user info");
@ -197,7 +197,7 @@ bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
} }
free((void*)envlist); free((void*)envlist);
moarEnv(user, session, pw, behavior); moarEnv(user, session, pw, config);
// TODO: chown stdin to user // TODO: chown stdin to user
// does it inherit stdin from parent and // does it inherit stdin from parent and

View File

@ -1,155 +1,221 @@
#include <errno.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#include "config.h" #include "config.h"
#include "desktop.h"
#include "log.h"
#include "util.h" #include "util.h"
// Alr so ima explain the bitfield returned by `cb` a bit // NOLINTNEXTLINE(modernize-macro-to-enum)
// 4 bits: #define UPPER_HALF_BYTE 4
// 0b0001: break out of parsing (returning true) int parse_hex(char* _at, char x1, char x2) {
// 0b0010: free the value // make linter happy
// 0b0100: free the key u_char* at = (u_char*)_at;
// 0b1000: break out of parsing (returning false)
//
// This would return true if everything goes fine, false otherwise (malloc
// error, broke parsing, etc)
// NOLINTBEGIN(modernize-macro-to-enum)
#define LN_BREAK_OK 0b0001
#define LN_FREE_VALUE 0b0010
#define LN_FREE_KEY 0b0100
#define LN_FREE_KV (LN_FREE_KEY | LN_FREE_VALUE)
#define LN_BREAK_ERR 0b1000
// NOLINTEND(modernize-macro-to-enum)
bool line_parser(FILE* fd, u_char (*cb)(char* key, char* value)) {
bool ok = false;
char* buf = NULL; *at = 0;
size_t alloc_size = 0;
size_t read_size; if ('0' <= x1 && x1 <= '9') {
while ((read_size = getline(&buf, &alloc_size, fd)) != -1) { *at += (x1 - '0') << UPPER_HALF_BYTE;
ok = true; } else if ('A' <= x1 && x1 <= 'F') {
char* key = malloc(read_size); *at += (x1 - 'A' + 10) << UPPER_HALF_BYTE;
if (key == NULL) { } else if ('a' <= x1 && x1 <= 'f') {
ok = false; *at += (x1 - 'a' + 10) << UPPER_HALF_BYTE;
break;
}
char* value = malloc(read_size);
if (value == NULL) {
free(key);
ok = false;
break;
}
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
if (sscanf(buf, "%[^ ] = %[^\n]\n", key, value) == 2) {
u_char ret = cb(key, value);
if (ret & LN_FREE_KEY) free(key);
if (ret & LN_FREE_VALUE) free(value);
if (ret & LN_BREAK_ERR) {
ok = false;
break;
}
if (ret & LN_BREAK_OK) {
break;
}
} else { } else {
free(key); return -1;
free(value);
}
} }
if (buf != NULL) free(buf); if ('0' <= x2 && x2 <= '9') {
return ok; *at += (x2 - '0');
} else if ('A' <= x2 && x2 <= 'F') {
*at += (x2 - 'A' + 10);
} else if ('a' <= x2 && x2 <= 'f') {
*at += (x2 - 'a' + 10);
} else {
return -1;
} }
struct config* g_config; return 0;
// Yanderedev code (wanna fix this with a table or smth)
// NOLINTNEXTLINE(readability-identifier-length,readability-function-cognitive-complexity)
u_char config_line_handler(char* k, char* v) {
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
if (strcmp(k, "colors.bg") == 0)
g_config->theme.colors.bg = v;
else if (strcmp(k, "colors.fg") == 0)
g_config->theme.colors.fg = v;
else if (strcmp(k, "colors.err") == 0)
g_config->theme.colors.err = v;
else if (strcmp(k, "colors.s_wayland") == 0)
g_config->theme.colors.s_wayland = v;
else if (strcmp(k, "colors.s_xorg") == 0)
g_config->theme.colors.s_xorg = v;
else if (strcmp(k, "colors.s_shell") == 0)
g_config->theme.colors.s_shell = v;
else if (strcmp(k, "colors.e_hostname") == 0)
g_config->theme.colors.e_hostname = v;
else if (strcmp(k, "colors.e_date") == 0)
g_config->theme.colors.e_date = v;
else if (strcmp(k, "colors.e_box") == 0)
g_config->theme.colors.e_box = v;
else if (strcmp(k, "colors.e_header") == 0)
g_config->theme.colors.e_header = v;
else if (strcmp(k, "colors.e_user") == 0)
g_config->theme.colors.e_user = v;
else if (strcmp(k, "colors.e_passwd") == 0)
g_config->theme.colors.e_passwd = v;
else if (strcmp(k, "colors.e_badpasswd") == 0)
g_config->theme.colors.e_badpasswd = v;
else if (strcmp(k, "colors.e_key") == 0)
g_config->theme.colors.e_key = v;
else if (strcmp(k, "chars.hb") == 0)
g_config->theme.chars.hb = v;
else if (strcmp(k, "chars.vb") == 0)
g_config->theme.chars.vb = v;
else if (strcmp(k, "chars.ctl") == 0)
g_config->theme.chars.ctl = v;
else if (strcmp(k, "chars.ctr") == 0)
g_config->theme.chars.ctr = v;
else if (strcmp(k, "chars.cbl") == 0)
g_config->theme.chars.cbl = v;
else if (strcmp(k, "chars.cbr") == 0)
g_config->theme.chars.cbr = v;
else if (strcmp(k, "functions.poweroff") == 0) {
g_config->functions.poweroff = find_keyname(v);
return LN_FREE_KV;
} else if (strcmp(k, "functions.reboot") == 0) {
g_config->functions.reboot = find_keyname(v);
return LN_FREE_KV;
} else if (strcmp(k, "functions.refresh") == 0) {
g_config->functions.refresh = find_keyname(v);
return LN_FREE_KV;
} else if (strcmp(k, "strings.f_poweroff") == 0)
g_config->strings.f_poweroff = v;
else if (strcmp(k, "strings.f_reboot") == 0)
g_config->strings.f_reboot = v;
else if (strcmp(k, "strings.f_refresh") == 0)
g_config->strings.f_refresh = v;
else if (strcmp(k, "strings.e_user") == 0)
g_config->strings.e_user = v;
else if (strcmp(k, "strings.e_passwd") == 0)
g_config->strings.e_passwd = v;
else if (strcmp(k, "strings.s_wayland") == 0)
g_config->strings.s_wayland = v;
else if (strcmp(k, "strings.s_xorg") == 0)
g_config->strings.s_xorg = v;
else if (strcmp(k, "strings.s_shell") == 0)
g_config->strings.s_shell = v;
else if (strcmp(k, "behavior.include_defshell") == 0) {
g_config->behavior.include_defshell = strcmp(v, "true") == 0;
return LN_FREE_KV;
} else if (strcmp(k, "behavior.source") == 0)
vec_push(&g_config->behavior.source, v);
else if (strcmp(k, "behavior.user_source") == 0)
vec_push(&g_config->behavior.user_source, v);
else
return LN_BREAK_ERR | LN_FREE_KV;
return LN_FREE_KEY;
} }
struct config* parse_config(char* path) { struct parser_error {
// struct stat sb; const char* NULLABLE msg;
errno = 0; size_t col;
};
#define FAIL(str) \
return (struct parser_error) { \
str, p - raw \
}
#define NOFAIL return (struct parser_error){NULL, 0}
struct parser_error parse_str_inplace(char* raw) {
// reader pointer
char* p = raw; // NOLINT(readability-identifier-length)
if (*p != '"') FAIL("Strign not quoted");
p++;
// writer iterator, by nature will always be under the reader
size_t i = 0; // NOLINT(readability-identifier-length)
while (*p != '\0') {
if (*p == '"') {
if (p[1] == '\0') {
raw[i] = '\0';
NOFAIL;
}
FAIL("String finished but theres content after");
}
if (*p == '\\') {
p++;
if (*p == '\0') break;
switch (*p) {
case '\\':
raw[i++] = '\\';
break;
case 't':
raw[i++] = '\t';
break;
case 'n':
raw[i++] = '\n';
break;
case '\"':
raw[i++] = '\"';
break;
case '\'':
raw[i++] = '\'';
break;
case 'x':
if (p[1] == '\0' || p[2] == '\0') goto unfinished;
if (parse_hex(&raw[i++], p[1], p[2]) != 0)
FAIL("Invalid hex escape sequence");
p += 2;
break;
default:
FAIL("Invalid escape variant");
}
} else
raw[i++] = *p;
p++;
}
unfinished:
FAIL("Unfinished string");
}
#undef FAIL
#undef NOFAIL
#define FAIL(str) return (struct parser_error){str, 0}
#define NOFAIL return (struct parser_error){NULL, 0}
union typ_ptr {
char** string;
bool* boolean;
enum keys* key;
struct Vector* vec;
uintptr_t ptr;
};
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
struct parser_error parse_key(enum introspection_type typ, union typ_ptr at,
char* key, size_t offset) {
char* aux_str = NULL;
struct parser_error aux_err;
switch (typ) {
case STRING:
aux_str = strdup(key);
if (!aux_str) FAIL("allocation failure");
aux_err = parse_str_inplace(aux_str);
if (aux_err.msg != NULL) return aux_err;
// FIXME: it be broken, prob the ptr arithmetic or smth, we mem leak instead 😎
// If the ptr is not the default it means it was prev allocated, we should
// free
// if (*(char**)((uintptr_t)(&default_config) + offset) != *at.string) {
// free(*at.string);
// }
*at.string = aux_str;
break;
case BOOL:
if (strcmp(key, "true") == 0)
*at.boolean = true;
else if (strcmp(key, "false") == 0)
*at.boolean = false;
else {
FAIL("Invalid key value, wasn't 'true' nor 'false'");
}
break;
case KEY:
// NOLINTNEXTLINE(performance-no-int-to-ptr)
if (find_keyname(at.key, key) != 0) {
FAIL("Keyboard KEY name not found");
}
break;
case STRING_ARRAY:
aux_str = strdup(key);
if (!aux_str) FAIL("allocation failure");
aux_err = parse_str_inplace(aux_str);
if (aux_err.msg != NULL) return aux_err;
vec_push(at.vec, aux_str);
break;
}
NOFAIL;
}
#undef FAIL
#undef NOFAIL
// NOLINTBEGIN(readability-identifier-length,readability-function-cognitive-complexity,readability-identifier-length)
struct status config_line_handler(void* _config, char* table, char* k,
char* v) {
struct config* config = (struct config*)_config;
struct status ret = {.finish = false};
const struct introspection_table* this_intros_table = NULL;
for (size_t i = 0; i < LEN(config_instrospection); i++) {
if (table == NULL) {
if (table != config_instrospection[i].tname) continue;
} else if (strcmp(config_instrospection[i].tname, table) != 0)
continue;
this_intros_table = &config_instrospection[i];
break;
}
if (this_intros_table == NULL) {
log_printf("[E] table '%s' is not part of the config\n", table);
return ret;
}
const struct introspection_item* this_intros_key = NULL;
for (size_t i = 0; i < this_intros_table->len; i++) {
if (strcmp(this_intros_table->intros[i].name, k) != 0) continue;
this_intros_key = &this_intros_table->intros[i];
break;
}
if (this_intros_key == NULL) {
log_printf("[E] key '%s' is not part of the table '%s' in config\n", k,
table);
return ret;
}
size_t offset = this_intros_table->offset + this_intros_key->offset;
union typ_ptr k_addr = {.ptr = (uintptr_t)config + offset};
struct parser_error err = parse_key(this_intros_key->typ, k_addr, v, offset);
if (err.msg != NULL) {
log_printf("[E] cfg parser, failed to parse [%s.%s] (%s): %s\n", table, k,
intros_tys_names[this_intros_key->typ], err.msg);
log_printf("%s\n%*c^\n", v, err.col);
return ret;
}
log_printf("[I] cfg parsed [%s.%s]\n", table, k);
return ret;
}
// NOLINTEND(readability-identifier-length,readability-function-cognitive-complexity,readability-identifier-length)
int parse_config(struct config* NNULLABLE config, char* NNULLABLE path) {
FILE* fd = fopen(path, "r"); FILE* fd = fopen(path, "r");
if (fd == NULL) { if (fd == NULL) {
perror("fopen"); perror("fopen");
@ -157,23 +223,14 @@ struct config* parse_config(char* path) {
"Please place a config file at /etc/lidm.ini or set the LIDM_CONF " "Please place a config file at /etc/lidm.ini or set the LIDM_CONF "
"env variable", "env variable",
stderr); stderr);
return NULL; return -1;
} }
// if(stat(path, &sb) != 0) {
// perror("stat");
// }
g_config = malloc(sizeof(struct config)); bool ret = read_desktop(fd, config, config_line_handler);
g_config->behavior.source = vec_new();
g_config->behavior.user_source = vec_new();
if (g_config == NULL) return NULL;
bool ret = line_parser(fd, config_line_handler);
(void)fclose(fd); (void)fclose(fd);
if (!ret) { if (!ret) {
free(g_config); return -1;
return NULL;
} }
return g_config; return 0;
} }

View File

@ -5,7 +5,20 @@
#include <string.h> #include <string.h>
#include "desktop.h" #include "desktop.h"
#include "macros.h"
char* trim_str(char* str) {
while (*str == ' ' || *str == '\t')
str++;
size_t i = strlen(str);
while (i > 0) {
if (str[i - 1] != ' ' && str[i - 1] != '\t' && str[i - 1] != '\n') break;
i--;
}
str[i] = '\0';
return str;
}
int read_desktop(FILE* fd, void* ctx, int read_desktop(FILE* fd, void* ctx,
struct status (*cb)(void* ctx, char* table, char* key, struct status (*cb)(void* ctx, char* table, char* key,
@ -19,34 +32,42 @@ int read_desktop(FILE* fd, void* ctx,
while ((read_size = getline(&buf, &alloc_size, fd)) > 0) { while ((read_size = getline(&buf, &alloc_size, fd)) > 0) {
ret = 0; ret = 0;
if (read_size <= 1) continue; char* buf_start = trim_str(buf);
size_t indent_size = buf_start - buf;
if (buf[0] == '[' && buf[read_size - 2] == ']') { if (read_size - indent_size < 1) continue;
if (*buf_start == '#') continue;
if (*buf_start == '[' && buf_start[strlen(buf_start) - 1] == ']') {
if (table_name != NULL) free(table_name); if (table_name != NULL) free(table_name);
table_name = realloc(buf, read_size); buf_start[strlen(buf_start) - 1] = '\0';
table_name[read_size - 1] = '\0'; // newline table_name = strdup(buf_start + 1);
buf = NULL; if (table_name == NULL) {
alloc_size = 0; ret = -1;
break;
}
} else { } else {
// Find '=' // Find '='
size_t eq_idx = 0; size_t eq_idx = 0;
while (buf[eq_idx] != '\0') { while (buf_start[eq_idx] != '\0') {
if (buf[eq_idx] == '=') break; if (buf_start[eq_idx] == '=') break;
eq_idx++; eq_idx++;
} }
// impossible with a min len of 1 (empty line) // impossible with a min len of 1 (empty line)
if (eq_idx == 0) continue; if (eq_idx == 0) continue;
// Check its not end // Check its not end
if (buf[eq_idx] != '=') { if (buf_start[eq_idx] != '=') {
ret = -1; ret = -1;
break; break;
} }
// Key & Value // Key & Value
char* key = buf; char* key = buf_start;
buf[eq_idx] = '\0'; // the equal buf_start[eq_idx] = '\0'; // the equal
char* value = &buf[eq_idx + 1]; key = trim_str(key);
buf[read_size - 1] = '\0'; // the newline char* value = &buf_start[eq_idx + 1];
buf_start[read_size - 1] = '\0'; // the newline
value = trim_str(value);
// Callback // Callback
struct status cb_ret = cb(ctx, table_name, key, value); struct status cb_ret = cb(ctx, table_name, key, value);

View File

@ -14,6 +14,7 @@
#include "util.h" #include "util.h"
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
// Logger
char* log_output = getenv("LIDM_LOG"); char* log_output = getenv("LIDM_LOG");
if (log_output) { if (log_output) {
FILE* log_fd = fopen(log_output, "w"); FILE* log_fd = fopen(log_output, "w");
@ -26,17 +27,17 @@ int main(int argc, char* argv[]) {
log_init(log_fd); log_init(log_fd);
} }
// Chvt
if (argc == 2) chvt_str(argv[1]); if (argc == 2) chvt_str(argv[1]);
struct config config = default_config;
char* conf_override = getenv("LIDM_CONF"); char* conf_override = getenv("LIDM_CONF");
struct config* config = char* conf_path = conf_override ? conf_override : "/etc/lidm.ini";
parse_config(conf_override == NULL ? "/etc/lidm.ini" : conf_override); if (parse_config(&config, conf_path) != 0) {
if (config == NULL) {
// NOLINT(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
(void)fputs("error parsing config\n", stderr); (void)fputs("error parsing config\n", stderr);
return 1; return 1;
} }
setup(*config); setup(&config);
struct Vector users = get_human_users(); struct Vector users = get_human_users();
struct Vector sessions = get_avaliable_sessions(); struct Vector sessions = get_avaliable_sessions();

View File

@ -35,7 +35,7 @@ struct status cb(void* _ctx, char* NULLABLE table, char* key, char* value) {
ret.finish = false; ret.finish = false;
if (table == NULL) return ret; if (table == NULL) return ret;
if (strcmp(table, "[Desktop Entry]") != 0) return ret; if (strcmp(table, "Desktop Entry") != 0) return ret;
char** NULLABLE copy_at = NULL; char** NULLABLE copy_at = NULL;
if (strcmp(key, "Name") == 0) { if (strcmp(key, "Name") == 0) {
@ -117,7 +117,7 @@ static int fn(const char* fpath, const struct stat* sb, int typeflag) {
// This code is designed to be run purely single threaded // This code is designed to be run purely single threaded
#define LIKELY_BOUND_SESSIONS 8 #define LIKELY_BOUND_SESSIONS 8
struct Vector get_avaliable_sessions() { struct Vector get_avaliable_sessions() {
struct Vector sessions = vec_new(); struct Vector sessions = VEC_NEW;
vec_reserve(&sessions, LIKELY_BOUND_SESSIONS); vec_reserve(&sessions, LIKELY_BOUND_SESSIONS);
cb_sessions = &sessions; cb_sessions = &sessions;

113
src/ui.c
View File

@ -49,11 +49,9 @@ static struct termios orig_term;
static struct termios term; static struct termios term;
static struct winsize window; static struct winsize window;
static struct theme theme; struct config* g_config = NULL;
static struct functions functions; void setup(struct config* config) {
static struct strings strings; g_config = config;
static struct behavior behavior;
void setup(struct config __config) {
ioctl(STDOUT_FILENO, TIOCGWINSZ, &window); ioctl(STDOUT_FILENO, TIOCGWINSZ, &window);
// 2 padding top and bottom for footer and vertical compensation // 2 padding top and bottom for footer and vertical compensation
@ -63,11 +61,6 @@ void setup(struct config __config) {
exit(1); exit(1);
} }
theme = __config.theme;
functions = __config.functions;
strings = __config.strings;
behavior = __config.behavior;
tcgetattr(STDOUT_FILENO, &orig_term); tcgetattr(STDOUT_FILENO, &orig_term);
term = orig_term; // save term term = orig_term; // save term
// "stty" attrs // "stty" attrs
@ -76,7 +69,8 @@ void setup(struct config __config) {
// save cursor pos, save screen, set color and reset screen // save cursor pos, save screen, set color and reset screen
// (applying color to all screen) // (applying color to all screen)
printf("\x1b[s\x1b[?47h\x1b[%s;%sm\x1b[2J", theme.colors.bg, theme.colors.fg); printf("\x1b[s\x1b[?47h\x1b[%s;%sm\x1b[2J", g_config->colors.bg,
g_config->colors.fg);
print_footer(); print_footer();
atexit(restore_all); atexit(restore_all);
@ -227,7 +221,7 @@ struct session get_current_session() {
if (of_session.current_opt != 0) { if (of_session.current_opt != 0) {
// this is for the default user shell :P, not the greatest // this is for the default user shell :P, not the greatest
// implementation but I want to get his done // implementation but I want to get his done
if (behavior.include_defshell && if (g_config->behavior.include_defshell &&
of_session.current_opt == gsessions->length + 1) { of_session.current_opt == gsessions->length + 1) {
struct session shell_session; struct session shell_session;
shell_session.type = SHELL; shell_session.type = SHELL;
@ -333,7 +327,8 @@ int load(struct Vector* users, struct Vector* sessions) {
hostname[15] = '\0'; hostname[15] = '\0';
} }
of_session = ofield_new(sessions->length + behavior.include_defshell); of_session =
ofield_new(sessions->length + g_config->behavior.include_defshell);
of_user = ofield_new(users->length); of_user = ofield_new(users->length);
of_passwd = ofield_new(0); of_passwd = ofield_new(0);
@ -345,15 +340,15 @@ int load(struct Vector* users, struct Vector* sessions) {
// put hostname // put hostname
printf("\x1b[%d;%dH\x1b[%sm%s\x1b[%sm", boxstart.y + 2, printf("\x1b[%d;%dH\x1b[%sm%s\x1b[%sm", boxstart.y + 2,
boxstart.x + 12 - (uint)strlen(hostname), theme.colors.e_hostname, boxstart.x + 12 - (uint)strlen(hostname), g_config->colors.e_hostname,
hostname, theme.colors.fg); hostname, g_config->colors.fg);
if (hostname != unknown_str) free(hostname); if (hostname != unknown_str) free(hostname);
// put date // put date
char* fmtd_time = fmt_time(); char* fmtd_time = fmt_time();
printf("\x1b[%d;%dH\x1b[%sm%s\x1b[%sm", boxstart.y + 2, printf("\x1b[%d;%dH\x1b[%sm%s\x1b[%sm", boxstart.y + 2,
boxstart.x + boxw - 3 - (uint)strlen(fmtd_time), theme.colors.e_date, boxstart.x + boxw - 3 - (uint)strlen(fmtd_time),
fmtd_time, theme.colors.fg); g_config->colors.e_date, fmtd_time, g_config->colors.fg);
free(fmtd_time); free(fmtd_time);
print_field(SESSION); print_field(SESSION);
@ -372,14 +367,14 @@ int load(struct Vector* users, struct Vector* sessions) {
if (ansi_code != -1) { if (ansi_code != -1) {
if (ansi_code == ESC) { if (ansi_code == ESC) {
esc = 2; esc = 2;
} else if (ansi_code == functions.refresh) { } else if (ansi_code == g_config->functions.refresh) {
restore_all(); restore_all();
return 0; return 0;
} else if (ansi_code == functions.reboot) { } else if (ansi_code == g_config->functions.reboot) {
restore_all(); restore_all();
reboot(RB_AUTOBOOT); reboot(RB_AUTOBOOT);
exit(0); exit(0);
} else if (ansi_code == functions.poweroff) { } else if (ansi_code == g_config->functions.poweroff) {
restore_all(); restore_all();
reboot(RB_POWER_OFF); reboot(RB_POWER_OFF);
exit(0); exit(0);
@ -395,7 +390,7 @@ int load(struct Vector* users, struct Vector* sessions) {
} else { } else {
if (len == 1 && *seq == '\n') { if (len == 1 && *seq == '\n') {
if (!launch(get_current_user().username, of_passwd.efield.content, if (!launch(get_current_user().username, of_passwd.efield.content,
get_current_session(), &restore_all, &behavior)) { get_current_session(), &restore_all, g_config)) {
print_passwd(box_start(), of_passwd.efield.length, true); print_passwd(box_start(), of_passwd.efield.length, true);
ffield_cursor_focus(); ffield_cursor_focus();
} }
@ -424,31 +419,31 @@ static void print_session(struct uint_point origin, struct session session,
clean_line(origin, 5); clean_line(origin, 5);
const char* session_type; const char* session_type;
if (session.type == XORG) { if (session.type == XORG) {
session_type = strings.s_xorg; session_type = g_config->strings.s_xorg;
} else if (session.type == WAYLAND) { } else if (session.type == WAYLAND) {
session_type = strings.s_wayland; session_type = g_config->strings.s_wayland;
} else { } else {
session_type = strings.s_shell; session_type = g_config->strings.s_shell;
} }
printf("\r\x1b[%luC\x1b[%sm%s\x1b[%sm", printf("\r\x1b[%luC\x1b[%sm%s\x1b[%sm",
(ulong)(origin.x + 11 - strlen(session_type)), theme.colors.e_header, (ulong)(origin.x + 11 - strlen(session_type)),
session_type, theme.colors.fg); g_config->colors.e_header, session_type, g_config->colors.fg);
char* session_color; char* session_color;
if (session.type == XORG) { if (session.type == XORG) {
session_color = theme.colors.s_xorg; session_color = g_config->colors.s_xorg;
} else if (session.type == WAYLAND) { } else if (session.type == WAYLAND) {
session_color = theme.colors.s_wayland; session_color = g_config->colors.s_wayland;
} else { } else {
session_color = theme.colors.s_shell; session_color = g_config->colors.s_shell;
} }
if (multiple) { if (multiple) {
printf("\r\x1b[%dC< \x1b[%sm%s\x1b[%sm >", origin.x + 14, session_color, printf("\r\x1b[%dC< \x1b[%sm%s\x1b[%sm >", origin.x + 14, session_color,
session.name, theme.colors.fg); session.name, g_config->colors.fg);
} else { } else {
printf("\r\x1b[%dC\x1b[%sm%s\x1b[%sm", origin.x + 14, session_color, printf("\r\x1b[%dC\x1b[%sm%s\x1b[%sm", origin.x + 14, session_color,
session.name, theme.colors.fg); session.name, g_config->colors.fg);
} }
} }
@ -457,17 +452,18 @@ static void print_user(struct uint_point origin, struct user user,
bool multiple) { bool multiple) {
clean_line(origin, 7); clean_line(origin, 7);
printf("\r\x1b[%luC\x1b[%sm%s\x1b[%sm", printf("\r\x1b[%luC\x1b[%sm%s\x1b[%sm",
(ulong)(origin.x + 11 - strlen(strings.e_user)), theme.colors.e_header, (ulong)(origin.x + 11 - strlen(g_config->strings.e_user)),
strings.e_user, theme.colors.fg); g_config->colors.e_header, g_config->strings.e_user,
g_config->colors.fg);
char* user_color = theme.colors.e_user; char* user_color = g_config->colors.e_user;
if (multiple) { if (multiple) {
printf("\r\x1b[%dC< \x1b[%sm%s\x1b[%sm >", origin.x + 14, user_color, printf("\r\x1b[%dC< \x1b[%sm%s\x1b[%sm >", origin.x + 14, user_color,
user.display_name, theme.colors.fg); user.display_name, g_config->colors.fg);
} else { } else {
printf("\r\x1b[%dC\x1b[%sm%s\x1b[%sm", origin.x + 14, user_color, printf("\r\x1b[%dC\x1b[%sm%s\x1b[%sm", origin.x + 14, user_color,
user.display_name, theme.colors.fg); user.display_name, g_config->colors.fg);
} }
} }
@ -476,14 +472,15 @@ static char passwd_prompt[33];
static void print_passwd(struct uint_point origin, uint length, bool err) { static void print_passwd(struct uint_point origin, uint length, bool err) {
clean_line(origin, 9); clean_line(origin, 9);
printf("\r\x1b[%luC\x1b[%sm%s\x1b[%sm", printf("\r\x1b[%luC\x1b[%sm%s\x1b[%sm",
(ulong)(origin.x + 11 - strlen(strings.e_passwd)), (ulong)(origin.x + 11 - strlen(g_config->strings.e_passwd)),
theme.colors.e_header, strings.e_passwd, theme.colors.fg); g_config->colors.e_header, g_config->strings.e_passwd,
g_config->colors.fg);
char* pass_color; char* pass_color;
if (err) if (err)
pass_color = theme.colors.e_badpasswd; pass_color = g_config->colors.e_badpasswd;
else else
pass_color = theme.colors.e_passwd; pass_color = g_config->colors.e_passwd;
ulong prompt_len = sizeof(passwd_prompt); ulong prompt_len = sizeof(passwd_prompt);
ulong actual_len = length > prompt_len ? prompt_len : length; ulong actual_len = length > prompt_len ? prompt_len : length;
@ -494,7 +491,7 @@ static void print_passwd(struct uint_point origin, uint length, bool err) {
printf("\r\x1b[%dC\x1b[%sm", origin.x + 14, pass_color); printf("\r\x1b[%dC\x1b[%sm", origin.x + 14, pass_color);
printf("%s", passwd_prompt); printf("%s", passwd_prompt);
printf("\x1b[%sm", theme.colors.fg); printf("\x1b[%sm", g_config->colors.fg);
} }
static void print_empty_row(uint w, uint n, char* edge1, char* edge2) { static void print_empty_row(uint w, uint n, char* edge1, char* edge2) {
@ -517,30 +514,34 @@ static void print_box() {
// TODO: check min sizes // TODO: check min sizes
const struct uint_point bstart = box_start(); const struct uint_point bstart = box_start();
printf("\x1b[%d;%dH\x1b[%sm", bstart.y, bstart.x, theme.colors.e_box); printf("\x1b[%d;%dH\x1b[%sm", bstart.y, bstart.x, g_config->colors.e_box);
fflush(stdout); fflush(stdout);
print_row(boxw - 2, 1, theme.chars.ctl, theme.chars.ctr, theme.chars.hb); print_row(boxw - 2, 1, g_config->chars.ctl, g_config->chars.ctr,
print_empty_row(boxw - 2, boxh - 2, theme.chars.vb, theme.chars.vb); g_config->chars.hb);
print_row(boxw - 2, 1, theme.chars.cbl, theme.chars.cbr, theme.chars.hb); print_empty_row(boxw - 2, boxh - 2, g_config->chars.vb, g_config->chars.vb);
printf("\x1b[%sm", theme.colors.fg); print_row(boxw - 2, 1, g_config->chars.cbl, g_config->chars.cbr,
g_config->chars.hb);
printf("\x1b[%sm", g_config->colors.fg);
fflush(stdout); fflush(stdout);
} }
static void print_footer() { static void print_footer() {
size_t bsize = snprintf(NULL, 0, "%s %s %s %s %s %s", strings.f_poweroff, size_t bsize = snprintf(
key_names[functions.poweroff], strings.f_reboot, NULL, 0, "%s %s %s %s %s %s", g_config->strings.f_poweroff,
key_names[functions.reboot], strings.f_refresh, key_names[g_config->functions.poweroff], g_config->strings.f_reboot,
key_names[functions.refresh]); key_names[g_config->functions.reboot], g_config->strings.f_refresh,
key_names[g_config->functions.refresh]);
uint row = window.ws_row - 1; uint row = window.ws_row - 1;
uint col = window.ws_col - 2 - bsize; uint col = window.ws_col - 2 - bsize;
printf( printf(
"\x1b[%3$d;%4$dH%8$s \x1b[%1$sm%5$s\x1b[%2$sm %9$s " "\x1b[%3$d;%4$dH%8$s \x1b[%1$sm%5$s\x1b[%2$sm %9$s "
"\x1b[%1$sm%6$s\x1b[%2$sm %10$s \x1b[%1$sm%7$s\x1b[%2$sm", "\x1b[%1$sm%6$s\x1b[%2$sm %10$s \x1b[%1$sm%7$s\x1b[%2$sm",
theme.colors.e_key, theme.colors.fg, row, col, g_config->colors.e_key, g_config->colors.fg, row, col,
key_names[functions.poweroff], key_names[functions.reboot], key_names[g_config->functions.poweroff],
key_names[functions.refresh], strings.f_poweroff, strings.f_reboot, key_names[g_config->functions.reboot],
strings.f_refresh); key_names[g_config->functions.refresh], g_config->strings.f_poweroff,
g_config->strings.f_reboot, g_config->strings.f_refresh);
fflush(stdout); fflush(stdout);
} }
@ -554,10 +555,10 @@ void print_errno(const char* descr) {
struct uint_point origin = box_start(); struct uint_point origin = box_start();
if (descr == NULL) if (descr == NULL)
fprintf(stderr, "\x1b[%d;%dH\x1b[%smunknown error(%d): %s", origin.y - 1, fprintf(stderr, "\x1b[%d;%dH\x1b[%smunknown error(%d): %s", origin.y - 1,
origin.x, theme.colors.err, errno, strerror(errno)); origin.x, g_config->colors.err, errno, strerror(errno));
else { else {
fprintf(stderr, "\x1b[%d;%dH\x1b[%sm%s(%d): %s", origin.y - 1, origin.x, fprintf(stderr, "\x1b[%d;%dH\x1b[%sm%s(%d): %s", origin.y - 1, origin.x,
theme.colors.err, descr, errno, strerror(errno)); g_config->colors.err, descr, errno, strerror(errno));
} }
} }

View File

@ -42,7 +42,7 @@ int build_user(struct user* NNULLABLE user, struct passwd* p) {
// NOLINTNEXTLINE(modernize-macro-to-enum) // NOLINTNEXTLINE(modernize-macro-to-enum)
#define LIKELY_BOUND_USERS 4 #define LIKELY_BOUND_USERS 4
struct Vector get_human_users() { struct Vector get_human_users() {
struct Vector users = vec_new(); struct Vector users = VEC_NEW;
vec_reserve(&users, LIKELY_BOUND_USERS); vec_reserve(&users, LIKELY_BOUND_USERS);
struct passwd* NULLABLE pwd; struct passwd* NULLABLE pwd;

View File

@ -11,13 +11,15 @@
static int selret_magic(); static int selret_magic();
enum keys find_keyname(char* name) { int find_keyname(enum keys* at, char* name) {
for (size_t i = 0; i < sizeof(key_mappings) / sizeof(key_mappings[0]); i++) { for (size_t i = 0; i < sizeof(key_mappings) / sizeof(key_mappings[0]); i++) {
if (strcmp(key_names[i], name) == 0) return (enum keys)i; if (strcmp(key_names[i], name) == 0) {
*at = (enum keys)i;
return 0;
}
} }
perror("key not found"); return -1;
exit(1);
} }
enum keys find_ansi(char* seq) { enum keys find_ansi(char* seq) {
@ -63,11 +65,11 @@ static int selret_magic() {
} }
// Vector shii // Vector shii
struct Vector vec_new() { const struct Vector VEC_NEW = {
struct Vector vec; .length = 0,
vec_reset(&vec); .capacity = 0,
return vec; .pages = NULL,
} };
int vec_resize(struct Vector* vec, size_t size) { int vec_resize(struct Vector* vec, size_t size) {
void** new_location = realloc(vec->pages, size * sizeof(void*)); void** new_location = realloc(vec->pages, size * sizeof(void*));
@ -120,9 +122,11 @@ void vec_clear(struct Vector* vec) {
} }
void vec_reset(struct Vector* vec) { void vec_reset(struct Vector* vec) {
vec->length = 0; *vec = (struct Vector){
vec->capacity = 0; .length = 0,
vec->pages = NULL; .capacity = 0,
.pages = NULL,
};
} }
void* vec_pop(struct Vector* vec) { void* vec_pop(struct Vector* vec) {