1
0
Fork 0
forked from poz/modfetch

Compare commits

...

47 commits

Author SHA1 Message Date
2e57ca9b3f
close file and don't malloc for no reason 2024-02-14 16:06:14 +01:00
e405ccdf2a
I'm so sorry krizej 2024-02-14 15:47:08 +01:00
fa46131ee5
add libx11 to devshell 2024-02-14 15:46:32 +01:00
a610d63088
Merge branch 'desktop-x11' 2024-02-13 19:26:01 +01:00
e2ac9aa4e6
add flake with direnv support 2024-02-13 19:20:24 +01:00
b990fabd4f
fix typo 2024-02-12 14:15:28 +01:00
4eb0ddca86
initial mod info work (need to rework this) 2024-02-12 00:58:38 +01:00
e0edbc83d6
add the rest of the flags (need to figure out a better way to do this) 2024-02-12 00:58:09 +01:00
e3bdb80e91
initial mod subcommands work 2024-02-12 00:49:52 +01:00
a6a1d0c65e
ignore discarded qualifiers warning upon assigning string literal
ccls still shows me the warning and another one for the `diagnostic
ignored` saying `unknown warning group`, not sure if it's fixable in
ccls, might be time to move to clang/d? although I've heard it has
issues too, not sure
2024-02-12 00:44:13 +01:00
d8a0393fe1
print help text to stdout instead of stderr
I looked it up and it would make sense to print it to stdout if the user
explicitly wants the help and to stderr if it's not intentional
2024-02-12 00:06:51 +01:00
55414046ea
moving config parsing before option parsing 2024-02-11 23:51:42 +01:00
c50902e45e
oops forgot 2024-02-11 23:48:03 +01:00
6eaf260d8d
refactor the actual fetch into another file 2024-02-11 23:47:34 +01:00
f3ef45abda
makefile cleanup 2024-02-11 23:46:59 +01:00
022f0462e9
move config and util from lib to prog 2024-02-11 23:27:01 +01:00
4ef40c8aed
what the fuck 2024-02-11 23:25:11 +01:00
79615a946b
add .ccls 2024-02-11 23:24:33 +01:00
d24c1d3767
big refactor of pretty much everything
I still need to make the Makefile better as it's pretty shit right now
but it's alright for now I think
2024-02-11 23:23:50 +01:00
5601945db1
move default config path to config.h 2024-02-11 22:37:03 +01:00
c128b281d8
add config and util to lib 2024-02-11 22:22:17 +01:00
8bfacabcd0
biggest change ever 2024-02-11 22:19:27 +01:00
41d9f87eab
add param names to semver function declarations 2024-02-11 22:13:02 +01:00
8081a8d302
refactor shit out of modfetch.c 2024-02-11 22:11:57 +01:00
d6961a75f3
small makefile fix 2024-02-11 21:59:58 +01:00
7d62a8acc4
fix mod function name shadowing 2024-02-11 20:05:31 +01:00
c08e2398ab
nevermind I can't free getenv() Xddd 2024-02-11 19:53:51 +01:00
cca7224559
free strings after use 2024-02-11 19:50:44 +01:00
c013f078ab
use ~/.config if $XDG_CONFIG_HOME isn't present 2024-02-11 19:49:14 +01:00
274df36583
tick off api versioning in readme 2024-02-11 19:44:32 +01:00
53ff1074ef
make api versioning actually work 2024-02-11 19:37:41 +01:00
49cf173286
add OPTS for user defined options 2024-02-11 18:39:24 +01:00
147f372a9b
update readme building instructions 2024-02-11 18:35:41 +01:00
6b8e1087bd
desktop: add x11 support (thanks krizej) 2024-02-11 18:34:25 +01:00
1640be4666
api versioning for modules 2024-02-11 17:50:10 +01:00
2695a19da4
cpmsv -> sveq
this name represents the function better I think
2024-02-11 17:48:01 +01:00
0e9a7d6bd8
"clean up" makefile
will need to rework it again later
2024-02-11 17:44:35 +01:00
ac6c61e10a
module_name -> name 2024-02-10 14:51:50 +01:00
dfa4ea2f44
move to semver everywhere 2024-02-10 14:11:26 +01:00
92a9fb163f
clean up makefile 2024-02-10 14:09:43 +01:00
d6b05cd7b7
add header guard in mod.h :PPP 2024-02-10 00:33:33 +01:00
ce5cd7ac79
add author for remaining modules 2024-02-10 00:04:36 +01:00
b15ab051f0 separator module (#1)
first module which uses the config :-)

Co-authored-by: krizej <60076189+krizej@users.noreply.github.com>
Reviewed-on: jacekpoz/modfetch#1
Co-authored-by: krizej <krizej@protonmail.com>
Co-committed-by: krizej <krizej@protonmail.com>
2024-02-09 22:48:16 +01:00
4baf04cda0
add another item to todo list 2024-02-09 22:14:57 +01:00
24e2d04c68
motherfucking strncpy 2024-02-09 22:07:49 +01:00
727556295e
explain the program better in the readme 2024-02-09 21:39:37 +01:00
7afaaa2376
am I stupid?? 2024-02-09 21:38:05 +01:00
22 changed files with 797 additions and 337 deletions

22
.ccls Normal file
View file

@ -0,0 +1,22 @@
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 Normal file
View file

@ -0,0 +1 @@
use flake

2
.gitignore vendored
View file

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

View file

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

View file

@ -2,9 +2,15 @@
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
`make` for the main binary
`make` to build everything
`make modfetch` for the main binary
`make modules` for all the default modules
@ -12,7 +18,7 @@ write your own using `mod.h` if you want to
## config
by default `$XDG_CURRENT_DESKTOP/modfetch.conf`
by default `$XDG_CONFIG_HOME/modfetch.conf`, `~/.config/modfetch.conf` if `$XDG_CONFIG_HOME` doesn't exist
format:
```
@ -26,10 +32,11 @@ format:
## TODO
- [x] ~~module api versioning~~
- [ ] module manager (url to module source, compiles and puts the binary somewhere)
- [ ] better config parsing
- [ ] more text positioning options aside from printing text vertically
- [ ] some way for module to throw errors
- [ ] some way for modules to throw errors
- [ ] module related options (list module, print specific module info, etc.)
## license

26
flake.lock Normal file
View file

@ -0,0 +1,26 @@
{
"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
}

36
flake.nix Normal file
View file

@ -0,0 +1,36 @@
{
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
];
};
}
);
};
}

25
include/config.h Normal file
View file

@ -0,0 +1,25 @@
#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

8
include/fetch.h Normal file
View file

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

22
include/mod.h Normal file
View file

@ -0,0 +1,22 @@
#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

17
include/semver.h Normal file
View file

@ -0,0 +1,17 @@
#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

20
include/util.h Normal file
View file

@ -0,0 +1,20 @@
#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
View file

@ -1,10 +0,0 @@
#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);

View file

@ -1,292 +0,0 @@
#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,20 +1,60 @@
#include "../mod.h"
/* DESKTOP - desktop environment / window manager / compositor module for modfetch
*
* author: jacekpoz
* 09 Feb 2024
*/
#include <mod.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef MF_DESKTOP_X11
#include <X11/Xlib.h>
#endif
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"; }
static const semver _version = {
.major = 0,
.minor = 1,
.patch = 0,
};
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;
return 0;
}
const char *get(void) {
char *ret;
if (asprintf(&ret, "Desktop: %s", getenv("XDG_CURRENT_DESKTOP")) < 0) {
char *current_desktop = getenv("XDG_CURRENT_DESKTOP");
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)");
exit(EXIT_FAILURE);
}

View file

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

80
modules/sep.c Normal file
View file

@ -0,0 +1,80 @@
/* 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;
}

126
src/config.c Normal file
View file

@ -0,0 +1,126 @@
#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;
}

38
src/fetch.c Normal file
View file

@ -0,0 +1,38 @@
#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());
}
}

149
src/main.c Normal file
View file

@ -0,0 +1,149 @@
#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;
}

29
src/semver.c Normal file
View file

@ -0,0 +1,29 @@
#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;
}

81
src/util.c Normal file
View file

@ -0,0 +1,81 @@
#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;
}