Files
fastfetch/src/fastfetch.c
T

1305 lines
42 KiB
C
Raw Normal View History

2021-02-18 22:25:36 +01:00
#include "fastfetch.h"
#include "common/commandoption.h"
#include "common/printing.h"
#include "common/parsing.h"
#include "common/io/io.h"
2022-12-14 11:30:50 +08:00
#include "common/time.h"
2023-06-11 00:51:29 +08:00
#include "common/jsonconfig.h"
2023-08-21 09:22:03 +08:00
#include "detection/version/version.h"
2023-02-01 15:56:25 +01:00
#include "util/stringUtils.h"
#include "logo/logo.h"
2023-07-28 14:12:48 +08:00
#include "fastfetch_datatext.h"
2021-02-18 22:25:36 +01:00
2022-06-18 14:25:31 +02:00
#include <stdlib.h>
2022-08-21 21:08:11 +02:00
#include <ctype.h>
2021-04-09 14:07:05 +02:00
#include <string.h>
2022-12-14 11:30:50 +08:00
#include <inttypes.h>
2021-02-22 16:52:51 +01:00
2022-10-13 18:12:54 +08:00
#ifdef WIN32
#include "util/windows/getline.h"
#endif
2023-03-07 16:13:53 +08:00
#include "modules/modules.h"
2023-03-07 14:56:13 +08:00
2021-03-21 11:18:06 +01:00
static void constructAndPrintCommandHelpFormat(const char* name, const char* def, uint32_t numArgs, ...)
2021-03-19 20:57:07 +01:00
{
2021-03-21 11:18:06 +01:00
va_list argp;
va_start(argp, numArgs);
2021-03-19 20:57:07 +01:00
2021-03-21 11:18:06 +01:00
printf("--%s-format:\n", name);
printf("Sets the format string for %s output.\n", name);
puts("To see how a format string is constructed, take a look at \"fastfetch --help format\".");
2021-05-15 08:39:20 +02:00
puts("The following values are passed:");
2021-03-19 20:57:07 +01:00
2021-03-21 11:18:06 +01:00
for(uint32_t i = 1; i <= numArgs; i++)
printf(" {%u}: %s\n", i, va_arg(argp, const char*));
2021-03-19 20:57:07 +01:00
printf("The default is something similar to \"%s\".\n", def);
2021-03-19 20:57:07 +01:00
2021-03-21 11:18:06 +01:00
va_end(argp);
2021-03-11 22:15:48 +01:00
}
2021-03-11 19:40:46 +01:00
static inline void printCommandHelp(const char* command)
2021-02-20 19:32:57 +01:00
{
2021-10-22 21:53:29 +02:00
if(command == NULL)
2022-12-12 13:43:10 +01:00
puts(FASTFETCH_DATATEXT_HELP);
2023-08-24 14:36:32 +08:00
else if(ffStrEqualsIgnCase(command, "color"))
2022-12-12 13:43:10 +01:00
puts(FASTFETCH_DATATEXT_HELP_COLOR);
else if(ffStrEqualsIgnCase(command, "format"))
2022-12-12 13:43:10 +01:00
puts(FASTFETCH_DATATEXT_HELP_FORMAT);
2023-08-24 14:36:32 +08:00
else if(ffStrEqualsIgnCase(command, "load-config") || ffStrEqualsIgnCase(command, "config"))
2022-12-12 13:43:10 +01:00
puts(FASTFETCH_DATATEXT_HELP_CONFIG);
2023-09-22 13:35:02 +08:00
else if(ffStrEqualsIgnCase(command, "title-format"))
{
constructAndPrintCommandHelpFormat("title", "{6}{7}{8}", 8,
"User name",
"Host name",
"Home directory",
"Executable path of current process",
"User's default shell",
"User name (colored)",
"@ symbol (colored)",
"Host name (colored)"
);
}
else if(ffStrEqualsIgnCase(command, "os-format"))
2021-03-27 17:48:32 +01:00
{
constructAndPrintCommandHelpFormat("os", "{3} {12}", 12,
2021-05-15 08:39:20 +02:00
"System name (typically just Linux)",
2021-03-27 17:48:32 +01:00
"Name of the OS",
"Pretty name of the OS",
"ID of the OS",
"ID like of the OS",
"Variant of the OS",
"Variant ID of the OS",
"Version of the OS",
"Version ID of the OS",
"Version codename of the OS",
"Build ID of the OS",
"Architecture of the OS"
);
}
else if(ffStrEqualsIgnCase(command, "host-format"))
2021-04-03 11:39:59 +02:00
{
constructAndPrintCommandHelpFormat("host", "{2} {3}", 8,
"product family",
"product name",
"product version",
"product sku",
"chassis type",
"chassis vendor",
"chassis version",
"sys vendor"
2021-04-03 11:39:59 +02:00
);
}
else if(ffStrEqualsIgnCase(command, "bios-format"))
{
constructAndPrintCommandHelpFormat("bios", "{2} {3}", 4,
"bios date",
"bios release",
"bios vendor",
"bios version"
);
}
else if(ffStrEqualsIgnCase(command, "board-format"))
{
constructAndPrintCommandHelpFormat("board", "{2} {3}", 3,
"board name",
"board vendor",
"board version"
);
}
else if(ffStrEqualsIgnCase(command, "chassis-format"))
2022-12-24 22:48:10 +08:00
{
constructAndPrintCommandHelpFormat("chassis", "{2} {3}", 4,
"chassis type",
"chassis vendor",
"chassis version"
);
}
else if(ffStrEqualsIgnCase(command, "kernel-format"))
2021-04-03 11:39:59 +02:00
{
constructAndPrintCommandHelpFormat("kernel", "{2}", 3,
"Kernel sysname",
"Kernel release",
"Kernel version"
);
}
else if(ffStrEqualsIgnCase(command, "uptime-format"))
2021-04-03 11:39:59 +02:00
{
constructAndPrintCommandHelpFormat("uptime", "{} days {} hours {} mins", 4,
"Days",
"Hours",
"Minutes",
"Seconds"
);
}
else if(ffStrEqualsIgnCase(command, "processes-format"))
2021-10-18 22:38:00 +08:00
{
constructAndPrintCommandHelpFormat("processes", "{}", 1,
"Count"
);
}
else if(ffStrEqualsIgnCase(command, "packages-format"))
2021-04-03 11:39:59 +02:00
{
constructAndPrintCommandHelpFormat("packages", "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (eopkg), {8} (xbps), {9} (nix-system), {10} (nix-user), {11} (nix-default), {12} (apk), {13} (pkg), {14} (flatpak-system), {15} (flatpack-user), {16} (snap), {17} (brew), {18} (brew-cask), {19} (port), {20} (scoop), {21} (choco), {22} (pkgtool), {23} (paludis), {24} (winget)", 24,
2021-04-03 11:39:59 +02:00
"Number of all packages",
"Number of pacman packages",
2021-05-15 18:17:34 +02:00
"Pacman branch on manjaro",
2021-05-01 21:22:07 +02:00
"Number of dpkg packages",
2021-06-08 20:01:29 +02:00
"Number of rpm packages",
2022-02-12 12:13:02 +01:00
"Number of emerge packages",
2022-12-27 17:36:58 +08:00
"Number of eopkg packages",
2021-06-08 20:01:29 +02:00
"Number of xbps packages",
2022-07-26 19:03:15 +02:00
"Number of nix-system packages",
2022-07-26 19:15:00 +02:00
"Number of nix-user packages",
2022-06-02 22:58:19 +02:00
"Number of nix-default packages",
2022-08-18 22:02:21 +02:00
"Number of apk packages",
2022-09-20 11:16:08 +02:00
"Number of pkg packages",
2023-03-24 17:47:03 +08:00
"Number of flatpak-system packages",
"Number of flatpak-user packages",
2022-09-05 05:15:19 -07:00
"Number of snap packages",
"Number of brew packages",
2022-12-27 17:36:58 +08:00
"Number of brew-cask packages",
"Number of macports packages",
"Number of scoop packages",
2023-07-03 23:18:36 +08:00
"Number of choco packages",
2023-07-20 16:43:29 +01:00
"Number of pkgtool packages",
"Number of paludis packages"
"Number of winget packages"
2021-04-03 11:39:59 +02:00
);
}
else if(ffStrEqualsIgnCase(command, "shell-format"))
2021-04-03 11:39:59 +02:00
{
2023-09-13 14:47:13 +08:00
constructAndPrintCommandHelpFormat("shell", "{3} {4}", 6,
2021-06-14 23:22:14 +02:00
"Shell process name",
"Shell path with exe name",
"Shell exe name",
"Shell version",
2023-09-13 14:47:13 +08:00
"Shell pid",
"Shell pretty name"
2021-04-03 11:39:59 +02:00
);
}
else if(ffStrEqualsIgnCase(command, "display-format"))
2021-04-03 11:39:59 +02:00
{
2023-06-12 10:35:28 +08:00
constructAndPrintCommandHelpFormat("display", "{}x{} @ {}Hz", 7,
2021-04-03 11:39:59 +02:00
"Screen width",
"Screen height",
2023-01-07 22:37:01 +01:00
"Screen refresh rate",
2023-02-02 18:16:56 +01:00
"Screen scaled width",
2023-06-12 10:35:28 +08:00
"Screen scaled height",
"Screen name",
"Screen type",
"Screen rotation"
2021-04-03 11:39:59 +02:00
);
}
2023-08-09 16:29:04 +08:00
else if(ffStrEqualsIgnCase(command, "brightness-format"))
{
constructAndPrintCommandHelpFormat("brightness", "{}", 2,
"Screen brightness",
"Screen name"
);
}
2023-08-09 16:31:24 +08:00
else if(ffStrEqualsIgnCase(command, "monitor-format"))
{
constructAndPrintCommandHelpFormat("monitor", "{}", 6,
"Display name",
"Display native resolution width in pixels",
"Display native resolution height in pixels",
"Display physical width in millimeters",
"Display physical height in millimeters",
"Display physical diagonal length in inches",
"Display physical pixels per inch (PPI)"
);
}
else if(ffStrEqualsIgnCase(command, "de-format"))
2021-04-03 11:39:59 +02:00
{
2022-01-02 16:55:26 +01:00
constructAndPrintCommandHelpFormat("de", "{2} {3}", 3,
2021-05-01 13:03:30 +02:00
"DE process name",
"DE pretty name",
"DE version"
2021-04-03 11:39:59 +02:00
);
}
else if(ffStrEqualsIgnCase(command, "wm-format"))
2021-04-03 11:39:59 +02:00
{
2022-01-02 16:55:26 +01:00
constructAndPrintCommandHelpFormat("wm", "{2} ({3})", 3,
2021-04-03 11:39:59 +02:00
"WM process name",
2021-04-25 21:32:35 +02:00
"WM pretty name",
"WM protocol name"
2021-04-03 11:39:59 +02:00
);
}
2023-06-24 07:20:27 +00:00
else if(ffStrEqualsIgnCase(command, "wmtheme-format"))
2021-04-03 23:10:14 +02:00
{
2023-06-24 07:20:27 +00:00
constructAndPrintCommandHelpFormat("wmtheme", "{}", 1,
2021-04-03 23:10:14 +02:00
"WM theme name"
);
}
else if(ffStrEqualsIgnCase(command, "theme-format"))
2021-04-03 11:39:59 +02:00
{
2023-06-08 14:32:15 +08:00
constructAndPrintCommandHelpFormat("theme", "{}", 1,
"Combined themes"
2021-04-03 11:39:59 +02:00
);
}
else if(ffStrEqualsIgnCase(command, "icons-format"))
2021-04-03 11:39:59 +02:00
{
2023-06-06 11:26:29 +08:00
constructAndPrintCommandHelpFormat("icons", "{}", 1,
"Combined icons"
2021-04-03 11:39:59 +02:00
);
}
else if(ffStrEqualsIgnCase(command, "wallpaper-format"))
2023-06-06 15:48:24 +08:00
{
constructAndPrintCommandHelpFormat("wallpaper", "{}", 1,
"Wallpaper image file"
);
}
else if(ffStrEqualsIgnCase(command, "font-format"))
2021-04-03 11:39:59 +02:00
{
2022-09-29 14:15:11 +02:00
constructAndPrintCommandHelpFormat("font", "{} [QT], {} [GTK2], {} [GTK3], {} [GTK4]", 4,
"Font 1",
"Font 2",
"Font 3",
"Font 4"
2021-04-03 11:39:59 +02:00
);
}
else if(ffStrEqualsIgnCase(command, "cursor-format"))
2021-06-20 21:04:55 +02:00
{
constructAndPrintCommandHelpFormat("cursor", "{} ({}pt)", 2,
"Cursor theme",
"Cursor size"
);
}
else if(ffStrEqualsIgnCase(command, "terminal-format"))
2021-04-03 11:39:59 +02:00
{
2022-09-12 15:57:05 +10:00
constructAndPrintCommandHelpFormat("terminal", "{3}", 10,
2021-04-03 11:39:59 +02:00
"Terminal process name",
2021-06-14 23:22:14 +02:00
"Terminal path with exe name",
2022-09-12 15:57:05 +10:00
"Terminal exe name",
"Terminal pid",
"Terminal pretty name",
"Terminal version"
2021-04-03 11:39:59 +02:00
);
}
2023-06-24 07:20:27 +00:00
else if(ffStrEqualsIgnCase(command, "terminalfont-format"))
2021-04-03 11:39:59 +02:00
{
2023-06-24 07:20:27 +00:00
constructAndPrintCommandHelpFormat("terminalfont", "{}", 4,
"Terminal font",
2021-04-05 19:25:34 +02:00
"Terminal font name",
"Termianl font size",
2021-05-23 19:40:34 +02:00
"Terminal font styles"
2021-04-03 11:39:59 +02:00
);
}
else if(ffStrEqualsIgnCase(command, "cpu-format"))
2021-04-03 11:39:59 +02:00
{
2022-09-04 14:12:04 -07:00
constructAndPrintCommandHelpFormat("cpu", "{1} ({5}) @ {7}GHz", 8,
"Name",
"Vendor",
"Physical core count",
"Logical core count",
"Online core count",
"Min frequency",
"Max frequency",
"Temperature"
2021-04-03 11:39:59 +02:00
);
}
2023-08-29 17:43:39 +08:00
else if(ffStrEqualsIgnCase(command, "cpuusage-format"))
2021-11-24 16:16:25 +08:00
{
2023-08-29 17:43:39 +08:00
constructAndPrintCommandHelpFormat("cpuusage", "{0}%", 1,
2021-11-24 16:16:25 +08:00
"CPU usage without percent mark"
);
}
else if(ffStrEqualsIgnCase(command, "gpu-format"))
2021-04-03 11:39:59 +02:00
{
2023-01-12 11:41:07 +01:00
constructAndPrintCommandHelpFormat("gpu", "{} {}", 6,
2021-04-03 11:39:59 +02:00
"GPU vendor",
2021-05-07 17:10:22 +02:00
"GPU name",
2022-06-17 23:38:33 +02:00
"GPU driver",
2022-09-16 18:11:39 +08:00
"GPU temperature",
2023-01-12 11:41:07 +01:00
"GPU core count",
"GPU type"
2021-04-03 11:39:59 +02:00
);
}
else if(ffStrEqualsIgnCase(command, "memory-format"))
2021-04-03 11:39:59 +02:00
{
2022-08-26 16:02:04 +02:00
constructAndPrintCommandHelpFormat("memory", "{} / {} ({}%)", 3,
"Used size",
"Total size",
"Percentage used"
2021-04-03 11:39:59 +02:00
);
}
else if(ffStrEqualsIgnCase(command, "swap-format"))
2022-09-08 14:51:31 +02:00
{
constructAndPrintCommandHelpFormat("swap", "{} / {} ({}%)", 3,
"Used size",
"Total size",
"Percentage used"
);
}
else if(ffStrEqualsIgnCase(command, "disk-format"))
2021-04-03 11:39:59 +02:00
{
2022-10-17 17:35:06 +02:00
constructAndPrintCommandHelpFormat("disk", "{1} / {2} ({3}%)", 9,
"Size used",
"Size total",
"Size percentage",
"Files used",
"Files total",
"Files percentage",
"True if external volume",
2022-10-17 17:35:06 +02:00
"True if hidden volume",
"Filesystem"
2021-04-03 11:39:59 +02:00
);
}
else if(ffStrEqualsIgnCase(command, "battery-format"))
2021-04-03 11:39:59 +02:00
{
2021-05-30 21:35:04 +02:00
constructAndPrintCommandHelpFormat("battery", "{}%, {}", 5,
2021-04-03 11:39:59 +02:00
"Battery manufactor",
"Battery model",
"Battery technology",
"Battery capacity",
"Battery status"
);
}
else if(ffStrEqualsIgnCase(command, "poweradapter-format"))
2022-09-23 14:59:28 +08:00
{
constructAndPrintCommandHelpFormat("poweradapter", "{}%, {}", 5,
"PowerAdapter watts",
"PowerAdapter name",
"PowerAdapter manufacturer",
"PowerAdapter model",
"PowerAdapter description"
);
}
2023-06-20 23:01:18 +08:00
else if(ffStrEqualsIgnCase(command, "lm-format"))
{
constructAndPrintCommandHelpFormat("lm", "{} ({})", 2,
"LM service",
"LM type"
);
}
else if(ffStrEqualsIgnCase(command, "locale-format"))
2021-04-03 11:39:59 +02:00
{
constructAndPrintCommandHelpFormat("locale", "{}", 1,
"Locale code"
);
}
else if(ffStrEqualsIgnCase(command, "local-ip-format"))
2022-01-11 12:36:32 +01:00
{
constructAndPrintCommandHelpFormat("local-ip", "{}", 1,
"Local IP address"
);
}
else if(ffStrEqualsIgnCase(command, "public-ip-format"))
2022-01-11 12:36:32 +01:00
{
constructAndPrintCommandHelpFormat("public-ip", "{}", 1,
"Public IP address"
);
}
else if(ffStrEqualsIgnCase(command, "wifi-format"))
2022-11-26 18:23:08 +08:00
{
constructAndPrintCommandHelpFormat("wifi", "{4} - {6}", 3,
"Interface description",
"Interface status",
"Connection status",
"Connection SSID",
"Connection mac address",
"Connection protocol",
2022-11-26 18:23:08 +08:00
"Connection signal quality (percentage)",
"Connection RX rate",
"Connection TX rate",
"Connection Security algorithm"
2022-11-26 18:23:08 +08:00
);
}
else if(ffStrEqualsIgnCase(command, "player-format"))
2022-02-13 23:51:13 +01:00
{
2022-09-11 13:09:33 +02:00
constructAndPrintCommandHelpFormat("player", "{}", 4,
"Pretty player name",
"Player name",
"Player Identifier",
2022-09-11 13:09:33 +02:00
"URL name"
2022-02-13 23:51:13 +01:00
);
}
else if(ffStrEqualsIgnCase(command, "media-format"))
2022-02-13 23:51:13 +01:00
{
constructAndPrintCommandHelpFormat("media", "{3} - {1}", 4,
2022-09-11 13:09:33 +02:00
"Pretty media name",
"Media name",
2022-02-13 23:51:13 +01:00
"Artist name",
2022-09-11 13:09:33 +02:00
"Album name"
2022-02-13 23:51:13 +01:00
);
}
else if(ffStrEqualsIgnCase(command, "datetime-format") == 0 || ffStrEqualsIgnCase(command, "date-format") || ffStrEqualsIgnCase(command, "time-format"))
2022-02-25 13:30:37 +01:00
{
constructAndPrintCommandHelpFormat("[date][time]", "{1}-{4}-{11} {14}:{18}:{20}", 20,
"year",
"last two digits of year",
"month",
"month with leading zero",
"month name",
"month name short",
"week number on year",
"weekday",
"weekday short",
"day in year",
"day in month",
"day in Week",
"hour",
2022-06-10 15:12:39 -05:00
"hour with leading zero",
2022-02-25 13:30:37 +01:00
"hour 12h format",
"hour 12h format with leading zero",
"minute",
"minute with leading zero",
"second",
"second with leading zero"
);
}
2023-08-21 09:19:16 +08:00
else if(ffStrEqualsIgnCase(command, "version-format"))
{
constructAndPrintCommandHelpFormat("version", "{1} {2}{3} (5)", 6,
"Project name",
"Version",
"Version tweak",
"Build type (debug or release)",
"Architecture",
"CMake build type (Debug, Release, RelWithDebInfo, MinSizeRel)"
);
}
else if(ffStrEqualsIgnCase(command, "vulkan-format"))
2022-06-01 00:04:58 +02:00
{
constructAndPrintCommandHelpFormat("vulkan", "{} (driver), {} (api version)", 3,
"Driver name",
"API version",
"Conformance version"
);
}
else if(ffStrEqualsIgnCase(command, "opengl-format"))
2022-06-04 15:21:34 +02:00
{
constructAndPrintCommandHelpFormat("opengl", "{}", 3,
"version",
"renderer",
2022-06-04 19:30:26 +02:00
"vendor",
"shading language version"
2022-06-04 15:21:34 +02:00
);
}
else if(ffStrEqualsIgnCase(command, "opencl-format"))
2022-06-05 00:18:40 +02:00
{
constructAndPrintCommandHelpFormat("opencl", "{}", 3,
"version",
"device",
"vendor"
);
}
else if(ffStrEqualsIgnCase(command, "bluetooth-format"))
2023-01-24 10:39:33 +01:00
{
constructAndPrintCommandHelpFormat("bluetooth", "{1} (4%)", 4,
"Name",
"Address",
"Type",
"Battery percentage"
);
}
else if(ffStrEqualsIgnCase(command, "sound-format"))
2023-01-28 13:45:40 +08:00
{
constructAndPrintCommandHelpFormat("sound", "{2} (3%)", 4,
"Main",
"Name",
"Volume",
"Identifier"
);
}
else if(ffStrEqualsIgnCase(command, "gamepad-format"))
2023-02-01 14:01:39 +08:00
{
constructAndPrintCommandHelpFormat("gamepad", "{1}", 1,
"Name",
"Identifier"
);
}
2021-02-20 19:32:57 +01:00
else
2021-04-03 11:39:59 +02:00
fprintf(stderr, "No specific help for command %s provided\n", command);
2021-02-20 19:32:57 +01:00
}
static void listAvailablePresets(void)
2021-04-25 11:48:25 +02:00
{
FF_LIST_FOR_EACH(FFstrbuf, path, instance.state.platform.dataDirs)
2021-04-25 11:48:25 +02:00
{
2023-01-28 17:19:45 +01:00
ffStrbufAppendS(path, "fastfetch/presets/");
ffListFilesRecursively(path->chars);
2021-04-25 11:48:25 +02:00
}
}
static void listAvailableLogos(void)
2021-04-25 11:48:25 +02:00
{
FF_LIST_FOR_EACH(FFstrbuf, path, instance.state.platform.dataDirs)
2023-01-08 01:41:09 +01:00
{
2023-01-28 17:19:45 +01:00
ffStrbufAppendS(path, "fastfetch/logos/");
ffListFilesRecursively(path->chars);
2023-01-08 01:41:09 +01:00
}
2021-04-25 11:48:25 +02:00
}
static void listConfigPaths(void)
2023-01-02 16:51:11 +08:00
{
FF_LIST_FOR_EACH(FFstrbuf, folder, instance.state.platform.configDirs)
2023-01-02 16:51:11 +08:00
{
2023-06-10 21:34:41 +08:00
bool exists = false;
ffStrbufAppendS(folder, "fastfetch/config.jsonc");
exists = ffPathExists(folder->chars, FF_PATHTYPE_FILE);
if (!exists)
{
ffStrbufSubstrBefore(folder, folder->length - (uint32_t) strlen("jsonc"));
ffStrbufAppendS(folder, "conf");
exists = ffPathExists(folder->chars, FF_PATHTYPE_FILE);
}
printf("%s%s\n", folder->chars, exists ? " (*)" : "");
2023-01-02 16:51:11 +08:00
}
}
static void listDataPaths(void)
2023-01-08 01:41:09 +01:00
{
FF_LIST_FOR_EACH(FFstrbuf, folder, instance.state.platform.dataDirs)
2023-01-08 01:41:09 +01:00
{
ffStrbufAppendS(folder, "fastfetch/");
puts(folder->chars);
}
}
static void listModules()
{
unsigned count = 0;
for (int i = 0; i <= 'Z' - 'A'; ++i)
{
for (FFModuleBaseInfo** modules = ffModuleInfos[i]; *modules; ++modules)
{
++count;
printf("%d)%s%s\n", count, count > 9 ? " " : " ", (*modules)->name);
}
}
}
static void parseOption(FFdata* data, const char* key, const char* value);
2021-04-25 11:48:25 +02:00
static bool parseJsoncFile(const char* path)
2023-06-10 21:34:41 +08:00
{
yyjson_read_err error;
yyjson_doc* doc = yyjson_read_file(path, YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS | YYJSON_READ_ALLOW_INF_AND_NAN, NULL, &error);
if (!doc)
2023-06-10 21:34:41 +08:00
{
if (error.code != YYJSON_READ_ERROR_FILE_OPEN)
{
fprintf(stderr, "Error: failed to parse JSON config file `%s` at pos %zu: %s\n", path, error.pos, error.msg);
2023-06-10 21:34:41 +08:00
exit(477);
}
return false;
}
if (instance.state.configDoc)
yyjson_doc_free(instance.state.configDoc); // for `--load-config`
instance.state.configDoc = doc;
return true;
2023-06-10 21:34:41 +08:00
}
static bool parseConfigFile(FFdata* data, const char* path)
2021-04-25 11:48:25 +02:00
{
2022-08-22 12:45:20 +02:00
FILE* file = fopen(path, "r");
if(file == NULL)
return false;
2022-08-21 21:08:11 +02:00
char* line = NULL;
2021-04-25 11:48:25 +02:00
size_t len = 0;
ssize_t read;
FF_STRBUF_AUTO_DESTROY unescaped = ffStrbufCreate();
2021-04-25 11:48:25 +02:00
2022-08-21 21:08:11 +02:00
while ((read = getline(&line, &len, file)) != -1)
2021-04-25 11:48:25 +02:00
{
2022-08-21 21:08:11 +02:00
char* lineStart = line;
char* lineEnd = line + read - 1;
//Trim line left
while(isspace(*lineStart))
++lineStart;
2021-04-25 11:48:25 +02:00
2022-08-21 21:08:11 +02:00
//Continue if line is empty or a comment
if(*lineStart == '\0' || *lineStart == '#')
2021-04-25 11:48:25 +02:00
continue;
2022-08-21 21:08:11 +02:00
//Trim line right
while(lineEnd > lineStart && isspace(*lineEnd))
--lineEnd;
*(lineEnd + 1) = '\0';
char* valueStart = strchr(lineStart, ' ');
2021-04-25 11:48:25 +02:00
2022-08-21 21:08:11 +02:00
//If the line has no white space, it is only a key
if(valueStart == NULL)
2021-04-25 11:48:25 +02:00
{
parseOption(data, lineStart, NULL);
2021-04-25 11:48:25 +02:00
continue;
}
2022-08-21 21:08:11 +02:00
//separate the key from the value
2021-04-25 11:48:25 +02:00
*valueStart = '\0';
++valueStart;
2022-08-21 21:08:11 +02:00
//Trim space of value left
while(isspace(*valueStart))
2021-04-25 11:48:25 +02:00
++valueStart;
//If we want whitespace in values, we need to quote it. This is done to keep consistency with shell.
2022-08-21 21:08:11 +02:00
if((*valueStart == '"' || *valueStart == '\'') && *valueStart == *lineEnd && lineEnd > valueStart)
2021-04-25 11:48:25 +02:00
{
2022-08-21 21:08:11 +02:00
++valueStart;
2022-08-22 12:14:08 +02:00
*lineEnd = '\0';
2022-08-21 21:08:11 +02:00
--lineEnd;
2021-04-25 11:48:25 +02:00
}
if (strchr(valueStart, '\\'))
{
// Unescape all `\x`s
const char* value = valueStart;
while(*value != '\0')
{
if(*value != '\\')
{
ffStrbufAppendC(&unescaped, *value);
++value;
continue;
}
++value;
switch(*value)
{
case 'n': ffStrbufAppendC(&unescaped, '\n'); break;
case 't': ffStrbufAppendC(&unescaped, '\t'); break;
case 'e': ffStrbufAppendC(&unescaped, '\e'); break;
case '\\': ffStrbufAppendC(&unescaped, '\\'); break;
default:
ffStrbufAppendC(&unescaped, '\\');
ffStrbufAppendC(&unescaped, *value);
break;
}
++value;
}
parseOption(data, lineStart, unescaped.chars);
ffStrbufClear(&unescaped);
}
else
{
parseOption(data, lineStart, valueStart);
}
2021-04-25 11:48:25 +02:00
}
2022-08-21 21:08:11 +02:00
if(line != NULL)
free(line);
2022-08-22 12:45:20 +02:00
fclose(file);
return true;
2021-04-25 11:48:25 +02:00
}
static void generateConfigFile(bool force, const char* type)
{
FFstrbuf* filename = (FFstrbuf*) ffListGet(&instance.state.platform.configDirs, 0);
// Paths generated in `init.c/initConfigDirs` end with `/`
bool isJsonc = false;
if (type)
{
if (ffStrEqualsIgnCase(type, "jsonc"))
isJsonc = true;
else if (!ffStrEqualsIgnCase(type, "conf"))
{
fputs("config type can only be `jsonc` or `conf`\n", stderr);
exit(1);
}
}
ffStrbufAppendS(filename, isJsonc ? "fastfetch/config.jsonc" : "fastfetch/config.conf");
if (!force && ffPathExists(filename->chars, FF_PATHTYPE_FILE))
{
fprintf(stderr, "Config file exists in `%s`, use `--gen-config-force` to overwrite\n", filename->chars);
exit(1);
}
else
{
ffWriteFileData(
filename->chars,
isJsonc ? strlen(FASTFETCH_DATATEXT_CONFIG_USER_JSONC) : strlen(FASTFETCH_DATATEXT_CONFIG_USER),
isJsonc ? FASTFETCH_DATATEXT_CONFIG_USER_JSONC : FASTFETCH_DATATEXT_CONFIG_USER);
printf("A sample config file has been written in `%s`\n", filename->chars);
exit(0);
}
}
static void optionParseConfigFile(FFdata* data, const char* key, const char* value)
2021-04-25 11:48:25 +02:00
{
if(value == NULL)
{
fprintf(stderr, "Error: usage: %s <file>\n", key);
exit(413);
}
uint32_t fileNameLen = (uint32_t) strlen(value);
if(fileNameLen == 0)
{
fprintf(stderr, "Error: usage: %s <file>\n", key);
exit(413);
}
bool isJsonConfig = fileNameLen > strlen(".jsonc") && strcasecmp(value + fileNameLen - strlen(".jsonc"), ".jsonc") == 0;
2021-04-25 11:48:25 +02:00
2022-08-22 12:45:20 +02:00
//Try to load as an absolute path
if(isJsonConfig ? parseJsoncFile(value) : parseConfigFile(data, value))
2021-04-25 11:48:25 +02:00
return;
2022-08-22 12:45:20 +02:00
FF_STRBUF_AUTO_DESTROY absolutePath = ffStrbufCreateA(128);
if (ffPathExpandEnv(value, &absolutePath))
{
bool success = isJsonConfig ? parseJsoncFile(absolutePath.chars) : parseConfigFile(data, absolutePath.chars);
if(success)
return;
}
//Try to load as a relative path
2023-01-15 20:03:28 +01:00
FF_LIST_FOR_EACH(FFstrbuf, path, instance.state.platform.dataDirs)
2021-04-25 11:48:25 +02:00
{
2023-01-15 20:03:28 +01:00
//We need to copy it, because if a config file loads a config file, the value of path must be unchanged
ffStrbufSet(&absolutePath, path);
ffStrbufAppendS(&absolutePath, "fastfetch/presets/");
ffStrbufAppendS(&absolutePath, value);
2022-08-22 12:45:20 +02:00
bool success = isJsonConfig ? parseJsoncFile(absolutePath.chars) : parseConfigFile(data, absolutePath.chars);
2021-04-25 11:48:25 +02:00
2023-01-08 01:41:09 +01:00
if(success)
return;
2021-04-25 11:48:25 +02:00
}
{
//Try exe path
ffStrbufSet(&absolutePath, &instance.state.platform.exePath);
ffStrbufSubstrBeforeLastC(&absolutePath, '/');
ffStrbufAppendS(&absolutePath, "/presets/");
ffStrbufAppendS(&absolutePath, value);
bool success = isJsonConfig ? parseJsoncFile(absolutePath.chars) : parseConfigFile(data, absolutePath.chars);
if(success)
return;
}
2022-08-22 12:45:20 +02:00
//File not found
2021-04-25 11:48:25 +02:00
fprintf(stderr, "Error: couldn't find config: %s\n", value);
exit(414);
}
2021-12-20 12:12:55 +01:00
static inline void optionCheckString(const char* key, const char* value, FFstrbuf* buffer)
2021-03-11 19:40:46 +01:00
{
if(value == NULL)
{
2021-04-03 11:39:59 +02:00
fprintf(stderr, "Error: usage: %s <str>\n", key);
2021-03-11 19:40:46 +01:00
exit(477);
}
2021-11-17 10:59:31 +01:00
ffStrbufEnsureFree(buffer, 63); //This is not needed, as ffStrbufSetS will resize capacity if needed, but giving a higher start should improve performance
2021-12-20 12:12:55 +01:00
}
static void printVersion()
2023-08-21 09:22:03 +08:00
{
FFVersionResult result = {};
ffDetectVersion(&result);
printf("%s %s%s%s (%s)\n", result.projectName, result.version, result.versionTweak, result.debugMode ? "-debug" : "", result.architecture);
}
2023-01-19 14:57:54 +08:00
static void parseOption(FFdata* data, const char* key, const char* value)
{
///////////////////////
//Informative options//
///////////////////////
if(ffStrEqualsIgnCase(key, "-h") || ffStrEqualsIgnCase(key, "--help"))
{
printCommandHelp(value);
exit(0);
}
else if(ffStrEqualsIgnCase(key, "-v") || ffStrEqualsIgnCase(key, "--version"))
{
printVersion();
2022-03-20 17:12:52 +01:00
exit(0);
}
else if(ffStrEqualsIgnCase(key, "--version-raw"))
{
2022-03-20 13:16:04 +01:00
puts(FASTFETCH_PROJECT_VERSION);
exit(0);
}
else if(ffStrStartsWithIgnCase(key, "--print"))
2021-03-27 19:26:58 +01:00
{
2022-10-22 13:02:04 +08:00
const char* subkey = key + strlen("--print");
if(ffStrEqualsIgnCase(subkey, "-config-system"))
2022-10-22 13:02:04 +08:00
{
puts(FASTFETCH_DATATEXT_CONFIG_SYSTEM);
exit(0);
}
else if(ffStrEqualsIgnCase(subkey, "-config-user"))
2022-10-22 13:02:04 +08:00
{
puts(FASTFETCH_DATATEXT_CONFIG_USER);
exit(0);
}
else if(ffStrEqualsIgnCase(subkey, "-structure"))
2022-10-22 13:02:04 +08:00
{
puts(FASTFETCH_DATATEXT_STRUCTURE);
exit(0);
}
else if(ffStrEqualsIgnCase(subkey, "-logos"))
2022-10-22 13:02:04 +08:00
{
ffLogoBuiltinPrint();
2022-10-22 13:02:04 +08:00
exit(0);
}
else
goto error;
}
else if(ffStrStartsWithIgnCase(key, "--list"))
{
2022-10-22 13:02:04 +08:00
const char* subkey = key + strlen("--list");
if(ffStrEqualsIgnCase(subkey, "-modules"))
2022-10-22 13:02:04 +08:00
{
listModules();
2022-10-22 13:02:04 +08:00
exit(0);
}
else if(ffStrEqualsIgnCase(subkey, "-presets"))
2022-10-22 13:02:04 +08:00
{
listAvailablePresets();
2022-10-22 13:02:04 +08:00
exit(0);
}
else if(ffStrEqualsIgnCase(subkey, "-config-paths"))
2023-01-02 16:51:11 +08:00
{
listConfigPaths();
2023-01-02 16:51:11 +08:00
exit(0);
}
else if(ffStrEqualsIgnCase(subkey, "-data-paths"))
2023-01-08 01:41:09 +01:00
{
listDataPaths();
2023-01-08 01:41:09 +01:00
exit(0);
}
else if(ffStrEqualsIgnCase(subkey, "-features"))
2022-10-22 13:02:04 +08:00
{
ffListFeatures();
exit(0);
}
else if(ffStrEqualsIgnCase(subkey, "-logos"))
2022-10-22 13:02:04 +08:00
{
2023-01-28 17:19:45 +01:00
puts("Builtin logos:");
2022-10-22 13:02:04 +08:00
ffLogoBuiltinList();
2023-01-28 17:19:45 +01:00
puts("\nCustom logos:");
listAvailableLogos();
2022-10-22 13:02:04 +08:00
exit(0);
}
else if(ffStrEqualsIgnCase(subkey, "-logos-autocompletion"))
2022-10-22 13:02:04 +08:00
{
ffLogoBuiltinListAutocompletion();
exit(0);
}
else
goto error;
2022-04-02 14:35:43 +02:00
}
else if(ffStrStartsWithIgnCase(key, "--set"))
{
const char* subkey = key + strlen("--set");
if(*subkey != '\0' && !ffStrEqualsIgnCase(subkey, "-keyless"))
goto error;
FF_STRBUF_AUTO_DESTROY customValueStr = ffStrbufCreate();
ffOptionParseString(key, value, &customValueStr);
uint32_t index = ffStrbufFirstIndexC(&customValueStr, '=');
if(index == 0 || index == customValueStr.length)
{
fprintf(stderr, "Error: usage: %s <key>=<str>\n", key);
exit(477);
}
2023-08-12 09:40:37 +08:00
FF_STRBUF_AUTO_DESTROY customKey = ffStrbufCreateNS(index, customValueStr.chars);
FFCustomValue* customValue = NULL;
FF_LIST_FOR_EACH(FFCustomValue, x, data->customValues)
{
if(ffStrbufEqual(&x->key, &customKey))
{
ffStrbufDestroy(&x->key);
ffStrbufDestroy(&x->value);
customValue = x;
break;
}
}
if(!customValue) customValue = (FFCustomValue*) ffListAdd(&data->customValues);
ffStrbufInitMove(&customValue->key, &customKey);
ffStrbufSubstrAfter(&customValueStr, index);
ffStrbufInitMove(&customValue->value, &customValueStr);
customValue->printKey = *subkey == '\0';
}
2021-03-05 23:00:28 +01:00
2022-04-02 14:35:43 +02:00
///////////////////
//General options//
///////////////////
2023-08-24 14:36:32 +08:00
else if(ffStrEqualsIgnCase(key, "--load-config") || ffStrEqualsIgnCase(key, "--config"))
optionParseConfigFile(data, key, value);
else if(ffStrEqualsIgnCase(key, "--gen-config"))
generateConfigFile(false, value);
else if(ffStrEqualsIgnCase(key, "--gen-config-force"))
generateConfigFile(true, value);
else if(ffStrEqualsIgnCase(key, "--thread") || ffStrEqualsIgnCase(key, "--multithreading"))
instance.config.multithreading = ffOptionParseBoolean(value);
else if(ffStrEqualsIgnCase(key, "--stat"))
{
if((instance.config.stat = ffOptionParseBoolean(value)))
instance.config.showErrors = true;
}
else if(ffStrEqualsIgnCase(key, "--allow-slow-operations"))
instance.config.allowSlowOperations = ffOptionParseBoolean(value);
else if(ffStrEqualsIgnCase(key, "--escape-bedrock"))
instance.config.escapeBedrock = ffOptionParseBoolean(value);
else if(ffStrEqualsIgnCase(key, "--pipe"))
instance.config.pipe = ffOptionParseBoolean(value);
else if(ffStrEqualsIgnCase(key, "--load-user-config"))
2023-06-12 15:48:26 +08:00
data->loadUserConfig = ffOptionParseBoolean(value);
2023-07-10 22:12:07 +08:00
else if(ffStrEqualsIgnCase(key, "--processing-timeout"))
instance.config.processingTimeout = ffOptionParseInt32(key, value);
2023-08-29 11:13:55 +08:00
else if(ffStrEqualsIgnCase(key, "--format"))
{
switch (ffOptionParseEnum(key, value, (FFKeyValuePair[]) {
{ "default", 0},
{ "json", 1 },
{},
}))
{
case 0:
if (instance.state.resultDoc)
{
yyjson_mut_doc_free(instance.state.resultDoc);
instance.state.resultDoc = NULL;
}
break;
case 1:
if (!instance.state.resultDoc)
{
instance.state.resultDoc = yyjson_mut_doc_new(NULL);
yyjson_mut_doc_set_root(instance.state.resultDoc, yyjson_mut_arr(instance.state.resultDoc));
}
break;
}
}
2022-04-02 14:35:43 +02:00
2023-06-12 15:22:46 +08:00
#if defined(__linux__) || defined(__FreeBSD__)
else if(ffStrEqualsIgnCase(key, "--player-name"))
ffOptionParseString(key, value, &instance.config.playerName);
else if (ffStrEqualsIgnCase(key, "--os-file"))
ffOptionParseString(key, value, &instance.config.osFile);
2023-07-31 23:11:39 +08:00
else if(ffStrEqualsIgnCase(key, "--ds-force-drm"))
instance.config.dsForceDrm = ffOptionParseBoolean(value);
2023-06-25 14:33:13 +08:00
#elif defined(_WIN32)
else if (ffStrEqualsIgnCase(key, "--wmi-timeout"))
instance.config.wmiTimeout = ffOptionParseInt32(key, value);
2023-06-12 15:22:46 +08:00
#endif
2022-04-02 14:35:43 +02:00
////////////////
//Logo options//
////////////////
else if(ffParseLogoCommandOptions(&instance.config.logo, key, value)) {}
2022-04-02 14:35:43 +02:00
///////////////////
//Display options//
///////////////////
else if(ffStrEqualsIgnCase(key, "--show-errors"))
instance.config.showErrors = ffOptionParseBoolean(value);
else if(ffStrEqualsIgnCase(key, "--disable-linewrap"))
instance.config.disableLinewrap = ffOptionParseBoolean(value);
else if(ffStrEqualsIgnCase(key, "--hide-cursor"))
instance.config.hideCursor = ffOptionParseBoolean(value);
else if(ffStrEqualsIgnCase(key, "-s") || ffStrEqualsIgnCase(key, "--structure"))
2023-06-12 15:48:26 +08:00
ffOptionParseString(key, value, &data->structure);
else if(ffStrEqualsIgnCase(key, "--separator"))
ffOptionParseString(key, value, &instance.config.keyValueSeparator);
else if(ffStrStartsWith(key, "--color"))
2023-06-10 21:34:41 +08:00
{
const char* subkey = key + strlen("--color");
if(*subkey == '\0')
{
optionCheckString(key, value, &instance.config.colorKeys);
ffOptionParseColor(value, &instance.config.colorKeys);
ffStrbufSet(&instance.config.colorTitle, &instance.config.colorKeys);
}
else if(ffStrEqualsIgnCase(subkey, "-keys"))
{
optionCheckString(key, value, &instance.config.colorKeys);
ffOptionParseColor(value, &instance.config.colorKeys);
}
else if(ffStrEqualsIgnCase(subkey, "-title"))
{
optionCheckString(key, value, &instance.config.colorTitle);
ffOptionParseColor(value, &instance.config.colorTitle);
}
else
goto error;
2023-06-10 21:34:41 +08:00
}
2023-08-15 17:10:09 +08:00
else if(ffStrEqualsIgnCase(key, "--key-width"))
instance.config.keyWidth = ffOptionParseUInt32(key, value);
2023-08-04 16:05:25 +08:00
else if(ffStrEqualsIgnCase(key, "--bright-color"))
instance.config.brightColor = ffOptionParseBoolean(value);
else if(ffStrEqualsIgnCase(key, "--binary-prefix"))
2022-08-26 16:02:04 +02:00
{
instance.config.binaryPrefixType = (FFBinaryPrefixType) ffOptionParseEnum(key, value, (FFKeyValuePair[]) {
2023-06-12 15:48:26 +08:00
{ "iec", FF_BINARY_PREFIX_TYPE_IEC },
{ "si", FF_BINARY_PREFIX_TYPE_SI },
{ "jedec", FF_BINARY_PREFIX_TYPE_JEDEC },
{}
});
2022-08-26 16:02:04 +02:00
}
else if(ffStrEqualsIgnCase(key, "--size-ndigits"))
instance.config.sizeNdigits = (uint8_t) ffOptionParseUInt32(key, value);
else if(ffStrEqualsIgnCase(key, "--size-max-prefix"))
{
instance.config.sizeMaxPrefix = (uint8_t) ffOptionParseEnum(key, value, (FFKeyValuePair[]) {
{ "B", 0 },
{ "kB", 1 },
{ "MB", 2 },
{ "GB", 3 },
{ "TB", 4 },
{ "PB", 5 },
{ "EB", 6 },
{ "ZB", 7 },
{ "YB", 8 },
{}
});
}
else if(ffStrEqualsIgnCase(key, "--temperature-unit"))
{
instance.config.temperatureUnit = (FFTemperatureUnit) ffOptionParseEnum(key, value, (FFKeyValuePair[]) {
{ "CELSIUS", FF_TEMPERATURE_UNIT_CELSIUS },
{ "C", FF_TEMPERATURE_UNIT_CELSIUS },
{ "FAHRENHEIT", FF_TEMPERATURE_UNIT_FAHRENHEIT },
{ "F", FF_TEMPERATURE_UNIT_FAHRENHEIT },
{ "KELVIN", FF_TEMPERATURE_UNIT_KELVIN },
{ "K", FF_TEMPERATURE_UNIT_KELVIN },
{},
});
}
else if(ffStrEqualsIgnCase(key, "--percent-type"))
2023-08-24 09:35:17 +08:00
instance.config.percentType = (uint8_t) ffOptionParseUInt32(key, value);
else if(ffStrEqualsIgnCase(key, "--percent-ndigits"))
instance.config.percentNdigits = (uint8_t) ffOptionParseUInt32(key, value);
else if(ffStrEqualsIgnCase(key, "--no-buffer"))
instance.config.noBuffer = ffOptionParseBoolean(value);
else if(ffStrStartsWithIgnCase(key, "--bar"))
{
const char* subkey = key + strlen("--bar");
if(ffStrEqualsIgnCase(subkey, "-char-elapsed"))
ffOptionParseString(key, value, &instance.config.barCharElapsed);
else if(ffStrEqualsIgnCase(subkey, "-char-total"))
ffOptionParseString(key, value, &instance.config.barCharTotal);
else if(ffStrEqualsIgnCase(subkey, "-width"))
instance.config.barWidth = (uint8_t) ffOptionParseUInt32(key, value);
else if(ffStrEqualsIgnCase(subkey, "-border"))
instance.config.barBorder = ffOptionParseBoolean(value);
else
goto error;
}
2022-04-02 14:35:43 +02:00
///////////////////
//Library options//
///////////////////
else if(ffStrStartsWithIgnCase(key, "--lib"))
2022-10-22 13:02:04 +08:00
{
const char* subkey = key + strlen("--lib");
if(ffStrEqualsIgnCase(subkey, "-PCI"))
ffOptionParseString(key, value, &instance.config.libPCI);
else if(ffStrEqualsIgnCase(subkey, "-vulkan"))
ffOptionParseString(key, value, &instance.config.libVulkan);
else if(ffStrEqualsIgnCase(subkey, "-freetype"))
ffOptionParseString(key, value, &instance.config.libfreetype);
else if(ffStrEqualsIgnCase(subkey, "-wayland"))
ffOptionParseString(key, value, &instance.config.libWayland);
else if(ffStrEqualsIgnCase(subkey, "-xcb-randr"))
ffOptionParseString(key, value, &instance.config.libXcbRandr);
else if(ffStrEqualsIgnCase(subkey, "-xcb"))
ffOptionParseString(key, value, &instance.config.libXcb);
else if(ffStrEqualsIgnCase(subkey, "-Xrandr"))
ffOptionParseString(key, value, &instance.config.libXrandr);
else if(ffStrEqualsIgnCase(subkey, "-X11"))
ffOptionParseString(key, value, &instance.config.libX11);
else if(ffStrEqualsIgnCase(subkey, "-gio"))
ffOptionParseString(key, value, &instance.config.libGIO);
else if(ffStrEqualsIgnCase(subkey, "-DConf"))
ffOptionParseString(key, value, &instance.config.libDConf);
else if(ffStrEqualsIgnCase(subkey, "-dbus"))
ffOptionParseString(key, value, &instance.config.libDBus);
else if(ffStrEqualsIgnCase(subkey, "-XFConf"))
ffOptionParseString(key, value, &instance.config.libXFConf);
else if(ffStrEqualsIgnCase(subkey, "-sqlite") || ffStrEqualsIgnCase(subkey, "-sqlite3"))
ffOptionParseString(key, value, &instance.config.libSQLite3);
else if(ffStrEqualsIgnCase(subkey, "-rpm"))
ffOptionParseString(key, value, &instance.config.librpm);
else if(ffStrEqualsIgnCase(subkey, "-imagemagick"))
ffOptionParseString(key, value, &instance.config.libImageMagick);
else if(ffStrEqualsIgnCase(subkey, "-z"))
ffOptionParseString(key, value, &instance.config.libZ);
else if(ffStrEqualsIgnCase(subkey, "-chafa"))
ffOptionParseString(key, value, &instance.config.libChafa);
else if(ffStrEqualsIgnCase(subkey, "-egl"))
ffOptionParseString(key, value, &instance.config.libEGL);
else if(ffStrEqualsIgnCase(subkey, "-glx"))
ffOptionParseString(key, value, &instance.config.libGLX);
else if(ffStrEqualsIgnCase(subkey, "-osmesa"))
ffOptionParseString(key, value, &instance.config.libOSMesa);
else if(ffStrEqualsIgnCase(subkey, "-opencl"))
ffOptionParseString(key, value, &instance.config.libOpenCL);
2023-09-17 08:09:31 +03:00
else if(ffStrEqualsIgnCase(subkey, "-pulse"))
ffOptionParseString(key, value, &instance.config.libPulse);
else if(ffStrEqualsIgnCase(subkey, "-nm"))
ffOptionParseString(key, value, &instance.config.libnm);
else if(ffStrEqualsIgnCase(subkey, "-ddcutil"))
ffOptionParseString(key, value, &instance.config.libDdcutil);
2022-10-22 13:02:04 +08:00
else
goto error;
}
2022-04-02 14:35:43 +02:00
///////////////////////
//Module args options//
///////////////////////
else if(ffParseModuleOptions(key, value)) {}
2022-04-02 14:35:43 +02:00
//////////////////
//Unknown option//
//////////////////
2021-12-19 22:39:22 +01:00
else
{
2022-10-22 13:02:04 +08:00
error:
2021-04-03 11:39:59 +02:00
fprintf(stderr, "Error: unknown option: %s\n", key);
2021-03-05 23:00:28 +01:00
exit(400);
}
}
static void parseConfigFiles(FFdata* data)
2022-07-21 14:37:13 +02:00
{
for(uint32_t i = instance.state.platform.configDirs.length; i > 0; --i)
2023-06-10 21:34:41 +08:00
{
FFstrbuf* dir = ffListGet(&instance.state.platform.configDirs, i - 1);
2023-06-10 21:34:41 +08:00
uint32_t dirLength = dir->length;
ffStrbufAppendS(dir, "fastfetch/config.jsonc");
bool success = parseJsoncFile(dir->chars);
2023-06-10 21:34:41 +08:00
ffStrbufSubstrBefore(dir, dirLength);
if (success) return;
}
for(uint32_t i = instance.state.platform.configDirs.length; i > 0; --i)
{
if(!data->loadUserConfig)
return;
2021-02-27 20:31:54 +01:00
FFstrbuf* dir = ffListGet(&instance.state.platform.configDirs, i - 1);
uint32_t dirLength = dir->length;
ffStrbufAppendS(dir, "fastfetch/config.conf");
parseConfigFile(data, dir->chars);
ffStrbufSubstrBefore(dir, dirLength);
}
}
static void parseArguments(FFdata* data, int argc, const char** argv)
{
2022-07-21 14:37:13 +02:00
if(!data->loadUserConfig)
return;
for(int i = 1; i < argc; i++)
{
const char* key = argv[i];
if(*key != '-')
{
fprintf(stderr, "Error: invalid option: %s. An option must start with `-`\n", key);
exit(400);
}
2021-11-18 18:25:24 +01:00
if(i == argc - 1 || (
2023-08-25 10:32:03 +08:00
argv[i + 1][0] == '-' &&
argv[i + 1][1] != '\0' && // `-` is used as an alias for `/dev/stdin`
2021-11-18 18:25:24 +01:00
strcasecmp(argv[i], "--separator-string") != 0 // Separator string can start with a -
)) {
parseOption(data, argv[i], NULL);
}
else
{
parseOption(data, argv[i], argv[i + 1]);
++i;
}
}
2021-02-27 20:31:54 +01:00
}
2021-12-07 17:20:11 +01:00
int main(int argc, const char** argv)
{
ffInitInstance();
2021-04-07 17:36:03 +02:00
2021-12-07 17:20:11 +01:00
//Data stores things only needed for the configuration of fastfetch
FFdata data;
ffStrbufInit(&data.structure);
ffListInit(&data.customValues, sizeof(FFCustomValue));
2022-07-21 14:37:13 +02:00
data.loadUserConfig = true;
2021-03-27 18:14:19 +01:00
2023-01-20 16:05:03 +08:00
if(!getenv("NO_CONFIG"))
parseConfigFiles(&data);
parseArguments(&data, argc, argv);
2021-06-17 13:49:25 +02:00
if (instance.state.configDoc)
{
const char* error = NULL;
if (
(error = ffParseLogoJsonConfig()) ||
(error = ffParseGeneralJsonConfig()) ||
(error = ffParseDisplayJsonConfig()) ||
(error = ffParseLibraryJsonConfig()) ||
false
) {
fputs(error, stderr);
exit(477);
}
}
const bool useJsonConfig = data.structure.length == 0 && instance.state.configDoc;
if(useJsonConfig)
2023-08-29 11:13:55 +08:00
ffPrintJsonConfig(true /* prepare */);
else
2023-08-29 11:13:55 +08:00
ffPrepareCommandOption(&data);
2022-09-29 17:14:50 +08:00
ffStart();
2023-06-13 11:08:49 +08:00
#if defined(_WIN32)
if (!instance.config.noBuffer) fflush(stdout);
#endif
if (useJsonConfig)
ffPrintJsonConfig(false);
2023-06-11 00:51:29 +08:00
else
2023-08-29 11:13:55 +08:00
ffPrintCommandOption(&data);
2023-06-11 00:51:29 +08:00
2023-08-29 11:13:55 +08:00
if (instance.state.resultDoc)
{
2023-09-18 20:12:33 +08:00
yyjson_mut_write_fp(stdout, instance.state.resultDoc, YYJSON_WRITE_INF_AND_NAN_AS_NULL | YYJSON_WRITE_PRETTY_TWO_SPACES, NULL, NULL);
2021-12-07 17:20:11 +01:00
}
2022-09-22 22:02:58 +02:00
ffFinish();
2022-09-22 22:02:58 +02:00
2022-09-22 21:28:20 +08:00
ffStrbufDestroy(&data.structure);
FF_LIST_FOR_EACH(FFCustomValue, customValue, data.customValues)
{
ffStrbufDestroy(&customValue->key);
ffStrbufDestroy(&customValue->value);
}
ffListDestroy(&data.customValues);
ffDestroyInstance();
2021-02-18 22:25:36 +01:00
}