Compare commits

..

2 commits

22 changed files with 337 additions and 797 deletions

22
.ccls
View file

@ -1,22 +0,0 @@
gcc
%c -std=c99
%h
-Iinclude
-D_GNU_SOURCE
-Wall
-Wextra
-Wpedantic
-Wstrict-aliasing
-Wfloat-equal
-Wundef
-Wshadow
-Wpointer-arith
-Wcast-align
-Wstrict-prototypes
-Wstrict-overflow=5
-Wwrite-strings
-Wcast-qual
-Wswitch-default
-Wswitch-enum
-Wconversion
-Wunreachable-code

1
.envrc
View file

@ -1 +0,0 @@
use flake

2
.gitignore vendored
View file

@ -1,3 +1 @@
target/ target/
.ccls-cache/
.direnv/

View file

@ -6,46 +6,36 @@ CFLAGS += -Wcast-qual -Wswitch-default -Wswitch-enum
CFLAGS += -Wconversion -Wunreachable-code CFLAGS += -Wconversion -Wunreachable-code
# for asprintf() and getline() # for asprintf() and getline()
CFLAGS += -D_GNU_SOURCE CFLAGS += -D_GNU_SOURCE
CFLAGS += -Iinclude
LDFLAGS = LDFLAGS =
NAME = modfetch SRC = modfetch.c
OBJ = $(BIN)/$(SRC:.c=.o)
SRC = src
BIN = target BIN = target
TEMPMOD = $(wildcard modules/*.c)
_PROG = config.c fetch.c main.c util.c MOD = $(TEMPMOD:modules/%=%)
PROG = $(addprefix $(SRC)/, $(_PROG)) PROGRAM = modfetch
_LIB = semver.c
LIB = $(addprefix $(SRC)/, $(_LIB))
OBJ = $(_PROG:.c=.o) $(_LIB:.c=.o)
_MOD = $(wildcard modules/*.c)
MOD = $(_MOD:modules/%=%)
.PHONY: all clean .PHONY: all clean
all: dirs modules $(NAME) all: dirs modules $(PROGRAM)
modules: dirs $(MOD:.c=.so) modules: dirs $(MOD:.c=.so)
dirs: dirs:
mkdir -p $(BIN) mkdir -p ./$(BIN)
$(NAME): $(OBJ) run: all
$(CC) $(addprefix $(BIN)/, $^) $(LDFLAGS) -o $(BIN)/$@ $(BIN)/$(PROGRAM)
%.so: modules/%.c $(LIB) $(PROGRAM): $(OBJ)
$(CC) $^ -shared -fPIC $(CFLAGS) $(LDFLAGS) -o $(BIN)/$@ $(CC) -o $(BIN)/$(PROGRAM) $^ $(LDFLAGS)
%.o: src/%.c $(BIN)/%.o: %.c
$(CC) -c $< $(CFLAGS) -o $(BIN)/$@ $(CC) -o $@ -c $< $(CFLAGS)
clean: clean_modules %.so: modules/%.c
rm -rf $(addprefix $(BIN)/, $(OBJ)) $(CC) -o $(BIN)/$@ $^ -shared -fPIC $(CFLAGS)
rm -rf $(BIN)/$(NAME)
clean_modules: clean:
rm -rf $(wildcard $(BIN)/*.so) rm -rf $(OBJ)
rm -rf $(PROGRAM)

View file

@ -2,15 +2,9 @@
modular fetch modular fetch
each printable thing is a separate module in a shared object
modfetch loads and calls them at runtime to print system info
## building ## building
`make` to build everything `make` for the main binary
`make modfetch` for the main binary
`make modules` for all the default modules `make modules` for all the default modules
@ -18,7 +12,7 @@ write your own using `mod.h` if you want to
## config ## config
by default `$XDG_CONFIG_HOME/modfetch.conf`, `~/.config/modfetch.conf` if `$XDG_CONFIG_HOME` doesn't exist by default `$XDG_CURRENT_DESKTOP/modfetch.conf`
format: format:
``` ```
@ -32,11 +26,10 @@ format:
## TODO ## TODO
- [x] ~~module api versioning~~
- [ ] module manager (url to module source, compiles and puts the binary somewhere) - [ ] module manager (url to module source, compiles and puts the binary somewhere)
- [ ] better config parsing - [ ] better config parsing
- [ ] more text positioning options aside from printing text vertically - [ ] more text positioning options aside from printing text vertically
- [ ] some way for modules to throw errors - [ ] some way for module to throw errors
- [ ] module related options (list module, print specific module info, etc.) - [ ] module related options (list module, print specific module info, etc.)
## license ## license

View file

@ -1,26 +0,0 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1707824078,
"narHash": "sha256-Au3wLi2d06bU7TDvahP2jIEeKwmjAxKHqi8l2uiBkGA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "99d7b32e4cfdaf763d9335b4d9ecf4415cbdc405",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

View file

@ -1,36 +0,0 @@
{
description = "modular fetch";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs";
};
outputs = {
nixpkgs,
...
}: let
systems = [ "x86_64-linux" "aarch64-linux" ];
forEachSystem = nixpkgs.lib.genAttrs systems;
pkgsForEach = nixpkgs.legacyPackages;
in {
devShells = forEachSystem (system:
let
pkgs = pkgsForEach.${system};
in {
default = pkgs.mkShell {
name = "modfetch";
nativeBuildInputs = with pkgs; [
gcc
gnumake
];
buildInputs = with pkgs; [
curl.dev
xorg.libX11.dev
];
};
}
);
};
}

View file

@ -1,25 +0,0 @@
#ifndef _MODFETCH_CONFIG_H
#define _MODFETCH_CONFIG_H
#include <stddef.h>
#include <stdio.h>
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;
char *default_config_path(void);
void parsing_error(size_t line);
Config parse_config(FILE *config_file);
#endif // _MODFETCH_CONFIG_H

View file

@ -1,8 +0,0 @@
#ifndef _MODFETCH_FETCH_H
#define _MODFETCH_FETCH_H
#include <config.h>
void fetch(Config config);
#endif // _MODFETCH_FETCH_H

View file

@ -1,22 +0,0 @@
#ifndef _MODFETCH_MOD_H
#define _MODFETCH_MOD_H
#include <semver.h>
#include <stdint.h>
static const semver API_VERSION = {
.major = 0,
.minor = 2,
.patch = 0,
};
static const uint8_t MFERR_APIVER = 1;
semver version(void);
const char *name(void);
uint8_t init(semver api_ver, char **config);
const char *get(void);
#endif // _MODFETCH_MOD_H

View file

@ -1,17 +0,0 @@
#ifndef _MODFETCH_SEMVER_H
#define _MODFETCH_SEMVER_H
#include <stdbool.h>
#include <stdint.h>
typedef struct {
uint8_t major;
uint8_t minor;
uint8_t patch;
} semver;
bool sveq(semver s1, semver s2);
char *svtoa(semver s);
#endif // _MODFETCH_SEMVER_H

View file

@ -1,20 +0,0 @@
#ifndef _MODFETCH_UTIL_H
#define _MODFETCH_UTIL_H
#include <stdint.h>
static const uint8_t MAX_MODULE_NAME_LENGTH = 128;
static const uint8_t MAX_MODULES = 128;
static const uint8_t INITIAL_CONFIG_SIZE = 8;
static const uint16_t MAX_PATH_LENGTH = 4096;
char *remove_whitespaces(const char *str);
char *resolve_env_vars(const char *str);
char *process_str(const char *str);
#endif // _MODFETCH_UTIL_H

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);

292
modfetch.c Normal file
View file

@ -0,0 +1,292 @@
#include <ctype.h>
#include <dlfcn.h>
#include <errno.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 INITIAL_CONFIG_SIZE = 8;
static const uint16_t MAX_PATH_LENGTH = 4096;
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[strlen(str)];
size_t i = 0;
size_t j = 0;
while (str[i] != '\0') {
if (isspace(str[i])) {
i += 1;
continue;
}
tmp[j] = str[i];
i += 1;
j += 1;
}
tmp[j] = '\0';
char *ret = malloc(j * sizeof(char));
strncpy(ret, tmp, j);
return ret;
}
char *resolve_env_vars(const char *str) {
size_t len = strlen(str);
char *ret = malloc(MAX_PATH_LENGTH * sizeof(char));
size_t ret_offset = 0;
bool in_env_var = false;
char env_var[MAX_PATH_LENGTH];
memset(env_var, 0, sizeof(env_var));
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;
}
// end of current env var
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);
memset(env_var, 0, sizeof(env_var));
env_var_offset = 0;
}
if (in_env_var) {
env_var[env_var_offset] = str[i];
env_var_offset += 1;
continue;
}
ret[ret_offset] = str[i];
ret_offset += 1;
}
ret[ret_offset] = '\0';
return ret;
}
char *process_str(const char *str) {
char *whitespaceless = remove_whitespaces(str);
char *ret = resolve_env_vars(whitespaceless);
free(whitespaceless);
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[MAX_MODULE_NAME_LENGTH];
Module current_module = {
.path = NULL,
.config = NULL,
};
size_t config_index = 0;
size_t config_multiplier = 1;
Config config = {
.modules = malloc(MAX_MODULES * sizeof(Module)),
.module_count = 0,
};
// TODO handle errors of all the function inside the while
errno = 0;
// TODO rewrite this to read the config char by char
while ((read = getline(&line, &len, config_file)) != -1) {
if (read <= 1)
continue;
// if config is passed to this module
if (line[read - 2u] == '{') {
if (in_config) {
fclose(config_file);
parsing_error(line_index);
}
in_config = true;
// get rid of the '{'
strncpy(current_path, line, (size_t)read - 2);
current_path[read - 2] = '\0';
current_module.path = process_str(current_path);
current_module.config = malloc(INITIAL_CONFIG_SIZE * sizeof(char*));
continue;
}
// end of config for current module
if (line[read - 2u] == '}') {
if (!in_config) {
fclose(config_file);
parsing_error(line_index);
}
in_config = false;
if (config_index >= INITIAL_CONFIG_SIZE * config_multiplier) {
config_multiplier *= 2;
current_module.config = realloc(current_module.config, INITIAL_CONFIG_SIZE * config_multiplier);
}
current_module.config[config_index] = NULL;
config.modules[config.module_count] = current_module;
config.module_count += 1;
config_index = 0;
continue;
}
if (in_config) {
if (config_index >= INITIAL_CONFIG_SIZE * config_multiplier) {
config_multiplier *= 2;
current_module.config = realloc(current_module.config, INITIAL_CONFIG_SIZE * config_multiplier);
}
current_module.config[config_index] = process_str(line);
config_index += 1;
continue;
}
// no config passed to this module
current_module.path = process_str(line);
current_module.config = malloc(sizeof(void*));
current_module.config[0] = NULL;
config.modules[config.module_count] = current_module;
config.module_count += 1;
free(line);
}
if (read == -1 && errno != 0)
free(line);
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 += 1;
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();
// stupid fucking c standard issue
// I'm not fixing this
// https://stackoverflow.com/questions/14134245/iso-c-void-and-function-pointers
#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;
}

View file

@ -1,60 +1,20 @@
/* DESKTOP - desktop environment / window manager / compositor module for modfetch #include "../mod.h"
*
* author: jacekpoz
* 09 Feb 2024
*/
#include <mod.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#ifdef MF_DESKTOP_X11
#include <X11/Xlib.h>
#endif
static const semver _version = { uint8_t version_major(void) { return 0; }
.major = 0, uint8_t version_minor(void) { return 1; }
.minor = 1, uint8_t version_patch(void) { return 0; }
.patch = 0, const char *module_name(void) { return "desktop"; }
};
semver version(void) { return _version; }
const char *name(void) { return "desktop"; }
uint8_t init(semver api_ver, char **config) {
if (!sveq(api_ver, API_VERSION)) {
return MFERR_APIVER;
}
void init(char **config) {
(void)config; (void)config;
return 0;
} }
const char *get(void) { const char *get(void) {
char *ret; char *ret;
char *current_desktop = getenv("XDG_CURRENT_DESKTOP"); if (asprintf(&ret, "Desktop: %s", getenv("XDG_CURRENT_DESKTOP")) < 0) {
if (current_desktop == NULL) {
#ifdef MF_DESKTOP_X11
Display *disp = XOpenDisplay(NULL);
Window root = XDefaultRootWindow(disp);
Atom atom = XInternAtom(disp, "_NET_WM_NAME", 0);
Atom type;
int fmt;
unsigned long num_items;
unsigned long bytes_after;
unsigned char *prop;
XGetWindowProperty(disp, root, atom, 0, 4096 / sizeof(int32_t), 0, AnyPropertyType,
&type, &fmt, &num_items, &bytes_after, &prop);
current_desktop = prop;
#else
current_desktop = "Unknown";
#endif
}
if (asprintf(&ret, "Desktop: %s", current_desktop) < 0) {
fprintf(stderr, "error: failed formatting (this shouldn't happen)"); fprintf(stderr, "error: failed formatting (this shouldn't happen)");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }

View file

@ -1,33 +1,16 @@
/* OS - operating system module for modfetch #include "../mod.h"
*
* author: jacekpoz
* 09 Feb 2024
*/
#include <mod.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
static const semver _version = { uint8_t version_major(void) { return 0; }
.major = 0, uint8_t version_minor(void) { return 1; }
.minor = 1, uint8_t version_patch(void) { return 0; }
.patch = 0, const char *module_name(void) { return "os"; }
};
semver version(void) { return _version; }
const char *name(void) { return "os"; }
uint8_t init(semver api_ver, char **config) {
if (!sveq(api_ver, API_VERSION)) {
return MFERR_APIVER;
}
void init(char **config) {
(void)config; (void)config;
return 0;
} }
const char *get(void) { const char *get(void) {
@ -44,7 +27,7 @@ const char *get(void) {
ssize_t read; ssize_t read;
char *line = NULL; char *line = NULL;
char *name = NULL; char *name = malloc(1024 * sizeof(char));
const char *name_label = "PRETTY_NAME="; const char *name_label = "PRETTY_NAME=";
const size_t name_label_len = strlen(name_label); const size_t name_label_len = strlen(name_label);
@ -61,12 +44,6 @@ const char *get(void) {
break; break;
} }
fclose(os_release);
if (name == NULL) {
name = "Unknown";
}
if (asprintf(&ret, "OS: %s", name) < 0) { if (asprintf(&ret, "OS: %s", name) < 0) {
fprintf(stderr, "error: failed formatting string (this shouldn't happen)"); fprintf(stderr, "error: failed formatting string (this shouldn't happen)");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);

View file

@ -1,80 +0,0 @@
/* SEP - separator module for modfetch
*
* options: <type> name (default value)
* <number> sep (8) specifies how long the separator should be
* <string> char (-) specifies the character (or string) to use for the separator
*
* author: krizej
* 09 Feb 2024
*/
#include <mod.h>
#include <string.h>
#include <stdlib.h>
static const semver _version = {
.major = 1,
.minor = 0,
.patch = 0,
};
semver version(void) { return _version; }
const char *name(void) { return "sep"; }
static size_t length = 8;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
static char *sep_char = "-";
#pragma GCC diagnostic pop
static size_t sep_char_length = 1;
uint8_t init(semver api_ver, char **config) {
if (!sveq(api_ver, API_VERSION)) {
return MFERR_APIVER;
}
for (char *opt = *config; opt != NULL; opt = *++config) {
if (strncmp(opt, "sep", 3) == 0) {
char *valstr = opt + 4; // skip 'sep='
long value = strtol(valstr, NULL, 10);
if (value >= 0) {
length = (size_t) value;
}
} else if (strncmp(opt, "char", 4) == 0) {
char *value = opt + 5; // skip 'char='
if (*value != 0) { // todo: see if there are any more characters that should be rejected
sep_char_length = strlen(value);
sep_char = malloc((sep_char_length + 1) * sizeof(char));
strncpy(sep_char, value, sep_char_length);
sep_char[sep_char_length] = '\0';
} else {
sep_char = NULL;
}
}
}
return 0;
}
const char *get(void) {
char *ret;
if (sep_char == NULL) {
ret = malloc(1 * sizeof(char));
*ret = 0;
return ret;
}
ret = malloc((length * sep_char_length + 1) * sizeof(char));
memset(ret, 0, length * sep_char_length + 1);
for (size_t i = 0; i < length; i++)
strncat(ret, sep_char, sep_char_length);
ret[length * sep_char_length] = '\0'; // just to be sure :-D
return ret;
}

View file

@ -1,126 +0,0 @@
#include <config.h>
#include <util.h>
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *default_config_path(void) {
char *config_path = NULL;
char *config_dir = getenv("XDG_CONFIG_HOME");
if (config_dir == NULL) {
char *home = getenv("HOME");
if (asprintf(&config_dir, "%s/.config", home) < 0) {
fprintf(stderr, "error: failed formatting config dir (this shouldn't happen)");
exit(EXIT_FAILURE);
}
}
if (asprintf(&config_path, "%s/modfetch.conf", config_dir) < 0) {
fprintf(stderr, "error: failed formatting config path (this shouldn't happen)");
exit(EXIT_FAILURE);
}
return config_path;
}
void parsing_error(size_t line) {
fprintf(stderr, "error: failed parsing config at line %zu", line);
exit(EXIT_FAILURE);
}
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[MAX_MODULE_NAME_LENGTH];
Module current_module = {
.path = NULL,
.config = NULL,
};
size_t config_index = 0;
size_t config_multiplier = 1;
Config config = {
.modules = malloc(MAX_MODULES * sizeof(Module)),
.module_count = 0,
};
// TODO handle errors of all the function inside the while
errno = 0;
// TODO rewrite this to read the config char by char
while ((read = getline(&line, &len, config_file)) != -1) {
if (read <= 1)
continue;
// if config is passed to this module
if (line[read - 2u] == '{') {
if (in_config) {
fclose(config_file);
parsing_error(line_index);
}
in_config = true;
// get rid of the '{'
strncpy(current_path, line, (size_t)read - 2);
current_path[read - 2] = '\0';
current_module.path = process_str(current_path);
current_module.config = malloc(INITIAL_CONFIG_SIZE * sizeof(char*));
continue;
}
// end of config for current module
if (line[read - 2u] == '}') {
if (!in_config) {
fclose(config_file);
parsing_error(line_index);
}
in_config = false;
if (config_index >= INITIAL_CONFIG_SIZE * config_multiplier) {
config_multiplier *= 2;
current_module.config = realloc(current_module.config, INITIAL_CONFIG_SIZE * config_multiplier);
}
current_module.config[config_index] = NULL;
config.modules[config.module_count] = current_module;
config.module_count += 1;
config_index = 0;
continue;
}
if (in_config) {
if (config_index >= INITIAL_CONFIG_SIZE * config_multiplier) {
config_multiplier *= 2;
current_module.config = realloc(current_module.config, INITIAL_CONFIG_SIZE * config_multiplier);
}
current_module.config[config_index] = process_str(line);
config_index += 1;
continue;
}
// no config passed to this module
current_module.path = process_str(line);
current_module.config = malloc(sizeof(void*));
current_module.config[0] = NULL;
config.modules[config.module_count] = current_module;
config.module_count += 1;
free(line);
}
if (read == -1 && errno != 0)
free(line);
return config;
}

View file

@ -1,38 +0,0 @@
#include <fetch.h>
#include <mod.h>
#include <semver.h>
#include <dlfcn.h>
#include <stdlib.h>
void fetch(Config config) {
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();
// stupid fucking c standard issue
// I'm not fixing this
// https://stackoverflow.com/questions/14134245/iso-c-void-and-function-pointers
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
semver (*mod_version)(void) = dlsym(handle, "version");
const char *(*mod_name)(void) = dlsym(handle, "name");
const char *(*mod_get)(void) = dlsym(handle, "get");
void (*mod_init)(semver, char**) = dlsym(handle, "init");
#pragma GCC diagnostic pop
mod_init(API_VERSION, current_module.config);
(void)mod_name;
(void)mod_version;
// printf("%s: %s\n", mod_name(), svtoa(mod_version()));
printf("%s\n", mod_get());
}
}

View file

@ -1,149 +0,0 @@
#include <config.h>
#include <fetch.h>
#include <mod.h>
#include <semver.h>
#include <util.h>
#include <dlfcn.h>
#include <errno.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 semver VERSION = {
.major = 0,
.minor = 1,
.patch = 0,
};
void print_mod_help(FILE *stream) {
fprintf(stream, "%s mod: module information\n", PNAME);
fprintf(stream, "\n");
fprintf(stream, "SUBCOMMANDS\n");
fprintf(stream, "\tlist\t\t\t\tlists all available modules\n");
fprintf(stream, "\tinfo <module>\t\t\tprints all info about module\n");
}
int main(int argc, char *argv[]) {
char *config_path = default_config_path();
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);
free(config_path);
bool hit_mod_cmd = false;
bool hit_mod_info = false;
for (size_t i = 1; i < (size_t)argc; ++i) {
if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--config") == 0) {
// no more args
if (i == (size_t)argc - 1) {
fprintf(stderr, "error: no config path passed\n");
exit(EXIT_FAILURE);
}
i += 1;
config_path = argv[i];
continue;
}
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
printf("%s: modular fetch [%s]\n", PNAME, svtoa(VERSION));
printf("\n");
printf("SUBCOMMANDS\n");
printf("\tmod\t\t\t\tmodule information\n");
printf("\n");
printf("OPTIONS\n");
printf("\t-h, --help\t\t\tdisplays this help text\n");
printf("\t-c, --config </path/to/config>\tchanges config path from the default ($XDG_CONFIG_HOME/%s.conf)\n", PNAME);
exit(EXIT_SUCCESS);
}
if (strncmp(argv[i], "mod", 3) == 0 || hit_mod_cmd) {
// no more args
if (i == (size_t)argc - 1) {
print_mod_help(stderr);
exit(EXIT_FAILURE);
}
i += 1;
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
print_mod_help(stdout);
exit(EXIT_SUCCESS);
}
// some other -/-- option in between the mod and the specific mod command
if (argv[i][0] == '-') {
hit_mod_cmd = true;
continue;
}
if (strcmp(argv[i], "list") == 0) {
for (size_t ii = 0; ii < config.module_count; ++ii) {
Module current_module = config.modules[ii];
void *handle = dlopen(current_module.path, RTLD_NOW);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
semver (*mod_version)(void) = dlsym(handle, "version");
const char *(*mod_name)(void) = dlsym(handle, "name");
#pragma GCC diagnostic pop
printf("%s-%s\n", mod_name(), svtoa(mod_version()));
}
}
if (strcmp(argv[i], "info") == 0 || hit_mod_info) {
// no more args
if (i == (size_t)argc - 1) {
fprintf(stderr, "error: no module passed\n");
exit(EXIT_FAILURE);
}
// some other -/-- option in between the info and the module
if (argv[i][0] == '-') {
hit_mod_info = true;
continue;
}
i += 1;
for (size_t ii = 0; ii < config.module_count; ++ii) {
Module current_module = config.modules[ii];
void *handle = dlopen(current_module.path, RTLD_NOW);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
semver (*mod_version)(void) = dlsym(handle, "version");
const char *(*mod_name)(void) = dlsym(handle, "name");
#pragma GCC diagnostic pop
const char *_mod_name = mod_name();
if (strcmp(argv[i], _mod_name) != 0)
continue;
printf("%s-%s\n", _mod_name, svtoa(mod_version()));
}
}
exit(EXIT_SUCCESS);
}
}
fetch(config);
return 0;
}

View file

@ -1,29 +0,0 @@
#include <semver.h>
#include <stdio.h>
#include <stdlib.h>
uint8_t num_digits(uint8_t x) {
return ((x) / 100) != 0 ? 3 :
((x) / 10) != 0 ? 2 :
1;
}
bool sveq(semver s1, semver s2) {
return s1.major == s2.major &&
s1.minor == s2.minor &&
s1.patch == s2.patch;
}
char *svtoa(semver s) {
uint8_t major_len = num_digits(s.major);
uint8_t minor_len = num_digits(s.minor);
uint8_t patch_len = num_digits(s.patch);
// \0 dots
size_t len = (size_t)1 + 2 + major_len + minor_len + patch_len;
char *ret = malloc(len * sizeof(char));
snprintf(ret, len, "%d.%d.%d", s.major, s.minor, s.patch);
return ret;
}

View file

@ -1,81 +0,0 @@
#include <util.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
char *remove_whitespaces(const char *str) {
char tmp[strlen(str)];
size_t i = 0;
size_t j = 0;
while (str[i] != '\0') {
if (isspace(str[i])) {
i += 1;
continue;
}
tmp[j] = str[i];
i += 1;
j += 1;
}
char *ret = malloc(j * sizeof(char));
strncpy(ret, tmp, j);
ret[j] = '\0';
return ret;
}
char *resolve_env_vars(const char *str) {
size_t len = strlen(str);
char *ret = malloc(MAX_PATH_LENGTH * sizeof(char));
size_t ret_offset = 0;
bool in_env_var = false;
char env_var[MAX_PATH_LENGTH];
memset(env_var, 0, sizeof(env_var));
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;
}
// end of current env var
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);
memset(env_var, 0, sizeof(env_var));
env_var_offset = 0;
}
if (in_env_var) {
env_var[env_var_offset] = str[i];
env_var_offset += 1;
continue;
}
ret[ret_offset] = str[i];
ret_offset += 1;
}
ret[ret_offset] = '\0';
return ret;
}
char *process_str(const char *str) {
char *whitespaceless = remove_whitespaces(str);
char *ret = resolve_env_vars(whitespaceless);
free(whitespaceless);
return ret;
}