This commit is contained in:
jacekpoz 2024-02-09 12:05:06 +01:00
commit 40f0e25f32
No known key found for this signature in database
GPG key ID: 94E812A8B12AAE3C
8 changed files with 442 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
target/

19
LICENSE Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2024 jacekpoz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

43
Makefile Normal file
View file

@ -0,0 +1,43 @@
CC = gcc
CFLAGS = -std=c99 -O3 -Wall -Wextra -Wpedantic -Wstrict-aliasing
CFLAGS += -Wfloat-equal -Wundef -Wshadow -Wpointer-arith -Wcast-align
CFLAGS += -Wstrict-prototypes -Wstrict-overflow=5 -Wwrite-strings
CFLAGS += -Wcast-qual -Wswitch-default -Wswitch-enum
CFLAGS += -Wconversion -Wunreachable-code
# for asprintf() and getline()
CFLAGS += -D_GNU_SOURCE
LDFLAGS =
SRC = modfetch.c
OBJ = $(BIN)/$(SRC:.c=.o)
BIN = target
TEMPMOD = $(wildcard modules/*.c)
MOD = $(TEMPMOD:modules/%=%)
PROGRAM = modfetch
.PHONY: all clean distclean
all: dirs $(PROGRAM) distclean
modules: dirs $(MOD:.c=.so)
dirs:
mkdir -p ./$(BIN)
run: all
$(BIN)/$(PROGRAM)
$(PROGRAM): $(OBJ)
$(CC) -o $(BIN)/$(PROGRAM) $^ $(LDFLAGS)
$(BIN)/%.o: %.c
$(CC) -o $@ -c $< $(CFLAGS)
%.so: modules/%.c
$(CC) -o $(BIN)/$@ $^ -shared -fPIC $(CFLAGS)
clean: distclean
rm -rf $(BIN)
distclean:
rm -rf $(OBJ)

35
README.md Normal file
View file

@ -0,0 +1,35 @@
# modfetch
modular fetch
## building
`make` for the main binary
`make modules` for all the default modules
write your own using `mod.h` if you want to
## config
by default `$XDG_CURRENT_DESKTOP/modfetch.conf`
format:
```
/path/to/mod1.so
/path/to/mod2.so {
var1 = dupa
var2 = 2137
}
```
## TODO
[ ] module manager (url to module source, compiles and puts the binary somewhere)
[ ] better config parsing
[ ] more text positioning options aside from printing text vertically
## license
mit

10
mod.h Normal file
View file

@ -0,0 +1,10 @@
#include <stdint.h>
uint8_t version_major(void);
uint8_t version_minor(void);
uint8_t version_patch(void);
const char *module_name(void);
const char *get(void);
void init(char **config);

258
modfetch.c Normal file
View file

@ -0,0 +1,258 @@
#include <ctype.h>
#include <dlfcn.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <wordexp.h>
static const char *PNAME = "modfetch";
static const uint8_t VERSION_MAJOR = 0;
static const uint8_t VERSION_MINOR = 1;
static const uint8_t VERSION_PATCH = 0;
static const uint8_t MAX_MODULE_NAME_LENGTH = 128;
static const uint8_t MAX_MODULES = 128;
static const uint8_t MAX_MODULE_VARS = 128;
static const char *version_str(void) {
char *ver;
if (asprintf(&ver, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH) < 0) {
return NULL;
}
return ver;
}
typedef struct {
// path to the binary of this module
char *path;
// name=value strings for each value in config
char **config;
} Module;
typedef struct {
size_t module_count;
Module *modules;
} Config;
void parsing_error(size_t line) {
fprintf(stderr, "error: failed parsing config at line %zu", line);
exit(EXIT_FAILURE);
}
char *remove_whitespaces(const char *str) {
char *tmp = malloc(strlen(str) * sizeof(char));
size_t i = 0;
size_t j = 0;
while (str[i] != '\0') {
if (isspace(str[i])) {
++i;
continue;
}
tmp[j] = str[i];
++i;
++j;
}
tmp[j] = '\0';
char *ret = malloc(j * sizeof(char));
strncpy(ret, tmp, j);
free(tmp);
return ret;
}
char *resolve_env_vars(const char *str) {
size_t len = strlen(str);
char *ret = malloc(4096 * sizeof(char));
size_t ret_offset = 0;
bool in_env_var = false;
char *env_var = malloc(4096 * sizeof(char));
size_t env_var_offset = 0;
for (size_t i = 0; i < len; ++i) {
if (str[i] == '~') {
char *home = getenv("HOME");
strcat(ret, home);
ret_offset += strlen(home);
continue;
}
if (str[i] == '$') {
in_env_var = true;
continue;
}
if (in_env_var && !(isalpha(str[i]) || isdigit(str[i]) || str[i] == '_')) {
in_env_var = false;
char *env = getenv(env_var);
strcat(ret, env);
ret_offset += strlen(env);
free(env_var);
env_var = malloc(4096 * sizeof(char));
env_var_offset = 0;
}
if (in_env_var) {
env_var[env_var_offset] = str[i];
++env_var_offset;
continue;
}
ret[ret_offset] = str[i];
++ret_offset;
}
return ret;
}
Config parse_config(FILE *config_file) {
size_t len = 0;
ssize_t read;
char *line = NULL;
size_t line_index = 0;
bool in_config = false;
char *current_path = malloc(MAX_MODULE_NAME_LENGTH * sizeof(char));
Module current_module = {
.path = malloc(4096 * sizeof(char)),
.config = malloc(MAX_MODULE_VARS * sizeof(char)),
};
size_t config_index = 0;
Config config = {
.modules = malloc(MAX_MODULES * sizeof(Module)),
.module_count = 0,
};
size_t module_index = 0;
while ((read = getline(&line, &len, config_file)) != -1) {
// if config is passed
if (line[read - 2u] == '{') {
if (in_config) {
fclose(config_file);
parsing_error(line_index);
}
in_config = true;
strncpy(current_path, line, (size_t)read - 2);
current_module.path = resolve_env_vars(remove_whitespaces(current_path));
free(current_path);
current_path = malloc(MAX_MODULE_NAME_LENGTH);
continue;
}
// end of config for current module
if (line[read - 2u] == '}') {
if (!in_config) {
fclose(config_file);
parsing_error(line_index);
}
in_config = false;
current_module.config[config_index] = NULL;
config.modules[module_index] = current_module;
++module_index;
current_module.path = malloc(4096 * sizeof(char));
current_module.config = malloc(MAX_MODULE_VARS * sizeof(char));
continue;
}
if (in_config) {
current_module.config[config_index] = remove_whitespaces(line);
++config_index;
continue;
}
current_module.path = resolve_env_vars(remove_whitespaces(line));
free(current_module.config);
current_module.config = malloc(sizeof(void*));
current_module.config[0] = NULL;
config.modules[module_index] = current_module;
++module_index;
current_module.path = malloc(4096 * sizeof(char));
current_module.config = malloc(MAX_MODULE_VARS * sizeof(char));
}
config.module_count = module_index;
return config;
}
int main(int argc, char *argv[]) {
char *config_path;
if (asprintf(&config_path, "%s/modfetch.conf", getenv("XDG_CONFIG_HOME")) < 0) {
fprintf(stderr, "error: failed formatting config path (this shouldn't happen)");
exit(EXIT_FAILURE);
}
for (size_t i = 1; i < (size_t)argc; ++i) {
if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--config") == 0) {
if (i == (size_t)argc - 1) {
fprintf(stderr, "error: no config path passed\n");
exit(EXIT_FAILURE);
}
++i;
config_path = argv[i];
continue;
}
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
fprintf(stderr, "%s: modular fetch [%s]\n", PNAME, version_str());
fprintf(stderr, "\n");
fprintf(stderr, "OPTIONS\n");
fprintf(stderr, "\t-h, --help\t\t\tdisplays this help text\n");
fprintf(stderr, "\t-c, --config /path/to/config\tchanges config path from the default ($XDG_CONFIG_HOME/%s.conf)\n", PNAME);
exit(EXIT_SUCCESS);
}
}
FILE *config_file = fopen(config_path, "r");
if (config_file == NULL) {
fprintf(stderr, "error: failed to open config at: %s\n", config_path);
exit(EXIT_FAILURE);
}
Config config = parse_config(config_file);
fclose(config_file);
for (size_t i = 0; i < config.module_count; ++i) {
Module current_module = config.modules[i];
void *handle = dlopen(current_module.path, RTLD_NOW);
if (handle == NULL) {
fprintf(stderr, "error: failed opening module %s\n", current_module.path);
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror();
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
uint8_t (*version_major)(void) = dlsym(handle, "version_major");
uint8_t (*version_minor)(void) = dlsym(handle, "version_minor");
uint8_t (*version_patch)(void) = dlsym(handle, "version_patch");
const char *(*module_name)(void) = dlsym(handle, "module_name");
const char *(*get)(void) = dlsym(handle, "get");
void (*init)(char**) = dlsym(handle, "init");
#pragma GCC diagnostic pop
init(current_module.config);
(void)module_name;
(void)version_major;
(void)version_minor;
(void)version_patch;
//printf("%s: %d.%d.%d\n", module_name(), version_major(), version_minor(), version_patch());
printf("%s\n", get());
}
return 0;
}

23
modules/desktop.c Normal file
View file

@ -0,0 +1,23 @@
#include "../mod.h"
#include <stdio.h>
#include <stdlib.h>
uint8_t version_major(void) { return 0; }
uint8_t version_minor(void) { return 1; }
uint8_t version_patch(void) { return 0; }
const char *module_name(void) { return "desktop"; }
void init(char **config) {
(void)config;
}
const char *get(void) {
char *ret;
if (asprintf(&ret, "Desktop: %s", getenv("XDG_CURRENT_DESKTOP")) < 0) {
fprintf(stderr, "error: failed formatting (this shouldn't happen)");
exit(EXIT_FAILURE);
}
return ret;
}

53
modules/os.c Normal file
View file

@ -0,0 +1,53 @@
#include "../mod.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
uint8_t version_major(void) { return 0; }
uint8_t version_minor(void) { return 1; }
uint8_t version_patch(void) { return 0; }
const char *module_name(void) { return "os"; }
void init(char **config) {
(void)config;
}
const char *get(void) {
char *ret;
FILE *os_release = fopen("/etc/os-release", "r");
if (os_release == NULL) {
fprintf(stderr, "error: failed opening /etc/os-release\n");
exit(EXIT_FAILURE);
}
size_t len = 0;
ssize_t read;
char *line = NULL;
char *name = malloc(1024 * sizeof(char));
const char *name_label = "PRETTY_NAME=";
const size_t name_label_len = strlen(name_label);
while ((read = getline(&line, &len, os_release)) != -1) {
if (strncmp(line, name_label, name_label_len) != 0) {
continue;
}
// + 1 because of "
line += name_label_len + 1;
const size_t new_len = strlen(line);
// the ending "
line[new_len - 2] = '\0';
name = line;
break;
}
if (asprintf(&ret, "OS: %s", name) < 0) {
fprintf(stderr, "error: failed formatting string (this shouldn't happen)");
exit(EXIT_FAILURE);
}
return ret;
}