Merge pull request #2083 from fastfetch-cli/dev

Release: v2.56.0
This commit is contained in:
Carter Li
2025-12-05 22:42:51 +08:00
committed by GitHub
50 changed files with 927 additions and 392 deletions
-1
View File
@@ -446,7 +446,6 @@ jobs:
usesh: true
prepare: |
uname -a
pkg install pkg:/package/pkg
pkg update --accept
pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick
+50
View File
@@ -1,3 +1,53 @@
# 2.56.0
Features:
* Enhances config file loading. `--config` and `-c` with relative path now also searches paths defined in `fastfetch --list-config-paths` (typically `~/.config/fastfetch/`)
* This allows users to use `fastfetch -c my-config` without needing to specify the full path.
* Adds NUMA node count detection (CPU)
* Exposed via `{numa-nodes}` in custom format
* Supported on Linux, FreeBSD and Windows
* Supports the newest Alacritty config format (#2070, TerminalFont)
* Detects driver specific info for Zhaoxin GPUs (GPU, Linux)
* Detects Android OEM UI for certain OSes (DE, Android)
* Improves users detection on Linux (#2064, Users, Linux)
* Adds systemd fallback when utmp is unavailable
* Fixes resource leaks
* Always reports the newest session info
* Adds kiss package manager support (#2072, Packages, Linux)
* Reports `sshd` if `$SSH_TTY` is not available (Terminal)
* Zpool module rewrite (#2051, Zpool)
* Adds new Zpool properties: allocated, guid, readOnly
* Zpool module now uses runtime lookup for properties to ensure portability
* Adds NetBSD (requires `sudo`) and macOS support
* Adds `splitLines` option for Command module, which splits the output into sub modules, each containing one line of the output (Command)
```
* Command output:
Line 1
Line 2
Line 3
* Old behavior:
Command: Line 1
Line 2
Line 3
* With `"splitLines": true`:
Command 1: Line 1
Command 2: Line 2
Command 3: Line 3
```
Bugfixes:
* Fixes {m,o}ksh version detection on Linux (Shell)
* Fixes Alacritty config parsing for TOML format (#2070, TerminalFont)
* Improves builtin logo printing for piping and buffering (#2065, Logo)
* Uses absolute path when detecting shell and terminal version if available (#2067, TerminalShell)
Logos:
* Updates Codex Linux logo (#2071)
* Adds OS/2 Warp logo (#2062)
* Adds Amiga logo (#2061)
# 2.55.1
Bugfixes:
+12 -32
View File
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url
project(fastfetch
VERSION 2.55.1
VERSION 2.56.0
LANGUAGES C
DESCRIPTION "Fast neofetch-like system information tool"
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
@@ -92,7 +92,6 @@ cmake_dependent_option(ENABLE_DDCUTIL "Enable ddcutil" ON "LINUX" OFF)
cmake_dependent_option(ENABLE_DIRECTX_HEADERS "Enable DirectX headers for WSL" ON "LINUX" OFF)
cmake_dependent_option(ENABLE_ELF "Enable libelf" ON "LINUX OR ANDROID OR DragonFly OR Haiku OR GNU" OFF)
cmake_dependent_option(ENABLE_THREADS "Enable multithreading" ON "Threads_FOUND" OFF)
cmake_dependent_option(ENABLE_LIBZFS "Enable libzfs" ON "LINUX OR FreeBSD OR SunOS" OFF)
cmake_dependent_option(ENABLE_PCIACCESS "Enable libpciaccess" ON "GNU" OFF)
option(ENABLE_ZLIB "Enable zlib" ON)
@@ -107,6 +106,7 @@ option(INSTALL_LICENSE "Install license into /usr/share/licenses" ON)
option(ENABLE_EMBEDDED_PCIIDS "Embed pci.ids into fastfetch, requires `python`" OFF)
option(ENABLE_EMBEDDED_AMDGPUIDS "Embed amdgpu.ids into fastfetch, requires `python`" OFF)
option(ENABLE_WORDEXP "Enable using of wordexp(3) if available, instead of glob(3)" ON)
option(ENABLE_LIBZFS "Enable libzfs" ON)
set(BINARY_LINK_TYPE_OPTIONS dlopen dynamic static)
set(BINARY_LINK_TYPE dlopen CACHE STRING "How to link fastfetch")
@@ -410,6 +410,7 @@ set(LIBFASTFETCH_SRC
src/detection/version/version.c
src/detection/vulkan/vulkan.c
src/detection/weather/weather.c
src/detection/zpool/zpool.c
src/logo/builtin.c
src/logo/image/im6.c
src/logo/image/im7.c
@@ -582,7 +583,6 @@ if(LINUX)
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
src/detection/camera/camera_linux.c
src/detection/zpool/zpool_linux.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
)
@@ -656,7 +656,6 @@ elseif(ANDROID)
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
src/detection/camera/camera_android.c
src/detection/zpool/zpool_nosupport.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
)
@@ -739,7 +738,6 @@ elseif(FreeBSD)
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
src/detection/camera/camera_linux.c
src/detection/zpool/zpool_linux.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
)
@@ -833,7 +831,6 @@ elseif(NetBSD)
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
src/detection/camera/camera_linux.c
src/detection/zpool/zpool_linux.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
)
@@ -916,7 +913,6 @@ elseif(OpenBSD)
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
src/detection/camera/camera_linux.c
src/detection/zpool/zpool_nosupport.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
src/util/smbiosHelper.c
@@ -986,7 +982,6 @@ elseif(APPLE)
src/detection/de/de_nosupport.c
src/detection/wmtheme/wmtheme_apple.m
src/detection/camera/camera_apple.m
src/detection/zpool/zpool_nosupport.c
src/util/apple/cf_helpers.c
src/util/apple/osascript.m
src/util/apple/smc_temps.c
@@ -1056,7 +1051,6 @@ elseif(WIN32)
src/detection/de/de_nosupport.c
src/detection/wmtheme/wmtheme_windows.c
src/detection/camera/camera_windows.cpp
src/detection/zpool/zpool_nosupport.c
src/util/windows/getline.c
src/util/windows/com.cpp
src/util/windows/registry.c
@@ -1145,7 +1139,6 @@ elseif(SunOS)
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
src/detection/camera/camera_nosupport.c
src/detection/zpool/zpool_linux.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
)
@@ -1214,7 +1207,6 @@ elseif(Haiku)
src/detection/de/de_nosupport.c
src/detection/wmtheme/wmtheme_nosupport.c
src/detection/camera/camera_nosupport.c
src/detection/zpool/zpool_nosupport.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
src/util/haiku/version.cpp
@@ -1294,7 +1286,6 @@ elseif(GNU)
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
src/detection/camera/camera_nosupport.c
src/detection/zpool/zpool_nosupport.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
)
@@ -1611,24 +1602,6 @@ ff_lib_enable(PCIACCESS
"pciaccess"
"pciaccess"
)
# The system <libzfs.h> is only usable on SunOS. We provide our local copy of it so it's always available.
if(ENABLE_LIBZFS)
if(BINARY_LINK_TYPE STREQUAL "dlopen")
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LIBZFS=1)
else()
set(CMAKE_REQUIRED_LIBRARIES_BACKUP ${CMAKE_REQUIRED_LIBRARIES})
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} zfs)
check_function_exists("libzfs_init" LIBZFS_FOUND)
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES_BACKUP})
if(NOT LIBZFS_FOUND)
message(STATUS "Library: missing: LIBZFS")
else()
message(STATUS "Library: found LIBZFS")
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LIBZFS=1)
target_link_libraries(libfastfetch PRIVATE zfs)
endif()
endif()
endif()
if(ENABLE_THREADS)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_THREADS=1)
@@ -1643,6 +1616,15 @@ endif()
if(ENABLE_EMBEDDED_AMDGPUIDS)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_EMBEDDED_AMDGPUIDS=1)
endif()
if(ENABLE_LIBZFS)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LIBZFS=1)
if(NOT BINARY_LINK_TYPE STREQUAL "dlopen")
target_link_libraries(libfastfetch
PRIVATE "zfs"
)
endif()
endif()
if(LINUX)
target_link_libraries(libfastfetch
@@ -1733,8 +1715,6 @@ elseif(SunOS)
PRIVATE "socket"
PRIVATE "kstat"
PRIVATE "proc"
PRIVATE "zfs"
PRIVATE "nvpair"
PRIVATE "devinfo"
)
elseif(GNU)
+2 -1
View File
@@ -37,7 +37,8 @@ Some distributions package outdated versions of fastfetch. Older versions receiv
<img src="https://repology.org/badge/vertical-allrepos/fastfetch.svg?columns=2" alt="Packaging status" align="right">
</a>
* Debian: `apt install fastfetch` (for Debian 13 or newer)
* Ubuntu: [`ppa:zhangsongcui3371/fastfetch`](https://launchpad.net/~zhangsongcui3371/+archive/ubuntu/fastfetch) (Ubuntu 22.04 or newer; latest version)
* Debian / Ubuntu: `apt install fastfetch` (Debian 13 or newer; Ubuntu 25.04 or newer)
* Debian / Ubuntu: Download `fastfetch-linux-<proper architecture>.deb` from [Github release page](https://github.com/fastfetch-cli/fastfetch/releases/latest) and double-click it (for Ubuntu 20.04 or newer and Debian 11 or newer).
* Arch Linux: `pacman -S fastfetch`
* Fedora: `dnf install fastfetch`
+6
View File
@@ -1,3 +1,9 @@
fastfetch (2.55.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.55.1
-- Carter Li <zhangsongcui@live.cn> Mon, 17 Nov 2025 10:15:44 +0800
fastfetch (2.55.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.55.0
+5
View File
@@ -2111,6 +2111,11 @@
"type": "boolean",
"default": true
},
"splitLines": {
"description": "Set if the command output should be split into multiple lines",
"type": "boolean",
"default": false
},
"key": {
"$ref": "#/$defs/key"
},
+17
View File
@@ -1,4 +1,6 @@
#include "fastfetch.h"
#include "util/FFlist.h"
#include "util/FFstrbuf.h"
#include "common/font.h"
#include <string.h>
@@ -192,6 +194,21 @@ void ffFontInitValues(FFfont* font, const char* name, const char* size)
fontInitPretty(font);
}
void ffFontInitMoveValues(FFfont* font, FFstrbuf* name, FFstrbuf* size, FFstrbuf* style)
{
ffFontInit(font);
if (name) ffStrbufInitMove(&font->name, name);
if (size) ffStrbufInitMove(&font->size, size);
if (style)
{
FFstrbuf* styleBuf = FF_LIST_ADD(FFstrbuf, font->styles);
ffStrbufInitMove(styleBuf, style);
}
fontInitPretty(font);
}
void ffFontInitWithSpace(FFfont* font, const char* rawName)
{
const char* pspace = strrchr(rawName, ' ');
+1
View File
@@ -15,6 +15,7 @@ void ffFontInit(FFfont* font);
void ffFontInitQt(FFfont* font, const char* data);
void ffFontInitPango(FFfont* font, const char* data);
void ffFontInitValues(FFfont* font, const char* name, const char* size);
void ffFontInitMoveValues(FFfont* font, FFstrbuf* name, FFstrbuf* size, FFstrbuf* style);
void ffFontInitWithSpace(FFfont* font, const char* rawName);
void ffFontDestroy(FFfont* font);
+1 -1
View File
@@ -49,7 +49,7 @@ static void defaultConfig(void)
void ffInitInstance(void)
{
#ifdef WIN32
#ifdef _WIN32
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?source=recommendat>
setlocale(LC_ALL, ".UTF8");
#else
+14 -6
View File
@@ -171,19 +171,27 @@ static inline void ffUnsuppressIO(bool* suppressed)
void ffListFilesRecursively(const char* path, bool pretty);
static inline bool ffIsValidNativeFD(FFNativeFD fd)
{
#ifndef _WIN32
return fd >= 0;
#else
// https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
return fd != INVALID_HANDLE_VALUE && fd != NULL;
#endif
}
FF_C_NONNULL(1)
static inline bool wrapClose(FFNativeFD* pfd)
{
assert(pfd);
#ifndef WIN32
if (*pfd < 0)
return false;
if (!ffIsValidNativeFD(*pfd))
return false;
#ifndef _WIN32
close(*pfd);
#else
// https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
if (*pfd == NULL || *pfd == INVALID_HANDLE_VALUE)
return false;
CloseHandle(*pfd);
#endif
+4
View File
@@ -273,6 +273,10 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
ffStrbufEnsureFixedLengthFree(exePath, (uint32_t)length);
ffStrbufAppendNS(exePath, (uint32_t)length, buf);
}
else
{
ffStrbufSetS(exePath, filePath);
}
}
#elif defined(__APPLE__)
+1 -1
View File
@@ -90,7 +90,7 @@
"short": "c",
"long": "config",
"desc": "Specify the config file or preset to load",
"remark": "The file will be searched according to the order shown by \"fastfetch --list-config-paths\". Use \"none\" to disable further config loading. See also https://github.com/fastfetch-cli/fastfetch/wiki/Configuration for more info",
"remark": "The file will be searched according to the order shown by \"fastfetch --list-config-paths\". Use \"-\" to read config from stdin or \"none\" to disable further config loading. See also https://github.com/fastfetch-cli/fastfetch/wiki/Configuration for more info",
"arg": {
"type": "config"
}
+1
View File
@@ -21,6 +21,7 @@ typedef struct FFCPUResult
uint16_t coresPhysical;
uint16_t coresLogical;
uint16_t coresOnline;
uint16_t numaNodes;
uint32_t frequencyBase; // GHz
uint32_t frequencyMax; // GHz
+2
View File
@@ -100,5 +100,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
detectThermalTemp(&cpu->temperature);
}
cpu->numaNodes = (uint16_t) ffSysctlGetInt("vm.ndomains", 0);
return NULL;
}
+18
View File
@@ -138,6 +138,21 @@ static double detectCPUTemp(void)
return FF_CPU_TEMP_UNSET;
}
static void detectNumaNodes(FFCPUResult* cpu)
{
FF_AUTO_CLOSE_DIR DIR* dir = opendir("/sys/devices/system/node/");
if (!dir) return;
struct dirent* entry;
while ((entry = readdir(dir)) != NULL)
{
if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN)
continue;
if (ffStrStartsWith(entry->d_name, "node") && ffCharIsDigit(entry->d_name[strlen("node")]))
cpu->numaNodes++;
}
}
#ifdef __ANDROID__
#include "common/settings.h"
@@ -568,6 +583,8 @@ FF_MAYBE_UNUSED static const char* detectCPUX86(const FFCPUOptions* options, FFC
if (!detectFrequency(cpu, options) || cpu->frequencyBase == 0)
cpu->frequencyBase = (uint32_t) ffStrbufToUInt(&cpuMHz, 0);
detectNumaNodes(cpu);
return NULL;
}
@@ -877,6 +894,7 @@ FF_MAYBE_UNUSED static const char* detectCPUOthers(const FFCPUOptions* options,
detectPhysicalCores(cpu);
ffCPUDetectByCpuid(cpu);
detectNumaNodes(cpu);
return NULL;
}
+2
View File
@@ -230,6 +230,8 @@ static const char* detectNCores(FFCPUResult* cpu)
++cpu->coresPhysical;
else if (ptr->Relationship == RelationProcessorPackage)
++cpu->packages;
else if (ptr->Relationship == RelationNumaNode)
++cpu->numaNodes;
}
return NULL;
@@ -140,6 +140,61 @@ static bool detectWithGetprop(FFDisplayServerResult* ds)
return false;
}
static bool detectDE(FFDisplayServerResult* ds)
{
if (ffSettingsGetAndroidProperty("ro.vivo.os.build.display.id", &ds->dePrettyName)) // OriginOS 6
{
ffStrbufAppendC(&ds->dePrettyName, ' ');
ffSettingsGetAndroidProperty("ro.vivo.product.version", &ds->dePrettyName); // PD2505D_xxx
return true;
}
if (ffSettingsGetAndroidProperty("ro.build.version.magic", &ds->dePrettyName) ||
ffSettingsGetAndroidProperty("ro.build.version.emui", &ds->dePrettyName))
{
ffStrbufReplaceAllC(&ds->dePrettyName, '_', ' ');
return true;
}
if (ffSettingsGetAndroidProperty("ro.mi.os.version.name", &ds->dePrettyName))
{
// MiUI like
ffStrbufClear(&ds->dePrettyName);
ffSettingsGetAndroidProperty("ro.build.version.incremental", &ds->dePrettyName); // Detail version number
if (ffStrbufStartsWithS(&ds->dePrettyName, "OS"))
{
ds->dePrettyName.chars[0] = 'S';
ds->dePrettyName.chars[1] = ' ';
ffStrbufPrependS(&ds->dePrettyName, "HyperO");
}
else if (ffStrbufStartsWithS(&ds->dePrettyName, "V"))
{
ds->dePrettyName.chars[0] = ' ';
ffStrbufPrependS(&ds->dePrettyName, "MiUI");
}
else
ffStrbufSetStatic(&ds->dePrettyName, "MiUI");
return true;
}
if (ffSettingsGetAndroidProperty("ro.build.version.oplusrom", &ds->dePrettyName))
{
if (ffStrbufStartsWithS(&ds->dePrettyName, "V"))
ffStrbufSubstrAfter(&ds->dePrettyName, 0);
ffStrbufPrependS(&ds->dePrettyName, "ColorOS");
return true;
}
if (ffSettingsGetAndroidProperty("ro.oxygen.version", &ds->dePrettyName))
{
ffStrbufPrependS(&ds->dePrettyName, "OxygenOS");
return true;
}
if (ffSettingsGetAndroidProperty("ro.build.display.id", &ds->dePrettyName) && ffStrbufStartsWithS(&ds->dePrettyName, "RedMagicOS"))
{
ffStrbufInsertNC(&ds->dePrettyName, strlen("RedMagicOS"), 1, ' ');
return true;
}
return false;
}
void ffConnectDisplayServerImpl(FFDisplayServerResult* ds)
{
const char* error = ffdsConnectXcbRandr(ds);
@@ -158,4 +213,6 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds)
if (!detectWithGetprop(ds))
detectWithDumpsys(ds);
detectDE(ds);
}
+7
View File
@@ -15,11 +15,18 @@ extern const char* FF_GPU_VENDOR_NAME_AMD;
extern const char* FF_GPU_VENDOR_NAME_INTEL;
extern const char* FF_GPU_VENDOR_NAME_NVIDIA;
extern const char* FF_GPU_VENDOR_NAME_MTHREADS;
extern const char* FF_GPU_VENDOR_NAME_QUALCOMM;
extern const char* FF_GPU_VENDOR_NAME_MTK;
extern const char* FF_GPU_VENDOR_NAME_VMWARE;
extern const char* FF_GPU_VENDOR_NAME_PARALLEL;
extern const char* FF_GPU_VENDOR_NAME_MICROSOFT;
extern const char* FF_GPU_VENDOR_NAME_REDHAT;
extern const char* FF_GPU_VENDOR_NAME_ORACLE;
extern const char* FF_GPU_VENDOR_NAME_BROADCOM;
extern const char* FF_GPU_VENDOR_NAME_LOONGSON;
extern const char* FF_GPU_VENDOR_NAME_JINGJIA_MICRO;
extern const char* FF_GPU_VENDOR_NAME_HUAWEI;
extern const char* FF_GPU_VENDOR_NAME_ZHAOXIN;
typedef struct FFGPUMemory
{
+1 -1
View File
@@ -57,7 +57,7 @@ static const char* detectFrequency(FFGPUResult* gpu)
if (!freqProperty || CFGetTypeID(freqProperty) != CFDataGetTypeID())
return "\"voltage-states9-sram\" in \"pmgr\" is not found";
// voltage-states5-sram stores supported <frequency / voltage> pairs of gpu from the lowest to the highest
// voltage-states9-sram stores supported <frequency / voltage> pairs of gpu from the lowest to the highest
CFIndex propLength = CFDataGetLength(freqProperty);
if (propLength == 0 || propLength % (CFIndex) sizeof(uint32_t) * 2 != 0)
return "Invalid \"voltage-states9-sram\" length";
+52 -2
View File
@@ -4,10 +4,13 @@
#include "detection/gpu/gpu_driver_specific.h"
#include "common/io/io.h"
#include "common/library.h"
#include "modules/gpu/option.h"
#include "util/FFstrbuf.h"
#include "util/stringUtils.h"
#include "util/mallocHelper.h"
#include <inttypes.h>
#include <stdint.h>
#ifdef FF_HAVE_DRM_AMDGPU
#include <amdgpu.h>
@@ -263,7 +266,7 @@ static const char* drmDetectIntelSpecific(FFGPUResult* gpu, const char* drmKey,
#endif
}
static const char* pciDetectNouveauSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer)
static const char* pciDetectTempGeneral(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer)
{
if (options->temp)
{
@@ -306,6 +309,48 @@ static const char* drmDetectNouveauSpecific(FFGPUResult* gpu, const char* drmKey
#endif
}
static const char* pciDetectZxSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer)
{
gpu->type = FF_GPU_TYPE_INTEGRATED;
const uint32_t pciDirLen = pciDir->length;
ffStrbufAppendS(pciDir, "/zx_info/eclk");
if (ffReadFileBuffer(pciDir->chars, buffer))
gpu->frequency = (uint32_t) ffStrbufToUInt(buffer, FF_GPU_FREQUENCY_UNSET);
ffStrbufSubstrBefore(pciDir, pciDirLen);
if (options->driverSpecific)
{
ffStrbufAppendS(pciDir, "/zx_info/engine_3d_usage");
if (ffReadFileBuffer(pciDir->chars, buffer))
gpu->coreUsage = ffStrbufToDouble(buffer, FF_GPU_CORE_USAGE_UNSET);
ffStrbufSubstrBefore(pciDir, pciDirLen);
ffStrbufAppendS(pciDir, "/zx_info/fb_size");
if (ffReadFileBuffer(pciDir->chars, buffer))
gpu->shared.total = ffStrbufToUInt(buffer, FF_GPU_VMEM_SIZE_UNSET);
ffStrbufSubstrBefore(pciDir, pciDirLen);
if (gpu->shared.total != FF_GPU_VMEM_SIZE_UNSET)
{
gpu->shared.total *= 1024 * 1024;
ffStrbufAppendS(pciDir, "/zx_info/free_fb_mem");
if (ffReadFileBuffer(pciDir->chars, buffer))
gpu->shared.used = ffStrbufToUInt(buffer, FF_GPU_VMEM_SIZE_UNSET);
ffStrbufSubstrBefore(pciDir, pciDirLen);
if (gpu->shared.used != FF_GPU_VMEM_SIZE_UNSET)
{
gpu->shared.used *= 1024 * 1024;
gpu->shared.used = gpu->shared.total - gpu->shared.used;
}
}
}
return NULL;
}
static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf* buffer, FFstrbuf* deviceDir, const char* drmKey)
{
const uint32_t drmDirPathLength = deviceDir->length;
@@ -417,10 +462,15 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf
}
else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA && ffStrbufEqualS(&gpu->driver, "nouveau"))
{
pciDetectNouveauSpecific(options, gpu, deviceDir, buffer);
pciDetectTempGeneral(options, gpu, deviceDir, buffer);
if (options->driverSpecific && drmKey)
drmDetectNouveauSpecific(gpu, drmKey, buffer);
}
else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_ZHAOXIN && ffStrbufStartsWithS(&gpu->driver, "zx"))
{
pciDetectTempGeneral(options, gpu, deviceDir, buffer);
pciDetectZxSpecific(options, gpu, deviceDir, buffer);
}
else
{
ffGPUDetectDriverSpecific(options, gpu, (FFGpuDriverPciBusId) {
+1
View File
@@ -21,6 +21,7 @@ typedef struct FFPackagesResult
uint32_t guixUser;
uint32_t hpkgSystem;
uint32_t hpkgUser;
uint32_t kiss;
uint32_t linglong;
uint32_t lpkg;
uint32_t lpkgbuild;
+1
View File
@@ -420,6 +420,7 @@ static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts,
if (!(options->disabled & FF_PACKAGES_FLAG_EMERGE_BIT)) packageCounts->emerge += countFilesRecursive(baseDir, "/var/db/pkg", "SIZE");
if (!(options->disabled & FF_PACKAGES_FLAG_EOPKG_BIT)) packageCounts->eopkg += getNumElements(baseDir, "/var/lib/eopkg/package", true);
if (!(options->disabled & FF_PACKAGES_FLAG_FLATPAK_BIT)) packageCounts->flatpakSystem += getFlatpakPackages(baseDir, "/var/lib");
if (!(options->disabled & FF_PACKAGES_FLAG_KISS_BIT)) packageCounts->kiss += getNumElements(baseDir, "/var/db/kiss/installed", true);
if (!(options->disabled & FF_PACKAGES_FLAG_NIX_BIT))
{
packageCounts->nixDefault += ffPackagesGetNix(baseDir, "/nix/var/nix/profiles/default");
+36 -21
View File
@@ -8,13 +8,18 @@
static void detectAlacritty(FFTerminalFontResult* terminalFont)
{
FF_STRBUF_AUTO_DESTROY fontName = ffStrbufCreate();
// Maybe using a toml parser to read the config file is better?
// https://github.com/cktan/tomlc17
// Doc: https://alacritty.org/config-alacritty.html#s26
FF_STRBUF_AUTO_DESTROY fontNormal = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY fontFamily = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY fontStyle = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY fontSize = ffStrbufCreate();
do {
// Latest alacritty uses toml instead of yaml
FFpropquery fontQueryToml[] = {
{"family =", &fontName},
{"normal =", &fontNormal},
{"size =", &fontSize},
};
@@ -25,29 +30,39 @@ static void detectAlacritty(FFTerminalFontResult* terminalFont)
break;
if(ffParsePropFileConfigValues(".alacritty.toml", 2, fontQueryToml))
break;
FFpropquery fontQueryYaml[] = {
{"family:", &fontName},
{"size:", &fontSize},
};
if(ffParsePropFileConfigValues("alacritty/alacritty.yml", 2, fontQueryYaml))
break;
if(ffParsePropFileConfigValues("alacritty.yml", 2, fontQueryYaml))
break;
if(ffParsePropFileConfigValues(".alacritty.yml", 2, fontQueryYaml))
break;
} while (false);
//by default alacritty uses its own font called alacritty
if(fontName.length == 0)
ffStrbufAppendS(&fontName, "alacritty");
if(fontNormal.length > 0)
{
// { family = "Fira Code", style = "Medium" }
ffStrbufTrimSpace(&fontNormal);
ffStrbufTrimRight(&fontNormal, '}');
ffStrbufTrimLeft(&fontNormal, '{');
ffStrbufTrimSpace(&fontNormal);
// family = "Fira Code", style = "Medium"
ffStrbufReplaceAllC(&fontNormal, ',', '\n'); // Assume no commas in font names
ffParsePropLines(fontNormal.chars, "family =", &fontFamily);
ffParsePropLines(fontNormal.chars, "style =", &fontStyle);
}
if (fontFamily.length == 0)
{
#if __APPLE__
ffStrbufSetStatic(&fontFamily, "Menlo");
#elif _WIN32
ffStrbufSetStatic(&fontFamily, "Consolas");
#else
ffStrbufSetStatic(&fontFamily, "monospace");
#endif
}
if (fontStyle.length == 0)
ffStrbufSetStatic(&fontStyle, "Regular");
// the default font size is 11
if(fontSize.length == 0)
ffStrbufAppendS(&fontSize, "11");
ffStrbufSetStatic(&fontSize, "11.25");
ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars);
ffFontInitMoveValues(&terminalFont->font, &fontFamily, &fontSize, &fontStyle);
}
static void detectGhostty(const FFstrbuf* exe, FFTerminalFontResult* terminalFont)
+76 -37
View File
@@ -2,6 +2,7 @@
#include "common/io/io.h"
#include "common/processing.h"
#include "common/properties.h"
#include "util/path.h"
#include "util/stringUtils.h"
#include "util/binary.h"
@@ -64,12 +65,9 @@ static bool extractBashVersion(const char* line, FF_MAYBE_UNUSED uint32_t len, v
return false;
}
static bool getShellVersionBash(FFstrbuf* exe, FFstrbuf* exePath, FFstrbuf* version)
static bool getShellVersionBash(FFstrbuf* exe, FFstrbuf* version)
{
const char* path = exePath->chars;
if (*path == '\0')
path = exe->chars;
ffBinaryExtractStrings(path, extractBashVersion, version, (uint32_t) strlen("@(#)Bash version 0.0.0(0) release GNU"));
ffBinaryExtractStrings(exe->chars, extractBashVersion, version, (uint32_t) strlen("@(#)Bash version 0.0.0(0) release GNU"));
if (version->length > 0) return true;
if(!getExeVersionRaw(exe, version))
@@ -122,35 +120,39 @@ static bool getShellVersionPwsh(FFstrbuf* exe, FFstrbuf* version)
static bool getShellVersionKsh(FFstrbuf* exe, FFstrbuf* version)
{
#if __OpenBSD__ || __NetBSD__
if(ffProcessAppendStdErr(version, (char* const[]) {
exe->chars,
"--version",
NULL
}) == NULL && ffStrbufSubstrAfterFirstS(version, " (AT&T Research) "))
{
// version sh (AT&T Research) 93u+ 2012-08-01
ffStrbufSubstrBeforeFirstC(version, ' ');
return true;
}
ffStrbufClear(version);
if(ffProcessAppendStdOut(version, (char* const[]) {
exe->chars,
"-c",
"echo $KSH_VERSION",
NULL
}) != NULL)
return false;
}) == NULL && ffStrbufSubstrAfterFirstS(version, " KSH "))
{
// OKSH: @(#)PD KSH v5.2.14 99/07/13.2
// MKSH: @(#)MIRBSD KSH R59 2025/04/26 +Debian
// $OKSH_VERSION doesn't exist on OpenBSD
ffStrbufSubstrBeforeFirstC(version, ' ');
ffStrbufTrimLeft(version, 'v');
return true;
}
// @(#)PD KSH v5.2.14 99/07/13.2
ffStrbufSubstrAfterFirstC(version, 'v');
ffStrbufSubstrBeforeFirstC(version, ' ');
#else
if(ffProcessAppendStdErr(version, (char* const[]) {
exe->chars,
"--version",
NULL
}) != NULL)
return false;
// version sh (AT&T Research) 93u+ 2012-08-01
ffStrbufSubstrAfterLastC(version, ')');
ffStrbufTrim(version, ' ');
#endif
return true;
return false;
}
static bool getShellVersionOksh(FFstrbuf* exe, FFstrbuf* version)
{
// Homebrew version
if(ffProcessAppendStdOut(version, (char* const[]) {
exe->chars,
"-c",
@@ -230,13 +232,9 @@ static bool extractZshVersion(const char* line, FF_MAYBE_UNUSED uint32_t len, vo
return false;
}
static bool getShellVersionZsh(FFstrbuf* exe, FFstrbuf* exePath, FFstrbuf* version)
static bool getShellVersionZsh(FFstrbuf* exe, FFstrbuf* version)
{
const char* path = exePath->chars;
if (*path == '\0')
path = exe->chars;
ffBinaryExtractStrings(path, extractZshVersion, version, (uint32_t) strlen("zsh-0.0-0"));
ffBinaryExtractStrings(exe->chars, extractZshVersion, version, (uint32_t) strlen("zsh-0.0-0"));
if (version->length) return true;
return getExeVersionGeneral(exe, version); //zsh 5.9 (arm-apple-darwin21.3.0)
@@ -263,7 +261,7 @@ static bool getShellVersionWinPowerShell(FFstrbuf* exe, FFstrbuf* version)
}
#endif
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath, FFstrbuf* version)
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version)
{
if (!instance.config.general.detectVersion) return false;
@@ -271,9 +269,9 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath,
return false;
if(ffStrEqualsIgnCase(exeName, "bash"))
return getShellVersionBash(exe, exePath, version);
return getShellVersionBash(exe, version);
if(ffStrEqualsIgnCase(exeName, "zsh"))
return getShellVersionZsh(exe, exePath, version);
return getShellVersionZsh(exe, version);
if(ffStrEqualsIgnCase(exeName, "fish"))
return getShellVersionFish(exe, version);
if(ffStrEqualsIgnCase(exeName, "pwsh"))
@@ -282,7 +280,7 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath,
return getExeVersionGeneral(exe, version); //tcsh 6.24.07 (Astron) 2022-12-21 (aarch64-apple-darwin) options wide,nls,dl,al,kan,sm,rh,color,filec
if(ffStrEqualsIgnCase(exeName, "nu"))
return getShellVersionNushell(exe, version);
if(ffStrEqualsIgnCase(exeName, "ksh"))
if(ffStrEqualsIgnCase(exeName, "ksh") || ffStrEqualsIgnCase(exeName, "mksh"))
return getShellVersionKsh(exe, version);
if(ffStrEqualsIgnCase(exeName, "oksh"))
return getShellVersionOksh(exe, version);
@@ -325,7 +323,7 @@ static bool extractGeneralVersion(const char *str, FF_MAYBE_UNUSED uint32_t len,
FF_MAYBE_UNUSED static bool getTerminalVersionGnome(FFstrbuf* exe, FFstrbuf* version)
{
if (exe->chars[0] == '/')
if (ffIsAbsolutePath(exe->chars))
{
ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0"));
if (version->length) return true;
@@ -345,7 +343,7 @@ FF_MAYBE_UNUSED static bool getTerminalVersionGnome(FFstrbuf* exe, FFstrbuf* ver
FF_MAYBE_UNUSED static bool getTerminalVersionXfce4Terminal(FFstrbuf* exe, FFstrbuf* version)
{
if (exe->chars[0] == '/')
if (ffIsAbsolutePath(exe->chars))
{
ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0"));
if (version->length) return true;
@@ -582,6 +580,44 @@ static bool getTerminalVersionZed(FFstrbuf* exe, FFstrbuf* version)
return true;
}
static bool extractSshdVersion(const char *str, FF_MAYBE_UNUSED uint32_t len, void *userdata)
{
if (!ffStrStartsWith(str, "OpenSSH_") || !ffCharIsDigit(str[strlen("OpenSSH_")])) return true;
str += strlen("OpenSSH_");
int count = 0;
sscanf(str, "%*d.%*dp%*d%n", &count);
if (count == 0) return true;
ffStrbufSetS((FFstrbuf*) userdata, str);
return false;
}
static bool getTerminalVersionSshd(FFstrbuf* exe, FFstrbuf* version)
{
FF_STRBUF_AUTO_DESTROY exePath = ffStrbufCreate();
if (ffIsAbsolutePath(exe->chars))
ffStrbufSet(&exePath, exe);
else if (ffFindExecutableInPath("sshd", &exePath) != NULL)
return false;
ffBinaryExtractStrings(exePath.chars, extractSshdVersion, version, (uint32_t) strlen("OpenSSH0.0"));
if (version->length) return true;
if(ffProcessAppendStdOut(version, (char* const[]) {
exePath.chars,
"-V",
NULL
}) != NULL)
return false;
if (ffStrbufStartsWithS(version, "unknown ")) // `unknown option -- V` (ancient OpenSSH version)
ffStrbufSubstrAfterFirstC(version, '\n');
// OpenSSH_10.0p2 Ubuntu-5ubuntu5, OpenSSL 3.5.3 16 Sep 2025
ffStrbufSubstrAfterFirstC(version, '_');
ffStrbufSubstrBeforeFirstC(version, ',');
return true;
}
#ifndef _WIN32
static bool getTerminalVersionKitty(FFstrbuf* exe, FFstrbuf* version)
{
@@ -681,7 +717,7 @@ FF_MAYBE_UNUSED static bool getTerminalVersionPtyxis(FF_MAYBE_UNUSED FFstrbuf* e
FF_MAYBE_UNUSED static bool getTerminalVersionTilix(FFstrbuf* exe, FFstrbuf* version)
{
if (exe->chars[0] == '/')
if (ffIsAbsolutePath(exe->chars))
{
ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0"));
if (version->length) return true;
@@ -925,6 +961,9 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe
if(ffStrbufStartsWithIgnCaseS(processName, "tmux"))
return getTerminalVersionTmux(exe, version);
if(ffStrbufIgnCaseEqualS(processName, "sshd") || ffStrbufStartsWithIgnCaseS(processName, "sshd-"))
return getTerminalVersionSshd(exe, version);
#ifdef _WIN32
return getFileVersion(exe, NULL, version);
@@ -292,14 +292,14 @@ static void getUserShellFromEnv(FFShellResult* result)
}
}
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath, FFstrbuf* version);
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version);
bool fftsGetTerminalVersion(FFstrbuf* processName, FFstrbuf* exe, FFstrbuf* version);
static void setShellInfoDetails(FFShellResult* result)
{
ffStrbufClear(&result->version);
fftsGetShellVersion(&result->exe, result->exeName, &result->exePath, &result->version);
fftsGetShellVersion(result->exePath.length > 0 ? &result->exePath : &result->exe, result->exeName, &result->version);
if(ffStrbufEqualS(&result->processName, "pwsh"))
ffStrbufInitStatic(&result->prettyName, "PowerShell");
@@ -331,7 +331,12 @@ static void setTerminalInfoDetails(FFTerminalResult* result)
else if(ffStrbufStartsWithS(&result->processName, "screen-"))
ffStrbufInitStatic(&result->prettyName, "screen");
else if(ffStrbufEqualS(&result->processName, "sshd") || ffStrbufStartsWithS(&result->processName, "sshd-"))
ffStrbufInitCopy(&result->prettyName, &result->tty);
{
if (result->tty.length)
ffStrbufInitCopy(&result->prettyName, &result->tty);
else
ffStrbufSetStatic(&result->prettyName, "sshd");
}
#if defined(__ANDROID__)
@@ -383,7 +388,7 @@ static void setTerminalInfoDetails(FFTerminalResult* result)
else
ffStrbufInitCopy(&result->prettyName, &result->processName);
fftsGetTerminalVersion(&result->processName, &result->exe, &result->version);
fftsGetTerminalVersion(&result->processName, result->exePath.length > 0 ? &result->exePath : &result->exe, &result->version);
}
#if defined(MAXPATH)
@@ -12,7 +12,7 @@
#include <wchar.h>
#include <tlhelp32.h>
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, const FFstrbuf* exePath, FFstrbuf* version);
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version);
static uint32_t getShellInfo(FFShellResult* result, uint32_t pid)
{
@@ -332,7 +332,7 @@ const FFShellResult* ffDetectShell(void)
strcpy(tmp, result.exeName);
char* ext = strrchr(tmp, '.');
if (ext) *ext = '\0';
fftsGetShellVersion(&result.exe, tmp, &result.exePath, &result.version);
fftsGetShellVersion(result.exePath.length > 0 ? &result.exePath : &result.exe, tmp, &result.version);
}
return &result;
@@ -368,7 +368,7 @@ const FFTerminalResult* ffDetectTerminal(void)
if(result.processName.length > 0)
{
setTerminalInfoDetails(&result);
fftsGetTerminalVersion(&result.processName, &result.exe, &result.version);
fftsGetTerminalVersion(&result.processName, result.exePath.length > 0 ? &result.exePath : &result.exe, &result.version);
}
return &result;
+164 -23
View File
@@ -1,6 +1,10 @@
#include "common/io/io.h"
#include "common/properties.h"
#include "fastfetch.h"
#include "users.h"
#include <unistd.h>
#if FF_HAVE_UTMPX
#include <utmpx.h>
#else
@@ -15,7 +19,140 @@
#include <arpa/inet.h>
#endif
const char* ffDetectUsers(FFUsersOptions* options, FFlist* users)
#if __linux__
bool detectUserBySystemd(const FFstrbuf* pathUsers, FFlist* users)
{
FF_STRBUF_AUTO_DESTROY state = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY userName = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY loginTime = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY sessions = ffStrbufCreate();
// WARNING: This is private data. Do not parse
if (!ffParsePropFileValues(pathUsers->chars, 4, (FFpropquery[]) {
{"NAME=", &userName},
{"STATE=", &state},
{"REALTIME=", &loginTime},
{"ONLINE_SESSIONS=", &sessions},
}) || !ffStrbufEqualS(&state, "active"))
return false;
FFUserResult* user = FF_LIST_ADD(FFUserResult, *users);
ffStrbufInitMove(&user->name, &userName);
ffStrbufInit(&user->hostName);
ffStrbufInit(&user->sessionName);
ffStrbufInit(&user->clientIp);
ffStrbufSubstrBefore(&loginTime, loginTime.length - 3); // converts us to ms
user->loginTime = ffStrbufToUInt(&loginTime, 0);
FF_STRBUF_AUTO_DESTROY pathSessions = ffStrbufCreateS("/run/systemd/sessions/");
const uint32_t pathSessionsBaseLen = pathSessions.length;
FF_STRBUF_AUTO_DESTROY tty = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY remoteHost = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY service = ffStrbufCreate();
char* token = NULL;
size_t n = 0;
while (ffStrbufGetdelim(&token, &n, ' ', &sessions))
{
ffStrbufSubstrBefore(&pathSessions, pathSessionsBaseLen);
ffStrbufAppendS(&pathSessions, token);
ffStrbufClear(&remoteHost);
ffStrbufClear(&service);
ffStrbufClear(&tty);
ffStrbufClear(&loginTime);
// WARNING: This is private data. Do not parse
if (ffParsePropFileValues(pathSessions.chars, 4, (FFpropquery[]) {
{"REMOTE_HOST=", &remoteHost},
{"TTY=", &tty},
{"SERVICE=", &service},
{"REALTIME=", &loginTime},
}) && !ffStrbufEqualS(&service, "systemd-user"))
{
if (remoteHost.length)
{
ffStrbufTrimRight(&remoteHost, ']');
ffStrbufTrimLeft(&remoteHost, '[');
ffStrbufInitMove(&user->hostName, &remoteHost);
}
else
ffStrbufSetStatic(&user->hostName, "localhost");
ffStrbufInitMove(&user->sessionName, tty.length ? &tty : &service);
if (loginTime.length)
{
ffStrbufSubstrBefore(&loginTime, loginTime.length - 3); // converts us to ms
user->loginTime = ffStrbufToUInt(&loginTime, 0);
}
break;
}
}
return true;
}
const char* detectBySystemd(FFUsersOptions* options, FFlist* users)
{
// For some reason, debian/ubuntu no longer updates `/var/run/utmp` (#2064)
// Query systemd instead
FF_STRBUF_AUTO_DESTROY pathUsers = ffStrbufCreateS("/run/systemd/users/");
if (options->myselfOnly)
{
ffStrbufAppendUInt(&pathUsers, getuid());
detectUserBySystemd(&pathUsers, users);
}
else
{
const uint32_t pathUsersBaseLen = pathUsers.length;
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(pathUsers.chars);
if (!dirp) return "opendir(\"/run/systemd/users/\") failed";
struct dirent* entry;
while ((entry = readdir(dirp)))
{
if (entry->d_type != DT_REG) continue;
ffStrbufAppendS(&pathUsers, entry->d_name);
detectUserBySystemd(&pathUsers, users);
ffStrbufSubstrBefore(&pathUsers, pathUsersBaseLen);
}
}
return NULL;
}
#endif
#if __linux__ || __GNU__
static void fillUtmpIpAddr(FFUserResult* user, struct utmpx* n)
{
bool isIpv6 = false;
for (int i = 1; i < 4; ++i) {
if (n->ut_addr_v6[i] != 0) {
isIpv6 = true;
break;
}
}
if (isIpv6) {
char ipv6_str[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, n->ut_addr_v6, ipv6_str, INET6_ADDRSTRLEN) != NULL) {
ffStrbufSetS(&user->clientIp, ipv6_str);
}
} else if (n->ut_addr_v6[0] != 0) {
char ipv4_str[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, n->ut_addr_v6, ipv4_str, INET_ADDRSTRLEN) != NULL) {
ffStrbufSetS(&user->clientIp, ipv4_str);
}
}
}
#else
static void fillUtmpIpAddr(FF_MAYBE_UNUSED FFUserResult* user, FF_MAYBE_UNUSED struct utmpx* n)
{
}
#endif
const char* detectByUtmp(FFUsersOptions* options, FFlist* users)
{
struct utmpx* n = NULL;
setutxent();
@@ -32,37 +169,41 @@ next:
FF_LIST_FOR_EACH(FFUserResult, user, *users)
{
if(ffStrbufEqualS(&user->name, n->ut_user))
{
uint64_t newLoginTime = (uint64_t) n->ut_tv.tv_sec * 1000 + (uint64_t) n->ut_tv.tv_usec / 1000;
if (newLoginTime > user->loginTime)
{
ffStrbufSetS(&user->hostName, n->ut_host);
ffStrbufSetS(&user->sessionName, n->ut_line);
fillUtmpIpAddr(user, n);
user->loginTime = newLoginTime;
}
goto next;
}
}
FFUserResult* user = (FFUserResult*) ffListAdd(users);
FFUserResult* user = FF_LIST_ADD(FFUserResult, *users);
ffStrbufInitS(&user->name, n->ut_user);
ffStrbufInitS(&user->hostName, n->ut_host);
ffStrbufInitS(&user->sessionName, n->ut_line);
ffStrbufInit(&user->clientIp);
#if __linux__ || __GNU__
bool isIpv6 = false;
for (int i = 1; i < 4; ++i) {
if (n->ut_addr_v6[i] != 0) {
isIpv6 = true;
break;
}
}
if (isIpv6) {
char ipv6_str[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, n->ut_addr_v6, ipv6_str, INET6_ADDRSTRLEN) != NULL) {
ffStrbufSetS(&user->clientIp, ipv6_str);
}
} else if (n->ut_addr_v6[0] != 0) {
char ipv4_str[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, n->ut_addr_v6, ipv4_str, INET_ADDRSTRLEN) != NULL) {
ffStrbufSetS(&user->clientIp, ipv4_str);
}
}
#endif
fillUtmpIpAddr(user, n);
user->loginTime = (uint64_t) n->ut_tv.tv_sec * 1000 + (uint64_t) n->ut_tv.tv_usec / 1000;
}
endutxent();
return NULL;
}
const char* ffDetectUsers(FFUsersOptions* options, FFlist* users)
{
const char* err = detectByUtmp(options, users);
if (err) return err;
#if __linux__
if (users->length == 0) detectBySystemd(options, users);
#endif
return NULL;
}
+14 -55
View File
@@ -25,65 +25,24 @@
* CDDL HEADER END
*/
typedef enum {
ZPOOL_PROP_INVAL = -1,
ZPOOL_PROP_NAME,
ZPOOL_PROP_SIZE,
ZPOOL_PROP_CAPACITY,
ZPOOL_PROP_ALTROOT,
ZPOOL_PROP_HEALTH,
ZPOOL_PROP_GUID,
ZPOOL_PROP_VERSION,
ZPOOL_PROP_BOOTFS,
ZPOOL_PROP_DELEGATION,
ZPOOL_PROP_AUTOREPLACE,
ZPOOL_PROP_CACHEFILE,
ZPOOL_PROP_FAILUREMODE,
ZPOOL_PROP_LISTSNAPS,
ZPOOL_PROP_AUTOEXPAND,
ZPOOL_PROP_DEDUPDITTO,
ZPOOL_PROP_DEDUPRATIO,
ZPOOL_PROP_FREE,
ZPOOL_PROP_ALLOCATED,
ZPOOL_PROP_READONLY,
ZPOOL_PROP_ASHIFT,
ZPOOL_PROP_COMMENT,
ZPOOL_PROP_EXPANDSZ,
ZPOOL_PROP_FREEING,
ZPOOL_PROP_FRAGMENTATION,
ZPOOL_PROP_LEAKED,
ZPOOL_PROP_MAXBLOCKSIZE,
ZPOOL_PROP_TNAME,
ZPOOL_PROP_MAXDNODESIZE,
ZPOOL_PROP_MULTIHOST,
ZPOOL_PROP_CHECKPOINT,
ZPOOL_PROP_LOAD_GUID,
ZPOOL_PROP_AUTOTRIM,
ZPOOL_PROP_COMPATIBILITY,
ZPOOL_PROP_BCLONEUSED,
ZPOOL_PROP_BCLONESAVED,
ZPOOL_PROP_BCLONERATIO,
ZPOOL_NUM_PROPS
} zpool_prop_t;
typedef enum {
ZPROP_SRC_NONE = 0x1,
ZPROP_SRC_DEFAULT = 0x2,
ZPROP_SRC_TEMPORARY = 0x4,
ZPROP_SRC_LOCAL = 0x8,
ZPROP_SRC_INHERITED = 0x10,
ZPROP_SRC_RECEIVED = 0x20
} zprop_source_t;
typedef bool boolean_t;
// zpool_prop_t and zprop_source_t were previously enums in upstream OpenZFS.
// However, the enum values for these types vary greatly between different platforms,
// making it unsafe to use the enum values directly. To ensure portability,
// we define them as simple int typedefs and use zpool_name_to_prop to look up
// the correct value for a property at runtime.
typedef int zpool_prop_t;
typedef int zprop_source_t;
typedef int boolean_t;
typedef struct libzfs_handle libzfs_handle_t;
typedef struct zpool_handle zpool_handle_t;
typedef int (*zpool_iter_f)(zpool_handle_t *, void *);
extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *);
extern libzfs_handle_t *libzfs_init(void);
extern void libzfs_fini(libzfs_handle_t *);
extern uint64_t zpool_get_prop_int(zpool_handle_t *, zpool_prop_t, zprop_source_t *);
extern const char *zpool_get_name(zpool_handle_t *);
extern const char *zpool_get_state_str(zpool_handle_t *);
extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *);
extern zpool_prop_t zpool_name_to_prop(const char *);
// https://github.com/openzfs/zfs/blob/06c73cffabc30b61a695988ec8e290f43cb3768d/lib/libzfs/libzfs_pool.c#L300
extern uint64_t zpool_get_prop_int(zpool_handle_t *zhp, zpool_prop_t prop, zprop_source_t *srctype);
extern int zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len, zprop_source_t *srctype, boolean_t literal);
extern void zpool_close(zpool_handle_t *);
+126
View File
@@ -0,0 +1,126 @@
#include "zpool.h"
#if FF_HAVE_LIBZFS
#include "util/kmod.h"
#ifdef __sun
#include <libzfs.h>
#else
#include "libzfs_simplified.h"
#endif
#include "common/library.h"
typedef struct FFZfsData
{
FF_LIBRARY_SYMBOL(libzfs_fini)
FF_LIBRARY_SYMBOL(zpool_get_prop_int)
FF_LIBRARY_SYMBOL(zpool_get_prop)
FF_LIBRARY_SYMBOL(zpool_close)
// The fields in this struct store property IDs returned by `zpool_name_to_prop`,
// not the property values themselves.
struct {
int name;
int health;
int guid;
int size;
int free;
int allocated;
int fragmentation;
int readonly;
} props;
libzfs_handle_t* handle;
FFlist* result;
} FFZfsData;
static inline void cleanLibzfs(FFZfsData* data)
{
if (data->fflibzfs_fini && data->handle)
{
data->fflibzfs_fini(data->handle);
data->handle = NULL;
}
}
static int enumZpoolCallback(zpool_handle_t* zpool, void* param)
{
FFZfsData* data = (FFZfsData*) param;
zprop_source_t source;
FFZpoolResult* item = ffListAdd(data->result);
char buf[1024];
if (data->ffzpool_get_prop(zpool, data->props.name, buf, ARRAY_SIZE(buf), &source, false) == 0)
ffStrbufInitS(&item->name, buf);
else
ffStrbufInitStatic(&item->name, "unknown");
if (data->ffzpool_get_prop(zpool, data->props.health, buf, ARRAY_SIZE(buf), &source, false) == 0)
ffStrbufInitS(&item->state, buf);
else
ffStrbufInitStatic(&item->state, "unknown");
item->guid = data->ffzpool_get_prop_int(zpool, data->props.guid, &source);
item->total = data->ffzpool_get_prop_int(zpool, data->props.size, &source);
item->used = item->total - data->ffzpool_get_prop_int(zpool, data->props.free, &source);
item->allocated = data->ffzpool_get_prop_int(zpool, data->props.allocated, &source);
uint64_t fragmentation = data->ffzpool_get_prop_int(zpool, data->props.fragmentation, &source);
item->fragmentation = fragmentation == UINT64_MAX ? -DBL_MAX : (double) fragmentation;
item->readOnly = (bool) data->ffzpool_get_prop_int(zpool, data->props.readonly, &source);
data->ffzpool_close(zpool);
return 0;
}
const char* ffDetectZpool(FFlist* result /* list of FFZpoolResult */)
{
FF_LIBRARY_LOAD(libzfs, "dlopen libzfs" FF_LIBRARY_EXTENSION " failed", "libzfs" FF_LIBRARY_EXTENSION, 6);
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, libzfs_init);
libzfs_handle_t* handle = fflibzfs_init();
if (!handle)
{
if (!ffKmodLoaded("zfs")) return "`zfs` kernel module is not loaded";
return "libzfs_init() failed";
}
__attribute__((__cleanup__(cleanLibzfs))) FFZfsData data = {
.handle = handle,
.result = result,
};
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, zpool_name_to_prop);
#define FF_QUERY_ZPOOL_PROP_FROM_NAME(prop_name) do { \
data.props.prop_name = ffzpool_name_to_prop(#prop_name); \
if (data.props.prop_name < 0) \
return "Failed to query prop: " #prop_name; \
} while (false)
FF_QUERY_ZPOOL_PROP_FROM_NAME(name);
FF_QUERY_ZPOOL_PROP_FROM_NAME(health);
FF_QUERY_ZPOOL_PROP_FROM_NAME(guid);
FF_QUERY_ZPOOL_PROP_FROM_NAME(size);
FF_QUERY_ZPOOL_PROP_FROM_NAME(free);
FF_QUERY_ZPOOL_PROP_FROM_NAME(allocated);
FF_QUERY_ZPOOL_PROP_FROM_NAME(fragmentation);
FF_QUERY_ZPOOL_PROP_FROM_NAME(readonly);
#undef FF_QUERY_ZPOOL_PROP_FROM_NAME
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, zpool_iter);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, libzfs_fini);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_prop_int);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_prop);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_close);
if (ffzpool_iter(handle, enumZpoolCallback, &data) < 0)
return "zpool_iter() failed";
return NULL;
}
#else
const char* ffDetectZpool(FF_MAYBE_UNUSED FFlist* result)
{
return "fastfetch was compiled without libzfs support";
}
#endif
+3 -1
View File
@@ -7,10 +7,12 @@ typedef struct FFZpoolResult
{
FFstrbuf name;
FFstrbuf state;
uint64_t guid;
uint64_t used;
uint64_t total;
uint64_t version;
uint64_t allocated;
double fragmentation;
bool readOnly;
} FFZpoolResult;
const char* ffDetectZpool(FFlist* result /* list of FFZpoolResult */);
-117
View File
@@ -1,117 +0,0 @@
#include "zpool.h"
#ifdef FF_HAVE_LIBZFS
#include "util/kmod.h"
#ifdef __sun
#define FF_DISABLE_DLOPEN
#include <libzfs.h>
const char* zpool_get_state_str(zpool_handle_t* zpool)
{
if (zpool_get_state(zpool) == POOL_STATE_UNAVAIL)
return "FAULTED";
else
{
const char *str;
zpool_errata_t errata;
zpool_status_t status = zpool_get_status(zpool, (char**) &str, &errata);
if (status == ZPOOL_STATUS_IO_FAILURE_WAIT ||
status == ZPOOL_STATUS_IO_FAILURE_CONTINUE ||
status == ZPOOL_STATUS_IO_FAILURE_MMP)
return "SUSPENDED";
else
{
nvlist_t *nvroot = fnvlist_lookup_nvlist(zpool_get_config(zpool, NULL), ZPOOL_CONFIG_VDEV_TREE);
uint_t vsc;
vdev_stat_t *vs;
#ifdef __x86_64__
if (nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS, (uint64_t**) &vs, &vsc) != 0)
#else
if (nvlist_lookup_uint32_array(nvroot, ZPOOL_CONFIG_VDEV_STATS, (uint32_t**) &vs, &vsc) != 0)
#endif
return "UNKNOWN";
else
return zpool_state_to_name(vs->vs_state, vs->vs_aux);
}
}
}
#else
#include "libzfs_simplified.h"
#endif
#include "common/library.h"
typedef struct FFZfsData
{
FF_LIBRARY_SYMBOL(libzfs_fini)
FF_LIBRARY_SYMBOL(zpool_iter)
FF_LIBRARY_SYMBOL(zpool_get_prop_int)
FF_LIBRARY_SYMBOL(zpool_get_name)
FF_LIBRARY_SYMBOL(zpool_get_state_str)
libzfs_handle_t* handle;
FFlist* result;
} FFZfsData;
static inline void cleanLibzfs(FFZfsData* data)
{
if (data->fflibzfs_fini && data->handle)
{
data->fflibzfs_fini(data->handle);
data->handle = NULL;
}
}
static int enumZpoolCallback(zpool_handle_t* zpool, void* param)
{
FFZfsData* data = (FFZfsData*) param;
zprop_source_t source;
FFZpoolResult* item = ffListAdd(data->result);
ffStrbufInitS(&item->name, data->ffzpool_get_name(zpool));
ffStrbufInitS(&item->state, data->ffzpool_get_state_str(zpool));
item->version = data->ffzpool_get_prop_int(zpool, ZPOOL_PROP_VERSION, &source);
item->total = data->ffzpool_get_prop_int(zpool, ZPOOL_PROP_SIZE, &source);
item->used = item->total - data->ffzpool_get_prop_int(zpool, ZPOOL_PROP_FREE, &source);
uint64_t fragmentation = data->ffzpool_get_prop_int(zpool, ZPOOL_PROP_FRAGMENTATION, &source);
item->fragmentation = fragmentation == UINT64_MAX ? -DBL_MAX : (double) fragmentation;
return 0;
}
const char* ffDetectZpool(FFlist* result /* list of FFZpoolResult */)
{
FF_LIBRARY_LOAD(libzfs, "dlopen libzfs" FF_LIBRARY_EXTENSION " failed", "libzfs" FF_LIBRARY_EXTENSION, 6);
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, libzfs_init);
libzfs_handle_t* handle = fflibzfs_init();
if (!handle)
{
if (!ffKmodLoaded("zfs")) return "`zfs` kernel module is not loaded";
return "libzfs_init() failed";
}
__attribute__((__cleanup__(cleanLibzfs))) FFZfsData data = {
.handle = handle,
.result = result,
};
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, zpool_iter);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, libzfs_fini);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_prop_int);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_name);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_state_str);
if (ffzpool_iter(handle, enumZpoolCallback, &data) < 0)
return "zpool_iter() failed";
return NULL;
}
#else
const char* ffDetectZpool(FF_MAYBE_UNUSED FFlist* result)
{
return "Fastfetch was compiled without libzfs support";
}
#endif
-6
View File
@@ -1,6 +0,0 @@
#include "zpool.h"
const char* ffDetectZpool(FF_MAYBE_UNUSED FFlist* result /* list of FFZpoolResult */)
{
return "Not supported on this platform";
}
+16 -3
View File
@@ -14,7 +14,7 @@
#include <ctype.h>
#include <string.h>
#ifdef WIN32
#ifdef _WIN32
#include "util/windows/getline.h"
#endif
@@ -502,7 +502,20 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
if (parseJsoncFile(absolutePath.chars, flag)) return;
//Try to load as a relative path
//Try to load as a relative path with the config directory
FF_LIST_FOR_EACH(FFstrbuf, path, instance.state.platform.configDirs)
{
ffStrbufSet(&absolutePath, path);
ffStrbufAppendS(&absolutePath, "fastfetch/");
ffStrbufAppendS(&absolutePath, value);
if (needExtension)
ffStrbufAppendS(&absolutePath, ".jsonc");
if (parseJsoncFile(absolutePath.chars, flag)) return;
}
//Try to load as a preset
FF_LIST_FOR_EACH(FFstrbuf, path, instance.state.platform.dataDirs)
{
@@ -515,7 +528,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
if (parseJsoncFile(absolutePath.chars, flag)) return;
}
//Try to load as a relative path with the directory of fastfetch binary
//Try to load as a relative path with the directory of fastfetch binary, for Windows support
if (instance.state.platform.exePath.length)
{
+13
View File
@@ -0,0 +1,13 @@
----.---
:-==.-==-
$2 -=== ===:
===-:===.
$3 -+++:=++=
=+++ +++.
$6 .+++-=++=
$4.::. ::: $6 :**+ +**-
$4 :--:.---. $6 .+**=-***.
$5 :--- ---::$6***:***+
$5 .---::-$6=##*.*##-
$7 ----+$6##=:##*.
$7 :-$6###-*##+
+12 -12
View File
@@ -1,12 +1,12 @@
^"^"^". `"^"""`
";;;;;:. `;;;;;;.
^;":;:;: .;,,;:;:
.'`",, '":::. ^;'';;;;, .:: ,;;;:
';;;;:":::::' ^;` `;;;;" ::. ,;;;:
";:;:. ^;` ^;;;;^ ,;. ,;;;:
";;;: ^;` ";;;;^ ";' ,;;;:
";;;: ^;` ,;;;;`^;` ,;;;:
";;;: ^;` ,;;;;;^ ,;;;:
";:;: ^;` .:;:;" ,;:;:
,;;;;. ";" .:;, :;;;;.
."""""` ^"^ '" ."""""`
#: :+#@@@@%#
@@#: .*@@@@@@@@@
@@@@#- .@@@@@@@@@@@
@@@@@@%- %@@@@@@@@@@@
@@@@@@@@%= :@@@@%%%%@@@@
@@@@@@@@@@%==*-: .:=#
:*@@@@@@@@@@=
:*@@@@@@@@=
.*@@@@@@=
.+@@@@=
.+%@=
+=
+17
View File
@@ -0,0 +1,17 @@
.-==: . .
-+:::+= * *- = -. -%*
** *# :%- . :.*=: -%%
%@: -@% =%@%%*: - :. -%=
-@@: :@@- -+#%%+ .: :*.
-@@: :@@-- -% = .*
.%@- -%% ++ # . -: .
#* ** =.:*: +- = #%%%%#+-*:
-=====- .:- ..
$2 =*+ -+*:
.*+ :*+= -+*++**-
+*-++++ -++-:. .:-=++.=*+. =*-
-***:-+:*++=*++*: +*+=:-**+=**++-
:- :***==*+.=++ +*+=++*=.=*+.
.=-=*+***+*= +*+ :**+=++
. -*+ +*+ .=:
+24 -1
View File
@@ -39,7 +39,7 @@ static const FFlogo A[] = {
FF_COLOR_FG_256 "36",
},
},
// Aeon
// AerynOS
{
.names = {"AerynOS"},
.lines = FASTFETCH_DATATEXT_LOGO_AERYNOS,
@@ -181,6 +181,20 @@ static const FFlogo A[] = {
FF_COLOR_FG_256 "178",
}
},
// Amiga
{
.names = {"Amiga"},
.lines = FASTFETCH_DATATEXT_LOGO_AMIGA,
.colors = {
FF_COLOR_FG_RED,
FF_COLOR_FG_LIGHT_RED,
FF_COLOR_FG_YELLOW,
FF_COLOR_FG_BLUE,
FF_COLOR_FG_CYAN,
FF_COLOR_FG_LIGHT_YELLOW,
FF_COLOR_FG_GREEN,
}
},
// AmogOS
{
.names = {"AmogOS"},
@@ -3686,6 +3700,15 @@ static const FFlogo O[] = {
FF_COLOR_FG_DEFAULT,
},
},
// OS/2 Warp
{
.names = {"OS2Warp"},
.lines = FASTFETCH_DATATEXT_LOGO_OS2WARP,
.colors = {
FF_COLOR_FG_LIGHT_WHITE,
FF_COLOR_FG_LIGHT_BLUE,
}
},
// OS_Elbrus
{
.names = {"OS Elbrus"},
+2 -2
View File
@@ -891,13 +891,13 @@ static bool printCachedPixel(FFLogoRequestData* requestData)
if(requestData->type == FF_LOGO_TYPE_IMAGE_KITTY)
{
fd = getCacheFD(requestData, FF_CACHE_FILE_KITTY_COMPRESSED);
if(fd == FF_INVALID_FD)
if(!ffIsValidNativeFD(fd))
fd = getCacheFD(requestData, FF_CACHE_FILE_KITTY_UNCOMPRESSED);
}
else if(requestData->type == FF_LOGO_TYPE_IMAGE_SIXEL)
fd = getCacheFD(requestData, FF_CACHE_FILE_SIXEL);
if(fd == FF_INVALID_FD)
if(!ffIsValidNativeFD(fd))
return false;
ffPrintCharTimes('\n', options->paddingTop);
+8 -7
View File
@@ -733,22 +733,23 @@ void ffLogoPrintRemaining(void)
void ffLogoBuiltinPrint(void)
{
FFOptionsLogo* options = &instance.config.logo;
options->position = FF_LOGO_POSITION_TOP;
options->paddingRight = 2; // empty line after logo printing
FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate();
for(uint8_t ch = 0; ch < 26; ++ch)
{
for(const FFlogo* logo = ffLogoBuiltins[ch]; *logo->names; ++logo)
{
printf("\033[%sm%s:\033[0m\n", logo->colors[0], logo->names[0]);
if (instance.config.display.pipe)
ffStrbufSetF(&buf, "%s:\n", logo->names[0]);
else
ffStrbufSetF(&buf, "\e[%sm%s:\e[0m\n", logo->colors[0], logo->names[0]);
ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf);
logoPrintStruct(logo);
ffLogoPrintRemaining();
//reset everything
instance.state.logoHeight = 0;
instance.state.keysHeight = 0;
for(uint8_t i = 0; i < FASTFETCH_LOGO_MAX_COLORS; i++)
ffStrbufClear(&options->colors[i]);
putchar('\n');
}
}
}
+47 -7
View File
@@ -20,16 +20,39 @@ bool ffPrintCommand(FFCommandOptions* options)
return false;
}
if (options->moduleArgs.outputFormat.length == 0)
if (options->splitLines)
{
ffPrintLogoAndKey(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
ffStrbufPutTo(&result, stdout);
uint8_t index = 0;
char* line = NULL;
size_t len = 0;
while (ffStrbufGetline(&line, &len, &result))
{
if (options->moduleArgs.outputFormat.length == 0)
{
ffPrintLogoAndKey(FF_COMMAND_MODULE_NAME, ++index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
puts(line);
}
else
{
FF_PRINT_FORMAT_CHECKED(FF_COMMAND_MODULE_NAME, ++index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){
FF_FORMAT_ARG(line, "result")
}));
}
}
}
else
{
FF_PRINT_FORMAT_CHECKED(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){
FF_FORMAT_ARG(result, "result")
}));
if (options->moduleArgs.outputFormat.length == 0)
{
ffPrintLogoAndKey(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
ffStrbufPutTo(&result, stdout);
}
else
{
FF_PRINT_FORMAT_CHECKED(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){
FF_FORMAT_ARG(result, "result")
}));
}
}
return true;
@@ -74,6 +97,12 @@ void ffParseCommandJsonObject(FFCommandOptions* options, yyjson_val* module)
continue;
}
if (unsafe_yyjson_equals_str(key, "splitLines"))
{
options->splitLines = yyjson_get_bool(val);
continue;
}
ffPrintError(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
}
}
@@ -87,6 +116,7 @@ void ffGenerateCommandJsonConfig(FFCommandOptions* options, yyjson_mut_doc* doc,
yyjson_mut_obj_add_strbuf(doc, module, "text", &options->text);
yyjson_mut_obj_add_bool(doc, module, "useStdErr", options->useStdErr);
yyjson_mut_obj_add_bool(doc, module, "parallel", options->parallel);
yyjson_mut_obj_add_bool(doc, module, "splitLines", options->splitLines);
}
bool ffGenerateCommandJsonResult(FF_MAYBE_UNUSED FFCommandOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module)
@@ -106,7 +136,16 @@ bool ffGenerateCommandJsonResult(FF_MAYBE_UNUSED FFCommandOptions* options, yyjs
return false;
}
yyjson_mut_obj_add_strbuf(doc, module, "result", &result);
if (options->splitLines)
{
yyjson_mut_val* jsonArray = yyjson_mut_obj_add_arr(doc, module, "result");
char* line = NULL;
size_t len = 0;
while (ffStrbufGetline(&line, &len, &result))
yyjson_mut_arr_add_strncpy(doc, jsonArray, line, len);
}
else
yyjson_mut_obj_add_strbuf(doc, module, "result", &result);
return true;
}
@@ -132,6 +171,7 @@ void ffInitCommandOptions(FFCommandOptions* options)
ffStrbufInit(&options->text);
options->useStdErr = false;
options->parallel = true;
options->splitLines = false;
}
void ffDestroyCommandOptions(FFCommandOptions* options)
+1
View File
@@ -11,6 +11,7 @@ typedef struct FFCommandOptions
FFstrbuf text;
bool useStdErr;
bool parallel;
bool splitLines;
} FFCommandOptions;
static_assert(sizeof(FFCommandOptions) <= FF_OPTION_MAX_SIZE, "FFCommandOptions size exceeds maximum allowed size");
+8 -2
View File
@@ -5,7 +5,6 @@
#include "common/frequency.h"
#include "detection/cpu/cpu.h"
#include "modules/cpu/cpu.h"
#include "util/stringUtils.h"
static int sortCores(const FFCPUCore* a, const FFCPUCore* b)
{
@@ -111,6 +110,7 @@ bool ffPrintCPU(FFCPUOptions* options)
FF_FORMAT_ARG(coreTypes, "core-types"),
FF_FORMAT_ARG(cpu.packages, "packages"),
FF_FORMAT_ARG(cpu.march, "march"),
FF_FORMAT_ARG(cpu.numaNodes, "numa-nodes"),
}));
}
success = true;
@@ -217,6 +217,11 @@ bool ffGenerateCPUJsonResult(FFCPUOptions* options, yyjson_mut_doc* doc, yyjson_
else
yyjson_mut_obj_add_null(doc, obj, "march");
if (cpu.numaNodes > 0)
yyjson_mut_obj_add_uint(doc, obj, "numaNodes", cpu.numaNodes);
else
yyjson_mut_obj_add_null(doc, obj, "numaNodes");
success = true;
}
@@ -259,6 +264,7 @@ FFModuleBaseInfo ffCPUModuleInfo = {
{"Temperature (formatted)", "temperature"},
{"Logical core count grouped by frequency", "core-types"},
{"Processor package count", "packages"},
{"X86-64 CPU microarchitecture", "march"},
{"CPU microarchitecture", "march"},
{"NUMA node count", "numa-nodes"},
}))
};
+2 -2
View File
@@ -28,7 +28,7 @@ bool ffPrintCPUUsage(FFCPUUsageOptions* options)
{
sumValue += *percent;
#if WIN32
#if _WIN32
// Windows may return values greater than 100%, cap them to 100%
if (*percent > 100) *percent = 100;
#endif
@@ -48,7 +48,7 @@ bool ffPrintCPUUsage(FFCPUUsageOptions* options)
++index;
}
double avgValue = sumValue / (double) valueCount;
#if WIN32
#if _WIN32
// See above comment
if (avgValue > 100) avgValue = 100;
#endif
+1
View File
@@ -36,6 +36,7 @@ typedef enum __attribute__((__packed__)) FFPackagesFlags
FF_PACKAGES_FLAG_HPKG_BIT = 1ULL << 28,
FF_PACKAGES_FLAG_PISI_BIT = 1ULL << 29,
FF_PACKAGES_FLAG_SOAR_BIT = 1ULL << 30,
FF_PACKAGES_FLAG_KISS_BIT = 1ULL << 31,
FF_PACKAGES_FLAG_FORCE_UNSIGNED = UINT64_MAX,
} FFPackagesFlags;
static_assert(sizeof(FFPackagesFlags) == sizeof(uint64_t), "");
+8
View File
@@ -76,6 +76,7 @@ bool ffPrintPackages(FFPackagesOptions* options)
FF_PRINT_PACKAGE(apk)
FF_PRINT_PACKAGE(pkg)
FF_PRINT_PACKAGE(pkgsrc)
FF_PRINT_PACKAGE(kiss)
if (options->combined)
{
FF_PRINT_PACKAGE_ALL(hpkg)
@@ -194,6 +195,7 @@ bool ffPrintPackages(FFPackagesOptions* options)
FF_FORMAT_ARG(counts.hpkgUser, "hpkg-user"),
FF_FORMAT_ARG(counts.pisi, "pisi"),
FF_FORMAT_ARG(counts.soar, "soar"),
FF_FORMAT_ARG(counts.kiss, "kiss"),
FF_FORMAT_ARG(nixAll, "nix-all"),
FF_FORMAT_ARG(flatpakAll, "flatpak-all"),
FF_FORMAT_ARG(brewAll, "brew-all"),
@@ -268,6 +270,9 @@ void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module)
case 'H': if (false);
FF_TEST_PACKAGE_NAME(HPKG)
break;
case 'K': if (false);
FF_TEST_PACKAGE_NAME(KISS)
break;
case 'L': if (false);
FF_TEST_PACKAGE_NAME(LPKG)
FF_TEST_PACKAGE_NAME(LPKGBUILD)
@@ -346,6 +351,7 @@ void ffGeneratePackagesJsonConfig(FFPackagesOptions* options, yyjson_mut_doc* do
FF_TEST_PACKAGE_NAME(FLATPAK)
FF_TEST_PACKAGE_NAME(GUIX)
FF_TEST_PACKAGE_NAME(HPKG)
FF_TEST_PACKAGE_NAME(KISS)
FF_TEST_PACKAGE_NAME(LINGLONG)
FF_TEST_PACKAGE_NAME(LPKG)
FF_TEST_PACKAGE_NAME(LPKGBUILD)
@@ -425,6 +431,7 @@ bool ffGeneratePackagesJsonResult(FF_MAYBE_UNUSED FFPackagesOptions* options, yy
FF_APPEND_PACKAGE_COUNT(scoopGlobal)
FF_APPEND_PACKAGE_COUNT(snap)
FF_APPEND_PACKAGE_COUNT(soar)
FF_APPEND_PACKAGE_COUNT(kiss)
FF_APPEND_PACKAGE_COUNT(sorcery)
FF_APPEND_PACKAGE_COUNT(winget)
FF_APPEND_PACKAGE_COUNT(xbps)
@@ -498,6 +505,7 @@ FFModuleBaseInfo ffPackagesModuleInfo = {
{"Number of hpkg-user packages", "hpkg-user"},
{"Number of pisi packages", "pisi"},
{"Number of soar packages", "soar"},
{"Number of kiss packages", "kiss"},
{"Total number of all nix packages", "nix-all"},
{"Total number of all flatpak app packages", "flatpak-all"},
{"Total number of all brew packages", "brew-all"},
+39 -11
View File
@@ -4,6 +4,7 @@
#include "common/size.h"
#include "detection/zpool/zpool.h"
#include "modules/zpool/zpool.h"
#include "util/FFstrbuf.h"
#include "util/stringUtils.h"
static void printZpool(FFZpoolOptions* options, FFZpoolResult* result, uint8_t index)
@@ -22,6 +23,7 @@ static void printZpool(FFZpoolOptions* options, FFZpoolResult* result, uint8_t i
FF_PARSE_FORMAT_STRING_CHECKED(&buffer, &options->moduleArgs.key, ((FFformatarg[]) {
FF_FORMAT_ARG(index, "index"),
FF_FORMAT_ARG(result->name, "name"),
FF_FORMAT_ARG(result->guid, "guid"),
FF_FORMAT_ARG(options->moduleArgs.keyIcon, "icon"),
}));
}
@@ -29,10 +31,14 @@ static void printZpool(FFZpoolOptions* options, FFZpoolResult* result, uint8_t i
FF_STRBUF_AUTO_DESTROY usedPretty = ffStrbufCreate();
ffSizeAppendNum(result->used, &usedPretty);
FF_STRBUF_AUTO_DESTROY allocatedPretty = ffStrbufCreate();
ffSizeAppendNum(result->allocated, &allocatedPretty);
FF_STRBUF_AUTO_DESTROY totalPretty = ffStrbufCreate();
ffSizeAppendNum(result->total, &totalPretty);
double bytesPercentage = result->total > 0 ? (double) result->used / (double) result->total * 100.0 : 0;
double usedPercentage = result->total > 0 ? (double) result->used / (double) result->total * 100.0 : 0;
double allocatedPercentage = result->total > 0 ? (double) result->allocated / (double) result->total * 100.0 : 0;
FFPercentageTypeFlags percentType = options->percent.type == 0 ? instance.config.display.percentType : options->percent.type;
if(options->moduleArgs.outputFormat.length == 0)
@@ -41,20 +47,31 @@ static void printZpool(FFZpoolOptions* options, FFZpoolResult* result, uint8_t i
ffStrbufClear(&buffer);
ffStrbufSetF(&buffer, "%s / %s (", usedPretty.chars, totalPretty.chars);
ffPercentAppendNum(&buffer, bytesPercentage, options->percent, false, &options->moduleArgs);
ffPercentAppendNum(&buffer, usedPercentage, options->percent, false, &options->moduleArgs);
ffStrbufAppendS(&buffer, ", ");
ffPercentAppendNum(&buffer, allocatedPercentage, options->percent, false, &options->moduleArgs);
ffStrbufAppendS(&buffer, " allocated, ");
ffPercentAppendNum(&buffer, result->fragmentation, options->percent, false, &options->moduleArgs);
ffStrbufAppendF(&buffer, " frag) - %s", result->state.chars);
if (result->readOnly)
ffStrbufAppendS(&buffer, " [Read-only]");
ffStrbufPutTo(&buffer, stdout);
}
else
{
FF_STRBUF_AUTO_DESTROY bytesPercentageNum = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY usedPercentageNum = ffStrbufCreate();
if (percentType & FF_PERCENTAGE_TYPE_NUM_BIT)
ffPercentAppendNum(&bytesPercentageNum, bytesPercentage, options->percent, false, &options->moduleArgs);
FF_STRBUF_AUTO_DESTROY bytesPercentageBar = ffStrbufCreate();
ffPercentAppendNum(&usedPercentageNum, usedPercentage, options->percent, false, &options->moduleArgs);
FF_STRBUF_AUTO_DESTROY usedPercentageBar = ffStrbufCreate();
if (percentType & FF_PERCENTAGE_TYPE_BAR_BIT)
ffPercentAppendBar(&bytesPercentageBar, bytesPercentage, options->percent, &options->moduleArgs);
ffPercentAppendBar(&usedPercentageBar, usedPercentage, options->percent, &options->moduleArgs);
FF_STRBUF_AUTO_DESTROY allocatedPercentageNum = ffStrbufCreate();
if (percentType & FF_PERCENTAGE_TYPE_NUM_BIT)
ffPercentAppendNum(&allocatedPercentageNum, allocatedPercentage, options->percent, false, &options->moduleArgs);
FF_STRBUF_AUTO_DESTROY allocatedPercentageBar = ffStrbufCreate();
if (percentType & FF_PERCENTAGE_TYPE_BAR_BIT)
ffPercentAppendBar(&allocatedPercentageBar, allocatedPercentage, options->percent, &options->moduleArgs);
FF_STRBUF_AUTO_DESTROY fragPercentageNum = ffStrbufCreate();
if (percentType & FF_PERCENTAGE_TYPE_NUM_BIT)
@@ -65,13 +82,18 @@ static void printZpool(FFZpoolOptions* options, FFZpoolResult* result, uint8_t i
FF_PRINT_FORMAT_CHECKED(buffer.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]) {
FF_FORMAT_ARG(result->name, "name"),
FF_FORMAT_ARG(result->guid, "guid"),
FF_FORMAT_ARG(result->state, "state"),
FF_FORMAT_ARG(usedPretty, "size-used"),
FF_FORMAT_ARG(allocatedPretty, "size-allocated"),
FF_FORMAT_ARG(totalPretty, "size-total"),
FF_FORMAT_ARG(bytesPercentageNum, "size-percentage"),
FF_FORMAT_ARG(usedPercentageNum, "used-percentage"),
FF_FORMAT_ARG(allocatedPercentageNum, "allocated-percentage"),
FF_FORMAT_ARG(fragPercentageNum, "frag-percentage"),
FF_FORMAT_ARG(bytesPercentageBar, "size-percentage-bar"),
FF_FORMAT_ARG(usedPercentageBar, "used-percentage-bar"),
FF_FORMAT_ARG(allocatedPercentageBar, "allocated-percentage-bar"),
FF_FORMAT_ARG(fragPercentageBar, "frag-percentage-bar"),
FF_FORMAT_ARG(result->readOnly, "is-readonly"),
}));
}
}
@@ -149,13 +171,15 @@ bool ffGenerateZpoolJsonResult(FF_MAYBE_UNUSED FFZpoolOptions* options, yyjson_m
yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr);
yyjson_mut_obj_add_strbuf(doc, obj, "name", &zpool->name);
yyjson_mut_obj_add_strbuf(doc, obj, "state", &zpool->state);
yyjson_mut_obj_add_uint(doc, obj, "guid", zpool->guid);
yyjson_mut_obj_add_uint(doc, obj, "used", zpool->used);
yyjson_mut_obj_add_uint(doc, obj, "allocated", zpool->allocated);
yyjson_mut_obj_add_uint(doc, obj, "total", zpool->total);
yyjson_mut_obj_add_uint(doc, obj, "version", zpool->version);
if (zpool->fragmentation != -DBL_MAX)
yyjson_mut_obj_add_real(doc, obj, "fragmentation", zpool->fragmentation);
else
yyjson_mut_obj_add_null(doc, obj, "fragmentation");
yyjson_mut_obj_add_bool(doc, obj, "readOnly", zpool->readOnly);
}
FF_LIST_FOR_EACH(FFZpoolResult, zpool, results)
@@ -188,12 +212,16 @@ FFModuleBaseInfo ffZpoolModuleInfo = {
.generateJsonConfig = (void*) ffGenerateZpoolJsonConfig,
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
{"Zpool name", "name"},
{"Zpool guid", "guid"},
{"Zpool state", "state"},
{"Size used", "used"},
{"Size allocated", "allocated"},
{"Size total", "total"},
{"Size percentage num", "used-percentage"},
{"Size used percentage num", "used-percentage"},
{"Size allocated percentage num", "allocated-percentage"},
{"Fragmentation percentage num", "fragmentation-percentage"},
{"Size percentage bar", "used-percentage-bar"},
{"Size used percentage bar", "used-percentage-bar"},
{"Size allocated percentage bar", "allocated-percentage-bar"},
{"Fragmentation percentage bar", "fragmentation-percentage-bar"},
}))
};
+5 -19
View File
@@ -648,19 +648,7 @@ void ffStrbufInsertNC(FFstrbuf* strbuf, uint32_t index, uint32_t num, char c)
strbuf->length += num;
}
/**
* @brief Read a line from a FFstrbuf.
*
* @details Behaves like getline(3) but reads from a FFstrbuf.
*
* @param[in,out] lineptr The pointer to a pointer that will be set to the start of the line.
* Can be NULL for the first call.
* @param[in,out] n The pointer to the size of the buffer of lineptr.
* @param[in] buffer The buffer to read from. The buffer must not be a string literal.
*
* @return true if a line has been read, false if the end of the buffer has been reached.
*/
bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer)
bool ffStrbufGetdelim(char** lineptr, size_t* n, char delimiter, FFstrbuf* buffer)
{
assert(lineptr && n && buffer);
assert(buffer->allocated > 0 || (buffer->allocated == 0 && buffer->length == 0));
@@ -674,14 +662,14 @@ bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer)
*lineptr += *n;
if (*lineptr >= pBufferEnd) // non-empty last line
return false;
**lineptr = '\n';
**lineptr = delimiter;
++*lineptr;
}
if (*lineptr >= pBufferEnd) // empty last line
return false;
size_t remaining = (size_t) (pBufferEnd - *lineptr);
char* ending = memchr(*lineptr, '\n', remaining);
char* ending = memchr(*lineptr, delimiter, remaining);
if (ending)
{
*n = (size_t) (ending - *lineptr);
@@ -692,9 +680,7 @@ bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer)
return true;
}
/// @brief Restore the end of a line that was modified by ffStrbufGetline.
/// @warning This function should be called before breaking an ffStrbufGetline loop.
void ffStrbufGetlineRestore(char** lineptr, size_t* n, FFstrbuf* buffer)
void ffStrbufGetdelimRestore(char** lineptr, size_t* n, char delimiter, FFstrbuf* buffer)
{
assert(buffer && lineptr && n);
assert(buffer->allocated > 0 || (buffer->allocated == 0 && buffer->length == 0));
@@ -705,7 +691,7 @@ void ffStrbufGetlineRestore(char** lineptr, size_t* n, FFstrbuf* buffer)
*lineptr += *n;
if (*lineptr < buffer->chars + buffer->length)
**lineptr = '\n';
**lineptr = delimiter;
}
bool ffStrbufRemoveDupWhitespaces(FFstrbuf* strbuf)
+27 -2
View File
@@ -96,8 +96,33 @@ FF_C_NODISCARD uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultV
void ffStrbufUpperCase(FFstrbuf* strbuf);
void ffStrbufLowerCase(FFstrbuf* strbuf);
bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer);
void ffStrbufGetlineRestore(char** lineptr, size_t* n, FFstrbuf* buffer);
bool ffStrbufGetdelim(char** lineptr, size_t* n, char delimiter, FFstrbuf* buffer);
void ffStrbufGetdelimRestore(char** lineptr, size_t* n, char delimiter, FFstrbuf* buffer);
/**
* @brief Read a line from a FFstrbuf.
*
* @details Behaves like getline(3) but reads from a FFstrbuf.
*
* @param[in,out] lineptr The pointer to a pointer that will be set to the start of the line.
* Can be NULL for the first call.
* @param[in,out] n The pointer to the size of the buffer of lineptr.
* @param[in] buffer The buffer to read from. The buffer must not be a string literal.
*
* @return true if a line has been read, false if the end of the buffer has been reached.
*/
static inline bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer)
{
return ffStrbufGetdelim(lineptr, n, '\n', buffer);
}
/**
* @brief Restore the end of a line that was modified by ffStrbufGetline.
* @warning This function should be called before breaking an ffStrbufGetline loop.
*/
static inline void ffStrbufGetlineRestore(char** lineptr, size_t* n, FFstrbuf* buffer)
{
ffStrbufGetdelimRestore(lineptr, n, '\n', buffer);
}
bool ffStrbufRemoveDupWhitespaces(FFstrbuf* strbuf);
bool ffStrbufMatchSeparatedNS(const FFstrbuf* strbuf, uint32_t compLength, const char* comp, char separator);
bool ffStrbufMatchSeparatedIgnCaseNS(const FFstrbuf* strbuf, uint32_t compLength, const char* comp, char separator);
-10
View File
@@ -64,13 +64,3 @@ const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
return NULL;
}
#endif
bool ffIsAbsolutePath(const char* path)
{
#ifdef _WIN32
return (ffCharIsEnglishAlphabet(path[0]) && path[1] == ':' && (path[2] == '\\' || path[2] == '/')) // drive letter path
|| (path[0] == '\\' && path[1] == '\\'); // UNC path
#else
return path[0] == '/';
#endif
}
+11 -2
View File
@@ -1,6 +1,15 @@
#pragma once
#include "fastfetch.h"
#include "util/FFstrbuf.h"
#include "util/stringUtils.h"
const char* ffFindExecutableInPath(const char* name, FFstrbuf* result);
bool ffIsAbsolutePath(const char* path);
static inline bool ffIsAbsolutePath(const char* path)
{
#ifdef _WIN32
return (ffCharIsEnglishAlphabet(path[0]) && path[1] == ':' && (path[2] == '\\' || path[2] == '/')) // drive letter path
|| (path[0] == '\\' && path[1] == '\\'); // UNC path
#else
return path[0] == '/';
#endif
}