diff --git a/CHANGELOG.md b/CHANGELOG.md index a2dee4177..1869906db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,34 +1,62 @@ +# 2.12.0 + +Changes: +* The long deprecated flag based config files are removed. + * They can still be used with `xargs fastfetch < /path/to/config.conf` + * `--gen-config` can be used to migrate them to json based config files +* The long deprecated options `--set` and `--set-keyless` are removed. +* `Kernel` module now prints kernel name by default + +Features: +* Support `st` terminal font detection for font configuration compiled in `st` binary (TerminalFont, Linux) +* Add option `--color-output` to change output color of all modules except `Title`, `Separator` + * `display.color.output` in JSONC config file +* Add option `---output-color` to change output color of one specified module, which overrides the global option `--color-output` +* Add option `--publicip-ipv6` to print IPv6 address (PublicIP) +* Add new module `Loadavg` to print load averages (Loadavg) +* Add new module `PhysicalMemory` to print information of physical memory devices (PhysicalMemory) + * Requires root permission to work on Linux and FreeBSD +* Support specifying `--logo-width` only for `--kitty-direct` and `--iterm` (Logo) +* Add option `--localip-show-all-ips` to show all IPs assigned to the same interface (LocalIP) + * Default to `false` +* Improve compatibility with `(*term)` (#909, Terminal, macOS) + +Bugfixes: +* Rename option `--temperature-unit` to `--temp-unit` as documented in help messages +* Fix alternate logo doesn't work with `{ "type": "builtin" }` (#914, Logo) + +Logos: +* Fix DahliaOS detection +* Add openSUSE Slowroll +* Add macOS3 +* Add Quirinux + # 2.11.5 Bugfix: - * Fix logo printing for OpenMandriva (#896) * Remove `--os-file` in help messages # 2.11.4 Changes: - * Fastfetch will print a colorless ascii logo in `--pipe` mode for better `lolcat` compatibility. `fastfetch | lolcat` should work and no `--pipe false` needed. * Previously the logo would be disabled in `--pipe` mode. -* `--os-file` was removed and CMake option `-DCUSTOM_OS_RELEASE_PATH=/path/to/os-release` was added. This option should not used in most cases. + * Use `--pipe -l none` to get the old beheavior +* `--os-file` was removed and CMake option `-DCUSTOM_OS_RELEASE_PATH=/path/to/os-release` was introduced for configuring at compile time by package managers if needed. This option should not used in most cases. Bugfixes: - * Fix possible out-of-bound memory access (#868) -* Fix Apple Terminal detection (macOS, Terminal) -* Fix doubled output in custom formation (#852) +* Fix Apple Terminal detection (#878, macOS, Terminal) * Fix deprecation warning for macOS 14.0 hopefully (#860, macOS, Camera) -* Fix memory leaks when passing informative options +* Fix memory leaks when passing informative options (#888) * Fix JSON config `size.ndigits` doesn't work Features: - * Enable `--pipe` mode if environment variable `$NO_COLOR` is set * Support Armbian and Proxmox distro detection (OS, Linux) Logo: - * Add Armbian # 2.11.3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 89649978b..735998c1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url project(fastfetch - VERSION 2.11.5 + VERSION 2.12.0 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" @@ -297,6 +297,7 @@ set(LIBFASTFETCH_SRC src/detection/opencl/opencl.c src/detection/os/os.c src/detection/packages/packages.c + src/detection/physicalmemory/physicalmemory.c src/detection/publicip/publicip.c src/detection/terminaltheme/terminaltheme.c src/detection/terminalfont/terminalfont.c @@ -334,6 +335,7 @@ set(LIBFASTFETCH_SRC src/modules/gamepad/gamepad.c src/modules/kernel/kernel.c src/modules/lm/lm.c + src/modules/loadavg/loadavg.c src/modules/locale/locale.c src/modules/localip/localip.c src/modules/memory/memory.c @@ -344,6 +346,7 @@ set(LIBFASTFETCH_SRC src/modules/os/os.c src/modules/packages/packages.c src/modules/physicaldisk/physicaldisk.c + src/modules/physicalmemory/physicalmemory.c src/modules/processes/processes.c src/modules/player/player.c src/modules/poweradapter/poweradapter.c @@ -399,6 +402,7 @@ if(LINUX) src/detection/bluetooth/bluetooth_linux.c src/detection/disk/disk_linux.c src/detection/physicaldisk/physicaldisk_linux.c + src/detection/physicalmemory/physicalmemory_linux.c src/detection/diskio/diskio_linux.c src/detection/displayserver/linux/displayserver_linux.c src/detection/displayserver/linux/drm.c @@ -417,6 +421,7 @@ if(LINUX) src/detection/icons/icons_linux.c src/detection/libc/libc_linux.c src/detection/lm/lm_linux.c + src/detection/loadavg/loadavg_linux.c src/detection/locale/locale_linux.c src/detection/localip/localip_linux.c src/detection/gamepad/gamepad_linux.c @@ -464,6 +469,7 @@ elseif(ANDROID) src/detection/cpuusage/cpuusage_linux.c src/detection/disk/disk_linux.c src/detection/physicaldisk/physicaldisk_linux.c + src/detection/physicalmemory/physicalmemory_nosupport.c src/detection/diskio/diskio_linux.c src/detection/displayserver/displayserver_android.c src/detection/font/font_nosupport.c @@ -472,6 +478,7 @@ elseif(ANDROID) src/detection/icons/icons_nosupport.c src/detection/libc/libc_android.c src/detection/lm/lm_nosupport.c + src/detection/loadavg/loadavg_linux.c src/detection/locale/locale_linux.c src/detection/localip/localip_linux.c src/detection/gamepad/gamepad_nosupport.c @@ -520,6 +527,7 @@ elseif(BSD) src/detection/cursor/cursor_linux.c src/detection/disk/disk_bsd.c src/detection/physicaldisk/physicaldisk_bsd.c + src/detection/physicalmemory/physicalmemory_linux.c src/detection/diskio/diskio_bsd.c src/detection/displayserver/linux/displayserver_linux.c src/detection/displayserver/linux/drm.c @@ -538,6 +546,7 @@ elseif(BSD) src/detection/lm/lm_linux.c src/detection/icons/icons_linux.c src/detection/libc/libc_bsd.c + src/detection/loadavg/loadavg_bsd.c src/detection/locale/locale_linux.c src/detection/localip/localip_linux.c src/detection/gamepad/gamepad_bsd.c @@ -586,14 +595,16 @@ elseif(APPLE) src/detection/cursor/cursor_apple.m src/detection/disk/disk_bsd.c src/detection/physicaldisk/physicaldisk_apple.c + src/detection/physicalmemory/physicalmemory_apple.m src/detection/diskio/diskio_apple.c src/detection/displayserver/displayserver_apple.c src/detection/font/font_apple.m src/detection/gpu/gpu_apple.c src/detection/gpu/gpu_apple.m src/detection/host/host_apple.c - src/detection/lm/lm_nosupport.c src/detection/icons/icons_nosupport.c + src/detection/lm/lm_nosupport.c + src/detection/loadavg/loadavg_bsd.c src/detection/libc/libc_apple.c src/detection/locale/locale_linux.c src/detection/localip/localip_linux.c @@ -651,11 +662,13 @@ elseif(WIN32) src/detection/icons/icons_windows.c src/detection/libc/libc_windows.cpp src/detection/lm/lm_nosupport.c + src/detection/loadavg/loadavg_nosupport.c src/detection/locale/locale_windows.c src/detection/localip/localip_windows.c src/detection/gamepad/gamepad_windows.c src/detection/media/media_nosupport.c src/detection/memory/memory_windows.c + src/detection/physicalmemory/physicalmemory_linux.c src/detection/monitor/monitor_windows.c src/detection/netio/netio_windows.c src/detection/opengl/opengl_windows.c @@ -767,7 +780,7 @@ if(BSD AND EXISTS "/usr/local/bin/objdump") endif() endif() -target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE _XOPEN_SOURCE __STDC_WANT_LIB_EXT1__=1) +target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE _XOPEN_SOURCE __STDC_WANT_LIB_EXT1__=1 _FILE_OFFSET_BITS=64) if(WIN32) target_compile_definitions(libfastfetch PUBLIC WIN32_LEAN_AND_MEAN=1) elseif(APPLE) diff --git a/completions/bash b/completions/bash index 783ddefe0..d3d6964c7 100644 --- a/completions/bash +++ b/completions/bash @@ -191,7 +191,6 @@ __fastfetch_completion() "--escape-bedrock" "--pipe" "--title-fqdn" - "--escape-bedrock" "--disk-folders" "--disk-show-external" "--disk-show-hidden" @@ -227,8 +226,6 @@ __fastfetch_completion() "--separator" "-s" "--structure" - "--set" - "--set-keyless" "--player-name" "--percent-type" "--publicip-url" diff --git a/doc/json_schema.json b/doc/json_schema.json index 65d80d8aa..dc43ff932 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -4,9 +4,8 @@ "colors": { "type": "string", "enum": [ - "reset_", "bright_", "dim_", - "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", "default", - "bright_black", "bright_red", "bright_green", "bright_yellow", "bright_blue", "bright_magenta", "bright_cyan", "bright_white" + "reset_", "bright_", "dim_", "italic_", "underline_", "blink_", "inverse_", "hidden_", "strike_", "light_", + "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", "default" ] }, "key": { @@ -24,9 +23,13 @@ "default": 0 }, "format": { - "description": "Output format of the module", + "description": "Output format of the module. See `-h <module>-format` for detail", "type": "string" }, + "outputColor": { + "description": "Output color of the module. Left empty to use `display.color.output`", + "$ref": "#/$defs/colors" + }, "percent": { "description": "Threshold of percentage colors", "type": "object", @@ -380,6 +383,10 @@ "title": { "description": "Set the color of the title", "$ref": "#/$defs/colors" + }, + "output": { + "description": "Set the color of the module output", + "$ref": "#/$defs/colors" } } } @@ -694,6 +701,7 @@ "icons", "kernel", "lm", + "loadavg", "locale", "localip", "media", @@ -893,6 +901,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -927,6 +938,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -958,6 +972,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -984,6 +1001,21 @@ "minimum": 0, "maximum": 400, "default": 10 + }, + "key": { + "$ref": "#/$defs/key" + }, + "keyColor": { + "$ref": "#/$defs/keyColor" + }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, + "keyWidth": { + "$ref": "#/$defs/keyWidth" + }, + "format": { + "$ref": "#/$defs/format" } } }, @@ -1002,6 +1034,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1035,6 +1070,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1142,6 +1180,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1166,6 +1207,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1215,6 +1259,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1275,6 +1322,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1307,6 +1357,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1335,6 +1388,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1361,6 +1417,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1406,6 +1465,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1448,6 +1510,11 @@ "type": "boolean", "default": true }, + "showAllIps": { + "description": "Show all IPs bound to the same interface.\nBy default only the first IP is shown", + "type": "boolean", + "default": false + }, "compact": { "description": "Show all IPs in one line", "type": "boolean", @@ -1468,6 +1535,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1494,6 +1564,42 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, + "keyWidth": { + "$ref": "#/$defs/keyWidth" + }, + "format": { + "$ref": "#/$defs/format" + } + } + }, + { + "title": "Loadavg", + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "loadavg", + "description": "Print system load averages" + }, + "ndigits": { + "type": "integer", + "description": "Set the number of digits to keep after the decimal point", + "minimum": 0, + "maximum": 9, + "default": 2 + }, + "key": { + "$ref": "#/$defs/key" + }, + "keyColor": { + "$ref": "#/$defs/keyColor" + }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1531,6 +1637,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1564,6 +1673,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1618,6 +1730,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1648,6 +1763,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1677,12 +1795,20 @@ "minimum": 0, "default": "disabled (0)" }, + "ipv6": { + "description": "Whether to use IPv6 for public IP detection server", + "type": "boolean", + "default": false + }, "key": { "$ref": "#/$defs/key" }, "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1704,6 +1830,9 @@ "description": "Set the string to be printed", "type": "string", "default": "-" + }, + "outputColor": { + "$ref": "#/$defs/outputColor" } } }, @@ -1735,6 +1864,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1761,6 +1893,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1808,6 +1943,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1846,6 +1984,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, @@ -1874,6 +2015,9 @@ "keyColor": { "$ref": "#/$defs/keyColor" }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, "keyWidth": { "$ref": "#/$defs/keyWidth" }, diff --git a/presets/all.jsonc b/presets/all.jsonc index ddbf89289..cf7f2af93 100644 --- a/presets/all.jsonc +++ b/presets/all.jsonc @@ -10,6 +10,7 @@ "chassis", "kernel", "uptime", + "loadavg", "processes", "packages", "shell", @@ -33,6 +34,7 @@ "cpuusage", "gpu", "memory", + "physicalmemory", "swap", "disk", "battery", diff --git a/presets/archey.jsonc b/presets/archey.jsonc new file mode 100644 index 000000000..ae5401d08 --- /dev/null +++ b/presets/archey.jsonc @@ -0,0 +1,84 @@ +{ + "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", + "modules": [ + { + "type": "title", + "key": "User", + "format": "{1}" + }, + { + "type": "title", + "key": "Hostname", + "format": "{2}" + }, + { + "type": "host", + "key": "Model" + }, + { + "type": "os", + "format": "{3} {9} {12}" + }, + "kernel", + "uptime", + { + "type": "loadavg", + "key": "Load Average" + }, + "processes", + { + "type": "wm", + "key": "Window Manager" + }, + { + "type": "de", + "key": "Desktop Environment" + }, + { + "type": "shell", + "format": "{7}" + }, + { + "type": "terminal", + "format": "{5} {6} \u001b[37m█\u001b[97m█ \u001b[36m█\u001b[96m█ \u001b[35m█\u001b[95m█ \u001b[34m█\u001b[94m█ \u001b[33m█\u001b[93m█ \u001b[32m█\u001b[92m█ \u001b[31m█\u001b[91m█ \u001b[30m█\u001b[90m█" + }, + { + "type": "packages", + "format": "{1}" + }, + { + "type": "cpu", + "key": "Temperature", + "temp": true, + "format": "{8}" + }, + { + "type": "cpu", + "key": "CPU", + "format": "{4} x {1}" + }, + { + "type": "gpu", + "format": "{2}" + }, + { + "type": "memory", + "key": "RAM" + }, + { + "type": "disk", + "key": "Disk", + "folders": "/" + }, + { + "type": "localip", + "key": "LAN IP", + "showIpv6": true, + "showPrefixLen": false + }, + { + "type": "publicip", + "key": "WAN IP" + } + ] +} diff --git a/presets/ci.jsonc b/presets/ci.jsonc index 799a31c13..e78ac6ce3 100644 --- a/presets/ci.jsonc +++ b/presets/ci.jsonc @@ -6,6 +6,7 @@ "showErrors": true, "noBuffer": true }, + "logo": null, "modules": [ "title", "separator", @@ -16,6 +17,7 @@ "chassis", "kernel", "uptime", + "loadavg", "processes", "packages", "shell", @@ -39,6 +41,7 @@ "cpuusage", "gpu", "memory", + "physicalmemory", "swap", "disk", "battery", diff --git a/presets/hardware.jsonc b/presets/hardware.jsonc index d976285fc..c6c22a758 100644 --- a/presets/hardware.jsonc +++ b/presets/hardware.jsonc @@ -1,25 +1,37 @@ { "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", - "logo": { - "type": "builtin", - "source": "arch" - }, "modules": [ - "host", - "display", + { + "type": "host", + "format": "{?2}{2}{?}{?5} ({5}){?}" + }, + "chassis", + { + "type": "board", + "format": "{?1}{1}{?}{?2} ({2}){?}" + }, "cpu", - "gpu", { "type": "memory", "format": "{?2}{2}{?}" }, - { - "type": "disk", - "format": "{?2}{2}{?}" - }, + "physicaldisk", + "gpu", + "display", + "monitor", + { + "type": "wifi", + "format": "{?1}{1}{?}{?6} - {6}{?}" + }, + { + "type": "sound", + "format": "{?2}{2}{?}" + }, + "camera", { "type": "battery", - "format": "{?1}{1} {?}{?2}{2} {?}{?3}({3}){?}" - } + "format": "{?1}{1}{?}{?2} {2}{?}{?3} ({3}){?}" + }, + "poweradapter" ] } diff --git a/src/common/commandoption.c b/src/common/commandoption.c index d1824d0fc..3d9c93742 100644 --- a/src/common/commandoption.c +++ b/src/common/commandoption.c @@ -79,29 +79,10 @@ static void genJsonResult(FFModuleBaseInfo* baseInfo, yyjson_mut_doc* doc) static void parseStructureCommand( const char* line, - FFlist* customValues, void (*fn)(FFModuleBaseInfo *baseInfo, yyjson_mut_doc* jsonDoc), yyjson_mut_doc* jsonDoc ) { - // handle `--set` and `--set-keyless` - FF_LIST_FOR_EACH(FFCustomValue, customValue, *customValues) - { - if (ffStrbufEqualS(&customValue->key, line)) - { - __attribute__((__cleanup__(ffDestroyCustomOptions))) FFCustomOptions options; - ffInitCustomOptions(&options); - if (customValue->printKey) - ffStrbufAppend(&options.moduleArgs.key, &customValue->key); - ffStrbufAppend(&options.moduleArgs.outputFormat, &customValue->value); - if (__builtin_expect(jsonDoc != NULL, false)) - fn((FFModuleBaseInfo*) &options, jsonDoc); - else - ffPrintCustom(&options); - return; - } - } - if(ffCharIsEnglishAlphabet(line[0])) { for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(line[0]) - 'A']; *modules; ++modules) @@ -134,7 +115,7 @@ void ffPrintCommandOption(FFdata* data, yyjson_mut_doc* jsonDoc) if(instance.config.display.stat) ms = ffTimeGetTick(); - parseStructureCommand(data->structure.chars + startIndex, &data->customValues, genJsonResult, jsonDoc); + parseStructureCommand(data->structure.chars + startIndex, genJsonResult, jsonDoc); if(instance.config.display.stat) { @@ -177,7 +158,7 @@ void ffMigrateCommandOptionToJsonc(FFdata* data, yyjson_mut_doc* jsonDoc) uint32_t colonIndex = ffStrbufNextIndexC(&data->structure, startIndex, ':'); data->structure.chars[colonIndex] = '\0'; - parseStructureCommand(data->structure.chars + startIndex, &data->customValues, genJsonConfig, jsonDoc); + parseStructureCommand(data->structure.chars + startIndex, genJsonConfig, jsonDoc); startIndex = colonIndex + 1; } diff --git a/src/common/commandoption.h b/src/common/commandoption.h index 1736a4f12..f8cf12044 100644 --- a/src/common/commandoption.h +++ b/src/common/commandoption.h @@ -2,18 +2,10 @@ #include "fastfetch.h" -typedef struct FFCustomValue -{ - bool printKey; - FFstrbuf key; - FFstrbuf value; -} FFCustomValue; - // Things only needed by fastfetch typedef struct FFdata { FFstrbuf structure; - FFlist customValues; // List of FFCustomValue bool configLoaded; } FFdata; diff --git a/src/common/jsonconfig.c b/src/common/jsonconfig.c index de34b2b10..735b93032 100644 --- a/src/common/jsonconfig.c +++ b/src/common/jsonconfig.c @@ -22,6 +22,11 @@ bool ffJsonConfigParseModuleArgs(const char* key, yyjson_val* val, FFModuleArgs* ffStrbufSetNS(&moduleArgs->outputFormat, (uint32_t) yyjson_get_len(val), yyjson_get_str(val)); return true; } + else if(ffStrEqualsIgnCase(key, "outputColor")) + { + ffOptionParseColor(yyjson_get_str(val), &moduleArgs->outputColor); + return true; + } else if(ffStrEqualsIgnCase(key, "keyColor")) { ffOptionParseColor(yyjson_get_str(val), &moduleArgs->keyColor); diff --git a/src/common/modules.c b/src/common/modules.c index 718a8114a..ea9958eb3 100644 --- a/src/common/modules.c +++ b/src/common/modules.c @@ -71,6 +71,7 @@ static FFModuleBaseInfo* K[] = { static FFModuleBaseInfo* L[] = { (void*) &instance.config.modules.lm, + (void*) &instance.config.modules.loadavg, (void*) &instance.config.modules.locale, (void*) &instance.config.modules.localIP, NULL, @@ -98,6 +99,7 @@ static FFModuleBaseInfo* O[] = { static FFModuleBaseInfo* P[] = { (void*) &instance.config.modules.packages, (void*) &instance.config.modules.physicalDisk, + (void*) &instance.config.modules.physicalMemory, (void*) &instance.config.modules.player, (void*) &instance.config.modules.powerAdapter, (void*) &instance.config.modules.processes, diff --git a/src/common/networking.h b/src/common/networking.h index cc8285cc5..8122e4eb0 100644 --- a/src/common/networking.h +++ b/src/common/networking.h @@ -22,6 +22,7 @@ typedef struct FFNetworkingState { #endif uint32_t timeout; + bool ipv6; } FFNetworkingState; const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers); diff --git a/src/common/networking_linux.c b/src/common/networking_linux.c index 7a8054918..8bdd16bbd 100644 --- a/src/common/networking_linux.c +++ b/src/common/networking_linux.c @@ -14,7 +14,7 @@ static const char* connectAndSend(FFNetworkingState* state) struct addrinfo* addr; if(getaddrinfo(state->host.chars, "80", &(struct addrinfo) { - .ai_family = AF_INET, + .ai_family = state->ipv6 ? AF_INET6 : AF_INET, .ai_socktype = SOCK_STREAM, }, &addr) != 0) { diff --git a/src/common/networking_windows.c b/src/common/networking_windows.c index 27a884216..83b3fd864 100644 --- a/src/common/networking_windows.c +++ b/src/common/networking_windows.c @@ -51,7 +51,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho struct addrinfo* addr; if(getaddrinfo(host, "80", &(struct addrinfo) { - .ai_family = AF_INET, + .ai_family = state->ipv6 ? AF_INET6 : AF_INET, .ai_socktype = SOCK_STREAM, }, &addr) != 0) return "getaddrinfo() failed"; diff --git a/src/common/option.c b/src/common/option.c index 81efd69d1..a9fa3878f 100644 --- a/src/common/option.c +++ b/src/common/option.c @@ -39,6 +39,16 @@ bool ffOptionParseModuleArgs(const char* argumentKey, const char* subKey, const ffOptionParseString(argumentKey, value, &result->outputFormat); return true; } + else if(ffStrEqualsIgnCase(subKey, "output-color")) + { + if(value == NULL) + { + fprintf(stderr, "Error: usage: %s \n", argumentKey); + exit(477); + } + ffOptionParseColor(value, &result->outputColor); + return true; + } else if(ffStrEqualsIgnCase(subKey, "key-color")) { if(value == NULL) @@ -148,6 +158,12 @@ void ffOptionParseColor(const char* value, FFstrbuf* buffer) FF_APPEND_COLOR_CODE_COND(reset_, FF_COLOR_MODE_RESET) else FF_APPEND_COLOR_CODE_COND(bright_, FF_COLOR_MODE_BOLD) else FF_APPEND_COLOR_CODE_COND(dim_, FF_COLOR_MODE_DIM) + else FF_APPEND_COLOR_CODE_COND(italic_, FF_COLOR_MODE_ITALIC) + else FF_APPEND_COLOR_CODE_COND(underline_, FF_COLOR_MODE_UNDERLINE) + else FF_APPEND_COLOR_CODE_COND(blink_, FF_COLOR_MODE_BLINK) + else FF_APPEND_COLOR_CODE_COND(inverse_, FF_COLOR_MODE_INVERSE) + else FF_APPEND_COLOR_CODE_COND(hidden_, FF_COLOR_MODE_HIDDEN) + else FF_APPEND_COLOR_CODE_COND(strike_, FF_COLOR_MODE_STRIKETHROUGH) else FF_APPEND_COLOR_CODE_COND(black, FF_COLOR_FG_BLACK) else FF_APPEND_COLOR_CODE_COND(red, FF_COLOR_FG_RED) else FF_APPEND_COLOR_CODE_COND(green, FF_COLOR_FG_GREEN) @@ -180,6 +196,7 @@ void ffOptionInitModuleArg(FFModuleArgs* args) ffStrbufInit(&args->key); ffStrbufInit(&args->keyColor); ffStrbufInit(&args->outputFormat); + ffStrbufInit(&args->outputColor); args->keyWidth = 0; } @@ -188,4 +205,5 @@ void ffOptionDestroyModuleArg(FFModuleArgs* args) ffStrbufDestroy(&args->key); ffStrbufDestroy(&args->keyColor); ffStrbufDestroy(&args->outputFormat); + ffStrbufDestroy(&args->outputColor); } diff --git a/src/common/option.h b/src/common/option.h index a76c9e623..e7dba9b22 100644 --- a/src/common/option.h +++ b/src/common/option.h @@ -50,6 +50,7 @@ typedef struct FFModuleArgs FFstrbuf key; FFstrbuf keyColor; FFstrbuf outputFormat; + FFstrbuf outputColor; uint32_t keyWidth; } FFModuleArgs; diff --git a/src/common/percent.c b/src/common/percent.c index a80f3ed10..14ab93f55 100644 --- a/src/common/percent.c +++ b/src/common/percent.c @@ -6,7 +6,15 @@ #include "util/textModifier.h" #include "util/stringUtils.h" -void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFColorRangeConfig config) +static void appendOutputColor(FFstrbuf* buffer, const FFModuleArgs* module) +{ + if (module->outputColor.length) + ffStrbufAppendF(buffer, "\e[%sm", module->outputColor.chars); + else if (instance.config.display.colorOutput.length) + ffStrbufAppendF(buffer, "\e[%sm", instance.config.display.colorOutput.chars); +} + +void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFColorRangeConfig config, const FFModuleArgs* module) { uint8_t green = config.green, yellow = config.yellow; assert(green <= 100 && yellow <= 100); @@ -66,16 +74,19 @@ void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFColorRangeConfig con if(options->barBorder) { if(!options->pipe) - ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_WHITE "m ]"); + ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_WHITE "m ]");\ else ffStrbufAppendS(buffer, " ]"); } if(!options->pipe) + { ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET); + appendOutputColor(buffer, module); + } } -void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFColorRangeConfig config, bool parentheses) +void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFColorRangeConfig config, bool parentheses, const FFModuleArgs* module) { uint8_t green = config.green, yellow = config.yellow; assert(green <= 100 && yellow <= 100); @@ -120,6 +131,7 @@ void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFColorRangeConfig con if (colored && !options->pipe) { ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET); + appendOutputColor(buffer, module); } if (parentheses) diff --git a/src/common/percent.h b/src/common/percent.h index 6d7c6b2e8..8ae87708c 100644 --- a/src/common/percent.h +++ b/src/common/percent.h @@ -2,6 +2,7 @@ #include "util/FFstrbuf.h" #include "common/parsing.h" +#include "common/option.h" enum { @@ -21,8 +22,8 @@ enum // [yellow, green): print yellow // [0, yellow): print red -void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFColorRangeConfig config); -void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFColorRangeConfig config, bool parentheses); +void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFColorRangeConfig config, const FFModuleArgs* module); +void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFColorRangeConfig config, bool parentheses, const FFModuleArgs* module); typedef struct yyjson_val yyjson_val; typedef struct yyjson_mut_doc yyjson_mut_doc; diff --git a/src/common/printing.c b/src/common/printing.c index 2eaf41a16..846d49122 100644 --- a/src/common/printing.c +++ b/src/common/printing.c @@ -49,8 +49,13 @@ void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModu ffStrbufWriteTo(&instance.config.display.keyValueSeparator, stdout); if(!instance.config.display.pipe) + { fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout); - + if (moduleArgs->outputColor.length) + ffPrintColor(&moduleArgs->outputColor); + else if (instance.config.display.colorOutput.length) + ffPrintColor(&instance.config.display.colorOutput); + } if (!instance.config.display.pipe && !(printType & FF_PRINT_TYPE_NO_CUSTOM_KEY_WIDTH)) { diff --git a/src/common/temps.c b/src/common/temps.c index d72f46f0e..e413d4c5c 100644 --- a/src/common/temps.c +++ b/src/common/temps.c @@ -3,7 +3,7 @@ #include "util/textModifier.h" #include "util/stringUtils.h" -void ffTempsAppendNum(double celsius, FFstrbuf* buffer, FFColorRangeConfig config) +void ffTempsAppendNum(double celsius, FFstrbuf* buffer, FFColorRangeConfig config, const FFModuleArgs* module) { if (celsius != celsius) // ignores NaN return; @@ -37,7 +37,7 @@ void ffTempsAppendNum(double celsius, FFstrbuf* buffer, FFColorRangeConfig confi } } - switch (options->temperatureUnit) + switch (options->tempUnit) { case FF_TEMPERATURE_UNIT_CELSIUS: ffStrbufAppendF(buffer, "%.*f°C", options->tempNdigits, celsius); @@ -51,7 +51,13 @@ void ffTempsAppendNum(double celsius, FFstrbuf* buffer, FFColorRangeConfig confi } if (!options->pipe) + { ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET); + if (module->outputColor.length) + ffStrbufAppendF(buffer, "\e[%sm", module->outputColor.chars); + else if (instance.config.display.colorOutput.length) + ffStrbufAppendF(buffer, "\e[%sm", instance.config.display.colorOutput.chars); + } } bool ffTempsParseCommandOptions(const char* key, const char* subkey, const char* value, bool* useTemp, FFColorRangeConfig* config) diff --git a/src/common/temps.h b/src/common/temps.h index bbbc74766..2236713d2 100644 --- a/src/common/temps.h +++ b/src/common/temps.h @@ -1,8 +1,9 @@ #pragma once #include "common/parsing.h" +#include "common/option.h" -void ffTempsAppendNum(double celsius, FFstrbuf* buffer, FFColorRangeConfig config); +void ffTempsAppendNum(double celsius, FFstrbuf* buffer, FFColorRangeConfig config, const FFModuleArgs* module); bool ffTempsParseCommandOptions(const char* key, const char* subkey, const char* value, bool* useTemp, FFColorRangeConfig* config); bool ffTempsParseJsonObject(const char* key, yyjson_val* value, bool* useTemp, FFColorRangeConfig* config); void ffTempsGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FF_MAYBE_UNUSED bool defaultTemp, FFColorRangeConfig defaultConfig, bool temp, FFColorRangeConfig config); diff --git a/src/data/help.json b/src/data/help.json index 2e2538e4f..7e3631e35 100644 --- a/src/data/help.json +++ b/src/data/help.json @@ -436,7 +436,7 @@ { "long": "color-keys", "desc": "Set the color of the keys", - "remark": "See `-h color` for the list of available colors", + "remark": "Doesn't affect module Title, Separator and Colors. See `-h color` for the list of available colors", "arg": { "type": "color" } @@ -449,6 +449,14 @@ "type": "color" } }, + { + "long": "color-output", + "desc": "Set the color of module output", + "remark": "See `-h color` for the list of available colors", + "arg": { + "type": "color" + } + }, { "long": "color", "desc": "Set the color of both the keys and title", @@ -481,22 +489,6 @@ "default": ": " } }, - { - "long": "set", - "desc": "Hard set the value of a key", - "remark": "Deprecated. Use \"Custom\" module instead", - "arg": { - "type": "key=value" - } - }, - { - "long": "set-keyless", - "desc": "Hard set the value of a key, but don't print the key or the separator", - "remark": "Deprecated. Use \"Custom\" module instead", - "arg": { - "type": "key=value" - } - }, { "long": "show-errors", "desc": "Print occurring errors", @@ -521,7 +513,7 @@ "arg": { "type": "bool", "optional": true, - "default": true + "default": false } }, { @@ -937,6 +929,14 @@ "default": "-" } }, + { + "long": "separator-output-color", + "desc": "Set the color of the separator module", + "arg": { + "type": "color", + "default": "default" + } + }, { "long": "disk-folders", "desc": "A colon (semicolon on Windows) separated list of folder paths to be detected", @@ -1228,6 +1228,14 @@ "default": false } }, + { + "long": "loadavg-ndigits", + "desc": "Set the number of digits to keep after the decimal point when printing load average", + "arg": { + "type": "num", + "default": 2 + } + }, { "long": "localip-show-ipv4", "desc": "Show IPv4 addresses in local ip module", @@ -1290,6 +1298,15 @@ "default": true } }, + { + "long": "localip-show-all-ips", + "desc": "Show all IPs bound to the same interface. By default only the first IP is shown", + "arg": { + "type": "bool", + "optional": true, + "default": false + } + }, { "long": "localip-compact", "desc": "Show all IPs in one line", @@ -1340,6 +1357,14 @@ "type": "str" } }, + { + "long": "publicip-ipv6", + "desc": "Whether to use IPv6 for public IP detection server", + "arg": { + "type": "bool", + "default": false + } + }, { "long": "weather-location", "desc": "Set the location to be used", @@ -1514,6 +1539,14 @@ }, "pseudo": true }, + { + "long": "-output-color", + "desc": "Override the global \"--color-output\" option for each specific module", + "arg": { + "type": "color" + }, + "pseudo": true + }, { "long": "-key-width", "desc": "Override the global \"--key-width\" option for each specific module", diff --git a/src/data/help_color.txt b/src/data/help_color.txt index a05ef5d7c..1804498e9 100644 --- a/src/data/help_color.txt +++ b/src/data/help_color.txt @@ -5,6 +5,6 @@ Infos about them can be found here: https://en.wikipedia.org/wiki/ANSI_escape_co Examples: --color 35: sets the color to magenta. `--color magenta` is also supported --color 38;5;38: sets the color to 38th color of ANSI 256 colors - --color 4;92: sets the color to bright Green with underline + --color 4;92: sets the color to bright Green with underline. `--color underline_green` is also supported --color 5;104: blinking text on a blue background If no color is set, the main color of the logo will be used. diff --git a/src/detection/battery/battery_windows.c b/src/detection/battery/battery_windows.c index 357000ec3..5a0a78a3c 100644 --- a/src/detection/battery/battery_windows.c +++ b/src/detection/battery/battery_windows.c @@ -154,8 +154,6 @@ static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results return NULL; } - - typedef struct FFSmbiosPortableBattery { FFSmbiosHeader Header; @@ -178,11 +176,18 @@ typedef struct FFSmbiosPortableBattery uint8_t SbdsDeviceChemistry; // string uint8_t DesignCapacityMultiplier; // varies uint16_t OEMSpecific; // varies -} FFSmbiosPortableBattery; +} __attribute__((__packed__)) FFSmbiosPortableBattery; + +static_assert(offsetof(FFSmbiosPortableBattery, OEMSpecific) == 0x16, + "FFSmbiosPortableBattery: Wrong struct alignment"); const char* detectBySmbios(FFBatteryResult* battery) { - const FFSmbiosPortableBattery* data = (const FFSmbiosPortableBattery*) (*ffGetSmbiosHeaderTable())[FF_SMBIOS_TYPE_PORTABLE_BATTERY]; + const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable(); + if (!smbiosTable) + return "Failed to get SMBIOS data"; + + const FFSmbiosPortableBattery* data = (const FFSmbiosPortableBattery*) (*smbiosTable)[FF_SMBIOS_TYPE_PORTABLE_BATTERY]; if (!data) return "Portable battery section is not found in SMBIOS data"; diff --git a/src/detection/bios/bios_windows.c b/src/detection/bios/bios_windows.c index 23ff25275..d8a305493 100644 --- a/src/detection/bios/bios_windows.c +++ b/src/detection/bios/bios_windows.c @@ -35,7 +35,7 @@ typedef struct FFSmbiosBios uint16_t BiosStartingAddressSegment; // varies uint8_t BiosReleaseDate; // string uint8_t BiosRomSize; // string - uint32_t BiosCharacteristics; // bit field + uint64_t BiosCharacteristics; // bit field // 2.4+ uint8_t BiosCharacteristicsExtensionBytes[2]; // bit field @@ -46,11 +46,18 @@ typedef struct FFSmbiosBios // 3.1+ uint16_t ExtendedBiosRomSize; // bit field -} FFSmbiosBios; +} __attribute__((__packed__)) FFSmbiosBios; + +static_assert(offsetof(FFSmbiosBios, ExtendedBiosRomSize) == 0x18, + "FFSmbiosBios: Wrong struct alignment"); const char* ffDetectBios(FFBiosResult* bios) { - const FFSmbiosBios* data = (const FFSmbiosBios*) (*ffGetSmbiosHeaderTable())[FF_SMBIOS_TYPE_BIOS]; + const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable(); + if (!smbiosTable) + return "Failed to get SMBIOS data"; + + const FFSmbiosBios* data = (const FFSmbiosBios*) (*smbiosTable)[FF_SMBIOS_TYPE_BIOS]; if (!data) return "BIOS section is not found in SMBIOS data"; diff --git a/src/detection/board/board_windows.c b/src/detection/board/board_windows.c index d16d4a905..bec6192e7 100644 --- a/src/detection/board/board_windows.c +++ b/src/detection/board/board_windows.c @@ -14,13 +14,20 @@ typedef struct FFSmbiosBaseboard uint8_t LocationInChassis; // string uint16_t ChassisHandle; // varies uint8_t BoardType; // enum - uint8_t NumberOfContainedObjectHandle; // varies - uint16_t ContainedObjectHandle[]; // varies -} FFSmbiosBaseboard; + uint8_t NumberOfContainedObjectHandles; // varies + uint16_t ContainedObjectHandles[]; // varies +} __attribute__((__packed__)) FFSmbiosBaseboard; + +static_assert(offsetof(FFSmbiosBaseboard, ContainedObjectHandles) == 0x0F, + "FFSmbiosBaseboard: Wrong struct alignment"); const char* ffDetectBoard(FFBoardResult* board) { - const FFSmbiosBaseboard* data = (const FFSmbiosBaseboard*) (*ffGetSmbiosHeaderTable())[FF_SMBIOS_TYPE_BASEBOARD_INFO]; + const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable(); + if (!smbiosTable) + return "Failed to get SMBIOS data"; + + const FFSmbiosBaseboard* data = (const FFSmbiosBaseboard*) (*smbiosTable)[FF_SMBIOS_TYPE_BASEBOARD_INFO]; if (!data) return "Baseboard information section is not found in SMBIOS data"; diff --git a/src/detection/chassis/chassis_windows.c b/src/detection/chassis/chassis_windows.c index 754b85a3d..a9f4aeedb 100644 --- a/src/detection/chassis/chassis_windows.c +++ b/src/detection/chassis/chassis_windows.c @@ -25,11 +25,18 @@ typedef struct FFSmbiosSystemEnclosure uint8_t ContainedElementCount; // varies uint8_t ContainedRecordLength; // varies uint8_t ContainedElements[]; // varies -} FFSmbiosSystemEnclosure; +} __attribute__((__packed__)) FFSmbiosSystemEnclosure; + +static_assert(offsetof(FFSmbiosSystemEnclosure, ContainedElements) == 0x15, + "FFSmbiosSystemEnclosure: Wrong struct alignment"); const char* ffDetectChassis(FFChassisResult* result) { - const FFSmbiosSystemEnclosure* data = (const FFSmbiosSystemEnclosure*) (*ffGetSmbiosHeaderTable())[FF_SMBIOS_TYPE_SYSTEM_ENCLOSURE]; + const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable(); + if (!smbiosTable) + return "Failed to get SMBIOS data"; + + const FFSmbiosSystemEnclosure* data = (const FFSmbiosSystemEnclosure*) (*smbiosTable)[FF_SMBIOS_TYPE_SYSTEM_ENCLOSURE]; if (!data) return "System enclosure is not found in SMBIOS data"; diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c index 81bae35a1..0f3bb55d5 100644 --- a/src/detection/cpu/cpu_windows.c +++ b/src/detection/cpu/cpu_windows.c @@ -48,7 +48,10 @@ typedef struct FFSmbiosProcessorInfo // 3.6+ uint16_t ThreadEnabled; // varies -} FFSmbiosProcessorInfo; +} __attribute__((__packed__)) FFSmbiosProcessorInfo; + +static_assert(offsetof(FFSmbiosProcessorInfo, ThreadEnabled) == 0x30, + "FFSmbiosProcessorInfo: Wrong struct alignment"); #if defined(__x86_64__) || defined(__i386__) @@ -77,7 +80,11 @@ inline static const char* detectSpeedByCpuid(FF_MAYBE_UNUSED FFCPUResult* cpu) static const char* detectMaxSpeedBySmbios(FFCPUResult* cpu) { - const FFSmbiosProcessorInfo* data = (const FFSmbiosProcessorInfo*) (*ffGetSmbiosHeaderTable())[FF_SMBIOS_TYPE_PROCESSOR_INFO]; + const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable(); + if (!smbiosTable) + return "Failed to get SMBIOS data"; + + const FFSmbiosProcessorInfo* data = (const FFSmbiosProcessorInfo*) (*smbiosTable)[FF_SMBIOS_TYPE_PROCESSOR_INFO]; if (!data) return "Processor information is not found in SMBIOS data"; diff --git a/src/detection/gpu/gpu.c b/src/detection/gpu/gpu.c index 3f65e1791..4f70d3452 100644 --- a/src/detection/gpu/gpu.c +++ b/src/detection/gpu/gpu.c @@ -14,42 +14,24 @@ const char* FF_GPU_VENDOR_NAME_MICROSOFT = "Microsoft"; const char* FF_GPU_VENDOR_NAME_REDHAT = "RedHat"; const char* FF_GPU_VENDOR_NAME_ORACLE = "Oracle"; -static inline bool arrayContains(const unsigned arr[], unsigned vendorId, unsigned length) -{ - for (unsigned i = 0; i < length; ++i) - { - if (arr[i] == vendorId) - return true; - } - return false; -} - const char* ffGetGPUVendorString(unsigned vendorId) { // https://devicehunt.com/all-pci-vendors - if(vendorId == 0x106b) - return FF_GPU_VENDOR_NAME_APPLE; - if(arrayContains((const unsigned[]) {0x1002, 0x1022}, vendorId, 2)) - return FF_GPU_VENDOR_NAME_AMD; - else if(arrayContains((const unsigned[]) {0x03e7, 0x8086, 0x8087}, vendorId, 3)) - return FF_GPU_VENDOR_NAME_INTEL; - else if(arrayContains((const unsigned[]) {0x0955, 0x10de, 0x12d2}, vendorId, 3)) - return FF_GPU_VENDOR_NAME_NVIDIA; - else if(arrayContains((const unsigned[]) {0x5143}, vendorId, 1)) - return FF_GPU_VENDOR_NAME_QUALCOMM; - else if(arrayContains((const unsigned[]) {0x14c3}, vendorId, 1)) - return FF_GPU_VENDOR_NAME_MTK; - else if(arrayContains((const unsigned[]) {0x15ad}, vendorId, 1)) - return FF_GPU_VENDOR_NAME_VMWARE; - else if(arrayContains((const unsigned[]) {0x1af4}, vendorId, 1)) - return FF_GPU_VENDOR_NAME_REDHAT; - else if(arrayContains((const unsigned[]) {0x1ab8}, vendorId, 1)) - return FF_GPU_VENDOR_NAME_PARALLEL; - else if(arrayContains((const unsigned[]) {0x1414}, vendorId, 1)) - return FF_GPU_VENDOR_NAME_MICROSOFT; - else if(arrayContains((const unsigned[]) {0x108e}, vendorId, 1)) - return FF_GPU_VENDOR_NAME_ORACLE; - return NULL; + switch (vendorId) + { + case 0x106b: return FF_GPU_VENDOR_NAME_APPLE; + case 0x1002: case 0x1022: return FF_GPU_VENDOR_NAME_AMD; + case 0x8086: case 0x8087: case 0x03e7: return FF_GPU_VENDOR_NAME_INTEL; + case 0x0955: case 0x10de: case 0x12d2: return FF_GPU_VENDOR_NAME_NVIDIA; + case 0x5143: return FF_GPU_VENDOR_NAME_QUALCOMM; + case 0x14c3: return FF_GPU_VENDOR_NAME_MTK; + case 0x15ad: return FF_GPU_VENDOR_NAME_VMWARE; + case 0x1af4: return FF_GPU_VENDOR_NAME_REDHAT; + case 0x1ab8: return FF_GPU_VENDOR_NAME_PARALLEL; + case 0x1414: return FF_GPU_VENDOR_NAME_MICROSOFT; + case 0x108e: return FF_GPU_VENDOR_NAME_ORACLE; + default: return NULL; + } } const char* detectByOpenGL(FFlist* gpus) diff --git a/src/detection/host/host_windows.c b/src/detection/host/host_windows.c index 8efe70a2f..c505d9071 100644 --- a/src/detection/host/host_windows.c +++ b/src/detection/host/host_windows.c @@ -18,17 +18,24 @@ typedef struct FFSmbiosSystemInfo uint8_t ClockSeqHiAndReserved; uint8_t ClockSeqLow; uint8_t Node[6]; - } UUID; // varies + } __attribute__((__packed__)) UUID; // varies uint8_t WakeUpType; // enum // 2.4+ uint8_t SKUNumber; // string uint8_t Family; // string -} FFSmbiosSystemInfo; +} __attribute__((__packed__)) FFSmbiosSystemInfo; + +static_assert(offsetof(FFSmbiosSystemInfo, Family) == 0x1A, + "FFSmbiosSystemInfo: Wrong struct alignment"); const char* ffDetectHost(FFHostResult* host) { - const FFSmbiosSystemInfo* data = (const FFSmbiosSystemInfo*) (*ffGetSmbiosHeaderTable())[FF_SMBIOS_TYPE_SYSTEM_INFO]; + const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable(); + if (!smbiosTable) + return "Failed to get SMBIOS data"; + + const FFSmbiosSystemInfo* data = (const FFSmbiosSystemInfo*) (*smbiosTable)[FF_SMBIOS_TYPE_SYSTEM_INFO]; if (!data) return "System information is not found in SMBIOS data"; diff --git a/src/detection/loadavg/loadavg.h b/src/detection/loadavg/loadavg.h new file mode 100644 index 000000000..4cfa696ed --- /dev/null +++ b/src/detection/loadavg/loadavg.h @@ -0,0 +1,5 @@ +#pragma once + +#include "fastfetch.h" + +const char* ffDetectLoadavg(double result[3]); diff --git a/src/detection/loadavg/loadavg_bsd.c b/src/detection/loadavg/loadavg_bsd.c new file mode 100644 index 000000000..71c4304c1 --- /dev/null +++ b/src/detection/loadavg/loadavg_bsd.c @@ -0,0 +1,20 @@ +#include "detection/loadavg/loadavg.h" + +#include + +#ifdef __FreeBSD__ + #include + #include + #include +#endif + +const char* ffDetectLoadavg(double result[3]) +{ + struct loadavg load; + size_t size = sizeof(load); + if (sysctl((int []) { CTL_VM, VM_LOADAVG }, 2, &load, &size, NULL, 0) < 0) + return "sysctl({CTL_VM, VM_LOADAVG}) failed"; + for (int i = 0; i < 3; i++) + result[i] = (double) load.ldavg[i] / (double) load.fscale; + return NULL; +} diff --git a/src/detection/loadavg/loadavg_linux.c b/src/detection/loadavg/loadavg_linux.c new file mode 100644 index 000000000..47085a744 --- /dev/null +++ b/src/detection/loadavg/loadavg_linux.c @@ -0,0 +1,31 @@ +#include "detection/loadavg/loadavg.h" +#include "common/io/io.h" + +#include + +const char* ffDetectLoadavg(double result[3]) +{ + #ifndef __ANDROID__ // cat: /proc/loadavg: Permission denied + + // Don't use syscall for container compatibility. #620 + char buf[64]; + ssize_t nRead = ffReadFileData("/proc/uptime", sizeof(buf) - 1, buf); + if (nRead > 0) + { + buf[nRead] = '\0'; + + if (sscanf(buf, "%lf %lf %lf", &result[0], &result[1], &result[2]) == 3) + return NULL; + } + + #endif + + // getloadavg requires higher ANDROID_API version + struct sysinfo si; + if (sysinfo(&si) < 0) + return "sysinfo() failed"; + + for (int i = 0; i < 3; i++) + result[i] = (double) si.loads[i] / (1 << SI_LOAD_SHIFT); + return NULL; +} diff --git a/src/detection/loadavg/loadavg_nosupport.c b/src/detection/loadavg/loadavg_nosupport.c new file mode 100644 index 000000000..2d51217a5 --- /dev/null +++ b/src/detection/loadavg/loadavg_nosupport.c @@ -0,0 +1,6 @@ +#include "detection/loadavg/loadavg.h" + +const char* ffDetectLoadavg(FF_MAYBE_UNUSED double result[3]) +{ + return "Not supported on this platform"; +} diff --git a/src/detection/localip/localip_linux.c b/src/detection/localip/localip_linux.c index 905dec568..9c23b987f 100644 --- a/src/detection/localip/localip_linux.c +++ b/src/detection/localip/localip_linux.c @@ -17,7 +17,7 @@ #include #endif -static void addNewIp(FFlist* list, const char* name, const char* addr, int type, bool defaultRoute) +static void addNewIp(FFlist* list, const char* name, const char* addr, int type, bool defaultRoute, bool firstOnly) { FFLocalIpResult* ip = NULL; @@ -40,11 +40,19 @@ static void addNewIp(FFlist* list, const char* name, const char* addr, int type, switch (type) { case AF_INET: - if (ip->ipv4.length) ffStrbufAppendC(&ip->ipv4, ','); + if (ip->ipv4.length) + { + if (firstOnly) return; + ffStrbufAppendC(&ip->ipv4, ','); + } ffStrbufAppendS(&ip->ipv4, addr); break; case AF_INET6: - if (ip->ipv6.length) ffStrbufAppendC(&ip->ipv6, ','); + if (ip->ipv6.length) + { + if (firstOnly) return; + ffStrbufAppendC(&ip->ipv6, ','); + } ffStrbufAppendS(&ip->ipv6, addr); break; case -1: @@ -67,7 +75,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) continue; bool isDefaultRoute = ffStrEquals(defaultRouteIfName, ifa->ifa_name); - if (options->defaultRouteOnly && !isDefaultRoute) + if ((options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) && !isDefaultRoute) continue; if ((ifa->ifa_flags & IFF_LOOPBACK) && !(options->showType & FF_LOCALIP_TYPE_LOOP_BIT)) @@ -96,7 +104,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) } } - addNewIp(results, ifa->ifa_name, addressBuffer, AF_INET, isDefaultRoute); + addNewIp(results, ifa->ifa_name, addressBuffer, AF_INET, isDefaultRoute, !(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT)); } else if (ifa->ifa_addr->sa_family == AF_INET6) { @@ -121,7 +129,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) } } - addNewIp(results, ifa->ifa_name, addressBuffer, AF_INET6, isDefaultRoute); + addNewIp(results, ifa->ifa_name, addressBuffer, AF_INET6, isDefaultRoute, !(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT)); } #if defined(__FreeBSD__) || defined(__APPLE__) else if (ifa->ifa_addr->sa_family == AF_LINK) @@ -133,7 +141,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) uint8_t* ptr = (uint8_t*) LLADDR((struct sockaddr_dl *)ifa->ifa_addr); snprintf(addressBuffer, sizeof(addressBuffer), "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); - addNewIp(results, ifa->ifa_name, addressBuffer, -1, isDefaultRoute); + addNewIp(results, ifa->ifa_name, addressBuffer, -1, isDefaultRoute, false); } #else else if (ifa->ifa_addr->sa_family == AF_PACKET) @@ -145,7 +153,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) uint8_t* ptr = ((struct sockaddr_ll *)ifa->ifa_addr)->sll_addr; snprintf(addressBuffer, sizeof(addressBuffer), "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); - addNewIp(results, ifa->ifa_name, addressBuffer, -1, isDefaultRoute); + addNewIp(results, ifa->ifa_name, addressBuffer, -1, isDefaultRoute, false); } #endif } diff --git a/src/detection/localip/localip_windows.c b/src/detection/localip/localip_windows.c index 96463c331..0650ae54a 100644 --- a/src/detection/localip/localip_windows.c +++ b/src/detection/localip/localip_windows.c @@ -78,7 +78,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next) { bool isDefaultRoute = adapter->IfIndex == defaultRouteIfIndex; - if (options->defaultRouteOnly && !isDefaultRoute) + if ((options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) && !isDefaultRoute) continue; bool isLoop = adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK; @@ -102,10 +102,14 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) newIp = false; } + uint32_t typesToAdd = options->showType & (FF_LOCALIP_TYPE_IPV4_BIT | FF_LOCALIP_TYPE_IPV6_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT); + for (IP_ADAPTER_UNICAST_ADDRESS* ifa = adapter->FirstUnicastAddress; ifa; ifa = ifa->Next) { if (ifa->Address.lpSockaddr->sa_family == AF_INET) { + if (!(typesToAdd & (FF_LOCALIP_TYPE_IPV4_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT))) continue; + SOCKADDR_IN* ipv4 = (SOCKADDR_IN*) ifa->Address.lpSockaddr; char addressBuffer[INET_ADDRSTRLEN + 4]; inet_ntop(AF_INET, &ipv4->sin_addr, addressBuffer, INET_ADDRSTRLEN); @@ -118,9 +122,14 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) addNewIp(results, name, addressBuffer, AF_INET, newIp, isDefaultRoute); newIp = false; + + typesToAdd &= ~(unsigned) FF_LOCALIP_TYPE_IPV4_BIT; + if (typesToAdd == 0) break; } else if (ifa->Address.lpSockaddr->sa_family == AF_INET6) { + if (!(typesToAdd & (FF_LOCALIP_TYPE_IPV6_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT))) continue; + SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*) ifa->Address.lpSockaddr; char addressBuffer[INET6_ADDRSTRLEN + 4]; inet_ntop(AF_INET6, &ipv6->sin6_addr, addressBuffer, INET6_ADDRSTRLEN); @@ -133,6 +142,9 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) addNewIp(results, name, addressBuffer, AF_INET6, newIp, isDefaultRoute); newIp = false; + + typesToAdd &= ~(unsigned) FF_LOCALIP_TYPE_IPV6_BIT; + if (typesToAdd == 0) break; } } } diff --git a/src/detection/physicalmemory/physicalmemory.c b/src/detection/physicalmemory/physicalmemory.c new file mode 100644 index 000000000..6591c3245 --- /dev/null +++ b/src/detection/physicalmemory/physicalmemory.c @@ -0,0 +1,43 @@ +#include "physicalmemory.h" + +static inline const char* getVendorString(unsigned vendorId) +{ + switch (vendorId) + { + case 0x017A: return "Apacer"; + case 0x0198: return "Kingston"; + case 0x029E: return "Corsair"; + case 0x04CB: return "A-DATA"; + case 0x04CD: return "G-Skill"; + case 0x059B: case 0x859B: return "Crucial"; + case 0x00CE: case 0x80CE: case 0xCE00: return "Samsung"; + case 0x014F: return "Transcend"; + case 0x2C00: case 0x802C: return "Micron"; + case 0xAD00: case 0x80AD: return "SK Hynix"; + case 0x5105: case 0x8551: return "Qimonda"; + case 0x02FE: return "Elpida"; + case 0x0467: return "Ramaxel"; + default: return NULL; + } +} + +void FFPhysicalMemoryUpdateVendorString(FFPhysicalMemoryResult* device) +{ + char vendorIdStr[5]; + if (ffStrbufStartsWithS(&device->vendor, "0x")) + { + if (device->vendor.length < 6) return; + memcpy(vendorIdStr, device->vendor.chars + 2, 4); + } + else + { + if (device->vendor.length < 4) return; + memcpy(vendorIdStr, device->vendor.chars, 4); + } + vendorIdStr[4] = '\0'; + char* pEnd = NULL; + uint32_t vendorId = (uint32_t) strtoul(vendorIdStr, &pEnd, 16); + if (*pEnd != '\0') return; + const char* vendorStr = getVendorString(vendorId); + if (vendorStr) ffStrbufSetStatic(&device->vendor, vendorStr); +} diff --git a/src/detection/physicalmemory/physicalmemory.h b/src/detection/physicalmemory/physicalmemory.h new file mode 100644 index 000000000..2580295ca --- /dev/null +++ b/src/detection/physicalmemory/physicalmemory.h @@ -0,0 +1,21 @@ +#pragma once + +#include "fastfetch.h" + +typedef struct FFPhysicalMemoryResult +{ + uint64_t size; // B + uint32_t maxSpeed; // MT/s + uint32_t runningSpeed; // MT/s + FFstrbuf type; + FFstrbuf formFactor; + FFstrbuf locator; + FFstrbuf partNumber; + FFstrbuf vendor; + FFstrbuf serial; + bool ecc; +} FFPhysicalMemoryResult; + +const char* ffDetectPhysicalMemory(FFlist* result); // list of FFPhysicalMemoryResult + +void FFPhysicalMemoryUpdateVendorString(FFPhysicalMemoryResult* device); diff --git a/src/detection/physicalmemory/physicalmemory_apple.m b/src/detection/physicalmemory/physicalmemory_apple.m new file mode 100644 index 000000000..243b35aca --- /dev/null +++ b/src/detection/physicalmemory/physicalmemory_apple.m @@ -0,0 +1,121 @@ +#include "physicalmemory.h" +#include "common/processing.h" +#include "util/smbiosHelper.h" +#include "util/stringUtils.h" + +#import + +static void appendDevice( + FFlist* result, + NSString* type, + NSString* vendor, + NSString* size, + + // Intel only + NSString* locator, + NSString* serial, + NSString* partNumber, + NSString* speed, + bool ecc) +{ + FFPhysicalMemoryResult* device = ffListAdd(result); + ffStrbufInitS(&device->type, type.UTF8String); + ffStrbufInit(&device->formFactor); + ffStrbufInitS(&device->locator, locator.UTF8String); + ffStrbufInitS(&device->vendor, vendor.UTF8String); + FFPhysicalMemoryUpdateVendorString(device); + ffStrbufInitS(&device->serial, serial.UTF8String); + ffCleanUpSmbiosValue(&device->serial); + ffStrbufInitS(&device->partNumber, partNumber.UTF8String); + ffCleanUpSmbiosValue(&device->partNumber); + device->size = 0; + device->maxSpeed = 0; + device->runningSpeed = 0; + device->ecc = ecc; + + if (size) + { + char* unit = NULL; + device->size = strtoul(size.UTF8String, &unit, 10); + if (*unit == ' ') ++unit; + + switch (*unit) + { + case 'G': device->size *= 1024ULL * 1024 * 1024; break; + case 'M': device->size *= 1024ULL * 1024; break; + case 'K': device->size *= 1024ULL; break; + case 'T': device->size *= 1024ULL * 1024 * 1024 * 1024; break; + } + } + + if (speed) + { + char* unit = NULL; + device->maxSpeed = (uint32_t) strtoul(speed.UTF8String, &unit, 10); + if (*unit == ' ') ++unit; + + switch (*unit) + { + case 'T': device->maxSpeed *= 1000 * 1000; break; + case 'G': device->maxSpeed *= 1000; break; + case 'K': device->maxSpeed /= 1000; break; + } + device->runningSpeed = device->maxSpeed; + } +} + +const char* ffDetectPhysicalMemory(FFlist* result) +{ + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + if (ffProcessAppendStdOut(&buffer, (char* const[]) { + "system_profiler", + "SPMemoryDataType", + "-xml", + "-detailLevel", + "full", + NULL + }) != NULL) + return "Starting `system_profiler SPMemoryDataType -xml -detailLevel full` failed"; + + NSArray* arr = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytes:buffer.chars length:buffer.length] + options:NSPropertyListImmutable + format:nil + error:nil]; + if (!arr || !arr.count) + return "system_profiler SPMemoryDataType returned an empty array"; + + for (NSDictionary* data in arr[0][@"_items"]) + { + if (data[@"_items"]) + { + // for Intel + for (NSDictionary* item in data[@"_items"]) + { + appendDevice(result, + item[@"dimm_type"], + item[@"dimm_manufacturer"], + item[@"dimm_size"], + item[@"_name"], + item[@"dimm_serial_number"], + item[@"dimm_part_number"], + item[@"dimm_speed"], + !![data[@"global_ecc_state"] isEqualToString:@"ecc_enabled"]); + } + } + else + { + // for Apple Silicon + appendDevice(result, + data[@"dimm_type"], + data[@"dimm_manufacturer"], + data[@"SPMemoryDataType"], + nil, + nil, + nil, + nil, + false); + } + } + + return NULL; +} diff --git a/src/detection/physicalmemory/physicalmemory_linux.c b/src/detection/physicalmemory/physicalmemory_linux.c new file mode 100644 index 000000000..a773d3d93 --- /dev/null +++ b/src/detection/physicalmemory/physicalmemory_linux.c @@ -0,0 +1,204 @@ +#include "physicalmemory.h" +#include "util/smbiosHelper.h" + +// 7.18 +typedef struct FFSmbiosMemoryDevice +{ + FFSmbiosHeader Header; + + // 2.1+ + uint16_t PhysicalMemoryArrayHandle; // varies + uint16_t MemoryErrorInformationHandle; //varies + uint16_t TotalWidth; // varies + uint16_t DataWidth; // varies + uint16_t Size; // varies + uint8_t FormFactor; // enum + uint8_t DeviceSet; // varies + uint8_t DeviceLocator; // string + uint8_t BankLocator; // string + uint8_t MemoryType; // enum + uint16_t TypeDetail; // bit field + + // 2.3+ + uint16_t Speed; // varies + uint8_t Manufacturer; // string + uint8_t SerialNumber; // string + uint8_t AssetTag; // string + uint8_t PartNumber; // string + + // 2.6+ + uint8_t Attributes; // varies + + // 2.7+ + uint32_t ExtendedSize; // varies + uint16_t ConfiguredMemorySpeed; // varies + + // 2.8+ + uint16_t MinimumVoltage; // varies + uint16_t MaximumVoltage; // varies + uint16_t ConfiguredVoltage; // varies + + // 3.2+ + uint8_t MemoryTechnology; // varies + uint16_t MemoryOperatingMode; // bit field + uint8_t FirmwareVersion; // string + uint16_t ModuleManufacturerID; // varies + uint16_t ModuleProductID; // varies + uint16_t MemorySubsystemControllerManufacturerID; // vaies + uint16_t MemorySubsystemControllerProductID; // varies + uint64_t NonVolatileSize; // varies + uint64_t VolatileSize; // varies + uint64_t CacheSize; // varies + uint64_t LogicalSize; // varies + + // 3.3+ + uint32_t ExtendedSpeed; // varies + uint32_t ExtendedConfiguredSpeed; // varies + + // 3.7+ + uint16_t Pmic0ManufacturerID; // varies + uint16_t Pmic0RevisionNumber; // varies + uint16_t RcdManufacturerID; // varies + uint16_t RcdRevisionNumber; // varies +} __attribute__((__packed__)) FFSmbiosMemoryDevice; + +static_assert(offsetof(FFSmbiosMemoryDevice, RcdRevisionNumber) == 0x62, + "FFSmbiosMemoryDevice: Wrong struct alignment"); + +const char* ffDetectPhysicalMemory(FFlist* result) +{ + const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable(); + if (!smbiosTable) + return "Failed to get SMBIOS data"; + + const FFSmbiosMemoryDevice* data = (const FFSmbiosMemoryDevice*) (*smbiosTable)[FF_SMBIOS_TYPE_MEMORY_DEVICE]; + if (!data) + return "Memory device is not found in SMBIOS data"; + + for (; data->Header.Type == FF_SMBIOS_TYPE_MEMORY_DEVICE; + data = (const FFSmbiosMemoryDevice*) ffSmbiosNextEntry(&data->Header)) + { + if (data->Size == 0) continue; + + const char* strings = (const char*) data + data->Header.Length; + + FFPhysicalMemoryResult* device = ffListAdd(result); + ffStrbufInit(&device->type); + ffStrbufInit(&device->formFactor); + ffStrbufInit(&device->locator); + ffStrbufInit(&device->vendor); + ffStrbufInit(&device->serial); + ffStrbufInit(&device->partNumber); + device->size = 0; + device->maxSpeed = 0; + device->runningSpeed = 0; + device->ecc = false; + + if (data->TotalWidth != 0xFFFF && data->DataWidth != 0xFFFF) + device->ecc = data->TotalWidth > data->DataWidth; + + if (data->Size != 0xFFFF) + { + if (data->Size == 0x7FFF) + device->size = (data->ExtendedSize & ~(1 << 31)) * 1024 * 1024; + else if (data->Size & (1 << 15)) + { + // in kB + device->size = (data->Size & ~(1u << 15)) * 1024ULL; + } + else + { + // in MB + device->size = data->Size * 1024ULL * 1024ULL; + } + } + + ffStrbufSetF(&device->locator, "%s/%s", ffSmbiosLocateString(strings, data->BankLocator), ffSmbiosLocateString(strings, data->DeviceLocator)); + + switch (data->FormFactor) + { + case 0x01: ffStrbufSetStatic(&device->formFactor, "Other"); break; + case 0x02: ffStrbufSetStatic(&device->formFactor, "Unknown"); break; + case 0x03: ffStrbufSetStatic(&device->formFactor, "SIMM"); break; + case 0x04: ffStrbufSetStatic(&device->formFactor, "SIP"); break; + case 0x05: ffStrbufSetStatic(&device->formFactor, "Chip"); break; + case 0x06: ffStrbufSetStatic(&device->formFactor, "DIP"); break; + case 0x07: ffStrbufSetStatic(&device->formFactor, "ZIP"); break; + case 0x08: ffStrbufSetStatic(&device->formFactor, "Proprietary Card"); break; + case 0x09: ffStrbufSetStatic(&device->formFactor, "DIMM"); break; + case 0x0A: ffStrbufSetStatic(&device->formFactor, "TSOP"); break; + case 0x0B: ffStrbufSetStatic(&device->formFactor, "Row of chips"); break; + case 0x0C: ffStrbufSetStatic(&device->formFactor, "RIMM"); break; + case 0x0D: ffStrbufSetStatic(&device->formFactor, "SODIMM"); break; + case 0x0E: ffStrbufSetStatic(&device->formFactor, "SRIMM"); break; + case 0x0F: ffStrbufSetStatic(&device->formFactor, "FBDIMM"); break; + case 0x10: ffStrbufSetStatic(&device->formFactor, "Die"); break; + default: ffStrbufSetF(&device->formFactor, "Unknown (%d)", (int) data->FormFactor); break; + } + + switch (data->MemoryType) + { + case 0x01: ffStrbufSetStatic(&device->type, "Other"); break; + case 0x02: ffStrbufSetStatic(&device->type, "Unknown"); break; + case 0x03: ffStrbufSetStatic(&device->type, "DRAM"); break; + case 0x04: ffStrbufSetStatic(&device->type, "EDRAM"); break; + case 0x05: ffStrbufSetStatic(&device->type, "VRAM"); break; + case 0x06: ffStrbufSetStatic(&device->type, "SRAM"); break; + case 0x07: ffStrbufSetStatic(&device->type, "RAM"); break; + case 0x08: ffStrbufSetStatic(&device->type, "ROM"); break; + case 0x09: ffStrbufSetStatic(&device->type, "FLASH"); break; + case 0x0A: ffStrbufSetStatic(&device->type, "EEPROM"); break; + case 0x0B: ffStrbufSetStatic(&device->type, "FEPROM"); break; + case 0x0C: ffStrbufSetStatic(&device->type, "EPROM"); break; + case 0x0D: ffStrbufSetStatic(&device->type, "CDRAM"); break; + case 0x0E: ffStrbufSetStatic(&device->type, "3DRAM"); break; + case 0x0F: ffStrbufSetStatic(&device->type, "SDRAM"); break; + case 0x10: ffStrbufSetStatic(&device->type, "SGRAM"); break; + case 0x11: ffStrbufSetStatic(&device->type, "RDRAM"); break; + case 0x12: ffStrbufSetStatic(&device->type, "DDR"); break; + case 0x13: ffStrbufSetStatic(&device->type, "DDR2"); break; + case 0x14: ffStrbufSetStatic(&device->type, "DDR2 FB-DIMM"); break; + case 0x15: + case 0x16: + case 0x17: ffStrbufSetStatic(&device->type, "Reserved"); break; + case 0x18: ffStrbufSetStatic(&device->type, "DDR3"); break; + case 0x19: ffStrbufSetStatic(&device->type, "FBD2"); break; + case 0x1A: ffStrbufSetStatic(&device->type, "DDR4"); break; + case 0x1B: ffStrbufSetStatic(&device->type, "LPDDR"); break; + case 0x1C: ffStrbufSetStatic(&device->type, "LPDDR2"); break; + case 0x1D: ffStrbufSetStatic(&device->type, "LPDDR3"); break; + case 0x1E: ffStrbufSetStatic(&device->type, "LPDDR4"); break; + case 0x1F: ffStrbufSetStatic(&device->type, "Logical non-volatile device"); break; + case 0x20: ffStrbufSetStatic(&device->type, "HBM"); break; + case 0x21: ffStrbufSetStatic(&device->type, "HBM2"); break; + case 0x22: ffStrbufSetStatic(&device->type, "DDR5"); break; + case 0x23: ffStrbufSetStatic(&device->type, "LPDDR5"); break; + case 0x24: ffStrbufSetStatic(&device->type, "HBM3"); break; + default: ffStrbufSetF(&device->type, "Unknown (%d)", (int) data->MemoryType); break; + } + + if (data->Header.Length > offsetof(FFSmbiosMemoryDevice, Speed)) // 2.3+ + { + if (data->Speed) + device->maxSpeed = data->Speed == 0xFFFF ? data->ExtendedSpeed : data->Speed; + + ffStrbufSetStatic(&device->vendor, ffSmbiosLocateString(strings, data->Manufacturer)); + FFPhysicalMemoryUpdateVendorString(device); + + ffStrbufSetStatic(&device->serial, ffSmbiosLocateString(strings, data->SerialNumber)); + ffCleanUpSmbiosValue(&device->serial); + + ffStrbufSetStatic(&device->partNumber, ffSmbiosLocateString(strings, data->PartNumber)); + ffCleanUpSmbiosValue(&device->partNumber); + } + + if (data->Header.Length > offsetof(FFSmbiosMemoryDevice, ConfiguredMemorySpeed)) // 2.7+ + { + if (data->ConfiguredMemorySpeed) + device->runningSpeed = data->ConfiguredMemorySpeed == 0xFFFF + ? data->ExtendedConfiguredSpeed : data->ConfiguredMemorySpeed; + } + } + + return NULL; +} diff --git a/src/detection/physicalmemory/physicalmemory_nosupport.c b/src/detection/physicalmemory/physicalmemory_nosupport.c new file mode 100644 index 000000000..a035753d7 --- /dev/null +++ b/src/detection/physicalmemory/physicalmemory_nosupport.c @@ -0,0 +1,6 @@ +#include "physicalmemory.h" + +const char* ffDetectPhysicalMemory(FF_MAYBE_UNUSED FFlist* result) +{ + return "Not supported on this platform"; +} diff --git a/src/detection/publicip/publicip.c b/src/detection/publicip/publicip.c index d144f7219..aa5f22419 100644 --- a/src/detection/publicip/publicip.c +++ b/src/detection/publicip/publicip.c @@ -2,21 +2,24 @@ #include "common/networking.h" #define FF_UNITIALIZED ((const char*)(uintptr_t) -1) -static FFNetworkingState state; -static const char* status = FF_UNITIALIZED; +static FFNetworkingState states[2]; +static const char* statuses[2] = { FF_UNITIALIZED, FF_UNITIALIZED }; void ffPreparePublicIp(FFPublicIpOptions* options) { - if (status != FF_UNITIALIZED) + FFNetworkingState* state = &states[options->ipv6]; + const char** status = &statuses[options->ipv6]; + if (*status != FF_UNITIALIZED) { - fputs("Error: this module can only be used once due to internal limitations\n", stderr); + fputs("Error: PublicIp module can only be used once due to internal limitations\n", stderr); exit(1); } - state.timeout = options->timeout; + state->timeout = options->timeout; + state->ipv6 = options->ipv6; if (options->url.length == 0) - status = ffNetworkingSendHttpRequest(&state, "ipinfo.io", "/json", NULL); + *status = ffNetworkingSendHttpRequest(state, options->ipv6 ? "v6.ipinfo.io" : "ipinfo.io", "/json", NULL); else { FF_STRBUF_AUTO_DESTROY host = ffStrbufCreateCopy(&options->url); @@ -40,7 +43,7 @@ void ffPreparePublicIp(FFPublicIpOptions* options) host.chars[pathStartIndex] = '\0'; } - status = ffNetworkingSendHttpRequest(&state, host.chars, path.length == 0 ? "/" : path.chars, NULL); + *status = ffNetworkingSendHttpRequest(state, host.chars, path.length == 0 ? "/" : path.chars, NULL); } } @@ -53,14 +56,16 @@ static inline void wrapYyjsonFree(yyjson_doc** doc) const char* ffDetectPublicIp(FFPublicIpOptions* options, FFPublicIpResult* result) { - if (status == FF_UNITIALIZED) + FFNetworkingState* state = &states[options->ipv6]; + const char** status = &statuses[options->ipv6]; + if (*status == FF_UNITIALIZED) ffPreparePublicIp(options); - if (status != NULL) - return status; + if (*status != NULL) + return *status; FF_STRBUF_AUTO_DESTROY response = ffStrbufCreateA(4096); - const char* error = ffNetworkingRecvHttpResponse(&state, &response); + const char* error = ffNetworkingRecvHttpResponse(state, &response); if (error == NULL) ffStrbufSubstrAfterFirstS(&response, "\r\n\r\n"); else diff --git a/src/detection/terminalfont/terminalfont_linux.c b/src/detection/terminalfont/terminalfont_linux.c index 247959fc3..bcb640f3d 100644 --- a/src/detection/terminalfont/terminalfont_linux.c +++ b/src/detection/terminalfont/terminalfont_linux.c @@ -3,6 +3,7 @@ #include "common/properties.h" #include "common/parsing.h" #include "common/io/io.h" +#include "common/processing.h" #include "detection/terminalshell/terminalshell.h" #include "detection/displayserver/displayserver.h" #include "util/mallocHelper.h" @@ -265,9 +266,9 @@ static void detectXterm(FFTerminalFontResult* terminalFont) ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars); } -static void detectSt(FFTerminalFontResult* terminalFont, uint32_t pid) +static void detectSt(FFTerminalFontResult* terminalFont, const FFTerminalResult* terminal) { - FF_STRBUF_AUTO_DESTROY size = ffStrbufCreateF("/proc/%u/cmdline", pid); + FF_STRBUF_AUTO_DESTROY size = ffStrbufCreateF("/proc/%u/cmdline", terminal->pid); FF_STRBUF_AUTO_DESTROY font = ffStrbufCreate(); if (!ffAppendFileBuffer(size.chars, &font)) { @@ -276,20 +277,53 @@ static void detectSt(FFTerminalFontResult* terminalFont, uint32_t pid) } const char* p = memmem(font.chars, font.length, "\0-f", sizeof("\0-f")); // find parameter of `-f` - if (!p) + if (p) { - ffStrbufAppendF(&terminalFont->error, "st was not executed with `-f` parameter"); - return; + // st was executed with `-f` parameter + ffStrbufSubstrAfter(&font, (uint32_t) (p + (sizeof("\0-f") - 1) - font.chars)); + ffStrbufRecalculateLength(&font); + } + else + { + ffStrbufClear(&font); + if (ffProcessAppendStdOut(&font, (char* const[]) { + "strings", + terminal->exePath.chars, + NULL, + }) != NULL || font.length == 0) + { + ffStrbufAppendS(&terminalFont->error, "Failed to run `strings st`"); + return; + } + + // Search font config string in st binary + uint32_t middleIndex = ffStrbufFirstIndexS(&font, "size="); + if (middleIndex == font.length) + { + ffStrbufAppendS(&terminalFont->error, "No font config found in st binary"); + return; + } + + uint32_t startIndex = ffStrbufPreviousIndexC(&font, middleIndex, '\n'); + if (startIndex == font.length) startIndex = 0; + uint32_t endIndex = ffStrbufNextIndexC(&font, middleIndex, '\n'); + + ffStrbufSubstrBefore(&font, endIndex); + ffStrbufSubstrAfter(&font, startIndex); } - ffStrbufSubstrAfter(&font, (uint32_t) (p + (sizeof("\0-f") - 1) - font.chars)); - ffStrbufRecalculateLength(&font); + // JetBrainsMono Nerd Font Mono:pixelsize=12:antialias=true:autohint=true - // `monospace:size=15` || `monospace` || `:size=15` - uint32_t index = ffStrbufFirstIndexS(&font, ":size="); + uint32_t index = ffStrbufFirstIndexC(&font, ':'); if (index != font.length) { - ffStrbufSetS(&size, font.chars + index + strlen(":size=")); + uint32_t sIndex = ffStrbufNextIndexS(&font, index + 1, "size="); + if (sIndex != font.length) + { + sIndex += strlen("size="); + uint32_t sIndexEnd = ffStrbufNextIndexC(&font, sIndex, ':'); + ffStrbufSetNS(&size, sIndexEnd - sIndex, font.chars + sIndex); + } ffStrbufSubstrBefore(&font, index); } else @@ -365,7 +399,7 @@ void ffDetectTerminalFontPlatform(const FFTerminalResult* terminal, FFTerminalFo else if(ffStrbufIgnCaseEqualS(&terminal->processName, "xterm")) detectXterm(terminalFont); else if(ffStrbufIgnCaseEqualS(&terminal->processName, "st")) - detectSt(terminalFont, terminal->pid); + detectSt(terminalFont, terminal); else if(ffStrbufIgnCaseEqualS(&terminal->processName, "warp")) detectWarp(terminalFont); else if(ffStrbufIgnCaseEqualS(&terminal->processName, "weston-terminal")) diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index b2ad822dd..006f40a37 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -46,7 +46,7 @@ static void getProcessInformation(pid_t pid, FFstrbuf* processName, FFstrbuf* ex ssize_t length = readlink(filePath, exePath->chars, exePath->allocated - 1); if (length > 0) // doesn't contain trailing NUL { - exePath->chars[length + 1] = '\0'; + exePath->chars[length] = '\0'; exePath->length = (uint32_t) length; } @@ -307,10 +307,19 @@ static pid_t getTerminalInfo(FFTerminalResult* result, pid_t pid) #ifdef __APPLE__ // https://github.com/fastfetch-cli/fastfetch/discussions/501 - if (ffStrEndsWith(name, " (figterm)") || ffStrEndsWith(name, " (cwterm)")) + const char* pLeft = strstr(name, " ("); + if (pLeft) { - if (__builtin_expect(getProcessNameAndPpid(ppid, name, &ppid, NULL) != NULL, false)) - return 0; + pLeft += 2; + const char* pRight = strstr(pLeft, "term)"); + if (pRight && pRight[5] == '\0') + { + for (; pLeft < pRight; ++pLeft) + if (*pLeft < 'a' || *pLeft > 'z') + break; + if (pLeft == pRight && getProcessNameAndPpid(ppid, name, &ppid, NULL) != NULL) + return 0; + } } #endif diff --git a/src/detection/weather/weather.c b/src/detection/weather/weather.c index de3281348..fccdd2d12 100644 --- a/src/detection/weather/weather.c +++ b/src/detection/weather/weather.c @@ -8,7 +8,7 @@ void ffPrepareWeather(FFWeatherOptions* options) { if (status != FF_UNITIALIZED) { - fputs("Error: this module can only be used once due to internal limitations\n", stderr); + fputs("Error: Weather module can only be used once due to internal limitations\n", stderr); exit(1); } diff --git a/src/fastfetch.c b/src/fastfetch.c index a57b0a3f4..722c61462 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -317,7 +317,43 @@ static void listModules(bool pretty) } } -static void parseOption(FFdata* data, const char* key, const char* value); +// Temporary copy before new release of yyjson +static bool ffyyjson_locate_pos(const char *str, size_t len, size_t pos, + size_t *line, size_t *col, size_t *chr) { + size_t line_sum = 0, line_pos = 0, chr_sum = 0; + const uint8_t *cur = (const uint8_t *)str; + const uint8_t *end = cur + pos; + + if (!str || pos > len) { + if (line) *line = 0; + if (col) *col = 0; + if (chr) *chr = 0; + return false; + } + + while (cur < end) { + uint8_t c = *cur; + chr_sum += 1; + if (__builtin_expect(c < 0x80, true)) { /* 0xxxxxxx (0x00-0x7F) ASCII */ + if (c == '\n') { + line_sum += 1; + line_pos = chr_sum; + } + cur += 1; + } + else if (c < 0xC0) cur += 1; /* 10xxxxxx (0x80-0xBF) Invalid */ + else if (c < 0xE0) cur += 2; /* 110xxxxx (0xC0-0xDF) 2-byte UTF-8 */ + else if (c < 0xF0) cur += 3; /* 1110xxxx (0xE0-0xEF) 3-byte UTF-8 */ + else if (c < 0xF8) cur += 4; /* 11110xxx (0xF0-0xF7) 4-byte UTF-8 */ + else cur += 1; /* 11111xxx (0xF8-0xFF) Invalid */ + } + + if (line) *line = line_sum + 1; + if (col) *col = chr_sum - line_pos + 1; + if (chr) *chr = chr_sum; + return true; +} + static bool parseJsoncFile(const char* path) { @@ -325,12 +361,16 @@ static bool parseJsoncFile(const char* path) { yyjson_read_err error; - instance.state.configDoc = yyjson_read_file(path, YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS | YYJSON_READ_ALLOW_INF_AND_NAN, NULL, &error); + instance.state.configDoc = yyjson_read_file(path, YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS, NULL, &error); if (!instance.state.configDoc) { 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); + size_t row = 0, col = error.pos; + FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate(); + if (ffAppendFileBuffer(path, &content)) + ffyyjson_locate_pos(content.chars, content.length, error.pos, &row, &col, NULL); + fprintf(stderr, "Error: failed to parse JSON config file `%s` at (%zu, %zu): %s\n", path, row, col, error.msg); exit(477); } return false; @@ -360,104 +400,6 @@ static bool parseJsoncFile(const char* path) return true; } -static bool parseConfigFile(FFdata* data, const char* path) -{ - FF_AUTO_CLOSE_FILE FILE* file = fopen(path, "r"); - if(file == NULL) - return false; - - char* line = NULL; - size_t len = 0; - ssize_t read; - FF_STRBUF_AUTO_DESTROY unescaped = ffStrbufCreate(); - - while ((read = getline(&line, &len, file)) != -1) - { - char* lineStart = line; - char* lineEnd = line + read - 1; - - //Trim line left - while(isspace(*lineStart)) - ++lineStart; - - //Continue if line is empty or a comment - if(*lineStart == '\0' || *lineStart == '#') - continue; - - //Trim line right - while(lineEnd > lineStart && isspace(*lineEnd)) - --lineEnd; - *(lineEnd + 1) = '\0'; - - char* valueStart = strchr(lineStart, ' '); - - //If the line has no white space, it is only a key - if(valueStart == NULL) - { - parseOption(data, lineStart, NULL); - continue; - } - - //separate the key from the value - *valueStart = '\0'; - ++valueStart; - - //Trim space of value left - while(isspace(*valueStart)) - ++valueStart; - - //If we want whitespace in values, we need to quote it. This is done to keep consistency with shell. - if((*valueStart == '"' || *valueStart == '\'') && *valueStart == *lineEnd && lineEnd > valueStart) - { - ++valueStart; - *lineEnd = '\0'; - --lineEnd; - } - - 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); - } - } - - if(line != NULL) - free(line); - - return true; -} - static void generateConfigFile(bool force, const char* filePath) { if (!filePath) @@ -467,11 +409,6 @@ static void generateConfigFile(bool force, const char* filePath) } else { - if (ffStrEqualsIgnCase(filePath, "conf") || ffStrEqualsIgnCase(filePath, "jsonc")) - { - fputs("Error: specifying file type is no longer supported\n", stderr); - exit(477); - } ffStrbufSetS(&instance.state.genConfigPath, filePath); } @@ -507,19 +444,15 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val if (ffStrEqualsIgnCase(value, "none")) return; - bool isJsonConfig = fileNameLen > strlen(".jsonc") && strcasecmp(value + fileNameLen - strlen(".jsonc"), ".jsonc") == 0; + if (ffStrEndsWithIgnCase(value, ".conf")) + { + fprintf(stderr, "Error: flag based config files are no longer not supported: %s\n", value); + exit(414); + } //Try to load as an absolute path - if(isJsonConfig ? parseJsoncFile(value) : parseConfigFile(data, value)) - return; - - { - bool success = isJsonConfig ? parseJsoncFile(value) : parseConfigFile(data, value); - - if(success) - return; - } + if (parseJsoncFile(value)) return; //Try to load as a relative path @@ -531,15 +464,14 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val ffStrbufAppendS(&absolutePath, "fastfetch/presets/"); ffStrbufAppendS(&absolutePath, value); - bool success = isJsonConfig ? parseJsoncFile(absolutePath.chars) : parseConfigFile(data, absolutePath.chars); + bool success = parseJsoncFile(absolutePath.chars); if (!success) { ffStrbufAppendS(&absolutePath, ".jsonc"); success = parseJsoncFile(absolutePath.chars); } - if(success) - return; + if (success) return; } { @@ -549,15 +481,14 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val ffStrbufAppendS(&absolutePath, "/presets/"); ffStrbufAppendS(&absolutePath, value); - bool success = isJsonConfig ? parseJsoncFile(absolutePath.chars) : parseConfigFile(data, absolutePath.chars); + bool success = parseJsoncFile(absolutePath.chars); if (!success) { ffStrbufAppendS(&absolutePath, ".jsonc"); success = parseJsoncFile(absolutePath.chars); } - if(success) - return; + if (success) return; } //File not found @@ -695,38 +626,7 @@ static void parseCommand(FFdata* data, char* key, char* value) static void parseOption(FFdata* data, const char* key, const char* value) { - if(ffStrEqualsIgnCase(key, "--set") || ffStrEqualsIgnCase(key, "--set-keyless")) - { - 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 =\n", key); - exit(477); - } - - 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 = key[5] == '\0'; - } - - else if(ffStrEqualsIgnCase(key, "-s") || ffStrEqualsIgnCase(key, "--structure")) + if(ffStrEqualsIgnCase(key, "-s") || ffStrEqualsIgnCase(key, "--structure")) ffOptionParseString(key, value, &data->structure); else if( @@ -744,7 +644,7 @@ static void parseOption(FFdata* data, const char* key, const char* value) } } -static void parseConfigFiles(FFdata* data) +static void parseConfigFiles(void) { if (__builtin_expect(instance.state.genConfigPath.length == 0, true)) { @@ -758,15 +658,6 @@ static void parseConfigFiles(FFdata* data) if (success) return; } } - FF_LIST_FOR_EACH(FFstrbuf, dir, instance.state.platform.configDirs) - { - uint32_t dirLength = dir->length; - - ffStrbufAppendS(dir, "fastfetch/config.conf"); - bool success = parseConfigFile(data, dir->chars); - ffStrbufSubstrBefore(dir, dirLength); - if (success) return; - } } static void parseArguments(FFdata* data, int argc, char** argv, void (*parser)(FFdata* data, char* key, char* value)) @@ -868,13 +759,12 @@ int main(int argc, char** argv) //Data stores things only needed for the configuration of fastfetch FFdata data = { .structure = ffStrbufCreate(), - .customValues = ffListCreate(sizeof(FFCustomValue)), .configLoaded = false, }; parseArguments(&data, argc, argv, parseCommand); if(!data.configLoaded && !getenv("NO_CONFIG")) - parseConfigFiles(&data); + parseConfigFiles(); parseArguments(&data, argc, argv, (void*) parseOption); if (__builtin_expect(instance.state.genConfigPath.length == 0, true)) @@ -883,10 +773,4 @@ int main(int argc, char** argv) writeConfigFile(&data, &instance.state.genConfigPath); ffStrbufDestroy(&data.structure); - FF_LIST_FOR_EACH(FFCustomValue, customValue, data.customValues) - { - ffStrbufDestroy(&customValue->key); - ffStrbufDestroy(&customValue->value); - } - ffListDestroy(&data.customValues); } diff --git a/src/logo/ascii/macos3.txt b/src/logo/ascii/macos3.txt new file mode 100644 index 000000000..d3900c02a --- /dev/null +++ b/src/logo/ascii/macos3.txt @@ -0,0 +1,16 @@ +${c1} -:+:. +${c1} :++++. +${c1} /+++/. +${c1} .:-::- .+/:-\`\`.::- +${c1} .:/++++++/::::/++++++/::` +${c2} .:///////////////////////::` +${c2} /////////////////////////` +${c3}+++++++++++++++++++++++++` +${c3}++++++++++++++++++++++++ +${c4}ssssssssssssssssssssssss. +${c4}:ssssssssssssssssssssssss- +${c5} osssssssssssssssssssssssss` +${c5} `syyyyyyyyyyyyyyyyyyyyyyyyys` +${c6} `ossssssssssssssssssssssss +${c6} :ooooooooooooooooooo+. +${c6} `:++oo+/:- -:/+o+/- diff --git a/src/logo/ascii/opensuse_slowroll.txt b/src/logo/ascii/opensuse_slowroll.txt new file mode 100644 index 000000000..e63697fb7 --- /dev/null +++ b/src/logo/ascii/opensuse_slowroll.txt @@ -0,0 +1,20 @@ + _,......,_ + _aaQQQQQQWQQQQQQQaa. + ajQQQ??^' '^??QQQw. + ,aQQP^ ?4QQa + wQQP ^4QQa + ,QQP' ,aaaaa. ^$Q6, + ,QQP ajQQQWWWWQQQw. 4QQ + QQP aQQP? ?$QQ. 4Q6 +jQQ jQQ' ?WQr 'QQb +QQf ]QQ' ]QQ QQ6 +QQf ]QQ ]QQ) QQQ +QQ6 'QQ6 ]QQ QQf +]QQ. ^4QQaa .QQP ,QQ' + $QQ. ^?QQQQf _yQP ,QQP + ^WQQ6. aQQ? _QQP + ^4QQQ6a. ajQQP' aQQP + ?QQQQQWQaaaaaaajQWQP? aQQP' + ?QQQQP????????^' saWQP? + ^?QQQgaaa._ __aaaQQQP?' + '^?QWQQQQQQQQ@??^' \ No newline at end of file diff --git a/src/logo/ascii/quirinux.txt b/src/logo/ascii/quirinux.txt new file mode 100644 index 000000000..2046bd947 --- /dev/null +++ b/src/logo/ascii/quirinux.txt @@ -0,0 +1,17 @@ +$2 @=++++++++++=@ +$2 =++++++++++++++++++= +$2 *++++++++++++++++++++++* +$2 =++++++++++++++++++++++++++= +$2 *++++++++$1-..........-$2++++++++* +$2 =++++++++$1..............$2++++++++= +$2@++++++++$1:.....$2:++$1:.....:$2++++++++@ +$2=++++++++$1:.....$2++++$1.....:$2++++++++= +$2=++++++++$1:.....$2++++$1.....:$2++++++++= +$2#++++++++$1:.....$2++++$1.....:$2++++++++# +$2 +++++++++$1......$2--$1......$2+++++++++ +$2 @++++++++$1:............:$2++++++++@ +$2 @+++++++++++$1-....-$2+++++++++++@ +$2 *++++++++++$1::::$2++++++++++* +$2 *++++++++++++++++++++* +$2 @*++++++++++++++*@ +$2 @#====#@ diff --git a/src/logo/builtin.c b/src/logo/builtin.c index 263e714dc..be92989e6 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -1115,9 +1115,9 @@ static const FFlogo C[] = { }; static const FFlogo D[] = { - // Dahlia + // DahliaOS { - .names = {"dahlia"}, + .names = {"dahliaOS"}, .lines = FASTFETCH_DATATEXT_LOGO_DAHLIA, .colors = { FF_COLOR_FG_RED, @@ -2461,6 +2461,21 @@ static const FFlogo M[] = { .colorKeys = FF_COLOR_FG_YELLOW, .colorTitle = FF_COLOR_FG_GREEN, }, + // MacOS3 + { + .names = {"macos3", "mac3"}, + .type = FF_LOGO_LINE_TYPE_ALTER_BIT, + .lines = FASTFETCH_DATATEXT_LOGO_MACOS3, + .colors = { + FF_COLOR_FG_GREEN, + FF_COLOR_FG_YELLOW, + FF_COLOR_FG_RED, + FF_COLOR_FG_MAGENTA, + FF_COLOR_FG_BLUE, + }, + .colorKeys = FF_COLOR_FG_YELLOW, + .colorTitle = FF_COLOR_FG_GREEN, + }, // MainsailOS { .names = {"MainsailOS"}, @@ -3004,6 +3019,16 @@ static const FFlogo O[] = { .colorKeys = FF_COLOR_FG_GREEN, .colorTitle = FF_COLOR_FG_GREEN, }, + // openSUSESlowroll + { + .names = {"opensuse_slowroll", "opensuse-slowroll", "opensuse-tumbleweed-slowroll"}, + .lines = FASTFETCH_DATATEXT_LOGO_OPENSUSE_SLOWROLL, + .colors = { + FF_COLOR_FG_WHITE, + }, + .colorKeys = FF_COLOR_FG_GREEN, + .colorTitle = FF_COLOR_FG_GREEN, + }, // OpenMandriva { .names = {"openmandriva", "open-mandriva", "open_mandriva", "openmandriva lx"}, @@ -3442,6 +3467,15 @@ static const FFlogo Q[] = { FF_COLOR_FG_WHITE, }, }, + // Quirinux + { + .names = {"Quirinux"}, + .lines = FASTFETCH_DATATEXT_LOGO_QUIRINUX, + .colors = { + FF_COLOR_FG_WHITE, + FF_COLOR_FG_MAGENTA, + }, + }, // LAST {}, }; diff --git a/src/logo/image/image.c b/src/logo/image/image.c index a64a3d5e5..3daf8cfde 100644 --- a/src/logo/image/image.c +++ b/src/logo/image/image.c @@ -84,9 +84,10 @@ static bool printImageIterm(bool printError) ffStrbufAppendNC(&buf, options->paddingTop, '\n'); ffStrbufAppendNC(&buf, options->paddingLeft, ' '); } - ffStrbufAppendF(&buf, "\e]1337;File=inline=1:%s\a", - base64.chars - ); + if (options->width) + ffStrbufAppendF(&buf, "\e]1337;File=inline=1;width=%u:%s\a", (unsigned) options->width, base64.chars); + else + ffStrbufAppendF(&buf, "\e]1337;File=inline=1:%s\a", base64.chars); ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf); if (!options->separate) @@ -164,7 +165,11 @@ static bool printImageKittyDirect(bool printError) ffPrintCharTimes('\n', options->paddingTop); ffPrintCharTimes(' ', options->paddingLeft); } - printf("\e_Ga=T,f=100,t=f;%s\e\\", base64.chars); + + if (options->width) + printf("\e_Ga=T,f=100,t=f,c=%u;%s\e\\", (unsigned) options->width, base64.chars); + else + printf("\e_Ga=T,f=100,t=f;%s\e\\", base64.chars); fflush(stdout); if (!options->separate) { diff --git a/src/logo/logo.c b/src/logo/logo.c index c78926339..f8956ec74 100644 --- a/src/logo/logo.c +++ b/src/logo/logo.c @@ -436,7 +436,7 @@ static bool logoTryKnownType(void) } if(options->type == FF_LOGO_TYPE_BUILTIN) - return logoPrintBuiltinIfExists(&options->source, FF_LOGO_SIZE_NORMAL); + return logoPrintBuiltinIfExists(&options->source, FF_LOGO_SIZE_UNKNOWN); if(options->type == FF_LOGO_TYPE_SMALL) return logoPrintBuiltinIfExists(&options->source, FF_LOGO_SIZE_SMALL); @@ -461,12 +461,6 @@ static bool logoTryKnownType(void) return logoPrintImageIfExists(options->type, instance.config.display.showErrors); } -static void logoPrintKnownType(void) -{ - if(!logoTryKnownType()) - logoPrintDetected(FF_LOGO_SIZE_UNKNOWN); -} - void ffLogoPrint(void) { //When generate JSON result, we don't have a logo or padding. @@ -497,7 +491,17 @@ void ffLogoPrint(void) //If the source and source type is set to something else than auto, always print with the set type. if(options->source.length > 0 && options->type != FF_LOGO_TYPE_AUTO) { - logoPrintKnownType(); + if(!logoTryKnownType()) + { + if (instance.config.display.showErrors) + { + // Image logo should have been handled + if(options->type == FF_LOGO_TYPE_BUILTIN || options->type == FF_LOGO_TYPE_SMALL) + fprintf(stderr, "Logo: Failed to load %s logo: %s \n", options->type == FF_LOGO_TYPE_BUILTIN ? "builtin" : "builtin small", options->source.chars); + } + + logoPrintDetected(FF_LOGO_SIZE_UNKNOWN); + } return; } diff --git a/src/modules/battery/battery.c b/src/modules/battery/battery.c index ee82e42d5..918fc7819 100644 --- a/src/modules/battery/battery.c +++ b/src/modules/battery/battery.c @@ -25,7 +25,7 @@ static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uin { if(instance.config.display.percentType & FF_PERCENTAGE_TYPE_BAR_BIT) { - ffPercentAppendBar(&str, result->capacity, options->percent); + ffPercentAppendBar(&str, result->capacity, options->percent, &options->moduleArgs); } if(instance.config.display.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) @@ -33,7 +33,7 @@ static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uin if(str.length > 0) ffStrbufAppendC(&str, ' '); - ffPercentAppendNum(&str, result->capacity, options->percent, str.length > 0); + ffPercentAppendNum(&str, result->capacity, options->percent, str.length > 0, &options->moduleArgs); } } @@ -50,7 +50,7 @@ static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uin if(str.length > 0) ffStrbufAppendS(&str, " - "); - ffTempsAppendNum(result->temperature, &str, options->tempConfig); + ffTempsAppendNum(result->temperature, &str, options->tempConfig, &options->moduleArgs); } ffStrbufPutTo(&str, stdout); @@ -58,9 +58,9 @@ static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uin else { FF_STRBUF_AUTO_DESTROY capacityStr = ffStrbufCreate(); - ffPercentAppendNum(&capacityStr, result->capacity, options->percent, false); + ffPercentAppendNum(&capacityStr, result->capacity, options->percent, false, &options->moduleArgs); FF_STRBUF_AUTO_DESTROY tempStr = ffStrbufCreate(); - ffTempsAppendNum(result->temperature, &tempStr, options->tempConfig); + ffTempsAppendNum(result->temperature, &tempStr, options->tempConfig, &options->moduleArgs); FF_PRINT_FORMAT_CHECKED(FF_BATTERY_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, FF_BATTERY_NUM_FORMAT_ARGS, ((FFformatarg[]) { {FF_FORMAT_ARG_TYPE_STRBUF, &result->manufacturer}, {FF_FORMAT_ARG_TYPE_STRBUF, &result->modelName}, diff --git a/src/modules/bluetooth/bluetooth.c b/src/modules/bluetooth/bluetooth.c index c638252e7..6beef48b7 100644 --- a/src/modules/bluetooth/bluetooth.c +++ b/src/modules/bluetooth/bluetooth.c @@ -19,7 +19,7 @@ static void printDevice(FFBluetoothOptions* options, const FFBluetoothResult* de { if (buffer.length) ffStrbufAppendC(&buffer, ' '); - ffPercentAppendNum(&buffer, device->battery, options->percent, buffer.length > 0); + ffPercentAppendNum(&buffer, device->battery, options->percent, buffer.length > 0, &options->moduleArgs); } if (!device->connected) @@ -30,7 +30,7 @@ static void printDevice(FFBluetoothOptions* options, const FFBluetoothResult* de else { FF_STRBUF_AUTO_DESTROY percentageStr = ffStrbufCreate(); - ffPercentAppendNum(&percentageStr, device->battery, options->percent, false); + ffPercentAppendNum(&percentageStr, device->battery, options->percent, false, &options->moduleArgs); FF_PRINT_FORMAT_CHECKED(FF_BLUETOOTH_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, FF_BLUETOOTH_NUM_FORMAT_ARGS, ((FFformatarg[]) { {FF_FORMAT_ARG_TYPE_STRBUF, &device->name}, diff --git a/src/modules/brightness/brightness.c b/src/modules/brightness/brightness.c index f5defe068..8e2aff84d 100644 --- a/src/modules/brightness/brightness.c +++ b/src/modules/brightness/brightness.c @@ -52,7 +52,7 @@ void ffPrintBrightness(FFBrightnessOptions* options) if (instance.config.display.percentType & FF_PERCENTAGE_TYPE_BAR_BIT) { - ffPercentAppendBar(&str, percent, options->percent); + ffPercentAppendBar(&str, percent, options->percent, &options->moduleArgs); } if(instance.config.display.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) @@ -60,7 +60,7 @@ void ffPrintBrightness(FFBrightnessOptions* options) if(str.length > 0) ffStrbufAppendC(&str, ' '); - ffPercentAppendNum(&str, percent, options->percent, str.length > 0); + ffPercentAppendNum(&str, percent, options->percent, str.length > 0, &options->moduleArgs); } ffStrbufPutTo(&str, stdout); @@ -68,7 +68,7 @@ void ffPrintBrightness(FFBrightnessOptions* options) else { FF_STRBUF_AUTO_DESTROY valueStr = ffStrbufCreate(); - ffPercentAppendNum(&valueStr, percent, options->percent, false); + ffPercentAppendNum(&valueStr, percent, options->percent, false, &options->moduleArgs); FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_BRIGHTNESS_NUM_FORMAT_ARGS, ((FFformatarg[]) { {FF_FORMAT_ARG_TYPE_STRBUF, &valueStr}, {FF_FORMAT_ARG_TYPE_STRBUF, &item->name}, diff --git a/src/modules/cpu/cpu.c b/src/modules/cpu/cpu.c index 5da296f30..43ec9fe9a 100644 --- a/src/modules/cpu/cpu.c +++ b/src/modules/cpu/cpu.c @@ -57,7 +57,7 @@ void ffPrintCPU(FFCPUOptions* options) if(cpu.temperature == cpu.temperature) //FF_CPU_TEMP_UNSET { ffStrbufAppendS(&str, " - "); - ffTempsAppendNum(cpu.temperature, &str, options->tempConfig); + ffTempsAppendNum(cpu.temperature, &str, options->tempConfig, &options->moduleArgs); } ffStrbufPutTo(&str, stdout); @@ -65,7 +65,7 @@ void ffPrintCPU(FFCPUOptions* options) else { FF_STRBUF_AUTO_DESTROY tempStr = ffStrbufCreate(); - ffTempsAppendNum(cpu.temperature, &tempStr, options->tempConfig); + ffTempsAppendNum(cpu.temperature, &tempStr, options->tempConfig, &options->moduleArgs); FF_PRINT_FORMAT_CHECKED(FF_CPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, FF_CPU_NUM_FORMAT_ARGS, ((FFformatarg[]){ {FF_FORMAT_ARG_TYPE_STRBUF, &cpu.name}, {FF_FORMAT_ARG_TYPE_STRBUF, &cpu.vendor}, diff --git a/src/modules/cpuusage/cpuusage.c b/src/modules/cpuusage/cpuusage.c index 0909763f7..aa3bfccac 100644 --- a/src/modules/cpuusage/cpuusage.c +++ b/src/modules/cpuusage/cpuusage.c @@ -52,12 +52,12 @@ void ffPrintCPUUsage(FFCPUUsageOptions* options) if (!options->separate) { if(instance.config.display.percentType & FF_PERCENTAGE_TYPE_BAR_BIT) - ffPercentAppendBar(&str, avgValue, options->percent); + ffPercentAppendBar(&str, avgValue, options->percent, &options->moduleArgs); if(instance.config.display.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) { if(str.length > 0) ffStrbufAppendC(&str, ' '); - ffPercentAppendNum(&str, avgValue, options->percent, str.length > 0); + ffPercentAppendNum(&str, avgValue, options->percent, str.length > 0, &options->moduleArgs); } } else @@ -66,7 +66,7 @@ void ffPrintCPUUsage(FFCPUUsageOptions* options) { if(str.length > 0) ffStrbufAppendC(&str, ' '); - ffPercentAppendNum(&str, *percent, options->percent, false); + ffPercentAppendNum(&str, *percent, options->percent, false, &options->moduleArgs); } } ffStrbufPutTo(&str, stdout); @@ -74,11 +74,11 @@ void ffPrintCPUUsage(FFCPUUsageOptions* options) else { FF_STRBUF_AUTO_DESTROY avgStr = ffStrbufCreate(); - ffPercentAppendNum(&avgStr, avgValue, options->percent, false); + ffPercentAppendNum(&avgStr, avgValue, options->percent, false, &options->moduleArgs); FF_STRBUF_AUTO_DESTROY minStr = ffStrbufCreate(); - ffPercentAppendNum(&minStr, minValue, options->percent, false); + ffPercentAppendNum(&minStr, minValue, options->percent, false, &options->moduleArgs); FF_STRBUF_AUTO_DESTROY maxStr = ffStrbufCreate(); - ffPercentAppendNum(&maxStr, maxValue, options->percent, false); + ffPercentAppendNum(&maxStr, maxValue, options->percent, false, &options->moduleArgs); FF_PRINT_FORMAT_CHECKED(FF_CPUUSAGE_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, FF_CPUUSAGE_NUM_FORMAT_ARGS, ((FFformatarg[]){ {FF_FORMAT_ARG_TYPE_STRBUF, &avgStr}, {FF_FORMAT_ARG_TYPE_STRBUF, &maxStr}, diff --git a/src/modules/disk/disk.c b/src/modules/disk/disk.c index e44290479..8942754be 100644 --- a/src/modules/disk/disk.c +++ b/src/modules/disk/disk.c @@ -60,7 +60,7 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk) { if(instance.config.display.percentType & FF_PERCENTAGE_TYPE_BAR_BIT) { - ffPercentAppendBar(&str, bytesPercentage, options->percent); + ffPercentAppendBar(&str, bytesPercentage, options->percent, &options->moduleArgs); ffStrbufAppendC(&str, ' '); } @@ -69,7 +69,7 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk) if(instance.config.display.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) { - ffPercentAppendNum(&str, bytesPercentage, options->percent, str.length > 0); + ffPercentAppendNum(&str, bytesPercentage, options->percent, str.length > 0, &options->moduleArgs); ffStrbufAppendC(&str, ' '); } } @@ -105,10 +105,10 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk) else { FF_STRBUF_AUTO_DESTROY bytesPercentageStr = ffStrbufCreate(); - ffPercentAppendNum(&bytesPercentageStr, bytesPercentage, options->percent, false); + ffPercentAppendNum(&bytesPercentageStr, bytesPercentage, options->percent, false, &options->moduleArgs); FF_STRBUF_AUTO_DESTROY filesPercentageStr = ffStrbufCreate(); double filesPercentage = disk->filesTotal > 0 ? ((double) disk->filesUsed / (double) disk->filesTotal) * 100.0 : 0; - ffPercentAppendNum(&filesPercentageStr, filesPercentage, options->percent, false); + ffPercentAppendNum(&filesPercentageStr, filesPercentage, options->percent, false, &options->moduleArgs); bool isExternal = !!(disk->type & FF_DISK_VOLUME_TYPE_EXTERNAL_BIT); bool isHidden = !!(disk->type & FF_DISK_VOLUME_TYPE_HIDDEN_BIT); diff --git a/src/modules/gamepad/gamepad.c b/src/modules/gamepad/gamepad.c index d14beb279..bc5417d58 100644 --- a/src/modules/gamepad/gamepad.c +++ b/src/modules/gamepad/gamepad.c @@ -19,14 +19,14 @@ static void printDevice(FFGamepadOptions* options, const FFGamepadDevice* device { if (buffer.length) ffStrbufAppendC(&buffer, ' '); - ffPercentAppendNum(&buffer, device->battery, options->percent, buffer.length > 0); + ffPercentAppendNum(&buffer, device->battery, options->percent, buffer.length > 0, &options->moduleArgs); } ffStrbufPutTo(&buffer, stdout); } else { FF_STRBUF_AUTO_DESTROY percentageStr = ffStrbufCreate(); - ffPercentAppendNum(&percentageStr, device->battery, options->percent, false); + ffPercentAppendNum(&percentageStr, device->battery, options->percent, false, &options->moduleArgs); FF_PRINT_FORMAT_CHECKED(FF_GAMEPAD_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, FF_GAMEPAD_NUM_FORMAT_ARGS, ((FFformatarg[]) { {FF_FORMAT_ARG_TYPE_STRBUF, &device->name}, diff --git a/src/modules/gpu/gpu.c b/src/modules/gpu/gpu.c index aa63b16dd..47d9fa91e 100644 --- a/src/modules/gpu/gpu.c +++ b/src/modules/gpu/gpu.c @@ -45,7 +45,7 @@ static void printGPUResult(FFGPUOptions* options, uint8_t index, const FFGPUResu if(gpu->temperature == gpu->temperature) //FF_GPU_TEMP_UNSET { ffStrbufAppendS(&output, " - "); - ffTempsAppendNum(gpu->temperature, &output, options->tempConfig); + ffTempsAppendNum(gpu->temperature, &output, options->tempConfig, &options->moduleArgs); } if(gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET && gpu->dedicated.total != 0) @@ -61,7 +61,7 @@ static void printGPUResult(FFGPUOptions* options, uint8_t index, const FFGPUResu if(gpu->dedicated.used != FF_GPU_VMEM_SIZE_UNSET) { ffStrbufAppendS(&output, ", "); - ffPercentAppendNum(&output, (double) gpu->dedicated.used / (double) gpu->dedicated.total * 100.0, options->percent, false); + ffPercentAppendNum(&output, (double) gpu->dedicated.used / (double) gpu->dedicated.total * 100.0, options->percent, false, &options->moduleArgs); } ffStrbufAppendC(&output, ')'); } @@ -74,7 +74,7 @@ static void printGPUResult(FFGPUOptions* options, uint8_t index, const FFGPUResu else { FF_STRBUF_AUTO_DESTROY tempStr = ffStrbufCreate(); - ffTempsAppendNum(gpu->temperature, &tempStr, options->tempConfig); + ffTempsAppendNum(gpu->temperature, &tempStr, options->tempConfig, &options->moduleArgs); FF_PRINT_FORMAT_CHECKED(FF_GPU_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, FF_GPU_NUM_FORMAT_ARGS, ((FFformatarg[]) { {FF_FORMAT_ARG_TYPE_STRBUF, &gpu->vendor}, {FF_FORMAT_ARG_TYPE_STRBUF, &gpu->name}, diff --git a/src/modules/kernel/kernel.c b/src/modules/kernel/kernel.c index 2839af7a0..43cdf766f 100644 --- a/src/modules/kernel/kernel.c +++ b/src/modules/kernel/kernel.c @@ -11,7 +11,7 @@ void ffPrintKernel(FFKernelOptions* options) if(options->moduleArgs.outputFormat.length == 0) { ffPrintLogoAndKey(FF_KERNEL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - ffStrbufWriteTo(&platform->systemRelease, stdout); + printf("%s %s", platform->systemName.chars, platform->systemRelease.chars); if(platform->systemDisplayVersion.length > 0) printf(" (%s)\n", platform->systemDisplayVersion.chars); @@ -77,7 +77,7 @@ void ffGenerateKernelJsonResult(FF_MAYBE_UNUSED FFKernelOptions* options, yyjson void ffPrintKernelHelpFormat(void) { - FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_KERNEL_MODULE_NAME, "{2}", FF_KERNEL_NUM_FORMAT_ARGS, ((const char* []) { + FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_KERNEL_MODULE_NAME, "{1} {2}", FF_KERNEL_NUM_FORMAT_ARGS, ((const char* []) { "Sysname", "Release", "Version", diff --git a/src/modules/loadavg/loadavg.c b/src/modules/loadavg/loadavg.c new file mode 100644 index 000000000..0caf0f445 --- /dev/null +++ b/src/modules/loadavg/loadavg.c @@ -0,0 +1,131 @@ +#include "common/printing.h" +#include "common/jsonconfig.h" +#include "detection/loadavg/loadavg.h" +#include "modules/loadavg/loadavg.h" +#include "util/stringUtils.h" + +#define FF_LOADAVG_NUM_FORMAT_ARGS 3 + +void ffPrintLoadavg(FFLoadavgOptions* options) +{ + double result[3] = { 0.0 / 0.0, 0.0 / 0.0, 0.0 / 0.0 }; + + const char* error = ffDetectLoadavg(result); + if(error) + { + ffPrintError(FF_LOADAVG_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + return; + } + + if(options->moduleArgs.outputFormat.length == 0) + { + ffPrintLogoAndKey(FF_LOADAVG_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + printf("%.*f, %.*f, %.*f\n", options->ndigits, result[0], options->ndigits, result[1], options->ndigits, result[2]); + } + else + { + FF_PRINT_FORMAT_CHECKED(FF_LOADAVG_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, FF_LOADAVG_NUM_FORMAT_ARGS, ((FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_DOUBLE, &result[0]}, + {FF_FORMAT_ARG_TYPE_DOUBLE, &result[1]}, + {FF_FORMAT_ARG_TYPE_DOUBLE, &result[2]}, + })); + } +} + +bool ffParseLoadavgCommandOptions(FFLoadavgOptions* options, const char* key, const char* value) +{ + const char* subKey = ffOptionTestPrefix(key, FF_LOADAVG_MODULE_NAME); + if (!subKey) return false; + if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) + return true; + + if (ffStrEqualsIgnCase(subKey, "ndigits")) + { + options->ndigits = (uint8_t) ffOptionParseUInt32(key, value); + return true; + } + + return false; +} + +void ffParseLoadavgJsonObject(FFLoadavgOptions* options, yyjson_val* module) +{ + yyjson_val *key_, *val; + size_t idx, max; + yyjson_obj_foreach(module, idx, max, key_, val) + { + const char* key = yyjson_get_str(key_); + if(ffStrEqualsIgnCase(key, "type")) + continue; + + if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) + continue; + + if (ffStrEqualsIgnCase(key, "ndigits")) + { + options->ndigits = (uint8_t) yyjson_get_uint(val); + continue; + } + + ffPrintError(FF_LOADAVG_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", key); + } +} + +void ffGenerateLoadavgJsonConfig(FFLoadavgOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + __attribute__((__cleanup__(ffDestroyLoadavgOptions))) FFLoadavgOptions defaultOptions; + ffInitLoadavgOptions(&defaultOptions); + + ffJsonConfigGenerateModuleArgsConfig(doc, module, &defaultOptions.moduleArgs, &options->moduleArgs); + + if (defaultOptions.ndigits != options->ndigits) + yyjson_mut_obj_add_uint(doc, module, "ndigits", options->ndigits); +} + +void ffGenerateLoadavgJsonResult(FF_MAYBE_UNUSED FFLoadavgOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + double result[3] = { 0.0 / 0.0, 0.0 / 0.0, 0.0 / 0.0 }; + + const char* error = ffDetectLoadavg(result); + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + for (size_t i = 0; i < 3; i++) + yyjson_mut_arr_add_real(doc, arr, result[i]); +} + +void ffPrintLoadavgHelpFormat(void) +{ + FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_LOADAVG_MODULE_NAME, "{1}, {2}, {3}", FF_LOADAVG_NUM_FORMAT_ARGS, ((const char* []) { + "Load average over 1min", + "Load average over 5min", + "Load average over 15min", + })); +} + +void ffInitLoadavgOptions(FFLoadavgOptions* options) +{ + ffOptionInitModuleBaseInfo( + &options->moduleInfo, + FF_LOADAVG_MODULE_NAME, + "Print system load averages", + ffParseLoadavgCommandOptions, + ffParseLoadavgJsonObject, + ffPrintLoadavg, + ffGenerateLoadavgJsonResult, + ffPrintLoadavgHelpFormat, + ffGenerateLoadavgJsonConfig + ); + ffOptionInitModuleArg(&options->moduleArgs); + + options->ndigits = 2; +} + +void ffDestroyLoadavgOptions(FFLoadavgOptions* options) +{ + ffOptionDestroyModuleArg(&options->moduleArgs); +} diff --git a/src/modules/loadavg/loadavg.h b/src/modules/loadavg/loadavg.h new file mode 100644 index 000000000..1d546f57b --- /dev/null +++ b/src/modules/loadavg/loadavg.h @@ -0,0 +1,9 @@ +#pragma once + +#include "fastfetch.h" + +#define FF_LOADAVG_MODULE_NAME "Loadavg" + +void ffPrintLoadavg(FFLoadavgOptions* options); +void ffInitLoadavgOptions(FFLoadavgOptions* options); +void ffDestroyLoadavgOptions(FFLoadavgOptions* options); diff --git a/src/modules/loadavg/option.h b/src/modules/loadavg/option.h new file mode 100644 index 000000000..bc8df7077 --- /dev/null +++ b/src/modules/loadavg/option.h @@ -0,0 +1,13 @@ +#pragma once + +// This file will be included in "fastfetch.h", do NOT put unnecessary things here + +#include "common/option.h" + +typedef struct FFLoadavgOptions +{ + FFModuleBaseInfo moduleInfo; + FFModuleArgs moduleArgs; + + uint8_t ndigits; +} FFLoadavgOptions; diff --git a/src/modules/localip/localip.c b/src/modules/localip/localip.c index 6f02338ad..9cb042eb2 100644 --- a/src/modules/localip/localip.c +++ b/src/modules/localip/localip.c @@ -192,15 +192,27 @@ bool ffParseLocalIpCommandOptions(FFLocalIpOptions* options, const char* key, co return true; } - if (ffStrEqualsIgnCase(subKey, "name-prefix")) + if (ffStrEqualsIgnCase(subKey, "default-route-only")) { - ffOptionParseString(key, value, &options->namePrefix); + if (ffOptionParseBoolean(value)) + options->showType |= FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT; + else + options->showType &= ~FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT; return true; } - if (ffStrEqualsIgnCase(subKey, "default-route-only")) + if (ffStrEqualsIgnCase(subKey, "show-all-ips")) { - options->defaultRouteOnly = ffOptionParseBoolean(value); + if (ffOptionParseBoolean(value)) + options->showType |= FF_LOCALIP_TYPE_ALL_IPS_BIT; + else + options->showType &= ~FF_LOCALIP_TYPE_ALL_IPS_BIT; + return true; + } + + if (ffStrEqualsIgnCase(subKey, "name-prefix")) + { + ffOptionParseString(key, value, &options->namePrefix); return true; } @@ -274,15 +286,27 @@ void ffParseLocalIpJsonObject(FFLocalIpOptions* options, yyjson_val* module) continue; } - if (ffStrEqualsIgnCase(key, "namePrefix")) + if (ffStrEqualsIgnCase(key, "defaultRouteOnly")) { - ffStrbufSetS(&options->namePrefix, yyjson_get_str(val)); + if (yyjson_get_bool(val)) + options->showType |= FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT; + else + options->showType &= ~FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT; continue; } - if (ffStrEqualsIgnCase(key, "defaultRouteOnly")) + if (ffStrEqualsIgnCase(key, "showAllIps")) { - options->defaultRouteOnly = yyjson_get_bool(val); + if (yyjson_get_bool(val)) + options->showType |= FF_LOCALIP_TYPE_ALL_IPS_BIT; + else + options->showType &= ~FF_LOCALIP_TYPE_ALL_IPS_BIT; + continue; + } + + if (ffStrEqualsIgnCase(key, "namePrefix")) + { + ffStrbufSetS(&options->namePrefix, yyjson_get_str(val)); continue; } @@ -316,13 +340,16 @@ void ffGenerateLocalIpJsonConfig(FFLocalIpOptions* options, yyjson_mut_doc* doc, if (options->showType & FF_LOCALIP_TYPE_COMPACT_BIT) yyjson_mut_obj_add_bool(doc, module, "compact", true); + + if (options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) + yyjson_mut_obj_add_bool(doc, module, "defaultRouteOnly", true); + + if (options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT) + yyjson_mut_obj_add_bool(doc, module, "showAllIps", true); } if (!ffStrbufEqual(&options->namePrefix, &defaultOptions.namePrefix)) yyjson_mut_obj_add_strbuf(doc, module, "namePrefix", &options->namePrefix); - - if (options->defaultRouteOnly != defaultOptions.defaultRouteOnly) - yyjson_mut_obj_add_bool(doc, module, "defaultRouteOnly", options->defaultRouteOnly); } void ffGenerateLocalIpJsonResult(FF_MAYBE_UNUSED FFLocalIpOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) @@ -383,15 +410,12 @@ void ffInitLocalIpOptions(FFLocalIpOptions* options) ); ffOptionInitModuleArg(&options->moduleArgs); - options->showType = FF_LOCALIP_TYPE_IPV4_BIT | FF_LOCALIP_TYPE_PREFIX_LEN_BIT; - ffStrbufInit(&options->namePrefix); - options->defaultRouteOnly = - #ifdef __ANDROID__ - false - #else - true + options->showType = FF_LOCALIP_TYPE_IPV4_BIT | FF_LOCALIP_TYPE_PREFIX_LEN_BIT + #ifndef __ANDROID__ + | FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT #endif ; + ffStrbufInit(&options->namePrefix); } void ffDestroyLocalIpOptions(FFLocalIpOptions* options) diff --git a/src/modules/localip/option.h b/src/modules/localip/option.h index 56092e98d..55b9192cd 100644 --- a/src/modules/localip/option.h +++ b/src/modules/localip/option.h @@ -13,7 +13,9 @@ typedef enum FFLocalIpType FF_LOCALIP_TYPE_MAC_BIT = 1 << 3, FF_LOCALIP_TYPE_PREFIX_LEN_BIT = 1 << 4, - FF_LOCALIP_TYPE_COMPACT_BIT = 1 << 10, + FF_LOCALIP_TYPE_COMPACT_BIT = 1 << 10, + FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT = 1 << 11, + FF_LOCALIP_TYPE_ALL_IPS_BIT = 1 << 12, } FFLocalIpType; typedef struct FFLocalIpOptions @@ -23,5 +25,4 @@ typedef struct FFLocalIpOptions FFLocalIpType showType; FFstrbuf namePrefix; - bool defaultRouteOnly; } FFLocalIpOptions; diff --git a/src/modules/memory/memory.c b/src/modules/memory/memory.c index 52ec97939..9f143d0aa 100644 --- a/src/modules/memory/memory.c +++ b/src/modules/memory/memory.c @@ -40,7 +40,7 @@ void ffPrintMemory(FFMemoryOptions* options) if(instance.config.display.percentType & FF_PERCENTAGE_TYPE_BAR_BIT) { - ffPercentAppendBar(&str, percentage, options->percent); + ffPercentAppendBar(&str, percentage, options->percent, &options->moduleArgs); ffStrbufAppendC(&str, ' '); } @@ -48,7 +48,7 @@ void ffPrintMemory(FFMemoryOptions* options) ffStrbufAppendF(&str, "%s / %s ", usedPretty.chars, totalPretty.chars); if(instance.config.display.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) - ffPercentAppendNum(&str, percentage, options->percent, str.length > 0); + ffPercentAppendNum(&str, percentage, options->percent, str.length > 0, &options->moduleArgs); ffStrbufTrimRight(&str, ' '); ffStrbufPutTo(&str, stdout); @@ -57,7 +57,7 @@ void ffPrintMemory(FFMemoryOptions* options) else { FF_STRBUF_AUTO_DESTROY percentageStr = ffStrbufCreate(); - ffPercentAppendNum(&percentageStr, percentage, options->percent, false); + ffPercentAppendNum(&percentageStr, percentage, options->percent, false, &options->moduleArgs); FF_PRINT_FORMAT_CHECKED(FF_MEMORY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, FF_MEMORY_NUM_FORMAT_ARGS, ((FFformatarg[]){ {FF_FORMAT_ARG_TYPE_STRBUF, &usedPretty}, {FF_FORMAT_ARG_TYPE_STRBUF, &totalPretty}, diff --git a/src/modules/modules.h b/src/modules/modules.h index 84c7f9dec..7bb11f8b2 100644 --- a/src/modules/modules.h +++ b/src/modules/modules.h @@ -28,6 +28,7 @@ #include "modules/icons/icons.h" #include "modules/kernel/kernel.h" #include "modules/lm/lm.h" +#include "modules/loadavg/loadavg.h" #include "modules/locale/locale.h" #include "modules/localip/localip.h" #include "modules/media/media.h" @@ -39,6 +40,7 @@ #include "modules/os/os.h" #include "modules/packages/packages.h" #include "modules/physicaldisk/physicaldisk.h" +#include "modules/physicalmemory/physicalmemory.h" #include "modules/player/player.h" #include "modules/poweradapter/poweradapter.h" #include "modules/processes/processes.h" diff --git a/src/modules/options.h b/src/modules/options.h index 1e998396a..cfcf931d0 100644 --- a/src/modules/options.h +++ b/src/modules/options.h @@ -27,6 +27,7 @@ #include "modules/gpu/option.h" #include "modules/icons/option.h" #include "modules/kernel/option.h" +#include "modules/loadavg/option.h" #include "modules/locale/option.h" #include "modules/lm/option.h" #include "modules/localip/option.h" @@ -39,6 +40,7 @@ #include "modules/os/option.h" #include "modules/packages/option.h" #include "modules/physicaldisk/option.h" +#include "modules/physicalmemory/option.h" #include "modules/player/option.h" #include "modules/poweradapter/option.h" #include "modules/processes/option.h" diff --git a/src/modules/physicaldisk/physicaldisk.c b/src/modules/physicaldisk/physicaldisk.c index 0b9eff49e..73380bcdf 100644 --- a/src/modules/physicaldisk/physicaldisk.c +++ b/src/modules/physicaldisk/physicaldisk.c @@ -97,14 +97,14 @@ void ffPrintPhysicalDisk(FFPhysicalDiskOptions* options) if(buffer.length > 0) ffStrbufAppendS(&buffer, " - "); - ffTempsAppendNum(dev->temperature, &buffer, options->tempConfig); + ffTempsAppendNum(dev->temperature, &buffer, options->tempConfig, &options->moduleArgs); } ffStrbufPutTo(&buffer, stdout); } else { FF_STRBUF_AUTO_DESTROY tempStr = ffStrbufCreate(); - ffTempsAppendNum(dev->temperature, &tempStr, options->tempConfig); + ffTempsAppendNum(dev->temperature, &tempStr, options->tempConfig, &options->moduleArgs); if (dev->type & FF_PHYSICALDISK_TYPE_READWRITE) readOnlyType = "Read-write"; FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_PHYSICALDISK_NUM_FORMAT_ARGS, ((FFformatarg[]){ diff --git a/src/modules/physicalmemory/option.h b/src/modules/physicalmemory/option.h new file mode 100644 index 000000000..96ebccc57 --- /dev/null +++ b/src/modules/physicalmemory/option.h @@ -0,0 +1,12 @@ +#pragma once + +// This file will be included in "fastfetch.h", do NOT put unnecessary things here + +#include "common/option.h" +#include "common/percent.h" + +typedef struct FFPhysicalMemoryOptions +{ + FFModuleBaseInfo moduleInfo; + FFModuleArgs moduleArgs; +} FFPhysicalMemoryOptions; diff --git a/src/modules/physicalmemory/physicalmemory.c b/src/modules/physicalmemory/physicalmemory.c new file mode 100644 index 000000000..8b92517a7 --- /dev/null +++ b/src/modules/physicalmemory/physicalmemory.c @@ -0,0 +1,192 @@ +#include "common/printing.h" +#include "common/jsonconfig.h" +#include "common/parsing.h" +#include "common/percent.h" +#include "detection/physicalmemory/physicalmemory.h" +#include "modules/physicalmemory/physicalmemory.h" +#include "util/stringUtils.h" + +#define FF_PHYSICALMEMORY_NUM_FORMAT_ARGS 11 +#define FF_PHYSICALMEMORY_DISPLAY_NAME "Physical Memory" + +void ffPrintPhysicalMemory(FFPhysicalMemoryOptions* options) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFPhysicalMemoryResult)); + const char* error = ffDetectPhysicalMemory(&result); + + if(error) + { + ffPrintError(FF_PHYSICALMEMORY_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + return; + } + + FF_STRBUF_AUTO_DESTROY prettySize = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate(); + + uint32_t i = 0; + FF_LIST_FOR_EACH(FFPhysicalMemoryResult, device, result) + { + ++i; + ffStrbufClear(&prettySize); + ffParseSize(device->size, &prettySize); + + if (options->moduleArgs.outputFormat.length == 0) + { + ffPrintLogoAndKey( + FF_PHYSICALMEMORY_DISPLAY_NAME, + result.length == 1 ? 0 : (uint8_t) i, + &options->moduleArgs, + FF_PRINT_TYPE_DEFAULT); + + fputs(prettySize.chars, stdout); + fputs(" - ", stdout); + ffStrbufWriteTo(&device->type, stdout); + if (device->maxSpeed > 0) + printf("-%u", device->maxSpeed); + if (device->runningSpeed > 0 && device->runningSpeed != device->maxSpeed) + printf(" @ %u MT/s", device->runningSpeed); + if (device->vendor.length > 0) + printf(" (%s)", device->vendor.chars); + if (device->ecc) + fputs(" - ECC", stdout); + putchar('\n'); + } + else + { + FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, FF_PHYSICALMEMORY_NUM_FORMAT_ARGS, ((FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_UINT64, &device->size}, + {FF_FORMAT_ARG_TYPE_STRBUF, &prettySize}, + {FF_FORMAT_ARG_TYPE_UINT, &device->maxSpeed}, + {FF_FORMAT_ARG_TYPE_UINT, &device->runningSpeed}, + {FF_FORMAT_ARG_TYPE_STRBUF, &device->type}, + {FF_FORMAT_ARG_TYPE_STRBUF, &device->formFactor}, + {FF_FORMAT_ARG_TYPE_DOUBLE, &device->locator}, + {FF_FORMAT_ARG_TYPE_STRBUF, &device->vendor}, + {FF_FORMAT_ARG_TYPE_STRBUF, &device->serial}, + {FF_FORMAT_ARG_TYPE_STRBUF, &device->partNumber}, + {FF_FORMAT_ARG_TYPE_BOOL, &device->ecc}, + })); + } + } + + FF_LIST_FOR_EACH(FFPhysicalMemoryResult, device, result) + { + ffStrbufDestroy(&device->type); + ffStrbufDestroy(&device->locator); + ffStrbufDestroy(&device->formFactor); + ffStrbufDestroy(&device->vendor); + ffStrbufDestroy(&device->serial); + ffStrbufDestroy(&device->partNumber); + } +} + +bool ffParsePhysicalMemoryCommandOptions(FFPhysicalMemoryOptions* options, const char* key, const char* value) +{ + const char* subKey = ffOptionTestPrefix(key, FF_PHYSICALMEMORY_MODULE_NAME); + if (!subKey) return false; + if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) + return true; + + return false; +} + +void ffParsePhysicalMemoryJsonObject(FFPhysicalMemoryOptions* options, yyjson_val* module) +{ + yyjson_val *key_, *val; + size_t idx, max; + yyjson_obj_foreach(module, idx, max, key_, val) + { + const char* key = yyjson_get_str(key_); + if(ffStrEqualsIgnCase(key, "type")) + continue; + + if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) + continue; + + ffPrintError(FF_PHYSICALMEMORY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", key); + } +} + +void ffGeneratePhysicalMemoryJsonConfig(FFPhysicalMemoryOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + __attribute__((__cleanup__(ffDestroyPhysicalMemoryOptions))) FFPhysicalMemoryOptions defaultOptions; + ffInitPhysicalMemoryOptions(&defaultOptions); + + ffJsonConfigGenerateModuleArgsConfig(doc, module, &defaultOptions.moduleArgs, &options->moduleArgs); +} + +void ffGeneratePhysicalMemoryJsonResult(FF_MAYBE_UNUSED FFPhysicalMemoryOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFPhysicalMemoryResult)); + const char* error = ffDetectPhysicalMemory(&result); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFPhysicalMemoryResult, device, result) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_uint(doc, obj, "size", device->size); + yyjson_mut_obj_add_uint(doc, obj, "maxSpeed", device->maxSpeed); + yyjson_mut_obj_add_uint(doc, obj, "runningSpeed", device->runningSpeed); + yyjson_mut_obj_add_strbuf(doc, obj, "type", &device->type); + yyjson_mut_obj_add_strbuf(doc, obj, "locator", &device->locator); + yyjson_mut_obj_add_strbuf(doc, obj, "formFactor", &device->formFactor); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &device->vendor); + yyjson_mut_obj_add_strbuf(doc, obj, "serial", &device->serial); + yyjson_mut_obj_add_strbuf(doc, obj, "partNumber", &device->partNumber); + yyjson_mut_obj_add_bool(doc, obj, "ecc", device->ecc); + } + + FF_LIST_FOR_EACH(FFPhysicalMemoryResult, device, result) + { + ffStrbufDestroy(&device->type); + ffStrbufDestroy(&device->locator); + ffStrbufDestroy(&device->formFactor); + ffStrbufDestroy(&device->vendor); + ffStrbufDestroy(&device->serial); + ffStrbufDestroy(&device->partNumber); + } +} + +void ffPrintPhysicalMemoryHelpFormat(void) +{ + FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_PHYSICALMEMORY_MODULE_NAME, "{7} {5}-{3}: {2}, running at {4} MT/s", FF_PHYSICALMEMORY_NUM_FORMAT_ARGS, ((const char* []) { + "Size (in bytes)", + "Size formatted", + "Max speed (in MT/s)", + "Running speed (in MT/s)", + "Type (DDR4, DDR5, etc.)", + "Form factor (SODIMM, DIMM, etc.)", + "Bank/Device Locator (BANK0/SIMM0, BANK0/SIMM1, etc.)", + "Vendor", + "Serial number", + "Part number", + "ECC enabled", + })); +} + +void ffInitPhysicalMemoryOptions(FFPhysicalMemoryOptions* options) +{ + ffOptionInitModuleBaseInfo( + &options->moduleInfo, + FF_PHYSICALMEMORY_MODULE_NAME, + "Print system physical memory devices", + ffParsePhysicalMemoryCommandOptions, + ffParsePhysicalMemoryJsonObject, + ffPrintPhysicalMemory, + ffGeneratePhysicalMemoryJsonResult, + ffPrintPhysicalMemoryHelpFormat, + ffGeneratePhysicalMemoryJsonConfig + ); + ffOptionInitModuleArg(&options->moduleArgs); +} + +void ffDestroyPhysicalMemoryOptions(FFPhysicalMemoryOptions* options) +{ + ffOptionDestroyModuleArg(&options->moduleArgs); +} diff --git a/src/modules/physicalmemory/physicalmemory.h b/src/modules/physicalmemory/physicalmemory.h new file mode 100644 index 000000000..c9e0048d1 --- /dev/null +++ b/src/modules/physicalmemory/physicalmemory.h @@ -0,0 +1,9 @@ +#pragma once + +#include "fastfetch.h" + +#define FF_PHYSICALMEMORY_MODULE_NAME "PhysicalMemory" + +void ffPrintPhysicalMemory(FFPhysicalMemoryOptions* options); +void ffInitPhysicalMemoryOptions(FFPhysicalMemoryOptions* options); +void ffDestroyPhysicalMemoryOptions(FFPhysicalMemoryOptions* options); diff --git a/src/modules/publicip/option.h b/src/modules/publicip/option.h index f05b95c23..427bab31a 100644 --- a/src/modules/publicip/option.h +++ b/src/modules/publicip/option.h @@ -11,4 +11,5 @@ typedef struct FFPublicIpOptions FFstrbuf url; uint32_t timeout; + bool ipv6; } FFPublicIpOptions; diff --git a/src/modules/publicip/publicip.c b/src/modules/publicip/publicip.c index eb5a41b0a..7de504790 100644 --- a/src/modules/publicip/publicip.c +++ b/src/modules/publicip/publicip.c @@ -59,6 +59,12 @@ bool ffParsePublicIpCommandOptions(FFPublicIpOptions* options, const char* key, return true; } + if (ffStrEqualsIgnCase(subKey, "ipv6")) + { + options->ipv6 = ffOptionParseBoolean(value); + return true; + } + return false; } @@ -87,6 +93,12 @@ void ffParsePublicIpJsonObject(FFPublicIpOptions* options, yyjson_val* module) continue; } + if (ffStrEqualsIgnCase(key, "ipv6")) + { + options->ipv6 = yyjson_get_bool(val); + continue; + } + ffPrintError(FF_PUBLICIP_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", key); } } @@ -100,6 +112,12 @@ void ffGeneratePublicIpJsonConfig(FFPublicIpOptions* options, yyjson_mut_doc* do if (!ffStrbufEqual(&options->url, &defaultOptions.url)) yyjson_mut_obj_add_strbuf(doc, module, "url", &options->url); + + if (defaultOptions.timeout != options->timeout) + yyjson_mut_obj_add_uint(doc, module, "timeout", options->timeout); + + if (defaultOptions.ipv6 != options->ipv6) + yyjson_mut_obj_add_bool(doc, module, "ipv6", options->ipv6); } void ffGeneratePublicIpJsonResult(FFPublicIpOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) @@ -148,6 +166,7 @@ void ffInitPublicIpOptions(FFPublicIpOptions* options) ffStrbufInit(&options->url); options->timeout = 0; + options->ipv6 = false; } void ffDestroyPublicIpOptions(FFPublicIpOptions* options) diff --git a/src/modules/separator/option.h b/src/modules/separator/option.h index c858e6193..6f4d49953 100644 --- a/src/modules/separator/option.h +++ b/src/modules/separator/option.h @@ -9,4 +9,5 @@ typedef struct FFSeparatorOptions FFModuleBaseInfo moduleInfo; FFstrbuf string; + FFstrbuf outputColor; } FFSeparatorOptions; diff --git a/src/modules/separator/separator.c b/src/modules/separator/separator.c index 7f8ac41d3..cf2be92bf 100644 --- a/src/modules/separator/separator.c +++ b/src/modules/separator/separator.c @@ -43,6 +43,8 @@ void ffPrintSeparator(FFSeparatorOptions* options) + (fqdn ? platform->hostName.length : ffStrbufFirstIndexC(&platform->hostName, '.')); // host name ffLogoPrintLine(); + if(options->outputColor.length) + ffPrintColor(&options->outputColor); if(__builtin_expect(options->string.length == 1, 1)) { ffPrintCharTimes(options->string.chars[0], titleLength); @@ -97,6 +99,12 @@ bool ffParseSeparatorCommandOptions(FFSeparatorOptions* options, const char* key return true; } + if (ffStrEqualsIgnCase(subKey, "output-color")) + { + ffOptionParseColor(value, &options->outputColor); + return true; + } + return false; } @@ -116,6 +124,12 @@ void ffParseSeparatorJsonObject(FFSeparatorOptions* options, yyjson_val* module) continue; } + if (ffStrEndsWithIgnCase(key, "outputColor")) + { + ffOptionParseColor(yyjson_get_str(val), &options->outputColor); + continue; + } + ffPrintError(FF_SEPARATOR_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", key); } } diff --git a/src/modules/sound/sound.c b/src/modules/sound/sound.c index 0cea6b4e0..9b9437be9 100644 --- a/src/modules/sound/sound.c +++ b/src/modules/sound/sound.c @@ -24,7 +24,7 @@ static void printDevice(FFSoundOptions* options, const FFSoundDevice* device, ui if (str.length) ffStrbufAppendC(&str, ' '); - ffPercentAppendBar(&str, device->volume, options->percent); + ffPercentAppendBar(&str, device->volume, options->percent, &options->moduleArgs); } if (instance.config.display.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) @@ -32,7 +32,7 @@ static void printDevice(FFSoundOptions* options, const FFSoundDevice* device, ui if (str.length) ffStrbufAppendC(&str, ' '); - ffPercentAppendNum(&str, device->volume, options->percent, str.length > 0); + ffPercentAppendNum(&str, device->volume, options->percent, str.length > 0, &options->moduleArgs); } } @@ -47,7 +47,7 @@ static void printDevice(FFSoundOptions* options, const FFSoundDevice* device, ui else { FF_STRBUF_AUTO_DESTROY percentageStr = ffStrbufCreate(); - ffPercentAppendNum(&percentageStr, device->volume, options->percent, false); + ffPercentAppendNum(&percentageStr, device->volume, options->percent, false, &options->moduleArgs); FF_PRINT_FORMAT_CHECKED(FF_SOUND_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, FF_SOUND_NUM_FORMAT_ARGS, ((FFformatarg[]) { {FF_FORMAT_ARG_TYPE_BOOL, &device->main}, diff --git a/src/modules/swap/swap.c b/src/modules/swap/swap.c index 36b4c8df6..4b68c4709 100644 --- a/src/modules/swap/swap.c +++ b/src/modules/swap/swap.c @@ -37,19 +37,19 @@ void ffPrintSwap(FFSwapOptions* options) { if(instance.config.display.percentType & FF_PERCENTAGE_TYPE_BAR_BIT) { - ffPercentAppendBar(&str, 0, options->percent); + ffPercentAppendBar(&str, 0, options->percent, &options->moduleArgs); ffStrbufAppendC(&str, ' '); } if(!(instance.config.display.percentType & FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT)) ffStrbufAppendS(&str, "Disabled"); else - ffPercentAppendNum(&str, 0, options->percent, str.length > 0); + ffPercentAppendNum(&str, 0, options->percent, str.length > 0, &options->moduleArgs); } else { if(instance.config.display.percentType & FF_PERCENTAGE_TYPE_BAR_BIT) { - ffPercentAppendBar(&str, percentage, options->percent); + ffPercentAppendBar(&str, percentage, options->percent, &options->moduleArgs); ffStrbufAppendC(&str, ' '); } @@ -57,7 +57,7 @@ void ffPrintSwap(FFSwapOptions* options) ffStrbufAppendF(&str, "%s / %s ", usedPretty.chars, totalPretty.chars); if(instance.config.display.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) - ffPercentAppendNum(&str, percentage, options->percent, str.length > 0); + ffPercentAppendNum(&str, percentage, options->percent, str.length > 0, &options->moduleArgs); } ffStrbufTrimRight(&str, ' '); @@ -66,7 +66,7 @@ void ffPrintSwap(FFSwapOptions* options) else { FF_STRBUF_AUTO_DESTROY percentageStr = ffStrbufCreate(); - ffPercentAppendNum(&percentageStr, percentage, options->percent, false); + ffPercentAppendNum(&percentageStr, percentage, options->percent, false, &options->moduleArgs); FF_PRINT_FORMAT_CHECKED(FF_SWAP_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, FF_SWAP_NUM_FORMAT_ARGS, ((FFformatarg[]){ {FF_FORMAT_ARG_TYPE_STRBUF, &usedPretty}, {FF_FORMAT_ARG_TYPE_STRBUF, &totalPretty}, diff --git a/src/options/display.c b/src/options/display.c index 2ef919fc0..35815f7c0 100644 --- a/src/options/display.c +++ b/src/options/display.c @@ -48,6 +48,9 @@ const char* ffOptionsParseDisplayJsonConfig(FFOptionsDisplay* options, yyjson_va const char* colorTitle = yyjson_get_str(yyjson_obj_get(val, "title")); if (colorTitle) ffOptionParseColor(colorTitle, &options->colorTitle); + const char* colorOutput = yyjson_get_str(yyjson_obj_get(val, "output")); + if (colorOutput) + ffOptionParseColor(colorOutput, &options->colorOutput); } else return "display.color must be either a string or an object"; @@ -113,7 +116,7 @@ const char* ffOptionsParseDisplayJsonConfig(FFOptionsDisplay* options, yyjson_va {}, }); if (error) return error; - options->temperatureUnit = (FFTemperatureUnit) value; + options->tempUnit = (FFTemperatureUnit) value; } yyjson_val* ndigits = yyjson_obj_get(val, "ndigits"); @@ -244,6 +247,11 @@ bool ffOptionsParseDisplayCommandLine(FFOptionsDisplay* options, const char* key optionCheckString(key, value, &options->colorTitle); ffOptionParseColor(value, &options->colorTitle); } + else if(ffStrEqualsIgnCase(subkey, "output")) + { + optionCheckString(key, value, &options->colorOutput); + ffOptionParseColor(value, &options->colorOutput); + } else return false; } @@ -277,12 +285,12 @@ bool ffOptionsParseDisplayCommandLine(FFOptionsDisplay* options, const char* key {} }); } - else if(ffStrStartsWithIgnCase(key, "--temperature-")) + else if(ffStrStartsWithIgnCase(key, "--temp-")) { - const char* subkey = key + strlen("--temperature-"); + const char* subkey = key + strlen("--temp-"); if(ffStrEqualsIgnCase(subkey, "unit")) { - options->temperatureUnit = (FFTemperatureUnit) ffOptionParseEnum(key, value, (FFKeyValuePair[]) { + options->tempUnit = (FFTemperatureUnit) ffOptionParseEnum(key, value, (FFKeyValuePair[]) { { "CELSIUS", FF_TEMPERATURE_UNIT_CELSIUS }, { "C", FF_TEMPERATURE_UNIT_CELSIUS }, { "FAHRENHEIT", FF_TEMPERATURE_UNIT_FAHRENHEIT }, @@ -346,6 +354,7 @@ void ffOptionsInitDisplay(FFOptionsDisplay* options) { ffStrbufInit(&options->colorKeys); ffStrbufInit(&options->colorTitle); + ffStrbufInit(&options->colorOutput); options->brightColor = true; ffStrbufInitStatic(&options->keyValueSeparator, ": "); @@ -366,7 +375,7 @@ void ffOptionsInitDisplay(FFOptionsDisplay* options) options->noBuffer = false; options->keyWidth = 0; - options->temperatureUnit = FF_TEMPERATURE_UNIT_CELSIUS; + options->tempUnit = FF_TEMPERATURE_UNIT_CELSIUS; options->tempNdigits = 1; ffStrbufInitStatic(&options->tempColorGreen, FF_COLOR_FG_GREEN); ffStrbufInitStatic(&options->tempColorYellow, FF_COLOR_FG_LIGHT_YELLOW); @@ -389,6 +398,7 @@ void ffOptionsDestroyDisplay(FFOptionsDisplay* options) { ffStrbufDestroy(&options->colorKeys); ffStrbufDestroy(&options->colorTitle); + ffStrbufDestroy(&options->colorOutput); ffStrbufDestroy(&options->keyValueSeparator); ffStrbufDestroy(&options->barCharElapsed); ffStrbufDestroy(&options->barCharTotal); @@ -478,9 +488,9 @@ void ffOptionsGenerateDisplayJsonConfig(FFOptionsDisplay* options, yyjson_mut_do { yyjson_mut_val* temperature = yyjson_mut_obj(doc); - if (options->temperatureUnit != defaultOptions.temperatureUnit) + if (options->tempUnit != defaultOptions.tempUnit) { - switch (options->temperatureUnit) + switch (options->tempUnit) { case FF_TEMPERATURE_UNIT_CELSIUS: yyjson_mut_obj_add_str(doc, obj, "unit", "C"); diff --git a/src/options/display.h b/src/options/display.h index eb3a7c68c..e2307b2d4 100644 --- a/src/options/display.h +++ b/src/options/display.h @@ -21,6 +21,7 @@ typedef struct FFOptionsDisplay //If one of those is empty, ffLogoPrint will set them FFstrbuf colorKeys; FFstrbuf colorTitle; + FFstrbuf colorOutput; bool brightColor; @@ -34,7 +35,7 @@ typedef struct FFOptionsDisplay FFBinaryPrefixType binaryPrefixType; uint8_t sizeNdigits; uint8_t sizeMaxPrefix; - FFTemperatureUnit temperatureUnit; + FFTemperatureUnit tempUnit; uint8_t tempNdigits; FFstrbuf tempColorGreen; FFstrbuf tempColorYellow; diff --git a/src/options/modules.c b/src/options/modules.c index 0de31e727..d3a501c32 100644 --- a/src/options/modules.c +++ b/src/options/modules.c @@ -29,6 +29,7 @@ void ffOptionsInitModules(FFOptionsModules* options) ffInitIconsOptions(&options->icons); ffInitKernelOptions(&options->kernel); ffInitLMOptions(&options->lm); + ffInitLoadavgOptions(&options->loadavg); ffInitLocalIpOptions(&options->localIP); ffInitLocaleOptions(&options->locale); ffInitMediaOptions(&options->media); @@ -40,6 +41,7 @@ void ffOptionsInitModules(FFOptionsModules* options) ffInitOpenGLOptions(&options->openGL); ffInitPackagesOptions(&options->packages); ffInitPhysicalDiskOptions(&options->physicalDisk); + ffInitPhysicalMemoryOptions(&options->physicalMemory); ffInitPlayerOptions(&options->player); ffInitPowerAdapterOptions(&options->powerAdapter); ffInitProcessesOptions(&options->processes); @@ -93,6 +95,7 @@ void ffOptionsDestroyModules(FFOptionsModules* options) ffDestroyIconsOptions(&options->icons); ffDestroyKernelOptions(&options->kernel); ffDestroyLMOptions(&options->lm); + ffDestroyLoadavgOptions(&options->loadavg); ffDestroyLocalIpOptions(&options->localIP); ffDestroyLocaleOptions(&options->locale); ffDestroyMediaOptions(&options->media); @@ -103,6 +106,7 @@ void ffOptionsDestroyModules(FFOptionsModules* options) ffDestroyOpenCLOptions(&options->openCL); ffDestroyOpenGLOptions(&options->openGL); ffDestroyPhysicalDiskOptions(&options->physicalDisk); + ffDestroyPhysicalMemoryOptions(&options->physicalMemory); ffDestroyPackagesOptions(&options->packages); ffDestroyPlayerOptions(&options->player); ffDestroyPowerAdapterOptions(&options->powerAdapter); diff --git a/src/options/modules.h b/src/options/modules.h index 7c19db986..dcd6e5bdc 100644 --- a/src/options/modules.h +++ b/src/options/modules.h @@ -30,6 +30,7 @@ typedef struct FFOptionsModules FFIconsOptions icons; FFKernelOptions kernel; FFLMOptions lm; + FFLoadavgOptions loadavg; FFLocalIpOptions localIP; FFLocaleOptions locale; FFMediaOptions media; @@ -41,6 +42,7 @@ typedef struct FFOptionsModules FFOpenGLOptions openGL; FFPackagesOptions packages; FFPhysicalDiskOptions physicalDisk; + FFPhysicalMemoryOptions physicalMemory; FFPlayerOptions player; FFPowerAdapterOptions powerAdapter; FFProcessesOptions processes; diff --git a/src/util/smbiosHelper.c b/src/util/smbiosHelper.c index b9418cab9..13b119ec4 100644 --- a/src/util/smbiosHelper.c +++ b/src/util/smbiosHelper.c @@ -1,6 +1,7 @@ #include "smbiosHelper.h" #include "common/io/io.h" #include "util/unused.h" +#include "util/mallocHelper.h" bool ffIsSmbiosValueSet(FFstrbuf* value) { @@ -11,10 +12,8 @@ bool ffIsSmbiosValueSet(FFstrbuf* value) !ffStrbufStartsWithIgnCaseS(value, "To be set") && !ffStrbufStartsWithIgnCaseS(value, "OEM") && !ffStrbufStartsWithIgnCaseS(value, "O.E.M.") && + !ffStrbufStartsWithIgnCaseS(value, "System Product") && !ffStrbufIgnCaseEqualS(value, "None") && - !ffStrbufIgnCaseEqualS(value, "System Product") && - !ffStrbufIgnCaseEqualS(value, "System Product Name") && - !ffStrbufIgnCaseEqualS(value, "System Product Version") && !ffStrbufIgnCaseEqualS(value, "System Name") && !ffStrbufIgnCaseEqualS(value, "System Version") && !ffStrbufIgnCaseEqualS(value, "Default string") && @@ -28,11 +27,38 @@ bool ffIsSmbiosValueSet(FFstrbuf* value) !ffStrbufIgnCaseEqualS(value, "No Enclosure") && !ffStrbufIgnCaseEqualS(value, "Chassis Version") && !ffStrbufIgnCaseEqualS(value, "All Series") && - !ffStrbufIgnCaseEqualS(value, "N/A") + !ffStrbufIgnCaseEqualS(value, "N/A") && + !ffStrbufIgnCaseEqualS(value, "0x0000") ; } +const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header) +{ + const char* p = ((const char*) header) + header->Length; + if (*p) + { + do + p += strlen(p) + 1; + while (*p); + } + else // The terminator is always double 0 even if there is no string + p ++; + + return (const FFSmbiosHeader*) (p + 1); +} + +#if defined(__linux__) || defined(__FreeBSD__) +#include +#include +#include + #ifdef __linux__ + #include "common/properties.h" +#else + #include "common/settings.h" + #define loff_t off_t // FreeBSD doesn't have loff_t +#endif + bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer) { if (ffReadFileBuffer(devicesPath, buffer)) @@ -52,25 +78,119 @@ bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* ffStrbufClear(buffer); return false; } + +typedef struct FFSmbios30EntryPoint +{ + uint8_t AnchorString[5]; + uint8_t EntryPointStructureChecksum; + uint8_t EntryPointLength; + uint8_t SmbiosMajorVersion; + uint8_t SmbiosMinorVersion; + uint8_t SmbiosDocrev; + uint8_t EntryPointRevision; + uint8_t Reversed; + uint32_t StructureTableMaximumSize; + uint64_t StructureTableAddress; +} __attribute__((__packed__)) FFSmbios30EntryPoint; + +static_assert(offsetof(FFSmbios30EntryPoint, StructureTableAddress) == 0x10, + "FFSmbiosProcessorInfo: Wrong struct alignment"); + +const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() +{ + static FFstrbuf buffer; + static FFSmbiosHeaderTable table; + + if (buffer.chars == NULL) + { + #ifdef __linux__ + if (!ffAppendFileBuffer("/sys/firmware/dmi/tables/DMI", &buffer)) + #endif + { + FF_STRBUF_AUTO_DESTROY strEntry = ffStrbufCreate(); + // Only support SMBIOS 3.x for simplification + #ifdef __FreeBSD__ + if (!ffSettingsGetFreeBSDKenv("hint.smbios.0.mem", &strEntry)) + return NULL; + #else + if (!ffParsePropFile("/sys/firmware/efi/systab", "SMBIOS3=", &strEntry)) + return NULL; + #endif + + loff_t pEntry = (loff_t) strtol(strEntry.chars, NULL, 0); + if (pEntry == 0) return NULL; + + FF_AUTO_CLOSE_FD int fd = open("/dev/mem", O_RDONLY); + if (fd < 0) return NULL; + + FFSmbios30EntryPoint entryPoint; + if (pread(fd, &entryPoint, sizeof(entryPoint), pEntry) != sizeof(entryPoint)) + { + // `pread /dev/mem` returns EFAULT in FreeBSD + // https://stackoverflow.com/questions/69372330/how-to-read-dev-mem-using-read + void* p = mmap(NULL, sizeof(entryPoint), PROT_READ, MAP_SHARED, fd, pEntry); + if (p == MAP_FAILED) return NULL; + memcpy(&entryPoint, p, sizeof(entryPoint)); + munmap(p, sizeof(entryPoint)); + } + + if (memcmp(entryPoint.AnchorString, "_SM3_", sizeof(entryPoint.AnchorString)) != 0 || + entryPoint.EntryPointLength != sizeof(entryPoint)) + return NULL; + + ffStrbufEnsureFixedLengthFree(&buffer, entryPoint.StructureTableMaximumSize); + if (pread(fd, buffer.chars, entryPoint.StructureTableMaximumSize, pEntry) == (loff_t) entryPoint.StructureTableMaximumSize) + { + buffer.length = (uint32_t) entryPoint.StructureTableMaximumSize; + buffer.chars[buffer.length] = '\0'; + } + else + { + // entryPoint.StructureTableAddress must be page aligned. + // Unaligned physical memory access results in all kinds of crashes. + void* p = mmap(NULL, entryPoint.StructureTableMaximumSize, PROT_READ, MAP_SHARED, fd, (loff_t) entryPoint.StructureTableAddress); + if (p == MAP_FAILED) + { + ffStrbufDestroy(&buffer); // free buffer and reset state + return NULL; + } + ffStrbufSetNS(&buffer, entryPoint.StructureTableMaximumSize, (char*) p); + munmap(p, entryPoint.StructureTableMaximumSize); + } + } + + for ( + const FFSmbiosHeader* header = (const FFSmbiosHeader*) buffer.chars; + (const uint8_t*) header < (const uint8_t*) buffer.chars + buffer.length; + header = ffSmbiosNextEntry(header) + ) + { + if (header->Type < FF_SMBIOS_TYPE_END_OF_TABLE) + { + if (!table[header->Type]) + table[header->Type] = header; + } + else if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE) + break; + } + } + + return &table; +} #elif defined(_WIN32) #include #pragma GCC diagnostic ignored "-Wmultichar" -const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header) +typedef struct FFRawSmbiosData { - const char* p = ((const char*) header) + header->Length; - if (*p) - { - do - p += strlen(p) + 1; - while (*p); - } - else // The terminator is always double 0 even if there is no string - p ++; - - return (const FFSmbiosHeader*) (p + 1); -} + uint8_t Used20CallingMethod; + uint8_t SMBIOSMajorVersion; + uint8_t SMBIOSMinorVersion; + uint8_t DmiRevision; + uint32_t Length; + uint8_t SMBIOSTableData[]; +} FFRawSmbiosData; const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() { @@ -81,7 +201,9 @@ const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() { const DWORD signature = 'RSMB'; uint32_t bufSize = GetSystemFirmwareTable(signature, 0, NULL, 0); - assert(bufSize > sizeof(FFRawSmbiosData)); + if (bufSize <= sizeof(FFRawSmbiosData)) + return NULL; + buffer = (FFRawSmbiosData*) malloc(bufSize); assert(buffer); FF_MAYBE_UNUSED uint32_t resultSize = GetSystemFirmwareTable(signature, 0, buffer, bufSize); diff --git a/src/util/smbiosHelper.h b/src/util/smbiosHelper.h index 6a80b3ab5..7d7a4b18d 100644 --- a/src/util/smbiosHelper.h +++ b/src/util/smbiosHelper.h @@ -12,9 +12,6 @@ static inline void ffCleanUpSmbiosValue(FFstrbuf* value) ffStrbufClear(value); } -#ifdef __linux__ -bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer); -#elif defined(_WIN32) // https://github.com/KunYi/DumpSMBIOS // https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.7.0.pdf @@ -78,19 +75,9 @@ typedef struct FFSmbiosHeader FFSmbiosType Type; uint8_t Length; uint16_t Handle; -} FFSmbiosHeader; +} __attribute__((__packed__)) FFSmbiosHeader; static_assert(sizeof(FFSmbiosHeader) == 4, "FFSmbiosHeader should be 4 bytes"); -typedef struct FFRawSmbiosData -{ - uint8_t Used20CallingMethod; - uint8_t SMBIOSMajorVersion; - uint8_t SMBIOSMinorVersion; - uint8_t DmiRevision; - uint32_t Length; - uint8_t SMBIOSTableData[]; -} FFRawSmbiosData; - static inline const char* ffSmbiosLocateString(const char* start, uint8_t index /* start from 1 */) { if (index == 0 || *start == '\0') @@ -104,6 +91,9 @@ typedef const FFSmbiosHeader* FFSmbiosHeaderTable[FF_SMBIOS_TYPE_END_OF_TABLE]; const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header); const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable(); + +#ifdef __linux__ +bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer); #endif #endif