diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml
new file mode 100644
index 0000000..40b10df
--- /dev/null
+++ b/.gitea/workflows/build.yml
@@ -0,0 +1,14 @@
+name: build
+on: [push,pull_request,workflow_dispatch]
+
+jobs:
+ clang-build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Clang
+ run: sudo apt update && sudo apt install -y clang
+
+ - name: Build Datum
+ run: clang zfetch.c -Wall -Wextra -Werror -pedantic-errors -fstack-protector-all -fsanitize=undefined -fsanitize=address -Wwrite-strings -o zfetch
\ No newline at end of file
diff --git a/README.md b/README.md
index b0dfc58..f23fc9d 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,100 @@
-# zFetch
-Minimalistic system fetcher for Linux platform written in C99.
\ No newline at end of file
+
+
zFetch
+

+
Lightweight system information fetcher for Linux.
+
+
+
+
+
+**zFetch** is a lightweight system information fetcher for Linux written in C99.
+Unlike other system fetchers, this tool focuses on providing a simple, easy-to-remember
+interface and configuration syntax, extremely fast startup and a reasonable amount
+of core features.
+
+## Installation
+zFetch consists on a single source file which can be compiled with any modern C
+compiler (Clang/GCC) on a Linux-based system. In order to build it, you can just run
+`gcc zfetch.c -o zfetch` or use the following Makefile rule:
+
+```sh
+$ make clean all
+rm -f *.o *.a zfetch
+clang -Wall -Wextra -Werror -pedantic-errors -Wwrite-strings -std=c99 -O3 zfetch.c -o zfetch
+```
+
+In both cases, you will get a binary file named `zfetch` that you can move wherever you
+want.
+
+## Usage
+As stated before, I've built zFetch to be extremely simple to use since I believe that system
+fetchers play a paramount role on providing essential data to whoever has a lot of machines to manage.
+
+In its most basic form, you can just invoke the program without any additional parameter and it will
+run with all option enabled, that is:
+
+
+
+If you instead would like to granularly enable or disable some features,
+you can do so by creating a configuration file in one of the following paths:
+
+- `$HOME/.zfetch.conf`;
+- `$HOME/.config/zfetch/conf` (**this takes precedence**).
+
+Inside it, you can specify which option to enable and which one to disable:
+
+```conf
+HOST = 1
+OS = 0
+UPTIME = 1
+CPU = 1
+MEMORY = 1
+DISK = 1
+IP = 1
+LOGO = 1
+BARS = 0
+```
+
+Any other line will be considered invalid and silently skipped by the builtin parser.
+To retrieve which options are currently enabled, you can run the program with the `-s` flag, that is:
+
+```sh
+$ ./zfetch --list-opts # or -s
+HOST : ON
+OS : OFF
+UPTIME : ON
+CPU : ON
+MEMORY : ON
+DISK : ON
+IP : ON
+LOGO : ON
+BARS : OFF
+```
+
+You can also dynamically specify a different path by using the `-c` CLI argument:
+
+```sh
+$ ./zfetch -c $PWD/config -s
+Using custom config file: '/home/marco/Projects/zfetch/config'
+HOST : OFF
+OS : OFF
+UPTIME : OFF
+CPU : OFF
+MEMORY : ON
+DISK : ON
+IP : ON
+LOGO : OFF
+BARS : ON
+```
+
+Finally, you can list all supported distribution, using the `--list-logos/-a` flag:
+
+```sh
+$ /zfetch -a
+Available logos: alpine, arch, debian, fedora, mint, gentoo, artix, linux, nixos, redhat, slackware, suse, ubuntu.
+```
+
+
+## License
+
+[MIT](https://choosealicense.com/licenses/mit/)
\ No newline at end of file
diff --git a/imgs/ex1.png b/imgs/ex1.png
new file mode 100644
index 0000000..3af6714
Binary files /dev/null and b/imgs/ex1.png differ
diff --git a/imgs/preview.gif b/imgs/preview.gif
new file mode 100644
index 0000000..0717721
Binary files /dev/null and b/imgs/preview.gif differ
diff --git a/zfetch.c b/zfetch.c
index 335badb..e2f93c5 100644
--- a/zfetch.c
+++ b/zfetch.c
@@ -198,6 +198,17 @@ static const char *logo_ubuntu[] = {
NULL
};
+static const char *logo_mint[] = {
+ C2 " __________",
+ C2 "|_ \\",
+ C2 " |" C7" | _____" C2 " |",
+ C2 " |" C7" | | | |" C2 " |",
+ C2 " |" C7" | | | |" C2 " |",
+ C2 " |" C7" \\_____/" C2 " |",
+ C2 " \\_________/",
+ NULL
+};
+
static const char *logo_suse[] = {
C2 " _______",
C2 "__| __ \\",
@@ -232,6 +243,7 @@ static const logo_entry_t available_logos[] = {
{ C6 "arch" RESET, logo_arch },
{ C1 "debian" RESET, logo_debian },
{ C4 "fedora" RESET, logo_fedora },
+ { C2 "mint" RESET, logo_mint },
{ C5 "gentoo" RESET, logo_gentoo },
{ C6 "artix" RESET, logo_artix },
{ C3 "linux" RESET, logo_linux },
@@ -440,6 +452,7 @@ static const char **get_logo(const char *id) {
else if (STRCMP(id, "slackware")) { return logo_slackware; }
else if (STRCMP(id, "alpine")) { return logo_alpine; }
else if (STRCMP(id, "ubuntu")) { return logo_ubuntu; }
+ else if (STRCMP(id, "mint")) { return logo_mint; }
else if (STRCMP(id, "opensuse") || STRCMP(id, "opensuse-tumbleweed") ||
STRCMP(id, "opensuse-leap") || STRCMP(id, "sles") ||
STRCMP(id, "suse")) { return logo_suse; }
@@ -469,7 +482,7 @@ static const char *get_logo_accent(const char **logo) {
return C5; // Magenta
} else if (logo == logo_linux || logo == logo_ubuntu) {
return C3; // Yellow
- } else if (logo == logo_suse) {
+ } else if (logo == logo_suse || logo == logo_mint) {
return C2; // Green
}
@@ -565,6 +578,35 @@ static inline void str_to_lower(char *str) {
}
}
+static inline size_t fmt_uptime(char *buf, size_t buf_size, size_t offset, uint64_t value, const char *unit) {
+ if (value == 0) {
+ return offset;
+ }
+
+ const char *sep = (offset == 0) ? "" : ", ";
+
+ if (offset >= buf_size) {
+ return offset;
+ }
+
+ int n = snprintf(buf + offset, buf_size - offset,
+ "%s%" PRIu64 " %s%s",
+ sep, value, unit,
+ value == 1 ? "" : "s");
+
+ if (n < 0) {
+ return offset;
+ }
+
+ const size_t written = (size_t)n;
+
+ if (written >= buf_size - offset) {
+ return buf_size;
+ }
+
+ return offset + written;
+}
+
/*
* Features
*/
@@ -589,16 +631,15 @@ static void get_hostname(char *buf, size_t buf_size) {
static void get_uptime(char *buf, size_t buf_size) {
FILE *fp = fopen("/proc/uptime", "r");
uint64_t total = 0;
- uint64_t days, hours, minutes;
if (fp == NULL) {
snprintf(buf, buf_size, "unknown");
return;
}
- // The /proc/uptime has two fields formatted as follows:
+ // The /proc/uptime file has two fields formatted as follows:
// . .
- // We only read the seconds and ignore the fractional part
+ // We only read and ignore
if (fscanf(fp, "%" SCNu64, &total) != 1) {
fclose(fp);
snprintf(buf, buf_size, "unknown");
@@ -607,21 +648,19 @@ static void get_uptime(char *buf, size_t buf_size) {
fclose(fp);
- days = total / 86400ULL;
- hours = (total % 86400ULL) / 3600ULL;
- minutes = (total % 3600ULL) / 60ULL;
+ const uint64_t days = total / 86400ULL;
+ const uint64_t hours = (total % 86400ULL) / 3600ULL;
+ const uint64_t minutes = (total % 3600ULL) / 60ULL;
+ const uint64_t seconds = total % 60ULL;
- if (days > 0) {
- snprintf(buf, buf_size, "%" PRIu64 " day%s, %" PRIu64 " hour%s, %" PRIu64 " min",
- days, days == 1 ? "" : "s",
- hours, hours == 1 ? "" : "s",
- minutes);
- } else if (hours > 0) {
- snprintf(buf, buf_size, "%" PRIu64 " hour%s, %" PRIu64 " min",
- hours, hours == 1 ? "" : "s",
- minutes);
- } else {
- snprintf(buf, buf_size, "%" PRIu64 " min", minutes);
+ size_t offset = 0;
+
+ offset = fmt_uptime(buf, buf_size, offset, days, "day");
+ offset = fmt_uptime(buf, buf_size, offset, hours, "hour");
+ offset = fmt_uptime(buf, buf_size, offset, minutes, "minute");
+
+ if (offset == 0) {
+ offset = fmt_uptime(buf, buf_size, offset, seconds, "second");
}
}
@@ -709,7 +748,7 @@ static size_t get_ips(char entries[][IFACE_ENTRY_LEN], size_t max_entries) {
char ipv4[INET_ADDRSTRLEN];
struct sockaddr_in *sin;
- // Skip loopback, disabled interfaces and non-IPV4 interfaces
+ // Skip loopback, disabled interfaces and non-IPv4 interfaces
if (!ifa->ifa_addr) { continue; }
if (ifa->ifa_addr->sa_family != AF_INET) { continue; }
if (!(ifa->ifa_flags & IFF_UP)) { continue; }
@@ -767,9 +806,9 @@ static void get_options(uint16_t options, const char *config_path) {
for (size_t idx = 0; idx < ARR_LEN(flags); idx++) {
const bool enabled = (options & flags[idx].flag) != 0;
- printf("%-11s %s%s%s\n", flags[idx].name,
+ printf("%-6s : %s%s%s\n", flags[idx].name,
enabled ? C2 : C1,
- enabled ? "enabled" : "disabled",
+ enabled ? "ON" : "OFF",
RESET);
}
}
@@ -865,7 +904,8 @@ int main(int argc, char **argv) {
};
uint16_t options = 0;
os_t os_release;
- line_t lines[64];
+ // number of available options + max number of network interfaces
+ line_t lines[9 + IFACE_ENTRY_LEN];
size_t line_count = 0;
bool list_logos = false, list_opts = false;
int opt;