/* * zFetch - minimalistic system fetcher for Linux platform * * Copyright (c) 2026 Marco Cetica * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #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; }