First upload

This commit is contained in:
2025-11-14 18:50:22 +01:00
commit 2da0e9c97e
5 changed files with 1028 additions and 0 deletions
+55
View File
@@ -0,0 +1,55 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# debug information files
*.dwo
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Marco Cetica <email@marcocetica.com>
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.
+11
View File
@@ -0,0 +1,11 @@
TARGET = zfetch
CC = clang
CFLAGS = -Wall -Wextra -Werror -pedantic-errors -Wwrite-strings -std=c99 -O3
all: clean $(TARGET)
$(TARGET): zfetch.c
$(CC) $(CFLAGS) $^ -o $@
clean:
rm -f *.o *.a $(TARGET)
+2
View File
@@ -0,0 +1,2 @@
# zFetch
Minimalistic system fetcher for Linux platform written in C99.
+939
View File
@@ -0,0 +1,939 @@
/*
* zFetch - minimalistic system fetcher for Linux platform
*
* Copyright (c) 2026 Marco Cetica <email@marcocetica.com>
*
* 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.
*/
#define _DEFAULT_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <sys/statvfs.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <getopt.h>
#include <ctype.h>
#define Z_PATH_MAX 4096
// Colors
#define RESET "\033[0m"
#define BOLD "\033[1m"
#define C1 "\033[31m" // Red
#define C2 "\033[32m" // Green
#define C3 "\033[33m" // Yellow
#define C4 "\033[34m" // Blue
#define C5 "\033[35m" // Magenta
#define C6 "\033[36m" // Cyan
#define C7 "\033[37m" // White
// Normal bars
#define BAR1 "\033[40m " RESET
#define BAR2 "\033[41m " RESET
#define BAR3 "\033[42m " RESET
#define BAR4 "\033[43m " RESET
#define BAR5 "\033[44m " RESET
#define BAR6 "\033[45m " RESET
#define BAR7 "\033[46m " RESET
#define BAR8 "\033[47m " RESET
#define BAR9 "\033[100m " RESET
// Bright bars
#define BAR10 "\033[101m " RESET
#define BAR11 "\033[102m " RESET
#define BAR12 "\033[103m " RESET
#define BAR13 "\033[104m " RESET
#define BAR14 "\033[105m " RESET
#define BAR15 "\033[106m " RESET
#define BAR16 "\033[107m " RESET
// Bitmasks to enable/disable options
#define OPT_HOST 1U << 0
#define OPT_OS 1U << 1
#define OPT_UPTIME 1U << 2
#define OPT_CPU 1U << 3
#define OPT_MEMORY 1U << 4
#define OPT_DISK 1U << 5
#define OPT_IP 1U << 6
#define OPT_LOGO 1U << 7
#define OPT_BARS 1U << 8
#define ARR_LEN(X) (sizeof(X) / sizeof(X[0]))
#define STRCMP(X, Y) (strcmp(X, Y) == 0)
// Operating system logos
static const char *logo_arch[] = {
C6 " /\\",
C6 " / \\",
C6 " /\\ \\",
C4 " / \\",
C4 " / ,, \\",
C4 " / | | -\\",
C4 " /_-'' ''-_\\",
NULL
};
static const char *logo_artix[] = {
C6 " /\\",
C6 " / \\",
C6 " /`'.,\\",
C6 " / ',",
C6 " / ,`\\",
C6 " / ,.'`. \\",
C6 "/.,'` `'.\\",
NULL
};
static const char *logo_debian[] = {
C1 " _____",
C1 " / __ \\",
C1 "| / |",
C1 "| \\___-",
C1 "-_",
C1 " --_",
NULL
};
static const char *logo_fedora[] = {
C4 " ,'''''.",
C4 " | ,. |",
C4 " | | '_'",
C4 " ,....| |.. ",
C4 ".' ,_;| ..' ",
C4 "| | | | ",
C4 "| ',_,' | ",
C4 " '. ,' ",
NULL
};
static const char *logo_gentoo[] = {
C5 " _-----_",
C5 "( \\",
C5 "\\ 0 \\",
C7 " \\ )",
C7 " / _/",
C7 "( _-",
C7 "\\____-",
NULL
};
static const char *logo_linux[] = {
C3 " ___",
C3 " (" C7 ".. " C3 "|",
C3 " (" C5 "<> " C3 "|",
C3 " / " C7 "__ " C3 "\\",
C3 " ( " C7 "/ \\ " C3 "/|",
C3 "_/\\ " C7 "__)/" C3 "_)",
C3 "\\/ " C3 "-____\\/",
NULL
};
static const char *logo_nixos[] = {
C4 " \\\\ \\\\ //",
C4 " ==\\\\__\\\\/ //",
C4 " // \\\\//",
C4 "==// //==",
C4 " //\\\\___//",
C4 "// /\\\\ \\\\==",
C4 " // \\\\ \\\\",
NULL
};
static const char *logo_slackware[] = {
C4 " ________",
C4 " / ______|",
C4 " | |______",
C4 " \\______ \\",
C4 " ______| |",
C4 "| |________/",
C4 "|____________",
NULL
};
static const char *logo_alpine[] = {
C4 " /\\ /\\",
C4 " /" C7 "/ " C4 "\\ \\",
C4 " /" C7 "/ " C4 "\\ \\",
C4 "/" C7 "// " C4 "\\ \\",
C7 "// " C4 "\\ \\",
C4 " \\",
NULL
};
static const char *logo_ubuntu[] = {
C3 " _",
C3 " ---(_)",
C3 " _/ --- \\",
C3 "(_) | |",
C3 " \\ --- _/",
C3 " ---(_)",
NULL
};
static const char *logo_suse[] = {
C2 " _______",
C2 "__| __ \\",
C2 " / .\\ \\",
C2 " \\__/ |",
C2 " _______|",
C2 " \\_______",
C2 "__________/",
NULL
};
static const char *logo_redhat[] = {
C1 " .M.:MMM",
C1 " MMMMMMMMMM.",
C1 " ,MMMMMMMMMMM",
C1 " .MM MMMMMMMMMMM",
C1 "MMMM MMMMMMMMM",
C1 "MMMMMM MM",
C1 " MMMMMMMMM ,MMMM",
C1 " MMMMMMMMMMMMMMMM:",
C1 " `MMMMMMMMMMMM" ,
NULL
};
typedef struct {
const char *name;
const char **logo;
} logo_entry_t;
static const logo_entry_t available_logos[] = {
{ C4 "alpine" RESET, logo_alpine },
{ C6 "arch" RESET, logo_arch },
{ C1 "debian" RESET, logo_debian },
{ C4 "fedora" RESET, logo_fedora },
{ C5 "gentoo" RESET, logo_gentoo },
{ C6 "artix" RESET, logo_artix },
{ C3 "linux" RESET, logo_linux },
{ C4 "nixos" RESET, logo_nixos },
{ C1 "redhat" RESET, logo_redhat },
{ C4 "slackware" RESET, logo_slackware },
{ C2 "suse" RESET, logo_suse },
{ C3 "ubuntu" RESET, logo_ubuntu }
};
typedef enum { false = 0x0, true } bool;
typedef struct {
const char *label;
char value[256];
} line_t;
typedef struct {
char id[64];
char pretty_name[128];
} os_t;
typedef void (*fill_fun_t)(char *, size_t);
// Helper function to trim leading and trailing whitespace
static void trim_whitespace(char *str) {
char *start = str;
char *end = NULL;
size_t len = 0;
while (*start && isspace((unsigned char)*start)) {
start++;
}
len = strlen(start);
if (start != str) {
memmove(str, start, len + 1);
}
if (*str == '\0') { return; }
end = str + strlen(str) - 1;
while (end >= str && isspace((unsigned char)*end)) {
*end-- = '\0';
}
}
// Helper function to remove leading and trailing quotes
static void strip_quotes(char *str) {
const size_t len = strlen(str);
if (len >= 2 && ((str[0] == '"' && str[len - 1] == '"') ||
(str[0] == '\'' && str[len - 1] == '\''))) {
memmove(str, str + 1, len - 2);
str[len - 2] = '\0';
}
}
// Copy into a fixed NULL terminated buffer
static void copy_str(char *dst, size_t dst_size, const char *str) {
if (dst_size == 0) { return; }
// Truncate source string
strncpy(dst, str, dst_size - 1);
dst[dst_size - 1] = '\0';
}
static uint16_t default_opts(void) {
return OPT_HOST | OPT_OS | OPT_UPTIME | OPT_CPU |
OPT_MEMORY | OPT_DISK | OPT_IP | OPT_LOGO | OPT_BARS;
}
static void set_option(uint16_t *options, const char *opt, int value) {
uint16_t flag = 0;
if (STRCMP(opt, "HOST")) { flag = OPT_HOST; }
else if (STRCMP(opt, "OS")) { flag = OPT_OS; }
else if (STRCMP(opt, "UPTIME")) { flag = OPT_UPTIME; }
else if (STRCMP(opt, "CPU")) { flag = OPT_CPU; }
else if (STRCMP(opt, "MEMORY")) { flag = OPT_MEMORY; }
else if (STRCMP(opt, "DISK")) { flag = OPT_DISK; }
else if (STRCMP(opt, "IP")) { flag = OPT_IP; }
else if (STRCMP(opt, "LOGO")) { flag = OPT_LOGO; }
else if (STRCMP(opt, "BARS")) { flag = OPT_BARS; }
if (flag == 0) { return; }
if (value) {
*options |= flag;
} else {
*options &= ~flag;
}
}
static void parse_config_file(uint16_t *options, const char *path) {
FILE *fp;
char line[256];
fp = fopen(path, "r");
if (fp == NULL) {
return;
}
while (fgets(line, sizeof(line), fp) != NULL) {
char *eq;
char *comment;
char key[128];
char value[128];
int parsed;
// Search comment lines. If found, truncate them
comment = strchr(line, '#');
if (comment != NULL) {
*comment = '\0';
}
// Skip empty lines or lines with spaces
// This also skips truncated lines from the previous step
trim_whitespace(line);
if (*line == '\0') { continue; }
// Look for an equal sign. If not found, skip it
// Otherwise, truncate at the equal sign
eq = strchr(line, '=');
if (eq == NULL) {
continue;
} else {
*eq = '\0';
}
// The left side becomes the key while the right side becomes the value
// For example:
// "CPU = 1" => "CPU_\0_1"
// key = trim("CPU_"), value = trim("_1")
copy_str(key, sizeof(key), line);
copy_str(value, sizeof(value), eq + 1);
trim_whitespace(key);
trim_whitespace(value);
// Convert value to integer
parsed = atoi(value);
if ((parsed == 0 || parsed == 1) && *key != '\0') {
set_option(options, key, parsed);
}
}
fclose(fp);
}
static uint16_t load_config(const char *config_path) {
char path[Z_PATH_MAX];
const char *home = getenv("HOME");
uint16_t options = default_opts();
if (config_path != NULL) {
parse_config_file(&options, config_path);
return options;
}
if (home == NULL || *home == '\0') {
return options;
}
snprintf(path, sizeof(path), "%s/.zfetch.conf", home);
parse_config_file(&options, path);
// ~/.config takes precedence over $HOME
snprintf(path, sizeof(path), "%s/.config/zfetch/config", home);
parse_config_file(&options, path);
return options;
}
static void read_os_release(os_t *os_rel) {
FILE *fp = fopen("/etc/os-release", "r");
char line[256];
copy_str(os_rel->id, sizeof(os_rel->id), "linux");
copy_str(os_rel->pretty_name, sizeof(os_rel->pretty_name), "linux");
if (fp == NULL) { return; }
// The parsing process is the same of the config file parser
while (fgets(line, sizeof(line), fp) != NULL) {
char *eq = strchr(line, '=');
char key[64];
char value[192];
if (eq == NULL) { continue; }
*eq = '\0';
copy_str(key, sizeof(key), line);
copy_str(value, sizeof(value), eq + 1);
trim_whitespace(key);
trim_whitespace(value);
strip_quotes(value);
if (STRCMP(key, "ID")) {
copy_str(os_rel->id, sizeof(os_rel->id), value);
} else if (STRCMP(key, "PRETTY_NAME")) {
copy_str(os_rel->pretty_name, sizeof(os_rel->pretty_name), value);
}
}
fclose(fp);
}
static const char **get_logo(const char *id) {
if (id == NULL) { return logo_linux; }
else if (STRCMP(id, "arch")) { return logo_arch; }
else if (STRCMP(id, "artix")) { return logo_artix; }
else if (STRCMP(id, "debian")) { return logo_debian; }
else if (STRCMP(id, "fedora")) { return logo_fedora; }
else if (STRCMP(id, "gentoo")) { return logo_gentoo; }
else if (STRCMP(id, "nixos")) { return logo_nixos; }
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, "opensuse") || STRCMP(id, "opensuse-tumbleweed") ||
STRCMP(id, "opensuse-leap") || STRCMP(id, "sles") ||
STRCMP(id, "suse")) { return logo_suse; }
else if (STRCMP(id, "rhel") || STRCMP(id, "redhat") ||
STRCMP(id, "centos") || STRCMP(id, "rocky") ||
STRCMP(id, "almalinux")) { return logo_redhat; }
return logo_linux;
}
static const char *get_percentage_color(double percent) {
if (percent >= 85.0) { return C1; } // Red
else if (percent >= 60.0) { return C3; } // Yellow/orange
return C2; // Green
}
static const char *get_logo_accent(const char **logo) {
if (logo == logo_arch || logo == logo_artix) {
return C6; // Cyan
} else if (logo == logo_debian || logo == logo_redhat) {
return C1; // Red
} else if (logo == logo_fedora || logo == logo_nixos ||
logo == logo_slackware || logo == logo_alpine) {
return C4; // Blue
} else if (logo == logo_gentoo) {
return C5; // Magenta
} else if (logo == logo_linux || logo == logo_ubuntu) {
return C3; // Yellow
} else if (logo == logo_suse) {
return C2; // Green
}
return C3; // Yellow for generic Tux logo
}
// Measure the printable width
static size_t get_printable_length(const char *str) {
size_t len = 0;
// Count while ignoring color escape sequences
while (*str != '\0') {
if (*str == '\033' && *(str + 1) == '[') {
str += 2;
while (*str != '\0' && *str != 'm') { str++; }
if (*str == 'm') { str++; }
continue;
}
len++;
str++;
}
return len;
}
// Find the widest printable row in order to align the info column nicely
static size_t get_logo_max_width(const char **logo) {
size_t max = 0;
size_t idx = 0;
while (logo[idx] != NULL) {
size_t width = get_printable_length(logo[idx]);
if (width > max) {
max = width;
}
idx++;
}
return max;
}
static void fmt_size(uint64_t used_kib, uint64_t total_kib, char *buf, size_t buf_size) {
const char *unit = "MB";
double divisor = 1024.0;
double used;
double total;
double percent = 0.0;
if (total_kib >= 1024ULL * 1024ULL) {
unit = "GB";
divisor = 1024.0 * 1024.0;
}
used = (double)used_kib / divisor;
total = (double)total_kib / divisor;
if (total_kib != 0) {
percent = ((double)used_kib / (double)total_kib) * 100.0;
}
snprintf(buf, buf_size, "%1.f %s / %.1f %s (%s%.0f%%%s)",
used, unit, total, unit, get_percentage_color(percent), percent, RESET);
}
// Retrieve the first line of a file
static bool get_head(const char *path, const char *prefix, char *buf, size_t buf_size) {
FILE *fp = fopen(path, "r");
char line[512];
if (fp == NULL) { return false; }
while (fgets(line, sizeof(line), fp) != NULL) {
if (strncmp(line, prefix, strlen(prefix)) == 0) {
snprintf(buf, buf_size, "%s", line + strlen(prefix));
trim_whitespace(buf);
fclose(fp);
return true;
}
}
fclose(fp);
return false;
}
static size_t append_line(line_t *lines, size_t count, size_t max_count, const char *label, fill_fun_t fill) {
if (count >= max_count) {
return count;
}
lines[count].label = label;
fill(lines[count].value, sizeof(lines[count].value));
return count + 1;
}
static void fill_os_value(char *buf, size_t buf_size) {
os_t os_release;
read_os_release(&os_release);
snprintf(buf, buf_size, "%s", os_release.pretty_name);
}
static void get_hostname(char *buf, size_t buf_size) {
char hostname[256];
if (gethostname(hostname, sizeof(hostname)) == 0) {
hostname[sizeof(hostname) - 1] = '\0';
snprintf(buf, buf_size, "%s", hostname);
} else {
snprintf(buf, buf_size, "unknown");
}
}
static void get_uptime(char *buf, size_t buf_size) {
FILE *fp = fopen("/proc/uptime", "r");
double seconds = 0.0;
uint64_t total, days, hours, minutes;
if (fp == NULL || fscanf(fp, "%lf", &seconds) != 1) {
if (fp != NULL) {
fclose(fp);
}
snprintf(buf, buf_size, "unknown");
return;
}
fclose(fp);
total = (uint64_t)seconds;
days = total / 86400UL;
hours = (total % 86400UL) / 3600UL;
minutes = (total % 3600UL) / 60UL;
if (days > 0) {
snprintf(buf, buf_size, "%lu day%s, %lu hour%s, %lu min",
days, days == 1 ? "" : "s",
hours, hours == 1 ? "" : "s",
minutes);
} else if (hours > 0) {
snprintf(buf, buf_size, "%lu hour%s, %lu min",
hours, hours == 1 ? "" : "s", minutes);
} else {
snprintf(buf, buf_size, "%lu min", minutes);
}
}
static void get_cpu(char *buf, size_t buf_size) {
if (!get_head("/proc/cpuinfo", "model name\t: ", buf, buf_size) &&
!get_head("/proc/cpuinfo", "Hardware\t: ", buf, buf_size)) {
struct utsname uts;
if (uname(&uts) == 0) {
snprintf(buf, buf_size, "%s", uts.machine);
} else {
snprintf(buf, buf_size, "unknown");
}
}
}
static void get_ram(char *buf, size_t buf_size) {
FILE *fp = fopen("/proc/meminfo", "r");
char key[64];
char unit[32];
unsigned long long value, total = 0, available = 0;
if (fp == NULL) {
snprintf(buf, buf_size, "unknown");
return;
}
while (fscanf(fp, "%63s %llu %31s", key, &value, unit) == 3) {
if (STRCMP(key, "MemTotal:")) {
total = value;
} else if (STRCMP(key, "MemAvailable:")) {
available = value;
}
if (total != 0 && available != 0) { break; }
}
fclose(fp);
if (total == 0) {
snprintf(buf, buf_size, "unknown");
return;
}
fmt_size(total - available, total, buf, buf_size);
}
static void get_disk(char *buf, size_t buf_size) {
struct statvfs fs;
uint64_t total_kib, avail_kib, used_kib;
if (statvfs("/", &fs) != 0) {
snprintf(buf, buf_size, "unknown");
return;
}
total_kib = ((uint64_t)fs.f_blocks * fs.f_frsize) / 1024ULL;
avail_kib = ((uint64_t)fs.f_bfree * fs.f_frsize) / 1024ULL;
used_kib = total_kib - avail_kib;
fmt_size(used_kib, total_kib, buf, buf_size);
}
static void get_ipv4(char *buf, size_t buf_size) {
struct ifaddrs *ifaddr;
struct ifaddrs *ifa;
if (getifaddrs(&ifaddr) != 0) {
snprintf(buf, buf_size, "unknown");
return;
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
struct sockaddr_in *addr;
// Skip loopback, disabled interfaces and non-IPV4 interfaces
if (ifa->ifa_addr == NULL) { continue; }
else if (ifa->ifa_addr->sa_family != AF_INET) { continue; }
else if (!(ifa->ifa_flags & IFF_UP)) { continue; }
else if (!(ifa->ifa_flags & IFF_RUNNING)) { continue; }
else if (ifa->ifa_flags & IFF_LOOPBACK) { continue; }
addr = (struct sockaddr_in *)ifa->ifa_addr;
if (inet_ntop(AF_INET, &addr->sin_addr, buf, buf_size) != NULL) {
freeifaddrs(ifaddr);
return;
}
}
freeifaddrs(ifaddr);
snprintf(buf, buf_size, "unknown");
}
static void get_available_logos(void) {
const size_t logo_count = ARR_LEN(available_logos);
if (logo_count == 0) {
puts("Available logos: none.");
return;
}
printf("Available logos: %s", available_logos[0].name);
for (size_t idx = 1; idx < logo_count; idx++) {
printf(", %s", available_logos[idx].name);
}
puts(".");
}
static void get_options(uint16_t options, const char *config_path) {
// Create a temporary mapping type
struct {
const char *name;
uint16_t flag;
} flags[] = {
{ "HOST", OPT_HOST },
{ "OS", OPT_OS },
{ "UPTIME", OPT_UPTIME },
{ "CPU", OPT_CPU },
{ "MEMORY", OPT_MEMORY },
{ "DISK", OPT_DISK },
{ "IP", OPT_IP },
{ "LOGO", OPT_LOGO },
{ "BARS", OPT_BARS }
};
if (config_path) {
printf(C3 "Using custom config file: '%s'\n" RESET, 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,
enabled ? C2 : C1,
enabled ? "enabled" : "disabled",
RESET);
}
}
static void system_fetcher(const char **logo, const line_t *lines, size_t line_count, bool show_logo, bool show_bars) {
const size_t gap = 3;
const char *accent = get_logo_accent(logo);
size_t logo_lines = 0, logo_width = 0, total_lines;
if (show_logo) {
while (logo[logo_lines] != NULL) {
logo_lines++;
}
logo_width = get_logo_max_width(logo);
}
total_lines = line_count;
if (show_logo && logo_lines > total_lines) {
total_lines = logo_lines;
}
if (show_bars && line_count + 2 > total_lines) {
total_lines = line_count + 2;
}
for (size_t idx = 0; idx < total_lines; idx++) {
if (show_logo && idx < logo_lines) {
printf("%s", logo[idx]);
}
if (idx < line_count) {
if (show_logo && idx < logo_lines) {
const size_t logo_len = get_printable_length(logo[idx]);
const size_t padding = (logo_width > logo_len ? logo_width - logo_len : 0) + gap;
printf("%*s", (int)padding, "");
} else if (show_logo) {
printf("%*s", (int)(logo_width + gap), "");
}
printf("%s" BOLD "%-9s" RESET " %s", accent, lines[idx].label, lines[idx].value);
} else if (show_bars && idx == line_count) {
if (show_logo) {
if (idx < logo_lines) {
const size_t logo_len = get_printable_length(logo[idx]);
const size_t padding = (logo_width > logo_len ? logo_width - logo_len : 0) + gap;
printf("%*s", (int)padding, "");
} else {
printf("%*s", (int)(logo_width + gap), "");
}
}
printf("%s%s%s%s%s%s%s%s", BAR1, BAR2, BAR3, BAR4,
BAR5, BAR6, BAR7, BAR8);
} else if (show_bars && idx == line_count + 1) {
if (show_logo) {
if (idx < logo_lines) {
const size_t logo_len = get_printable_length(logo[idx]);
const size_t padding = (logo_width > logo_len ? logo_width - logo_len : 0) + gap;
printf("%*s", (int)padding, "");
} else {
printf("%*s", (int)(logo_width + gap), "");
}
}
printf("%s%s%s%s%s%s%s%s", BAR9, BAR10, BAR11, BAR12,
BAR13, BAR14, BAR15, BAR16);
}
putchar('\n');
}
}
void print_usage(const char *name) {
printf("%s - minimalistic system fetcher for Linux platform.\n"
"Available options:\n"
"-l, --logo | Manually specify the logo\n"
"-c, --config | Specify a configuration file\n"
"-a, --list-logos | List available logos\n"
"-s, --list-opts | Show which options are enable and which are not\n"
"-h, --help | Show this helper\n\n"
"Project homepage: https://github.com/ceticamarco/zfetch\n"
" https://git.marcocetica.com/marco/zfetch\n", name);
}
int main(int argc, char **argv) {
const struct option long_opts[] = {
{ "logo", required_argument, NULL, 'l' },
{ "config", required_argument, NULL, 'c' },
{ "list-opts", no_argument, NULL, 'a' },
{ "list-opts", no_argument, NULL, 's' },
{ "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
uint16_t options = 0;
os_t os_release;
line_t lines[8];
size_t line_count = 0;
bool list_logos = false, list_opts = false;
int opt;
const char *short_opts = "c:l:ash";
const char **logo;
const char *config_path = NULL, *logo_name = NULL;
opterr = 0; optind = 1;
while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
switch (opt) {
case 'l':
logo_name = optarg;
break;
case 'c':
config_path = optarg;
break;
case 'a':
list_logos = true;
break;
case 's':
list_opts = true;
break;
case 'h':
print_usage(*(argv + 0));
return 0;
default:
print_usage(*(argv + 0));
return 1;
}
}
if (optind < argc) {
print_usage(*(argv + 0));
return 1;
}
if (list_logos) {
get_available_logos();
return 0;
}
options = load_config(config_path);
if (list_opts) {
get_options(options, config_path);
return 0;
}
read_os_release(&os_release);
logo = logo_name != NULL ? get_logo(logo_name) : get_logo(os_release.id);
if (options & OPT_HOST) {
line_count = append_line(lines, line_count, ARR_LEN(lines), "Host:", get_hostname);
}
if (options & OPT_OS) {
line_count = append_line(lines, line_count, ARR_LEN(lines), "OS:", fill_os_value);
}
if (options & OPT_UPTIME) {
line_count = append_line(lines, line_count, ARR_LEN(lines), "Uptime:", get_uptime);
}
if (options & OPT_CPU) {
line_count = append_line(lines, line_count, ARR_LEN(lines), "CPU:", get_cpu);
}
if (options & OPT_MEMORY) {
line_count = append_line(lines, line_count, ARR_LEN(lines), "Memory:", get_ram);
}
if (options & OPT_DISK) {
line_count = append_line(lines, line_count, ARR_LEN(lines), "Disk:", get_disk);
}
if (options & OPT_IP) {
line_count = append_line(lines, line_count, ARR_LEN(lines), "Local IP:", get_ipv4);
}
system_fetcher(logo, lines, line_count, (options & OPT_LOGO) != 0, (options & OPT_BARS) != 0);
return 0;
}