init
This commit is contained in:
commit
40f0e25f32
8 changed files with 442 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
target/
|
19
LICENSE
Normal file
19
LICENSE
Normal 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
43
Makefile
Normal 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
35
README.md
Normal 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
10
mod.h
Normal 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
258
modfetch.c
Normal 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
23
modules/desktop.c
Normal 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
53
modules/os.c
Normal 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;
|
||||||
|
}
|
Loading…
Reference in a new issue