From 22c3f7c8962d0b80506e84bec9eda554d09d7106 Mon Sep 17 00:00:00 2001 From: javalsai Date: Tue, 10 Jun 2025 23:44:09 +0200 Subject: [PATCH] feat|perf: generic&efficient desktop&config parser --- Makefile | 4 +- include/desktop.h | 17 ++++++ include/macros.h | 12 ++++ include/sessions.h | 7 ++- src/auth.c | 6 +- src/chvt.c | 3 +- src/desktop.c | 74 +++++++++++++++++++++++++ src/efield.c | 4 +- src/sessions.c | 135 ++++++++++++++++++++------------------------- src/ui.c | 4 +- 10 files changed, 178 insertions(+), 88 deletions(-) create mode 100644 include/desktop.h create mode 100644 include/macros.h create mode 100644 src/desktop.c diff --git a/Makefile b/Makefile index 379e9be..24408f3 100644 --- a/Makefile +++ b/Makefile @@ -12,10 +12,10 @@ ALLFLAGS=$(CFLAGS) -I$(IDIR) LIBS=-lpam -_DEPS = util.h ui.h config.h auth.h efield.h keys.h users.h sessions.h chvt.h +_DEPS = util.h ui.h config.h desktop.h auth.h efield.h keys.h users.h sessions.h chvt.h macros.h DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS)) -_OBJ = main.o util.o ui.o config.o auth.o efield.o users.o sessions.o chvt.o +_OBJ = main.o util.o ui.o config.o desktop.o auth.o efield.o users.o sessions.o chvt.o OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ)) $(ODIR)/%.o: $(CDIR)/%.c $(DEPS) diff --git a/include/desktop.h b/include/desktop.h new file mode 100644 index 0000000..8586b25 --- /dev/null +++ b/include/desktop.h @@ -0,0 +1,17 @@ +#ifndef DESKTOPH_ +#define DESKTOPH_ + +#include +#include + +#include "macros.h" + +struct status { + bool finish; + int ret; +}; + +int read_desktop(FILE* fd, void* ctx, struct status (*cb)(void* ctx, char* NULLABLE table, + char* key, char* value)); + +#endif diff --git a/include/macros.h b/include/macros.h new file mode 100644 index 0000000..35aba1f --- /dev/null +++ b/include/macros.h @@ -0,0 +1,12 @@ +// Do we just replace the compiler with clang?? +#if defined(__clang__) + #define NULLABLE _Nullable +#else + #define NULLABLE +#endif + +#if defined(__clang__) + #define NNULLABLE _Nonnull +#else + #define NNULLABLE +#endif diff --git a/include/sessions.h b/include/sessions.h index f0cc1bf..d10eaee 100644 --- a/include/sessions.h +++ b/include/sessions.h @@ -3,6 +3,7 @@ #include +#include "macros.h" #include "util.h" enum session_type { @@ -12,9 +13,9 @@ enum session_type { }; struct session { - char* name; - char* exec; - char* tryexec; + char* NNULLABLE name; + char* NNULLABLE exec; + char* NULLABLE tryexec; enum session_type type; }; diff --git a/src/auth.c b/src/auth.c index ba29bf2..3c1e9b0 100644 --- a/src/auth.c +++ b/src/auth.c @@ -132,7 +132,8 @@ void moarEnv(char* user, if (newbuf == NULL) continue; // can't bother strlcpy(newbuf, pw->pw_dir, newbuf_len); newbuf[home_len] = '/'; // assume pw_dir doesn't start with '/' :P - strlcpy(&newbuf[home_len + 1], file2sourcepath, newbuf_len - home_len - 1); + strlcpy(&newbuf[home_len + 1], file2sourcepath, + newbuf_len - home_len - 1); /* printf("DEBUG(user_source)!!!! %d %s\n", i, newbuf); */ sourceFileTry(newbuf); @@ -250,8 +251,7 @@ bool launch(char* user, pam_close_session(pamh, 0); pam_end(pamh, PAM_SUCCESS); - if (*reach_session == false) - return false; + if (*reach_session == false) return false; exit(0); } diff --git a/src/chvt.c b/src/chvt.c index 5d61460..513f026 100644 --- a/src/chvt.c +++ b/src/chvt.c @@ -41,6 +41,7 @@ int chvt(int n) { close(fd); } - (void)fputs("Couldn't get a file descriptor referring to the console.\n", stderr); + (void)fputs("Couldn't get a file descriptor referring to the console.\n", + stderr); return -1; } diff --git a/src/desktop.c b/src/desktop.c new file mode 100644 index 0000000..431b566 --- /dev/null +++ b/src/desktop.c @@ -0,0 +1,74 @@ +// NOLINTBEGIN(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,readability-function-cognitive-complexity) +#include +#include +#include +#include + +#include "desktop.h" +#include "macros.h" + +#define NOK \ + { \ + ret = -1; \ + break; \ + } +#define NOKFKEY \ + { \ + free(key); \ + NOK \ + } +int read_desktop(FILE* fd, void* ctx, + struct status (*cb)(void* ctx, char* NULLABLE table, char* key, + char* value)) { + char* table_name = NULL; + + bool ret = -1; + char* buf = NULL; + size_t alloc_size = 0; + size_t read_size; + while ((read_size = getline(&buf, &alloc_size, fd)) > 0) { + ret = 0; + + if (read_size <= 1) continue; + + if (buf[0] == '[' && buf[read_size - 2] == ']') { + if (table_name != NULL) free(table_name); + table_name = realloc(buf, read_size); + table_name[read_size - 1] = '\0'; // newline + buf = NULL; + alloc_size = 0; + } else { + // Find '=' + size_t eq_idx = 0; + while (buf[eq_idx] != '\0') { + if (buf[eq_idx] == '=') break; + eq_idx++; + } + // impossible with a min len of 1 (empty line) + if (eq_idx == 0) continue; + // Check its not end + if (buf[eq_idx] != '=') NOK; + + // Key & Value + char* key = buf; + buf[eq_idx] = '\0'; // the equal + char* value = &buf[eq_idx+1]; + buf[read_size-1] = '\0'; // the newline + + // Callback + struct status cb_ret = cb(ctx, table_name, key, value); + if (cb_ret.finish) { + ret = cb_ret.ret; + break; + } + } + } + + free(table_name); + + if (buf != NULL) free(buf); + return ret; +} +#undef NOK +#undef NOKFKEY +// NOLINTEND(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,readability-function-cognitive-complexity) diff --git a/src/efield.c b/src/efield.c index fb6ef9f..858db72 100644 --- a/src/efield.c +++ b/src/efield.c @@ -50,8 +50,8 @@ void field_update(struct editable_field* self, char* update) { } if (self->pos < self->length) { // move with immediate buffer - memmove(&self->content[self->pos + insert_len], - &self->content[self->pos], self->length - self->pos); + memmove(&self->content[self->pos + insert_len], &self->content[self->pos], + self->length - self->pos); } memcpy(&self->content[self->pos], update, insert_len); diff --git a/src/sessions.c b/src/sessions.c index ab3aa51..e96b5e9 100644 --- a/src/sessions.c +++ b/src/sessions.c @@ -7,6 +7,7 @@ #include #include +#include "desktop.h" #include "sessions.h" #include "util.h" @@ -19,105 +20,89 @@ static const struct source_dir sources[] = { {WAYLAND, "/usr/share/wayland-sessions"}, }; -static struct session new_session(enum session_type type, - char* name, - const char* exec, - const char* tryexec) { - struct session session; - session.type = type; - strcln(&session.name, name); - strcln(&session.exec, exec); - strcln(&session.tryexec, tryexec); +// static struct session new_session(enum session_type type, char* name, +// const char* exec, const char* tryexec) { +// struct session session; +// session.type = type; +// strcln(&session.name, name); +// strcln(&session.exec, exec); +// strcln(&session.tryexec, tryexec); - return session; -} +// return session; +// } static struct Vector* cb_sessions = NULL; -// NOTE: commented printf's here would be nice to have debug logs if I ever -// implement it -#define LN_NAME 0b0001 -#define LN_EXEC 0b0010 -#define LN_TEXEC 0b0100 -#define LN_ALL (LN_NAME | LN_EXEC | LN_TEXEC) +// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) +struct status cb(void* _ctx, char* NULLABLE table, char* key, char* value) { + struct session* ctx = (struct session*)_ctx; + struct status ret; + ret.finish = false; + + if (table == NULL) return ret; + if (strcmp(table, "[Desktop Entry]") != 0) return ret; + + char** NULLABLE copy_at = NULL; + if (strcmp(key, "Name") == 0) { + if (ctx->name == NULL) copy_at = &ctx->name; + } else if (strcmp(key, "Exec") == 0) { + if (ctx->exec == NULL) copy_at = &ctx->exec; + } else if (strcmp(key, "TryExec") == 0) { + if (ctx->tryexec == NULL) copy_at = &ctx->tryexec; + } + + if (copy_at != NULL) { + *copy_at = malloc((strlen(value) + 1) * sizeof(char)); + if (*copy_at == NULL) { + ret.finish = true; + ret.ret = -1; // malloc error + } + + strcpy(*copy_at, value); + } + + if (ctx->name != NULL && ctx->exec != NULL && ctx->tryexec != NULL) { + ret.finish = true; + ret.ret = 0; + } + + return ret; +} + static enum session_type session_type; // NOLINTNEXTLINE(readability-function-cognitive-complexity) static int fn(const char* fpath, const struct stat* sb, int typeflag) { if (!S_ISREG(sb->st_mode)) return 0; - // printf("gonna open %s\n", fpath); + struct session* ctx = malloc(sizeof(struct session)); + if (ctx == NULL) return 0; + ctx->name = NULL; + ctx->exec = NULL; + ctx->tryexec = NULL; + FILE* fd = fopen(fpath, "r"); if (fd == NULL) { + free(ctx); perror("fopen"); // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) (void)fprintf(stderr, "error opening file (r) '%s'\n", fpath); return 0; } - u_char found = 0; - - char* name_buf = NULL; - char* exec_buf = NULL; - char* tryexec_buf = NULL; - // This should be made a specific function - // Emm, if anything goes wrong just free the inner loop and `break;` fd and - // the rest is handled after - char* buf = NULL; - size_t alloc_size = 0; - size_t read_size; - while ((read_size = getline(&buf, &alloc_size, fd)) != -1) { - char* key = malloc(read_size + sizeof(char)); - if (key == NULL) { - free(buf); - break; - } - char* value = malloc(read_size + sizeof(char)); - if (value == NULL) { - free(buf); - free(key); - break; - } - value[0] = '\0'; // I'm not sure if sscanf would null this string out - // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) - if (sscanf(buf, "%[^=]=%[^\n]\n", key, value) == 2) { - if (strcmp(key, "Name") == 0) { - found &= LN_EXEC; - if(name_buf != NULL) free(name_buf); - name_buf = realloc(value, strlen(value) + sizeof(char)); - } else if (strcmp(key, "Exec") == 0) { - found &= LN_EXEC; - if(exec_buf != NULL) free(exec_buf); - exec_buf = realloc(value, strlen(value) + sizeof(char)); - } else if (strcmp(key, "TryExec") == 0) { - found &= LN_TEXEC; - if(tryexec_buf != NULL) free(tryexec_buf); - tryexec_buf = realloc(value, strlen(value) + sizeof(char)); - } else { - free(value); - } - } else { - free(value); - } - free(key); - // if (found == LN_ALL) break; + int ret = read_desktop(fd, ctx, &cb); + if (ret < 0) { // any error + free(ctx); + return 0; } - if(buf != NULL) free(buf); (void)fclose(fd); - // printf("\nend parsing...\n"); // just add this to the list - if (name_buf != NULL && exec_buf != NULL) { - struct session* session_i = malloc(sizeof(struct session)); - *session_i = new_session(session_type, name_buf, exec_buf, - tryexec_buf == NULL ? "" : tryexec_buf); - vec_push(cb_sessions, session_i); + if (ctx->name != NULL && ctx->exec != NULL) { + ctx->type = session_type; + vec_push(cb_sessions, ctx); } - if (name_buf != NULL) free(name_buf); - if (exec_buf != NULL) free(exec_buf); - if (tryexec_buf != NULL) free(tryexec_buf); - return 0; } diff --git a/src/ui.c b/src/ui.c index d4a552f..4f8864d 100644 --- a/src/ui.c +++ b/src/ui.c @@ -214,7 +214,7 @@ void ffield_cursor_focus() { struct user get_current_user() { if (of_user.current_opt != 0) - return *(struct user *)vec_get(gusers, of_user.current_opt - 1); + return *(struct user*)vec_get(gusers, of_user.current_opt - 1); else { struct user custom_user; custom_user.shell = "/usr/bin/bash"; @@ -234,7 +234,7 @@ struct session get_current_session() { shell_session.exec = shell_session.name = get_current_user().shell; return shell_session; } else - return *(struct session *)vec_get(gsessions, of_session.current_opt - 1); + return *(struct session*)vec_get(gsessions, of_session.current_opt - 1); } else { struct session custom_session; custom_session.type = SHELL;