uwufetch/uwufetch.c

836 lines
31 KiB
C

/*
* UwUfetch is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef UWUFETCH_VERSION
#define UWUFETCH_VERSION "unkown" // needs to be changed by the build script
#endif
#define _GNU_SOURCE // for strcasestr
#include "fetch.h"
#include <getopt.h>
#include <stdbool.h>
// COLORS
#define NORMAL "\x1b[0m"
#define BOLD "\x1b[1m"
#define BLACK "\x1b[30m"
#define RED "\x1b[31m"
#define GREEN "\x1b[32m"
#define SPRING_GREEN "\x1b[38;5;120m"
#define YELLOW "\x1b[33m"
#define BLUE "\x1b[34m"
#define MAGENTA "\x1b[0;35m"
#define CYAN "\x1b[36m"
#define WHITE "\x1b[37m"
#define PINK "\x1b[38;5;201m"
#define LPINK "\x1b[38;5;213m"
#ifdef _WIN32
#define BLOCK_CHAR "\xdb" // block char for colors
char* MOVE_CURSOR = "\033[21C"; // moves the cursor after printing the image or the ascii logo
#else
#define BLOCK_CHAR "\u2587"
char* MOVE_CURSOR = "\033[18C";
#endif // _WIN32
#ifdef __DEBUG__
static bool* verbose_enabled = NULL;
#endif
// all configuration flags available
struct configuration {
struct flags show; // all true by default
bool show_image, // false by default
show_colors; // true by default
bool show_gpu[256];
bool show_gpus; // global gpu toggle
};
// user's config stored on the disk
struct user_config {
char *config_directory, // configuration directory name
*cache_content; // cache file content
int read_enabled, write_enabled;
};
// reads the config file
struct configuration parse_config(struct info* user_info, struct user_config* user_config_file) {
LOG_I("parsing config");
char buffer[256]; // buffer for the current line
// enabling all flags by default
struct configuration config_flags;
memset(&config_flags, true, sizeof(config_flags));
config_flags.show_image = false;
FILE* config = NULL; // config file pointer
if (user_config_file->config_directory == NULL) { // if config directory is not set, try to open the default
if (getenv("HOME") != NULL) {
char homedir[512];
sprintf(homedir, "%s/.config/uwufetch/config", getenv("HOME"));
LOG_V(homedir);
config = fopen(homedir, "r");
if (!config) {
if (getenv("PREFIX") != NULL) {
char prefixed_etc[512];
sprintf(prefixed_etc, "%s/etc/uwufetch/config", getenv("PREFIX"));
LOG_V(prefixed_etc);
config = fopen(prefixed_etc, "r");
} else
config = fopen("/etc/uwufetch/config", "r");
}
}
} else
config = fopen(user_config_file->config_directory, "r");
if (config == NULL) return config_flags; // if config file does not exist, return the defaults
int gpu_cfg_count = 0;
// reading the config file
while (fgets(buffer, sizeof(buffer), config)) {
sscanf(buffer, "distro=%s", user_info->os_name);
if (sscanf(buffer, "image=\"%[^\"]\"", user_info->image_name)) {
if (user_info->image_name[0] == '~') { // replacing the ~ character with the home directory
memmove(&user_info->image_name[0], &user_info->image_name[1], strlen(user_info->image_name)); // remove the first char
char temp[128] = "/home/";
strcat(temp, user_info->user);
strcat(temp, user_info->image_name);
sprintf(user_info->image_name, "%s", temp);
}
config_flags.show_image = 1; // enable the image flag
}
// reading other values
if (sscanf(buffer, "user=%[truefalse]", buffer)) {
config_flags.show.user = !strcmp(buffer, "true");
LOG_V(config_flags.show.user);
}
if (sscanf(buffer, "os=%[truefalse]", buffer)) {
config_flags.show.os = strcmp(buffer, "false");
LOG_V(config_flags.show.os);
}
if (sscanf(buffer, "host=%[truefalse]", buffer)) {
config_flags.show.model = strcmp(buffer, "false");
LOG_V(config_flags.show.model);
}
if (sscanf(buffer, "kernel=%[truefalse]", buffer)) {
config_flags.show.kernel = strcmp(buffer, "false");
LOG_V(config_flags.show.kernel);
}
if (sscanf(buffer, "cpu=%[truefalse]", buffer)) {
config_flags.show.cpu = strcmp(buffer, "false");
LOG_V(config_flags.show.cpu);
}
if (sscanf(buffer, "gpu=%d", &gpu_cfg_count)) {
if (gpu_cfg_count > 255) {
LOG_E("gpu config index is too high, setting it to 255");
gpu_cfg_count = 255;
} else if (gpu_cfg_count < 0) {
LOG_E("gpu config index is too low, setting it to 0");
gpu_cfg_count = 0;
}
config_flags.show_gpu[gpu_cfg_count] = false;
LOG_V(config_flags.show_gpu[gpu_cfg_count]);
}
if (sscanf(buffer, "gpus=%[truefalse]", buffer)) { // global gpu toggle
if (strcmp(buffer, "false") == 0) {
config_flags.show_gpus = false;
config_flags.show.gpu = false; // enable getting gpu info
} else {
config_flags.show_gpus = true;
config_flags.show.gpu = true;
}
LOG_V(config_flags.show_gpus);
LOG_V(config_flags.show.gpu);
}
if (sscanf(buffer, "ram=%[truefalse]", buffer)) {
config_flags.show.ram = strcmp(buffer, "false");
LOG_V(config_flags.show.ram);
}
if (sscanf(buffer, "resolution=%[truefalse]", buffer)) {
config_flags.show.resolution = strcmp(buffer, "false");
LOG_V(config_flags.show.resolution);
}
if (sscanf(buffer, "shell=%[truefalse]", buffer)) {
config_flags.show.shell = strcmp(buffer, "false");
LOG_V(config_flags.show.shell);
}
if (sscanf(buffer, "pkgs=%[truefalse]", buffer)) {
config_flags.show.pkgs = strcmp(buffer, "false");
LOG_V(config_flags.show.pkgs);
}
if (sscanf(buffer, "uptime=%[truefalse]", buffer)) {
config_flags.show.uptime = strcmp(buffer, "false");
LOG_V(config_flags.show.uptime);
}
if (sscanf(buffer, "colors=%[truefalse]", buffer)) {
config_flags.show_colors = strcmp(buffer, "false");
LOG_V(config_flags.show_colors);
}
}
LOG_V(user_info->os_name);
LOG_V(user_info->image_name);
fclose(config);
return config_flags;
}
// prints logo (as an image) of the given system.
int print_image(struct info* user_info) {
LOG_I("printing image");
#ifndef __IPHONE__
char command[256];
if (strlen(user_info->image_name) < 1) {
char* repl_str = strcmp(user_info->os_name, "android") == 0 ? "/data/data/com.termux/files/usr/lib/uwufetch/%s.png"
: strcmp(user_info->os_name, "macos") == 0 ? "/usr/local/lib/uwufetch/%s.png"
: "/usr/lib/uwufetch/%s.png";
sprintf(user_info->image_name, repl_str, user_info->os_name); // image command for android
LOG_V(user_info->image_name);
}
sprintf(command, "viu -t -w 18 -h 9 %s 2> /dev/null", user_info->image_name); // creating the command to show the image
LOG_V(command);
if (system(command) != 0) // if viu is not installed or the image is missing
printf("\033[0E\033[3C%s\n"
" There was an\n"
" error: viu\n"
" is not installed\n"
" or the image file\n"
" was not found\n"
" see IMAGES.md\n"
" for more info.\n\n",
RED);
#else
// unfortunately, the iOS stdlib does not have system(); because it reports that it is not available under iOS during compilation
printf("\033[0E\033[3C%s\n"
" There was an\n"
" error: images\n"
" are currently\n"
" disabled on iOS.\n\n",
RED);
#endif
return 9;
}
// Replaces all terms in a string with another term.
void replace(char* original, char* search, char* replacer) {
char* ch;
char buffer[1024];
int offset = 0;
while ((ch = strstr(original + offset, search))) {
strncpy(buffer, original, ch - original);
buffer[ch - original] = 0;
sprintf(buffer + (ch - original), "%s%s", replacer, ch + strlen(search));
original[0] = 0;
strcpy(original, buffer);
offset = ch - original + strlen(replacer);
}
}
// Replaces all terms in a string with another term, case insensitive
void replace_ignorecase(char* original, char* search, char* replacer) {
char* ch;
char buffer[1024];
int offset = 0;
#ifdef _WIN32
#define strcasestr(o, s) strstr(o, s)
#endif
while ((ch = strcasestr(original + offset, search))) {
strncpy(buffer, original, ch - original);
buffer[ch - original] = 0;
sprintf(buffer + (ch - original), "%s%s", replacer, ch + strlen(search));
original[0] = 0;
strcpy(original, buffer);
offset = ch - original + strlen(replacer);
}
}
#ifdef _WIN32
// windows sucks and hasn't a strstep, so I copied one from https://stackoverflow.com/questions/8512958/is-there-a-windows-variant-of-strsep-function
char* strsep(char** stringp, const char* delim) {
char* start = *stringp;
char* p;
p = (start != NULL) ? strpbrk(start, delim) : NULL;
if (p == NULL)
*stringp = NULL;
else {
*p = '\0';
*stringp = p + 1;
}
return start;
}
#endif
// uwufies distro name
void uwu_name(struct info* user_info) {
#define STRING_TO_UWU(original, uwufied) \
if (strcmp(user_info->os_name, original) == 0) sprintf(user_info->os_name, "%s", uwufied)
// linux
STRING_TO_UWU("alpine", "Nyalpine");
else STRING_TO_UWU("amogos", "AmogOwOS");
else STRING_TO_UWU("android", "Nyandroid");
else STRING_TO_UWU("arch", "Nyarch Linuwu");
else STRING_TO_UWU("arcolinux", "ArcOwO Linuwu");
else STRING_TO_UWU("artix", "Nyartix Linuwu");
else STRING_TO_UWU("debian", "Debinyan");
else STRING_TO_UWU("devuan", "Devunyan");
else STRING_TO_UWU("deepin", "Dewepyn");
else STRING_TO_UWU("endeavouros", "endeavOwO");
else STRING_TO_UWU("EndeavourOS", "endeavOwO");
else STRING_TO_UWU("fedora", "Fedowa");
else STRING_TO_UWU("femboyos", "FemboyOWOS");
else STRING_TO_UWU("gentoo", "GentOwO");
else STRING_TO_UWU("gnu", "gnUwU");
else STRING_TO_UWU("guix", "gnUwU gUwUix");
else STRING_TO_UWU("linuxmint", "LinUWU Miwint");
else STRING_TO_UWU("manjaro", "Myanjawo");
else STRING_TO_UWU("manjaro-arm", "Myanjawo AWM");
else STRING_TO_UWU("neon", "KDE NeOwOn");
else STRING_TO_UWU("nixos", "nixOwOs");
else STRING_TO_UWU("opensuse-leap", "OwOpenSUSE Leap");
else STRING_TO_UWU("opensuse-tumbleweed", "OwOpenSUSE Tumbleweed");
else STRING_TO_UWU("pop", "PopOwOS");
else STRING_TO_UWU("raspbian", "RaspNyan");
else STRING_TO_UWU("rocky", "Wocky Linuwu");
else STRING_TO_UWU("slackware", "Swackwawe");
else STRING_TO_UWU("solus", "sOwOlus");
else STRING_TO_UWU("ubuntu", "Uwuntu");
else STRING_TO_UWU("void", "OwOid");
else STRING_TO_UWU("xerolinux", "xuwulinux");
// BSD
else STRING_TO_UWU("freebsd", "FweeBSD");
else STRING_TO_UWU("openbsd", "OwOpenBSD");
// Apple family
else STRING_TO_UWU("macos", "macOwOS");
else STRING_TO_UWU("ios", "iOwOS");
// Windows
else STRING_TO_UWU("windows", "WinyandOwOws");
else sprintf(user_info->os_name, "%s", "unknown");
#undef STRING_TO_UWU
}
// uwufies kernel name
void uwu_kernel(char* kernel) {
#define KERNEL_TO_UWU(str, original, uwufied) \
if (strcmp(str, original) == 0) sprintf(str, "%s", uwufied)
LOG_I("uwufing kernel");
char* temp_kernel = kernel;
char* token;
char splitted[16][128] = {};
int count = 0;
while ((token = strsep(&temp_kernel, " "))) { // split kernel name
strcpy(splitted[count], token);
count++;
}
strcpy(kernel, "");
for (int i = 0; i < 16; i++) {
// replace kernel name with uwufied version
KERNEL_TO_UWU(splitted[i], "Linux", "Linuwu");
else KERNEL_TO_UWU(splitted[i], "linux", "linuwu");
else KERNEL_TO_UWU(splitted[i], "alpine", "Nyalpine");
else KERNEL_TO_UWU(splitted[i], "amogos", "AmogOwOS");
else KERNEL_TO_UWU(splitted[i], "android", "Nyandroid");
else KERNEL_TO_UWU(splitted[i], "arch", "Nyarch Linuwu");
else KERNEL_TO_UWU(splitted[i], "artix", "Nyartix Linuwu");
else KERNEL_TO_UWU(splitted[i], "debian", "Debinyan");
else KERNEL_TO_UWU(splitted[i], "deepin", "Dewepyn");
else KERNEL_TO_UWU(splitted[i], "endeavouros", "endeavOwO");
else KERNEL_TO_UWU(splitted[i], "EndeavourOS", "endeavOwO");
else KERNEL_TO_UWU(splitted[i], "fedora", "Fedowa");
else KERNEL_TO_UWU(splitted[i], "femboyos", "FemboyOWOS");
else KERNEL_TO_UWU(splitted[i], "gentoo", "GentOwO");
else KERNEL_TO_UWU(splitted[i], "gnu", "gnUwU");
else KERNEL_TO_UWU(splitted[i], "guix", "gnUwU gUwUix");
else KERNEL_TO_UWU(splitted[i], "linuxmint", "LinUWU Miwint");
else KERNEL_TO_UWU(splitted[i], "manjaro", "Myanjawo");
else KERNEL_TO_UWU(splitted[i], "manjaro-arm", "Myanjawo AWM");
else KERNEL_TO_UWU(splitted[i], "neon", "KDE NeOwOn");
else KERNEL_TO_UWU(splitted[i], "nixos", "nixOwOs");
else KERNEL_TO_UWU(splitted[i], "opensuse-leap", "OwOpenSUSE Leap");
else KERNEL_TO_UWU(splitted[i], "opensuse-tumbleweed", "OwOpenSUSE Tumbleweed");
else KERNEL_TO_UWU(splitted[i], "pop", "PopOwOS");
else KERNEL_TO_UWU(splitted[i], "raspbian", "RaspNyan");
else KERNEL_TO_UWU(splitted[i], "rocky", "Wocky Linuwu");
else KERNEL_TO_UWU(splitted[i], "slackware", "Swackwawe");
else KERNEL_TO_UWU(splitted[i], "solus", "sOwOlus");
else KERNEL_TO_UWU(splitted[i], "ubuntu", "Uwuntu");
else KERNEL_TO_UWU(splitted[i], "void", "OwOid");
else KERNEL_TO_UWU(splitted[i], "xerolinux", "xuwulinux");
// BSD
else KERNEL_TO_UWU(splitted[i], "freebsd", "FweeBSD");
else KERNEL_TO_UWU(splitted[i], "openbsd", "OwOpenBSD");
// Apple family
else KERNEL_TO_UWU(splitted[i], "macos", "macOwOS");
else KERNEL_TO_UWU(splitted[i], "ios", "iOwOS");
// Windows
else KERNEL_TO_UWU(splitted[i], "windows", "WinyandOwOws");
if (i != 0) strcat(kernel, " ");
strcat(kernel, splitted[i]);
}
#undef KERNEL_TO_UWU
LOG_V(kernel);
}
// uwufies hardware names
void uwu_hw(char* hwname) {
LOG_I("uwufing hardware")
#define HW_TO_UWU(original, uwuified) replace_ignorecase(hwname, original, uwuified);
HW_TO_UWU("lenovo", "LenOwO")
HW_TO_UWU("cpu", "CPUwU");
HW_TO_UWU("core", "Cowe");
HW_TO_UWU("gpu", "GPUwU")
HW_TO_UWU("graphics", "Gwaphics")
HW_TO_UWU("corporation", "COwOpowation")
HW_TO_UWU("nvidia", "NyaVIDIA")
HW_TO_UWU("mobile", "Mwobile")
HW_TO_UWU("intel", "Inteww")
HW_TO_UWU("celeron", "Celewon")
HW_TO_UWU("radeon", "Radenyan")
HW_TO_UWU("geforce", "GeFOwOce")
HW_TO_UWU("raspberry", "Nyasberry")
HW_TO_UWU("broadcom", "Bwoadcom")
HW_TO_UWU("motorola", "MotOwOwa")
HW_TO_UWU("proliant", "ProLinyant")
HW_TO_UWU("poweredge", "POwOwEdge")
HW_TO_UWU("apple", "Nyapple")
HW_TO_UWU("electronic", "ElectrOwOnic")
HW_TO_UWU("processor", "Pwocessow")
HW_TO_UWU("microsoft", "MicOwOsoft")
HW_TO_UWU("ryzen", "Wyzen")
HW_TO_UWU("advanced", "Adwanced")
HW_TO_UWU("micro", "Micwo")
HW_TO_UWU("devices", "Dewices")
HW_TO_UWU("inc.", "Nyanc.")
HW_TO_UWU("lucienne", "Lucienyan")
HW_TO_UWU("tuxedo", "TUWUXEDO")
HW_TO_UWU("aura", "Uwura")
#undef HW_TO_UWU
}
// uwufies package manager names
void uwu_pkgman(char* pkgman_name) {
LOG_I("uwufing package managers")
#define PKGMAN_TO_UWU(original, uwuified) replace_ignorecase(pkgman_name, original, uwuified);
// these package managers do not have edits yet:
// apk, apt, guix, nix, pkg, xbps
PKGMAN_TO_UWU("brew-cask", "bwew-cawsk");
PKGMAN_TO_UWU("brew-cellar", "bwew-cewwaw");
PKGMAN_TO_UWU("emerge", "emewge");
PKGMAN_TO_UWU("flatpak", "fwatpakkies");
PKGMAN_TO_UWU("pacman", "pacnyan");
PKGMAN_TO_UWU("port", "powt");
PKGMAN_TO_UWU("snap", "snyap");
#undef PKGMAN_TO_UWU
}
// uwufies everything
void uwufy_all(struct info* user_info) {
LOG_I("uwufing everything");
if (strcmp(user_info->os_name, "windows"))
MOVE_CURSOR = "\033[21C"; // to print windows logo on not windows systems
uwu_kernel(user_info->kernel);
for (int i = 0; user_info->gpu_model[i][0]; i++) uwu_hw(user_info->gpu_model[i]);
uwu_hw(user_info->cpu_model);
LOG_V(user_info->cpu_model);
uwu_hw(user_info->model);
LOG_V(user_info->model);
uwu_pkgman(user_info->pkgman_name);
LOG_V(user_info->pkgman_name);
}
// prints all the collected info and returns the number of printed lines
int print_info(struct configuration* config_flags, struct info* user_info) {
int line_count = 0;
#ifdef _WIN32
// prints without overflowing the terminal width
#define responsively_printf(buf, format, ...) \
{ \
sprintf(buf, format, __VA_ARGS__); \
printf("%.*s\n", user_info->ws_col - 4, buf); \
line_count++; \
}
#else // _WIN32
// prints without overflowing the terminal width
#define responsively_printf(buf, format, ...) \
{ \
sprintf(buf, format, __VA_ARGS__); \
printf("%.*s\n", user_info->win.ws_col - 4, buf); \
line_count++; \
}
#endif // _WIN32
char print_buf[1024]; // for responsively print
// print collected info - from host to cpu info
if (config_flags->show.user)
responsively_printf(print_buf, "%s%s%s%s@%s", MOVE_CURSOR, NORMAL, BOLD, user_info->user, user_info->host);
uwu_name(user_info);
if (config_flags->show.os)
responsively_printf(print_buf, "%s%s%sOWOS %s%s", MOVE_CURSOR, NORMAL, BOLD, NORMAL, user_info->os_name);
if (config_flags->show.model)
responsively_printf(print_buf, "%s%s%sMOWODEL %s%s", MOVE_CURSOR, NORMAL, BOLD, NORMAL, user_info->model);
if (config_flags->show.kernel)
responsively_printf(print_buf, "%s%s%sKEWNEL %s%s", MOVE_CURSOR, NORMAL, BOLD, NORMAL, user_info->kernel);
if (config_flags->show.cpu)
responsively_printf(print_buf, "%s%s%sCPUWU %s%s", MOVE_CURSOR, NORMAL, BOLD, NORMAL, user_info->cpu_model);
for (int i = 0; i < 256; i++) {
if (config_flags->show_gpu[i])
if (user_info->gpu_model[i][0])
responsively_printf(print_buf, "%s%s%sGPUWU %s%s", MOVE_CURSOR, NORMAL, BOLD, NORMAL, user_info->gpu_model[i]);
}
if (config_flags->show.ram) // print ram
responsively_printf(print_buf, "%s%s%sMEMOWY %s%i MiB/%i MiB", MOVE_CURSOR, NORMAL, BOLD, NORMAL, (user_info->ram_used), user_info->ram_total);
if (config_flags->show.resolution) // print resolution
if (user_info->screen_width != 0 || user_info->screen_height != 0)
responsively_printf(print_buf, "%s%s%sWESOWUTION%s %dx%d", MOVE_CURSOR, NORMAL, BOLD, NORMAL, user_info->screen_width, user_info->screen_height);
if (config_flags->show.shell) // print shell name
responsively_printf(print_buf, "%s%s%sSHEWW %s%s", MOVE_CURSOR, NORMAL, BOLD, NORMAL, user_info->shell);
if (config_flags->show.pkgs) // print pkgs
responsively_printf(print_buf, "%s%s%sPKGS %s%d: %s", MOVE_CURSOR, NORMAL, BOLD, NORMAL, user_info->pkgs, user_info->pkgman_name);
// #endif
if (config_flags->show.uptime) {
switch (user_info->uptime) { // formatting the uptime which is store in seconds
case 0 ... 3599:
responsively_printf(print_buf, "%s%s%sUWUPTIME %s%lim", MOVE_CURSOR, NORMAL, BOLD, NORMAL, user_info->uptime / 60 % 60);
break;
case 3600 ... 86399:
responsively_printf(print_buf, "%s%s%sUWUPTIME %s%lih, %lim", MOVE_CURSOR, NORMAL, BOLD, NORMAL, user_info->uptime / 3600, user_info->uptime / 60 % 60);
break;
default:
responsively_printf(print_buf, "%s%s%sUWUPTIME %s%lid, %lih, %lim", MOVE_CURSOR, NORMAL, BOLD, NORMAL, user_info->uptime / 86400, user_info->uptime / 3600 % 24, user_info->uptime / 60 % 60);
}
}
// clang-format off
if (config_flags->show_colors)
printf("%s" BOLD BLACK BLOCK_CHAR BLOCK_CHAR RED BLOCK_CHAR
BLOCK_CHAR GREEN BLOCK_CHAR BLOCK_CHAR YELLOW
BLOCK_CHAR BLOCK_CHAR BLUE BLOCK_CHAR BLOCK_CHAR
MAGENTA BLOCK_CHAR BLOCK_CHAR CYAN BLOCK_CHAR
BLOCK_CHAR WHITE BLOCK_CHAR BLOCK_CHAR NORMAL "\n", MOVE_CURSOR);
// clang-format on
return line_count;
}
// writes cache to cache file
void write_cache(struct info* user_info) {
LOG_I("writing cache");
char cache_file[512];
sprintf(cache_file, "%s/.cache/uwufetch.cache", getenv("HOME")); // default cache file location
LOG_V(cache_file);
FILE* cache_fp = fopen(cache_file, "w");
if (cache_fp == NULL) {
LOG_E("Failed to write to %s!", cache_file);
return;
}
// writing all info to the cache file
fprintf( // writing most of the values to config file
cache_fp,
"user=%s\nhost=%s\nversion_name=%s\nhost_model=%s\nkernel=%s\ncpu=%"
"s\nscreen_width=%d\nscreen_height=%d\nshell=%s\npkgs=%d\npkgman_name=%"
"s\n",
user_info->user, user_info->host, user_info->os_name, user_info->model, user_info->kernel,
user_info->cpu_model, user_info->screen_width, user_info->screen_height, user_info->shell,
user_info->pkgs, user_info->pkgman_name);
for (int i = 0; user_info->gpu_model[i][0]; i++) // writing gpu names to file
fprintf(cache_fp, "gpu=%s\n", user_info->gpu_model[i]);
fclose(cache_fp);
return;
}
// reads cache file if it exists
int read_cache(struct info* user_info) {
LOG_I("reading cache");
char cache_file[512];
sprintf(cache_file, "%s/.cache/uwufetch.cache", getenv("HOME")); // cache file location
LOG_V(cache_file);
FILE* cache_fp = fopen(cache_file, "r");
if (cache_fp == NULL) return 0;
char buffer[256]; // line buffer
int gpuc = 0; // gpu counter
while (fgets(buffer, sizeof(buffer), cache_fp)) { // reading the file
sscanf(buffer, "user=%99[^\n]", user_info->user);
sscanf(buffer, "host=%99[^\n]", user_info->host);
sscanf(buffer, "version_name=%99[^\n]", user_info->os_name);
sscanf(buffer, "host_model=%99[^\n]", user_info->model);
sscanf(buffer, "kernel=%99[^\n]", user_info->kernel);
sscanf(buffer, "cpu=%99[^\n]", user_info->cpu_model);
if (sscanf(buffer, "gpu=%99[^\n]", user_info->gpu_model[gpuc]) != 0) gpuc++;
sscanf(buffer, "screen_width=%i", &user_info->screen_width);
sscanf(buffer, "screen_height=%i", &user_info->screen_height);
sscanf(buffer, "shell=%99[^\n]", user_info->shell);
sscanf(buffer, "pkgs=%i", &user_info->pkgs);
sscanf(buffer, "pkgman_name=%99[^\n]", user_info->pkgman_name);
}
LOG_V(user_info->user);
LOG_V(user_info->host);
LOG_V(user_info->os_name);
LOG_V(user_info->model);
LOG_V(user_info->kernel);
LOG_V(user_info->cpu_model);
LOG_V(user_info->gpu_model[gpuc]);
LOG_V(user_info->screen_width);
LOG_V(user_info->screen_height);
LOG_V(user_info->shell);
LOG_V(user_info->pkgs);
LOG_V(user_info->pkgman_name);
fclose(cache_fp);
return 1;
}
// prints logo (as ascii art) of the given system.
int print_ascii(struct info* user_info) {
FILE* file;
char ascii_file[1024];
// First tries to get ascii art file from local directory. Useful for debugging
sprintf(ascii_file, "./res/ascii/%s.txt", user_info->os_name);
LOG_V(ascii_file);
file = fopen(ascii_file, "r");
if (!file) { // if the file does not exist in the local directory, open it from the installation directory
if (strcmp(user_info->os_name, "android") == 0)
sprintf(ascii_file, "/data/data/com.termux/files/usr/lib/uwufetch/ascii/%s.txt", user_info->os_name);
else if (strcmp(user_info->os_name, "macos") == 0)
sprintf(ascii_file, "/usr/local/lib/uwufetch/ascii/%s.txt", user_info->os_name);
else
sprintf(ascii_file, "/usr/lib/uwufetch/ascii/%s.txt", user_info->os_name);
LOG_V(ascii_file);
file = fopen(ascii_file, "r");
if (!file) {
// Prevent infinite loops
if (strcmp(user_info->os_name, "unknown") == 0) {
LOG_E("No\nunknown\nascii\nfile\n\n\n\n");
return 7;
}
sprintf(user_info->os_name, "unknown"); // current os is not supported
LOG_V(user_info->os_name);
return print_ascii(user_info);
}
}
char buffer[256]; // line buffer
int line_count = 1;
printf("\n");
while (fgets(buffer, 256, file)) { // replacing color placecholders
replace(buffer, "{NORMAL}", NORMAL);
replace(buffer, "{BOLD}", BOLD);
replace(buffer, "{BLACK}", BLACK);
replace(buffer, "{RED}", RED);
replace(buffer, "{GREEN}", GREEN);
replace(buffer, "{SPRING_GREEN}", SPRING_GREEN);
replace(buffer, "{YELLOW}", YELLOW);
replace(buffer, "{BLUE}", BLUE);
replace(buffer, "{MAGENTA}", MAGENTA);
replace(buffer, "{CYAN}", CYAN);
replace(buffer, "{WHITE}", WHITE);
replace(buffer, "{PINK}", PINK);
replace(buffer, "{LPINK}", LPINK);
replace(buffer, "{BLOCK}", BLOCK_CHAR);
replace(buffer, "{BLOCK_VERTICAL}", BLOCK_CHAR);
replace(buffer, "{BACKGROUND_GREEN}", "\e[0;42m");
replace(buffer, "{BACKGROUND_RED}", "\e[0;41m");
replace(buffer, "{BACKGROUND_WHITE}", "\e[0;47m");
printf("%s", buffer); // print the line after setting the color
line_count++;
}
// Always set color to NORMAL, so there's no need to do this in every ascii file.
printf(NORMAL);
fclose(file);
return line_count;
}
/* prints distribution list
distributions are listed by distribution branch
to make the output easier to understand by the user.*/
void list(char* arg) {
LOG_I("printing supported distro list");
// clang-format off
printf("%s -d <options>\n"
" Available distributions:\n"
" "BLUE"Arch linux "NORMAL"based:\n"
" "BLUE"arch, arcolinux, "MAGENTA"artix, endeavouros "GREEN"manjaro, manjaro-arm, "BLUE"xerolinux\n\n"
" "RED"Debian/"YELLOW"Ubuntu "NORMAL"based:\n"
" "RED"amogos, debian, deepin, "GREEN"linuxmint, neon, "BLUE"pop, "RED"raspbian "YELLOW"ubuntu\n\n"
" "RED"BSD "NORMAL"based:\n"
" "RED"freebsd, "YELLOW"openbsd, "GREEN"m"YELLOW"a"RED"c"PINK"o"BLUE"s, "WHITE"ios\n\n"
" "RED"RHEL "NORMAL"based:\n"
" "BLUE"fedora, "GREEN"rocky\n\n"
" "NORMAL"Other/spare distributions:\n"
" "BLUE"alpine, "PINK"femboyos, gentoo, "MAGENTA"slackware, "WHITE"solus, "GREEN"void, opensuse-leap, android, "YELLOW"gnu, guix, "BLUE"windows, "WHITE"unknown\n\n",
arg); // Other/spare distributions colors
// clang-format on
}
// prints the usage
void usage(char* arg) {
LOG_I("printing usage");
printf("Usage: %s <args>\n"
" -c --config use custom config path\n"
" -d, --distro lets you choose the logo to print\n"
" -h, --help prints this help page\n"
#ifndef __IPHONE__
" -i, --image prints logo as image and use a custom image "
"if provided\n"
" %sworks in most terminals\n"
#else
" -i, --image prints logo as image and use a custom image "
"if provided\n"
" %sdisabled under iOS\n"
#endif
" read README.md for more info%s\n"
" -l, --list lists all supported distributions\n"
" -V, --version prints the current uwufetch version\n"
#ifdef __DEBUG__
" -v, --verbose logs everything\n"
#endif
" -w, --write-cache writes to the cache file (~/.cache/uwufetch.cache)\n"
" -r, --read-cache reads from the cache file (~/.cache/uwufetch.cache)\n",
arg,
#ifndef __IPHONE__
BLUE,
#else
RED,
#endif
NORMAL);
}
// the main function is on the bottom of the file to avoid double function declarations
int main(int argc, char* argv[]) {
#ifdef __DEBUG__
verbose_enabled = get_verbose_handle();
#endif
struct user_config user_config_file = {0};
struct info user_info = {0};
struct configuration config_flags = parse_config(&user_info, &user_config_file);
char* custom_distro_name = NULL;
char* custom_image_name = NULL;
#ifdef _WIN32
// packages disabled by default because chocolatey is too slow
config_flags.show.pkgs = 0;
#endif
int opt = 0;
struct option long_options[] = {
{"config", required_argument, NULL, 'c'},
{"distro", required_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{"image", optional_argument, NULL, 'i'},
{"list", no_argument, NULL, 'l'},
{"read-cache", no_argument, NULL, 'r'},
{"version", no_argument, NULL, 'V'},
#ifdef __DEBUG__
{"verbose", no_argument, NULL, 'v'},
#endif
{"write-cache", no_argument, NULL, 'w'},
{0}};
#ifdef __DEBUG__
#define OPT_STRING "c:d:hi::lrVvw"
#else
#define OPT_STRING "c:d:hi::lrVw"
#endif
// reading cmdline options
while ((opt = getopt_long(argc, argv, OPT_STRING, long_options, NULL)) != -1) {
switch (opt) {
case 'c': // set the config directory
user_config_file.config_directory = optarg;
config_flags = parse_config(&user_info, &user_config_file);
break;
case 'd': // set the distribution name
custom_distro_name = optarg;
break;
case 'h':
usage(argv[0]);
return 0;
case 'i': // set ascii logo as output
config_flags.show_image = true;
if (argv[optind]) custom_image_name = argv[optind];
break;
case 'l':
list(argv[0]);
return 0;
case 'r':
user_config_file.read_enabled = true;
break;
case 'V':
printf("UwUfetch version %s\n", UWUFETCH_VERSION);
return 0;
#ifdef __DEBUG__
case 'v':
*verbose_enabled = true;
LOG_I("version %s", UWUFETCH_VERSION);
break;
#endif
case 'w':
user_config_file.write_enabled = true;
break;
default:
return 1;
}
}
if (user_config_file.read_enabled) {
// if no cache file found write to it
if (!read_cache(&user_info)) {
user_config_file.read_enabled = false;
user_config_file.write_enabled = true;
} else {
int buf_sz = 256;
char buffer[buf_sz]; // line buffer
struct thread_varg vargp = {
buffer, &user_info, NULL, {true, true, true, true, true, true, true, true}};
if (config_flags.show.ram) get_ram(&vargp);
if (config_flags.show.uptime) {
LOG_I("getting additional not-cached info");
get_sys(&user_info);
get_upt(&vargp);
}
}
}
if (!user_config_file.read_enabled)
get_info(config_flags.show, &user_info);
LOG_V(user_info.gpu_model[1]);
if (user_config_file.write_enabled) {
write_cache(&user_info);
}
if (custom_distro_name) sprintf(user_info.os_name, "%s", custom_distro_name);
if (custom_image_name) sprintf(user_info.image_name, "%s", custom_image_name);
uwufy_all(&user_info);
// print ascii or image and align cursor for print_info()
printf("\033[%dA", config_flags.show_image ? print_image(&user_info) : print_ascii(&user_info));
// print info and move cursor down if the number of printed lines is smaller that the default image height
int to_move = 9 - print_info(&config_flags, &user_info);
printf("\033[%d%c", to_move < 0 ? -to_move : to_move, to_move < 0 ? 'A' : 'B');
LOG_I("Execution completed successfully!");
return 0;
}