diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3030dab6da..cdaff52751 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -388,8 +388,8 @@ jobs: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On . cmake --build . --target package --verbose -j4 ./fastfetch --list-features - time ./fastfetch - time ./fastfetch --format json + time ./fastfetch -c presets/ci.jsonc --stat false + time ./fastfetch -c presets/ci.jsonc --format json time ./flashfetch ldd fastfetch ctest @@ -426,8 +426,8 @@ jobs: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On . cmake --build . --target package --verbose -j4 ./fastfetch --list-features - time ./fastfetch - time ./fastfetch --format json + time ./fastfetch -c presets/ci.jsonc --stat false + time ./fastfetch -c presets/ci.jsonc --format json time ./flashfetch ldd fastfetch ctest diff --git a/CHANGELOG.md b/CHANGELOG.md index e57c6d662d..358c99164d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,31 @@ +# 2.23.0 + +Features: +* Support unity version detection (DE, Linux) +* Print model name in Battery keys if available (Battery) +* Add module `Zpool` +* Improve performance (Shell / Terminal, Linux) +* Support syntax of padded strings in `---format`. `{variablepadlength}` are supported. + * If pad length is greater than the length of the variable, the variable will be padded with spaces. + * `fastfetch -l none -s command --command-text 'echo 12345' --command-format 'output({1<20})'` prints `Command: output(12345 )` + * `fastfetch -l none -s command --command-text 'echo 12345' --command-format 'output({1>20})'` prints `Command: output( 12345)` + * If pad length is less than the length of the variable, the variable will be truncated. + +Bugfixes: +* Fix broken `--list-presets` +* Update zsh completion script +* Don't print `*` if `defaultRouteOnly` is set (NetIO) +* Fix Camera module incorrectly disabled on FreeBSD (Camera, FreeBSD) +* Fix hanging on screen 5.0 (Terminal) +* Improve image logo support on Windows (Logo, Windows) + +Logos: +* Update AmogOS +* Add Magix +* Make ubuntu logo colorable +* Add Steam Deck Logo +* add Huawei Cloud EulerOS + # 2.22.0 Features: diff --git a/CMakeLists.txt b/CMakeLists.txt index 1abe943d22..712250b8cb 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.22.0 + VERSION 2.23.0 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" @@ -71,6 +71,7 @@ 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 FreeBSD OR ANDROID" 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) option(ENABLE_SYSTEM_YYJSON "Use system provided (instead of fastfetch embedded) yyjson library" OFF) option(ENABLE_ASAN "Build fastfetch with ASAN (address sanitizer)" OFF) @@ -392,6 +393,7 @@ set(LIBFASTFETCH_SRC src/modules/wifi/wifi.c src/modules/wm/wm.c src/modules/wmtheme/wmtheme.c + src/modules/zpool/zpool.c src/options/display.c src/options/modules.c src/options/logo.c @@ -481,6 +483,7 @@ 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 ) @@ -543,6 +546,7 @@ elseif(ANDROID) src/detection/de/de_nosupport.c src/detection/wmtheme/wmtheme_nosupport.c src/detection/camera/camera_android.c + src/detection/zpool/zpool_nosupport.c src/util/platform/FFPlatform_unix.c src/util/binary_linux.c ) @@ -622,6 +626,7 @@ 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 ) @@ -686,6 +691,7 @@ 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/platform/FFPlatform_unix.c @@ -750,6 +756,7 @@ 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 @@ -832,6 +839,7 @@ 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 ) @@ -1091,6 +1099,25 @@ ff_lib_enable(DIRECTX_HEADERS "DirectX-Headers" "DirectX-Headers" ) +# The system 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) @@ -1153,6 +1180,8 @@ elseif(SunOS) PRIVATE "socket" PRIVATE "kstat" PRIVATE "proc" + PRIVATE "zfs" + PRIVATE "nvpair" ) elseif(ANDROID) CHECK_LIBRARY_EXISTS(-l:libandroid-wordexp.a wordexp "" HAVE_LIBANDROID_WORDEXP_STATIC) @@ -1196,11 +1225,16 @@ if(WIN32) endif() set(CMAKE_CXX_STANDARD 17) endif() -if(LINUX) +if(FreeBSD) + set(CMAKE_REQUIRED_INCLUDES "/usr/local/include" "/usr/include") +endif() +if(LINUX OR FreeBSD) CHECK_INCLUDE_FILE("linux/videodev2.h" HAVE_LINUX_VIDEODEV2) if(HAVE_LINUX_VIDEODEV2) target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LINUX_VIDEODEV2=1) endif() +endif() +if(LINUX) CHECK_INCLUDE_FILE("linux/wireless.h" HAVE_LINUX_WIRELESS) if(HAVE_LINUX_WIRELESS) target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LINUX_WIRELESS=1) @@ -1308,9 +1342,17 @@ if (BUILD_TESTS) PRIVATE libfastfetch ) + add_executable(fastfetch-test-format + tests/format.c + ) + target_link_libraries(fastfetch-test-format + PRIVATE libfastfetch + ) + enable_testing() add_test(NAME test-strbuf COMMAND fastfetch-test-strbuf) add_test(NAME test-list COMMAND fastfetch-test-list) + add_test(NAME test-format COMMAND fastfetch-test-format) endif() ################## diff --git a/completions/fastfetch.zsh b/completions/fastfetch.zsh index 924e4a338d..a6ce692798 100644 --- a/completions/fastfetch.zsh +++ b/completions/fastfetch.zsh @@ -3,9 +3,8 @@ function _fastfetch() { local state - local -a opts - opts=(${(f)"$( - python <colors") elif type == "command": - print(f"{command_prefix}:module:->modules") + print(f"{command_prefix}::module:->modules") elif type == "config": - print(f"{command_prefix}:presets:->presets") + print(f"{command_prefix}:preset:->presets") elif type == "enum": temp: str = " ".join(flag["arg"]["enum"]) - print(f'{command_prefix}:type:( {temp} )') + print(f'{command_prefix}:type:({temp})') elif type == "logo": - print(f"{command_prefix}:logo:->logo") + print(f"{command_prefix}:logo:->logos") elif type == "structure": - print(f"{command_prefix}:structure:->structure") + print(f"{command_prefix}:structure:->structures") elif type == "path": - print(f"{command_prefix}:path:_files -/") + print(f"{command_prefix}::path:_files") else: print(f"{command_prefix}:") else: @@ -62,30 +67,34 @@ if __name__ == "__main__": except Exception: sys.exit(1) EOF - )"}) + )}") - _arguments -C "$opts[@]" + _arguments "$opts[@]" case $state in + colors) + local -a colors=(black red green yellow blue magenta cyan white default) + _describe 'color' colors + ;; modules) - local -a modules=( ${(f)"$(fastfetch --list-modules autocompletion)"} ) - modules=( ${(L)^modules%%:*}-format format color ) + local -a modules=("${(f)$(fastfetch --list-modules autocompletion)}") + modules=(${(L)^modules[@]%%:*}-format format color) _describe 'module' modules ;; presets) local -a presets=( - ${$(fastfetch --list-presets autocompletion):#.*} + "${(f)$(fastfetch --list-presets autocompletion)}" "none:Disable loading config file" ) - _describe 'preset' presets + _describe 'preset' presets || _files ;; - structure) - local -a structures=( ${(f)"$(fastfetch --list-modules autocompletion)"} ) + structures) + local -a structures=("${(f)$(fastfetch --list-modules autocompletion)}") _describe 'structure' structures ;; - logo) + logos) local -a logos=( - $(fastfetch --list-logos autocompletion) + "${(f)$(fastfetch --list-logos autocompletion)}" "none:Don't print logo" "small:Print small ascii logo if available" ) diff --git a/debian/changelog b/debian/changelog index fd559ffe9b..d647181de7 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +fastfetch (2.22.0) jammy; urgency=medium + + * Update to 2.22.0 + + -- Carter Li Mon, 26 Aug 2024 18:53:35 +0800 + fastfetch (2.21.3) jammy; urgency=medium * Update to 2.21.3 diff --git a/debian/files b/debian/files index a2ab3b2000..19482ef876 100644 --- a/debian/files +++ b/debian/files @@ -1 +1 @@ -fastfetch_2.21.3_source.buildinfo universe/utils optional +fastfetch_2.22.0_source.buildinfo universe/utils optional diff --git a/doc/json_schema.json b/doc/json_schema.json index c7786dcea0..9829f034dd 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -820,7 +820,8 @@ "weather", "wm", "wifi", - "wmtheme" + "wmtheme", + "zpool" ] }, { @@ -993,6 +994,10 @@ { "const": "wmtheme", "description": "Print current theme of window manager" + }, + { + "const": "zpool", + "description": "Print ZFS storage pools" } ] }, diff --git a/presets/all.jsonc b/presets/all.jsonc index 7a473bf7a7..efcec55dd5 100644 --- a/presets/all.jsonc +++ b/presets/all.jsonc @@ -54,6 +54,7 @@ "physicalmemory", "swap", "disk", + "zpool", "battery", "poweradapter", "player", diff --git a/presets/ci.jsonc b/presets/ci.jsonc index d5774c5eb8..565e28b6ea 100644 --- a/presets/ci.jsonc +++ b/presets/ci.jsonc @@ -56,6 +56,7 @@ "physicalmemory", "swap", "disk", + "zpool", "battery", "poweradapter", "player", diff --git a/presets/examples/20.jsonc b/presets/examples/20.jsonc index 162e813f31..830daff3a9 100644 --- a/presets/examples/20.jsonc +++ b/presets/examples/20.jsonc @@ -111,8 +111,7 @@ { "type": "loadavg", "compact": false, - // {duration} is not fixed length, hack it - "key": "│ LOAD \u001b[s{duration}m\u001b[u\u001b[6C│{$1}" + "key": "│ LOAD {duration>2}m │{$1}" // pad duration to 2 chars }, { "type": "custom", diff --git a/src/common/format.c b/src/common/format.c index f6b9b562f5..74f59d865a 100644 --- a/src/common/format.c +++ b/src/common/format.c @@ -265,18 +265,34 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n continue; } - int32_t truncLength = INT32_MAX; - char* pColon = memchr(placeholderValue.chars, ':', placeholderValue.length); - if (pColon != NULL) + int32_t truncLength = 0; + char align = '\0'; + char* pSep = memchr(placeholderValue.chars, ':', placeholderValue.length); + if (pSep) + align = ':'; + else + { + pSep = memchr(placeholderValue.chars, '<', placeholderValue.length); + if (pSep) + align = '<'; + else + { + pSep = memchr(placeholderValue.chars, '>', placeholderValue.length); + if (pSep) + align = '>'; + } + } + + if (pSep) { char* pEnd = NULL; - truncLength = (int32_t) strtol(pColon + 1, &pEnd, 10); + truncLength = (int32_t) strtol(pSep + 1, &pEnd, 10); if (*pEnd != '\0') { appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length); continue; } - *pColon = '\0'; + *pSep = '\0'; } uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments); @@ -287,7 +303,7 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n if (index > numArgs) { - if (pColon) *pColon = ':'; + if (pSep) *pSep = align; appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length); continue; } @@ -299,14 +315,43 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n truncLength = -truncLength; } - uint32_t oldLength = buffer->length; - ffFormatAppendFormatArg(buffer, &arguments[index - 1]); - if (buffer->length - oldLength > (uint32_t) truncLength) + if (!align) + ffFormatAppendFormatArg(buffer, &arguments[index - 1]); + else { - ffStrbufSubstrBefore(buffer, oldLength + (uint32_t) truncLength); - ffStrbufTrimRightSpace(buffer); - if (ellipsis) - ffStrbufAppendS(buffer, "…"); + ffStrbufClear(&placeholderValue); + ffFormatAppendFormatArg(&placeholderValue, &arguments[index - 1]); + if (placeholderValue.length == (uint32_t) truncLength) + ffStrbufAppend(buffer, &placeholderValue); + else if (placeholderValue.length > (uint32_t) truncLength) + { + if (align == ':') + { + ffStrbufSubstrBefore(&placeholderValue, (uint32_t) truncLength); + ffStrbufTrimRightSpace(&placeholderValue); + } + else + ffStrbufSubstrBefore(&placeholderValue, (uint32_t) (!ellipsis? truncLength : truncLength - 1)); + ffStrbufAppend(buffer, &placeholderValue); + + if (ellipsis) + ffStrbufAppendS(buffer, "…"); + } + else if (align == ':') + ffStrbufAppend(buffer, &placeholderValue); + else + { + if (align == '<') + { + ffStrbufAppend(buffer, &placeholderValue); + ffStrbufAppendNC(buffer, (uint32_t) truncLength - placeholderValue.length, ' '); + } + else + { + ffStrbufAppendNC(buffer, (uint32_t) truncLength - placeholderValue.length, ' '); + ffStrbufAppend(buffer, &placeholderValue); + } + } } } diff --git a/src/common/init.c b/src/common/init.c index 2da9f30276..ba569c829c 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -247,6 +247,9 @@ void ffListFeatures(void) #if FF_HAVE_ELF "libelf\n" #endif + #if FF_HAVE_LIBZFS + "libzfs\n" + #endif #if FF_HAVE_DIRECTX_HEADERS "Directx Headers\n" #endif diff --git a/src/common/io/io_unix.c b/src/common/io/io_unix.c index d459c3d924..edd740ba50 100644 --- a/src/common/io/io_unix.c +++ b/src/common/io/io_unix.c @@ -144,7 +144,7 @@ const char* ffGetTerminalResponse(const char* request, const char* format, ...) { if (ftty < 0) { - ftty = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC); + ftty = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK); if (ftty < 0) return "open(\"/dev/tty\", O_RDWR | O_NOCTTY | O_CLOEXEC) failed"; @@ -232,12 +232,15 @@ void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indenta while((entry = readdir(dir)) != NULL) { + if(entry->d_name[0] == '.') // skip hidden files + continue; + bool isDir = false; -#ifdef _DIRENT_HAVE_D_TYPE +#ifndef __sun if(entry->d_type != DT_UNKNOWN && entry->d_type != DT_LNK) isDir = entry->d_type == DT_DIR; else -#else +#endif { struct stat stbuf; if (fstatat(dfd, entry->d_name, &stbuf, 0) < 0) @@ -245,12 +248,8 @@ void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indenta else isDir = S_ISDIR(stbuf.st_mode); } -#endif if (isDir) { - if(ffStrEquals(entry->d_name, ".") || ffStrEquals(entry->d_name, "..")) - continue; - ffStrbufAppendS(folder, entry->d_name); ffStrbufAppendC(folder, '/'); listFilesRecursively(baseLength, folder, (uint8_t) (indentation + 1), entry->d_name, pretty); diff --git a/src/common/modules.c b/src/common/modules.c index 3cd1589849..28f270165e 100644 --- a/src/common/modules.c +++ b/src/common/modules.c @@ -169,6 +169,7 @@ static FFModuleBaseInfo* Y[] = { }; static FFModuleBaseInfo* Z[] = { + (void*) &instance.config.modules.zpool, NULL, }; diff --git a/src/data/help_format.txt b/src/data/help_format.txt index 4474e8694f..9d84e61381 100644 --- a/src/data/help_format.txt +++ b/src/data/help_format.txt @@ -13,6 +13,10 @@ For example: "--title-format '{user-name:5}'" will truncate user name into 5-len If '' is negative, an ellipsis … will be appended at the end when the original string is truncated. Note: The string length is counted in raw bytes. Multi-byte unicode characters and ANSI escape codes are not taken into account. +In 2.23.0 or newer, `<` or `>` can be specified instead of `:` to set a left or right padding. +For example: "--title-format '{user-name<20}'" will generate ` `; +"--title-format '{user-name>20}'" will generate ` ` + If the value index is missing, meaning the placeholder is "{}", an internal counter sets the value index. This means that the format string "Values: {1} ({2})" is equivalent to "Values: {} ({})". Note that this counter only counts empty placeholders, so the format string "{2} {} {}" will contain the second value, then the first, and then the second again. diff --git a/src/detection/de/de_linux.c b/src/detection/de/de_linux.c index c17e8ac7f4..c31d10b944 100644 --- a/src/detection/de/de_linux.c +++ b/src/detection/de/de_linux.c @@ -178,6 +178,12 @@ static void getBudgie(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options) ffParsePropFileData("budgie/budgie-version.xml", "", result); } +static void getUnity(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options) +{ + if (ffParsePropFile("/usr/bin/unity", "parser = OptionParser(version= \"%prog ", result)) + ffStrbufSubstrBeforeFirstC(result, '"'); +} + const char* ffDetectDEVersion(const FFstrbuf* deName, FFstrbuf* result, FFDEOptions* options) { if (!instance.config.general.detectVersion) return "Disabled by config"; @@ -196,6 +202,8 @@ const char* ffDetectDEVersion(const FFstrbuf* deName, FFstrbuf* result, FFDEOpti getLXQt(result, options); else if (ffStrbufEqualS(deName, FF_DE_PRETTY_BUDGIE)) getBudgie(result, options); + else if (ffStrbufEqualS(deName, FF_DE_PRETTY_UNITY)) + getUnity(result, options); else return "Unsupported DE"; return NULL; diff --git a/src/detection/displayserver/linux/wmde.c b/src/detection/displayserver/linux/wmde.c index 8f50b69ab1..9d174b5ea7 100644 --- a/src/detection/displayserver/linux/wmde.c +++ b/src/detection/displayserver/linux/wmde.c @@ -238,6 +238,13 @@ static void applyPrettyNameIfDE(FFDisplayServerResult* result, const char* name) ffStrbufSetS(&result->deProcessName, "ukui-session"); ffStrbufSetS(&result->dePrettyName, FF_DE_PRETTY_UKUI); } + + else if( + ffStrStartsWithIgnCase(name, "Unity:Unity") + ) { + ffStrbufSetS(&result->deProcessName, "unity-session"); + ffStrbufSetS(&result->dePrettyName, FF_DE_PRETTY_UNITY); + } } diff --git a/src/detection/editor/editor.c b/src/detection/editor/editor.c index 9ae00eea87..8a0e58cd6b 100644 --- a/src/detection/editor/editor.c +++ b/src/detection/editor/editor.c @@ -14,26 +14,23 @@ static inline char* realpath(const char* restrict file_name, char* restrict reso } #endif -static bool extractNvimVersionFromBinary(const char* str, uint32_t len, void* userdata) +static bool extractNvimVersionFromBinary(const char* str, FF_MAYBE_UNUSED uint32_t len, void* userdata) { - if (len < strlen("NVIM v0.0.0")) return true; if (!ffStrStartsWith(str, "NVIM v")) return true; ffStrbufSetS((FFstrbuf*) userdata, str + strlen("NVIM v")); return false; } -static bool extractVimVersionFromBinary(const char* str, uint32_t len, void* userdata) +static bool extractVimVersionFromBinary(const char* str, FF_MAYBE_UNUSED uint32_t len, void* userdata) { - if (len < strlen("VIM - Vi IMproved 0.0")) return true; if (!ffStrStartsWith(str, "VIM - Vi IMproved ")) return true; ffStrbufSetS((FFstrbuf*) userdata, str + strlen("VIM - Vi IMproved ")); ffStrbufSubstrBeforeFirstC(userdata, ' '); return false; } -static bool extractNanoVersionFromBinary(const char* str, uint32_t len, void* userdata) +static bool extractNanoVersionFromBinary(const char* str, FF_MAYBE_UNUSED uint32_t len, void* userdata) { - if (len < strlen("GNU nano 0.0")) return true; if (!ffStrStartsWith(str, "GNU nano ")) return true; ffStrbufSetS((FFstrbuf*) userdata, str + strlen("GNU nano ")); return false; @@ -97,11 +94,11 @@ const char* ffDetectEditor(FFEditorResult* result) if (!instance.config.general.detectVersion) return NULL; if (ffStrbufEqualS(&result->exe, "nvim")) - ffBinaryExtractStrings(result->path.chars, extractNvimVersionFromBinary, &result->version); + ffBinaryExtractStrings(result->path.chars, extractNvimVersionFromBinary, &result->version, (uint32_t) strlen("NVIM v0.0.0")); else if (ffStrbufEqualS(&result->exe, "vim")) - ffBinaryExtractStrings(result->path.chars, extractVimVersionFromBinary, &result->version); + ffBinaryExtractStrings(result->path.chars, extractVimVersionFromBinary, &result->version, (uint32_t) strlen("VIM - Vi IMproved 0.0")); else if (ffStrbufEqualS(&result->exe, "nano")) - ffBinaryExtractStrings(result->path.chars, extractNanoVersionFromBinary, &result->version); + ffBinaryExtractStrings(result->path.chars, extractNanoVersionFromBinary, &result->version, (uint32_t) strlen("GNU nano 0.0")); if (result->version.length > 0) return NULL; diff --git a/src/detection/initsystem/initsystem_linux.c b/src/detection/initsystem/initsystem_linux.c index d135dce878..328c7694a6 100644 --- a/src/detection/initsystem/initsystem_linux.c +++ b/src/detection/initsystem/initsystem_linux.c @@ -1,20 +1,18 @@ #include "initsystem.h" #include "common/processing.h" #include "util/binary.h" +#include "util/stringUtils.h" + #include -FF_MAYBE_UNUSED static bool elfExtractStringsCallBack(const char* str, uint32_t len, void* data) +FF_MAYBE_UNUSED static bool extractSystemdVersion(const char* str, uint32_t len, void* userdata) { - if (len > strlen("systemd 0.0 running in ") && memcmp(str, "systemd ", strlen("systemd ")) == 0) - { - const char* pend = memmem(str + strlen("systemd "), len - strlen("systemd "), " running in ", strlen(" running in ")); - if (pend) - { - ffStrbufSetNS((FFstrbuf*) data, (uint32_t) (pend - str) - (uint32_t) strlen("systemd "), str + strlen("systemd ")); - return false; - } - } - return true; + if (!ffStrStartsWith(str, "systemd ")) return true; + const char* pstart = str + strlen("systemd "); + const char* pend = memmem(pstart, len - strlen("systemd "), " running in ", strlen(" running in ")); + if (!pend) return true; + ffStrbufSetNS((FFstrbuf*) userdata, (uint32_t) (pend - pstart), pstart); + return false; } const char* ffDetectInitSystem(FFInitSystemResult* result) @@ -49,7 +47,7 @@ const char* ffDetectInitSystem(FFInitSystemResult* result) #if __linux__ && !__ANDROID__ if (ffStrbufEqualS(&result->name, "systemd")) { - ffBinaryExtractStrings(result->exe.chars, elfExtractStringsCallBack, &result->version); + ffBinaryExtractStrings(result->exe.chars, extractSystemdVersion, &result->version, (uint32_t) strlen("systemd 0.0 running in x")); if (result->version.length == 0) { if (ffProcessAppendStdOut(&result->version, (char* const[]) { diff --git a/src/detection/terminalfont/terminalfont_linux.c b/src/detection/terminalfont/terminalfont_linux.c index 8dd15ee7a7..843648c576 100644 --- a/src/detection/terminalfont/terminalfont_linux.c +++ b/src/detection/terminalfont/terminalfont_linux.c @@ -287,10 +287,10 @@ static void detectXterm(FFTerminalFontResult* terminalFont) ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars); } -static bool elfExtractStringsCallBack(const char* str, uint32_t len, void* userData) +static bool extractStTermFont(const char* str, FF_MAYBE_UNUSED uint32_t len, void* userdata) { if (!ffStrContains(str, "size=")) return true; - ffStrbufSetNS((FFstrbuf*) userData, len, str); + ffStrbufSetNS((FFstrbuf*) userdata, len, str); return false; } @@ -315,7 +315,7 @@ static void detectSt(FFTerminalFontResult* terminalFont, const FFTerminalResult* { ffStrbufClear(&font); - const char* error = ffBinaryExtractStrings(terminal->exePath.chars, elfExtractStringsCallBack, &font); + const char* error = ffBinaryExtractStrings(terminal->exePath.chars, extractStTermFont, &font, (uint32_t) strlen("size=0")); if (error) { ffStrbufAppendS(&terminalFont->error, error); diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index 4073d7aefe..b397f7159c 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -64,16 +64,29 @@ static bool getExeVersionGeneral(FFstrbuf* exe, FFstrbuf* version) return true; } -static bool getShellVersionBash(FFstrbuf* exe, FFstrbuf* version) +static bool extractBashVersion(const char* line, FF_MAYBE_UNUSED uint32_t len, void *userdata) { + if (!ffStrStartsWith(line, "@(#)Bash version ")) return true; + const char* start = line + strlen("@(#)Bash version "); + const char* end = strchr(start, '('); + if (!end) return true; + ffStrbufSetNS((FFstrbuf*) userdata, (uint32_t) (end - start), start); + return false; +} + +static bool getShellVersionBash(FFstrbuf* exe, FFstrbuf* exePath, 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")); + if(!getExeVersionRaw(exe, version)) return false; // GNU bash, version 5.1.16(1)-release (x86_64-pc-msys)\nCopyright... - ffStrbufSubstrBeforeFirstC(version, '\n'); // GNU bash, version 5.1.16(1)-release (x86_64-pc-msys) - ffStrbufSubstrBeforeLastC(version, ' '); // GNU bash, version 5.1.16(1)-release - ffStrbufSubstrAfterLastC(version, ' '); // 5.1.16(1)-release - ffStrbufSubstrBeforeFirstC(version, '('); // 5.1.16 + ffStrbufSubstrBeforeFirstC(version, '('); // GNU bash, version 5.1.16 + ffStrbufSubstrAfterLastC(version, ' '); // 5.1.16 return true; } @@ -189,6 +202,29 @@ static bool getShellVersionXonsh(FFstrbuf* exe, FFstrbuf* version) return true; } +static bool extractZshVersion(const char* line, FF_MAYBE_UNUSED uint32_t len, void *userdata) +{ + if (!ffStrStartsWith(line, "zsh-")) return true; + const char* start = line + strlen("zsh-"); + const char* end = strchr(start, '-'); + if (!end) return true; + + ffStrbufSetNS((FFstrbuf*) userdata, (uint32_t) (end - start), start); + return false; +} + +static bool getShellVersionZsh(FFstrbuf* exe, FFstrbuf* exePath, FFstrbuf* version) +{ + const char* path = exePath->chars; + if (*path == '\0') + path = exe->chars; + + ffBinaryExtractStrings(path, 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) +} + #ifdef _WIN32 static bool getShellVersionWinPowerShell(FFstrbuf* exe, FFstrbuf* version) { @@ -203,7 +239,7 @@ static bool getShellVersionWinPowerShell(FFstrbuf* exe, FFstrbuf* version) } #endif -bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version) +bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath, FFstrbuf* version) { if (!instance.config.general.detectVersion) return false; @@ -211,9 +247,9 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version) return false; if(ffStrEqualsIgnCase(exeName, "bash")) - return getShellVersionBash(exe, version); + return getShellVersionBash(exe, exePath, version); if(ffStrEqualsIgnCase(exeName, "zsh")) - return getExeVersionGeneral(exe, version); //zsh 5.9 (arm-apple-darwin21.3.0) + return getShellVersionZsh(exe, exePath, version); if(ffStrEqualsIgnCase(exeName, "fish")) return getShellVersionFish(exe, version); if(ffStrEqualsIgnCase(exeName, "pwsh")) @@ -251,7 +287,7 @@ FF_MAYBE_UNUSED static bool getTerminalVersionTermux(FFstrbuf* version) return version->length > 0; } -static bool extractGnomeTerminalVersion(const char *str, FF_MAYBE_UNUSED uint32_t len, void *userdata) +static bool extractGeneralVersion(const char *str, FF_MAYBE_UNUSED uint32_t len, void *userdata) { if (!ffCharIsDigit(str[0])) return true; int count = 0; @@ -265,7 +301,7 @@ FF_MAYBE_UNUSED static bool getTerminalVersionGnome(FFstrbuf* exe, FFstrbuf* ver { if (exe->chars[0] == '/') { - ffBinaryExtractStrings(exe->chars, extractGnomeTerminalVersion, version); + ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0")); if (version->length) return true; } @@ -281,6 +317,17 @@ FF_MAYBE_UNUSED static bool getTerminalVersionGnome(FFstrbuf* exe, FFstrbuf* ver return true; } +FF_MAYBE_UNUSED static bool getTerminalVersionXfce4Terminal(FFstrbuf* exe, FFstrbuf* version) +{ + if (exe->chars[0] == '/') + { + ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0")); + if (version->length) return true; + } + + return getExeVersionGeneral(exe, version);//xfce4-terminal 1.0.4 (Xfce 4.18)... +} + FF_MAYBE_UNUSED static bool getTerminalVersionKgx(FFstrbuf* version) { if(ffProcessAppendStdOut(version, (char* const[]){ @@ -616,7 +663,7 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe return getExeVersionGeneral(exe, version);//yakuake 22.12.3 if(ffStrbufIgnCaseEqualS(processName, "xfce4-terminal")) - return getExeVersionGeneral(exe, version);//xfce4-terminal 1.0.4 (Xfce 4.18)... + return getTerminalVersionXfce4Terminal(exe, version); if(ffStrbufIgnCaseEqualS(processName, "terminator")) return getExeVersionGeneral(exe, version);//terminator 2.1.3 diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index 18be4f3b8b..da4e96dfd6 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -274,14 +274,14 @@ static void getUserShellFromEnv(FFShellResult* result) } } -bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version); +bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath, FFstrbuf* version); bool fftsGetTerminalVersion(FFstrbuf* processName, FFstrbuf* exe, FFstrbuf* version); static void setShellInfoDetails(FFShellResult* result) { ffStrbufClear(&result->version); - fftsGetShellVersion(&result->exe, result->exeName, &result->version); + fftsGetShellVersion(&result->exe, result->exeName, &result->exePath, &result->version); if(ffStrbufEqualS(&result->processName, "pwsh")) ffStrbufInitStatic(&result->prettyName, "PowerShell"); diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index ecac82e1fb..ca567bcc03 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -38,7 +38,7 @@ static bool getProductVersion(const wchar_t* filePath, FFstrbuf* version) return false; } -bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version); +bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, const FFstrbuf* exePath, FFstrbuf* version); static uint32_t getShellInfo(FFShellResult* result, uint32_t pid) { @@ -359,7 +359,7 @@ const FFShellResult* ffDetectShell(void) strcpy(tmp, result.exeName); char* ext = strrchr(tmp, '.'); if (ext) *ext = '\0'; - fftsGetShellVersion(&result.exe, tmp, &result.version); + fftsGetShellVersion(&result.exe, tmp, &result.exePath, &result.version); } return &result; diff --git a/src/detection/zpool/libzfs_simplified.h b/src/detection/zpool/libzfs_simplified.h new file mode 100644 index 0000000000..3b97c222dd --- /dev/null +++ b/src/detection/zpool/libzfs_simplified.h @@ -0,0 +1,89 @@ +#pragma once + +#include "fastfetch.h" + +// From https://github.com/openzfs/zfs/blob/master/include/libzfs.h + +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or https://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * 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; + +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 *); diff --git a/src/detection/zpool/zpool.h b/src/detection/zpool/zpool.h new file mode 100644 index 0000000000..8cd53da36a --- /dev/null +++ b/src/detection/zpool/zpool.h @@ -0,0 +1,15 @@ +#pragma once + +#include "fastfetch.h" + +typedef struct FFZpoolResult +{ + FFstrbuf name; + FFstrbuf state; + uint64_t used; + uint64_t total; + uint64_t version; + uint64_t fragmentation; +} FFZpoolResult; + +const char* ffDetectZpool(FFlist* result /* list of FFZpoolResult */); diff --git a/src/detection/zpool/zpool_linux.c b/src/detection/zpool/zpool_linux.c new file mode 100644 index 0000000000..e62febbd0a --- /dev/null +++ b/src/detection/zpool/zpool_linux.c @@ -0,0 +1,111 @@ +#include "zpool.h" + +#ifdef FF_HAVE_LIBZFS + +#ifdef __sun +#define FF_DISABLE_DLOPEN +#include + +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); + item->fragmentation = data->ffzpool_get_prop_int(zpool, ZPOOL_PROP_FRAGMENTATION, &source); + return 0; +} + +const char* ffDetectZpool(FFlist* result /* list of FFZpoolResult */) +{ + FF_LIBRARY_LOAD(libzfs, NULL, "dlopen libzfs" FF_LIBRARY_EXTENSION " failed", "libzfs" FF_LIBRARY_EXTENSION, 4); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, libzfs_init); + + libzfs_handle_t* handle = fflibzfs_init(); + if (!handle) 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 diff --git a/src/detection/zpool/zpool_nosupport.c b/src/detection/zpool/zpool_nosupport.c new file mode 100644 index 0000000000..f7b71a7dbf --- /dev/null +++ b/src/detection/zpool/zpool_nosupport.c @@ -0,0 +1,6 @@ +#include "zpool.h" + +const char* ffDetectZpool(FF_MAYBE_UNUSED FFlist* result /* list of FFZpoolResult */) +{ + return "Not supported on this platform"; +} diff --git a/src/logo/ascii/amogos.txt b/src/logo/ascii/amogos.txt index 24f4a45b16..99c759a44a 100644 --- a/src/logo/ascii/amogos.txt +++ b/src/logo/ascii/amogos.txt @@ -1,19 +1,19 @@ -${c1} ___________ - / \ - / ${c2}______${c1} \ - / ${c2}/ \${c1} \ - | ${c2}( )${c1} \ - / ${c2}\______/${c1} | - | | - / \ - | | - | | - / | - | | - | _______ | - ____/ / \ | - / | | | - | / ____/ | - \_________/ / | - \ __/ - \_______/ \ No newline at end of file + ___________ + / \ + / ${c2}______${c1} \ + / ${c2}/ \${c1} \ + | ${c2}( )${c1} \ + / ${c2}\______/${c1} | + | | + / \ + | | + | | + / | + | | + | _______ | + ____/ / \ | +/ | | | +| / ____/ | +\_________/ / | + \ __/ + \_______/ \ No newline at end of file diff --git a/src/logo/ascii/hce.txt b/src/logo/ascii/hce.txt new file mode 100644 index 0000000000..8b5ea50914 --- /dev/null +++ b/src/logo/ascii/hce.txt @@ -0,0 +1,24 @@ + ti + jGGGGj + tGGGGGGt + ,LGGLGGGGGGGG, + jGGGGjGGGGGGGGGj + GGGGGGGGGGGGGGGGGGGG +iGGGGGGt tLLLLLLi +,GGGGGi iGGGGG, +,jGGGG LGGGG, +,LjGGj LLLL, +,LLGG. iiii. +,LLj +,LLj jLL, +,LLLL. GGLL, +,LLLLt GGLL, +,LGGGG LGGGj, +,GGGGGi iGGGGG, +iGGGGGGj tGGGGGG, + GGGGGGGGGGGGGGGGGGGG + jGGGGGGGGGjGGGGj + ,LGGGGGGGGGGG, + tGGGGGGt + jGGGGj + it \ No newline at end of file diff --git a/src/logo/ascii/magix.txt b/src/logo/ascii/magix.txt new file mode 100644 index 0000000000..0ceb350a83 --- /dev/null +++ b/src/logo/ascii/magix.txt @@ -0,0 +1,19 @@ + ${c2}@ + @@--=====@@ + @@--==@@ @@====+@ + @-@@ @==@ + @=@ + @=@${c1} @=@ @-==== @=@${c2} + @=@${c1} @-===@==++@===+@${c2} + @=@${c1} @--====@@=====+@${c2} +-=@${c1} @--==========++@${c2} +==${c1} @--==========++@${c2} @=@ +@==${c1} @--=======@==++@${c2} @=+@ + @==${c1} @-==========++${c2} @=@ + @==${c1} @-=======@=%${c2} @=@ + @==${c1} @@@@@@${c2} @=@ + @====@@@ @@===+% + @@=====@@==++++@@ + =#@=@ + @==@++@ + @@@ diff --git a/src/logo/ascii/steamdeck.txt b/src/logo/ascii/steamdeck.txt new file mode 100644 index 0000000000..9f4b0662b7 --- /dev/null +++ b/src/logo/ascii/steamdeck.txt @@ -0,0 +1,24 @@ +$2 .xXK0kdc'.. + .OMMMMWNK0Odc'. + .OMMMMMMMMMMWNOl'. + .OMMMMMMMMMMMMMWXx;. + .:ddk0XWMMMMMMMMMMNx' + ..;oxONMMMMMMMWKc. +$1 ..,;::::;,'.$2 .;xXMMMMMMMNo. +$1 .':looooooooool:,..$2 .;OWMMMMMMNl. +$1 .:oooooooooooooooooc'$2 .xWMMMMMMK: +$1 'coooooooooooooooooool,$2 'OMMMMMMWx. +$1.coooooooooooooooooooool.$2 cNMMMMMM0, +$1'loooooooooooooooooooooo,$2 ,KMMMMMMX: +$1'loooooooooooooooooooooo,$2 ,KMMMMMMX: +$1.:oooooooooooooooooooooc.$2 lNMMMMMM0, +$1 .coooooooooooooooooool'$2 ,0MMMMMMWd. +$1 .:looooooooooooooolc.$2 'OWMMMMMMK; +$1 ..;cloooooooooc;'..$2 .c0WMMMMMMXc. +$1 ..',;;;;,'..$2 ..cONMMMMMMMXc. + ..,cxOKWMMMMMMMW0;. + .cxk0KNWMMMMMMMMMWXo. + .OMMMMMMMMMMMMMWKo' + .OMMMMMMMMMMWKx:. + .OMMMWNX0Oxl;. + .o0Oxoc,.. diff --git a/src/logo/ascii/steamdeck_small.txt b/src/logo/ascii/steamdeck_small.txt new file mode 100644 index 0000000000..287b637eb8 --- /dev/null +++ b/src/logo/ascii/steamdeck_small.txt @@ -0,0 +1,5 @@ + $2__ + \ +$1## $2\ +$1## $2/ + __/ \ No newline at end of file diff --git a/src/logo/ascii/ubuntu.txt b/src/logo/ascii/ubuntu.txt index 398d8bb206..51515e7823 100644 --- a/src/logo/ascii/ubuntu.txt +++ b/src/logo/ascii/ubuntu.txt @@ -1,20 +1,20 @@ - .... - .',:clooo: .:looooo:. - .;looooooooc .oooooooooo' - .;looooool:,''. :ooooooooooc - ;looool;. 'oooooooooo, - ;clool' .cooooooc. ,, - ... ...... .:oo, - .;clol:,. .loooo' - :ooooooooo, 'ooool -'ooooooooooo. loooo. -'ooooooooool coooo. - ,loooooooc. .loooo. - .,;;;'. ;ooooc - ... ,ooool. - .cooooc. ..',,'. .cooo. - ;ooooo:. ;oooooooc. :l. - .coooooc,.. coooooooooo. - .:ooooooolc:. .ooooooooooo' - .':loooooo; ,oooooooooc - ..';::c' .;loooo:' \ No newline at end of file + .... + $2.',:clooo: $1.:looooo:. + $2.;looooooooc $1.oooooooooo' + $2.;looooool:,''. $1:ooooooooooc + $2;looool;. $1'oooooooooo, + $2;clool' $1.cooooooc. $2,, + $2... $1...... $2.:oo, + $1.;clol:,. $2.loooo' + $1:ooooooooo, $2'ooool +$1'ooooooooooo. $2loooo. +$1'ooooooooool $2coooo. + $1,loooooooc. $2.loooo. + $1.,;;;'. $2;ooooc + $2... $2,ooool. + $2.cooooc. $1..',,'. $2.cooo. + $2;ooooo:. $1;oooooooc. $2:l. + $2.coooooc,.. $1coooooooooo. + $2.:ooooooolc:. $1.ooooooooooo' + $2.':loooooo; $1,oooooooooc + $2..';::c' $1.;loooo:' \ No newline at end of file diff --git a/src/logo/ascii/ubuntu2_small.txt b/src/logo/ascii/ubuntu2_small.txt deleted file mode 100644 index 4275646bc4..0000000000 --- a/src/logo/ascii/ubuntu2_small.txt +++ /dev/null @@ -1,11 +0,0 @@ - ..;,; .,;,. - .,lool: .ooooo, - ;oo;: .coool. - .... ''' ,l; -:oooo, 'oo. -looooc :oo' - '::' ,oo: - ,., .... co, - lo:;. :oooo; . - ':ooo; cooooc - ''' '''' \ No newline at end of file diff --git a/src/logo/ascii/ubuntu_old.txt b/src/logo/ascii/ubuntu_old.txt index 6e4c939f91..9193c8fc26 100644 --- a/src/logo/ascii/ubuntu_old.txt +++ b/src/logo/ascii/ubuntu_old.txt @@ -3,16 +3,16 @@ -+ssssssssssssssssssyyssss+- .ossssssssssssssssssd$2MMMNy$1sssso. /sssssssssss$2hdmmNNmmyNMMMMh$1ssssss/ - +sssssssss$2hmydMMMMMMMNddddy$1ssssssss+ - /ssssssss$2hNMMMyhhyyyyhmNMMMNh$1ssssssss/ + +sssssssss$2hm$1yd$2MMMMMMMNddddy$1ssssssss+ + /ssssssss$2hNMMM$1yh$2hyyyyhmNMMMNh$1ssssssss/ .ssssssss$2dMMMNh$1ssssssssss$2hNMMMd$1ssssssss. +ssss$2hhhyNMMNy$1ssssssssssss$2yNMMMy$1sssssss+ oss$2yNMMMNyMMh$1ssssssssssssss$2hmmmh$1ssssssso -oss$2yNMMMNyMMh$1ssssssssssssss$2hmmmh$1ssssssso +oss$2yNMMMNyMMh$1sssssssssssssshmmmhssssssso +ssss$2hhhyNMMNy$1ssssssssssss$2yNMMMy$1sssssss+ .ssssssss$2dMMMNh$1ssssssssss$2hNMMMd$1ssssssss. - /ssssssss$2hNMMMyhhyyyyhdNMMMNh$1ssssssss/ - +sssssssss$2dmydMMMMMMMMddddy$1ssssssss+ + /ssssssss$2hNMMM$1yh$2hyyyyhdNMMMNh$1ssssssss/ + +sssssssss$2dm$1yd$2MMMMMMMMddddy$1ssssssss+ /sssssssssss$2hdmNNNNmyNMMMMh$1ssssss/ .ossssssssssssssssss$2dMMMNy$1sssso. -+sssssssssssssssss$2yyy$1ssss+- diff --git a/src/logo/ascii/ubuntu2_old.txt b/src/logo/ascii/ubuntu_old2.txt similarity index 100% rename from src/logo/ascii/ubuntu2_old.txt rename to src/logo/ascii/ubuntu_old2.txt diff --git a/src/logo/ascii/ubuntu_old2_small.txt b/src/logo/ascii/ubuntu_old2_small.txt new file mode 100644 index 0000000000..491b534a6d --- /dev/null +++ b/src/logo/ascii/ubuntu_old2_small.txt @@ -0,0 +1,6 @@ + _ + ---(_) + _/ --- \ +(_) | | + \ --- _/ + ---(_) \ No newline at end of file diff --git a/src/logo/ascii/ubuntu_small.txt b/src/logo/ascii/ubuntu_small.txt index 491b534a6d..62c4223905 100644 --- a/src/logo/ascii/ubuntu_small.txt +++ b/src/logo/ascii/ubuntu_small.txt @@ -1,6 +1,11 @@ - _ - ---(_) - _/ --- \ -(_) | | - \ --- _/ - ---(_) \ No newline at end of file + $2..;,; $1.,;,. + $2.,lool: $1.ooooo, + $2;oo;: $1.coool. + $1.... $1''' $2,l; +$1:oooo, $2'oo. +$1looooc $2:oo' + $1'::' $2,oo: + $2,., $1.... $2co, + $2lo:;. $1:oooo; $2. + $2':ooo; $1cooooc + $2''' $1'''' \ No newline at end of file diff --git a/src/logo/builtin.c b/src/logo/builtin.c index 58f8348e06..5ee4830d94 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -1987,6 +1987,16 @@ static const FFlogo H[] = { FF_COLOR_FG_256 "123", }, }, + // Huawei Cloud EulerOS + { + .names = {"Huawei Cloud EulerOS", "hce"}, + .lines = FASTFETCH_DATATEXT_LOGO_HCE, + .colors = { + FF_COLOR_FG_RED, + }, + .colorKeys = FF_COLOR_FG_RED, + .colorTitle = FF_COLOR_FG_RED, + }, // Huayra { .names = {"Huayra"}, @@ -2623,6 +2633,17 @@ static const FFlogo M[] = { .colorKeys = FF_COLOR_FG_CYAN, .colorTitle = FF_COLOR_FG_WHITE, }, + // Magix + { + .names = {"Magix","MagixOS"}, + .lines = FASTFETCH_DATATEXT_LOGO_MAGIX, + .colors = { + FF_COLOR_FG_LIGHT_MAGENTA, + FF_COLOR_FG_CYAN, + }, + .colorKeys = FF_COLOR_FG_CYAN, + .colorTitle = FF_COLOR_FG_LIGHT_MAGENTA, + }, // MagpieOS { .names = {"MagpieOS", "Magpie"}, @@ -4127,7 +4148,7 @@ static const FFlogo S[] = { }, // SteamOS { - .names = {"steamos"}, + .names = {"SteamOS"}, .lines = FASTFETCH_DATATEXT_LOGO_STEAMOS, .colors = { FF_COLOR_FG_BLUE, @@ -4136,6 +4157,39 @@ static const FFlogo S[] = { .colorKeys = FF_COLOR_FG_BLUE, .colorTitle = FF_COLOR_FG_BLUE, }, + // Steam Deck + { + .names = {"SteamDeck"}, + .lines = FASTFETCH_DATATEXT_LOGO_STEAMDECK, + .colors = { + FF_COLOR_FG_BLUE, + FF_COLOR_FG_WHITE + }, + .colorKeys = FF_COLOR_FG_BLUE, + .colorTitle = FF_COLOR_FG_BLUE, + }, + // Steam Deck Small + { + .names = {"SteamDeck_small"}, + .lines = FASTFETCH_DATATEXT_LOGO_STEAMDECK_SMALL, + .colors = { + FF_COLOR_FG_BLUE, + FF_COLOR_FG_WHITE + }, + .colorKeys = FF_COLOR_FG_BLUE, + .colorTitle = FF_COLOR_FG_BLUE, + }, + // Steam Deck OLED + { + .names = {"SteamDeckOled"}, + .lines = FASTFETCH_DATATEXT_LOGO_STEAMDECK, + .colors = { + FF_COLOR_FG_RED, + FF_COLOR_FG_WHITE + }, + .colorKeys = FF_COLOR_FG_RED, + .colorTitle = FF_COLOR_FG_RED, + }, // Sulin { .names = {"Sulin"}, @@ -4273,6 +4327,26 @@ static const FFlogo U[] = { { .names = {"ubuntu", "ubuntu-linux"}, .lines = FASTFETCH_DATATEXT_LOGO_UBUNTU, + .colors = { + FF_COLOR_FG_RED, + FF_COLOR_FG_RED, + }, + }, + // UbuntuSmall + { + .names = {"ubuntu_small", "ubuntu-linux-small"}, + .type = FF_LOGO_LINE_TYPE_SMALL_BIT, + .lines = FASTFETCH_DATATEXT_LOGO_UBUNTU_SMALL, + .colors = { + FF_COLOR_FG_RED, + FF_COLOR_FG_RED, + }, + }, + // UbuntuOld + { + .names = {"ubuntu_old", "ubuntu-linux_old"}, + .type = FF_LOGO_LINE_TYPE_ALTER_BIT, + .lines = FASTFETCH_DATATEXT_LOGO_UBUNTU_OLD, .colors = { FF_COLOR_FG_RED, FF_COLOR_FG_WHITE, @@ -4280,6 +4354,30 @@ static const FFlogo U[] = { .colorKeys = FF_COLOR_FG_RED, .colorTitle = FF_COLOR_FG_RED, }, + // UbuntuOld2 + { + .names = {"ubuntu_old2"}, + .type = FF_LOGO_LINE_TYPE_ALTER_BIT, + .lines = FASTFETCH_DATATEXT_LOGO_UBUNTU_OLD2, + .colors = { + FF_COLOR_FG_RED, + FF_COLOR_FG_WHITE, + FF_COLOR_FG_YELLOW, + }, + .colorKeys = FF_COLOR_FG_RED, + .colorTitle = FF_COLOR_FG_RED, + }, + // UbuntuOld2Small + { + .names = {"ubuntu_old2_small", "ubuntu_old2-small"}, + .type = FF_LOGO_LINE_TYPE_SMALL_BIT | FF_LOGO_LINE_TYPE_ALTER_BIT, + .lines = FASTFETCH_DATATEXT_LOGO_UBUNTU_OLD2_SMALL, + .colors = { + FF_COLOR_FG_RED, + }, + .colorKeys = FF_COLOR_FG_RED, + .colorTitle = FF_COLOR_FG_RED, + }, // UbuntuBudgie { .names = {"ubuntu budgie", "ubuntu-budgie"}, @@ -4337,18 +4435,6 @@ static const FFlogo U[] = { .colorKeys = FF_COLOR_FG_GREEN, .colorTitle = FF_COLOR_FG_WHITE, }, - // UbuntuOld - { - .names = {"ubuntu_old", "ubuntu-linux_old"}, - .type = FF_LOGO_LINE_TYPE_ALTER_BIT, - .lines = FASTFETCH_DATATEXT_LOGO_UBUNTU_OLD, - .colors = { - FF_COLOR_FG_RED, - FF_COLOR_FG_WHITE, - }, - .colorKeys = FF_COLOR_FG_RED, - .colorTitle = FF_COLOR_FG_RED, - }, // UbuntuKde { .names = {"ubuntu kde", "ubuntu-kde", "ubuntu-plasma"}, @@ -4360,17 +4446,6 @@ static const FFlogo U[] = { .colorKeys = FF_COLOR_FG_BLUE, .colorTitle = FF_COLOR_FG_BLUE, }, - // UbuntuSmall - { - .names = {"ubuntu_small", "ubuntu-linux-small"}, - .type = FF_LOGO_LINE_TYPE_SMALL_BIT, - .lines = FASTFETCH_DATATEXT_LOGO_UBUNTU_SMALL, - .colors = { - FF_COLOR_FG_RED, - }, - .colorKeys = FF_COLOR_FG_RED, - .colorTitle = FF_COLOR_FG_RED, - }, // UbuntuStudio { .names = {"ubuntu studio", "ubuntu-studio"}, @@ -4407,28 +4482,6 @@ static const FFlogo U[] = { FF_COLOR_FG_WHITE, }, }, - // Ubuntu2Small - { - .names = {"ubuntu2_small", "ubuntu2-small"}, - .type = FF_LOGO_LINE_TYPE_SMALL_BIT | FF_LOGO_LINE_TYPE_ALTER_BIT, - .lines = FASTFETCH_DATATEXT_LOGO_UBUNTU2_SMALL, - .colors = { - FF_COLOR_FG_RED, - }, - .colorKeys = FF_COLOR_FG_RED, - .colorTitle = FF_COLOR_FG_RED, - }, - // Ubuntu2Old - { - .names = {"ubuntu2_old"}, - .type = FF_LOGO_LINE_TYPE_ALTER_BIT, - .lines = FASTFETCH_DATATEXT_LOGO_UBUNTU2_OLD, - .colors = { - FF_COLOR_FG_RED, - FF_COLOR_FG_WHITE, - FF_COLOR_FG_YELLOW, - }, - }, // Ultramarine { .names = {"Ultramarine", "Ultramarine Linux"}, diff --git a/src/logo/image/image.c b/src/logo/image/image.c index 58a4d5f9cf..c73fca10c2 100644 --- a/src/logo/image/image.c +++ b/src/logo/image/image.c @@ -83,6 +83,8 @@ static bool printImageIterm(bool printError) fprintf(stderr, "\nLogo (iterm): fail to query cursor position: %s\n", error); return true; // We already printed image logo, don't print ascii logo then } + if (X < options->paddingLeft + options->width) + X = (uint16_t) (options->paddingLeft + options->width); if (options->position == FF_LOGO_POSITION_LEFT) instance.state.logoWidth = X + options->paddingRight - 1; instance.state.logoHeight = Y; @@ -207,6 +209,8 @@ static bool printImageKittyDirect(bool printError) fprintf(stderr, "\nLogo (kitty-direct): fail to query cursor position: %s\n", error); return true; // We already printed image logo, don't print ascii logo then } + if (X < options->paddingLeft + options->width) + X = (uint16_t) (options->paddingLeft + options->width); if (options->position == FF_LOGO_POSITION_LEFT) instance.state.logoWidth = X + options->paddingRight - 1; instance.state.logoHeight = Y; diff --git a/src/modules/battery/battery.c b/src/modules/battery/battery.c index 8b65506a2f..43b9573863 100644 --- a/src/modules/battery/battery.c +++ b/src/modules/battery/battery.c @@ -11,9 +11,26 @@ static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uint8_t index) { + FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate(); + if (options->moduleArgs.key.length == 0) + { + if (result->modelName.length > 0) + ffStrbufSetF(&key, "%s (%s)", FF_BATTERY_MODULE_NAME, result->modelName.chars); + else + ffStrbufSetS(&key, FF_BATTERY_MODULE_NAME); + } + else + { + ffStrbufClear(&key); + FF_PARSE_FORMAT_STRING_CHECKED(&key, &options->moduleArgs.key, 2, ((FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_UINT8, &index, "index"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->modelName, "name"}, + })); + } + if(options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_BATTERY_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(key.chars, index, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY); FF_STRBUF_AUTO_DESTROY str = ffStrbufCreate(); bool showStatus = @@ -63,7 +80,7 @@ static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uin ffPercentAppendBar(&capacityBar, result->capacity, options->percent, &options->moduleArgs); FF_STRBUF_AUTO_DESTROY tempStr = ffStrbufCreate(); 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_PRINT_FORMAT_CHECKED(key.chars, index, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_BATTERY_NUM_FORMAT_ARGS, ((FFformatarg[]) { {FF_FORMAT_ARG_TYPE_STRBUF, &result->manufacturer, "manufacturer"}, {FF_FORMAT_ARG_TYPE_STRBUF, &result->modelName, "model-name"}, {FF_FORMAT_ARG_TYPE_STRBUF, &result->technology, "technology"}, diff --git a/src/modules/modules.h b/src/modules/modules.h index dbfebcf319..d6e6f5c14c 100644 --- a/src/modules/modules.h +++ b/src/modules/modules.h @@ -70,3 +70,4 @@ #include "modules/wifi/wifi.h" #include "modules/wm/wm.h" #include "modules/wmtheme/wmtheme.h" +#include "modules/zpool/zpool.h" diff --git a/src/modules/netio/netio.c b/src/modules/netio/netio.c index 18d8bc8735..f6d772746c 100644 --- a/src/modules/netio/netio.c +++ b/src/modules/netio/netio.c @@ -68,7 +68,7 @@ void ffPrintNetIO(FFNetIOOptions* options) if (!options->detectTotal) ffStrbufAppendS(&buffer, "/s"); ffStrbufAppendS(&buffer, " (OUT)"); - if (inf->defaultRoute) + if (inf->defaultRoute && !options->defaultRouteOnly) ffStrbufAppendS(&buffer, " *"); ffStrbufPutTo(&buffer, stdout); } diff --git a/src/modules/options.h b/src/modules/options.h index c705c75005..74ad4c0b49 100644 --- a/src/modules/options.h +++ b/src/modules/options.h @@ -70,3 +70,4 @@ #include "modules/wifi/option.h" #include "modules/wm/option.h" #include "modules/wmtheme/option.h" +#include "modules/zpool/option.h" diff --git a/src/modules/zpool/option.h b/src/modules/zpool/option.h new file mode 100644 index 0000000000..3143cf91b8 --- /dev/null +++ b/src/modules/zpool/option.h @@ -0,0 +1,14 @@ +#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 FFZpoolOptions +{ + FFModuleBaseInfo moduleInfo; + FFModuleArgs moduleArgs; + + FFColorRangeConfig percent; +} FFZpoolOptions; diff --git a/src/modules/zpool/zpool.c b/src/modules/zpool/zpool.c new file mode 100644 index 0000000000..6a9b8780f2 --- /dev/null +++ b/src/modules/zpool/zpool.c @@ -0,0 +1,214 @@ +#include "common/printing.h" +#include "common/jsonconfig.h" +#include "common/percent.h" +#include "detection/zpool/zpool.h" +#include "modules/zpool/zpool.h" +#include "util/stringUtils.h" + +#define FF_ZPOOL_NUM_FORMAT_ARGS 8 + +static void printZpool(FFZpoolOptions* options, FFZpoolResult* result, uint8_t index) +{ + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + if (options->moduleArgs.key.length == 0) + { + if (result->name.length > 0) + ffStrbufSetF(&buffer, "%s (%s)", FF_ZPOOL_MODULE_NAME, result->name.chars); + else + ffStrbufSetS(&buffer, FF_ZPOOL_MODULE_NAME); + } + else + { + ffStrbufClear(&buffer); + FF_PARSE_FORMAT_STRING_CHECKED(&buffer, &options->moduleArgs.key, 2, ((FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_UINT8, &index, "index"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->name, "name"}, + })); + } + + FF_STRBUF_AUTO_DESTROY usedPretty = ffStrbufCreate(); + ffParseSize(result->used, &usedPretty); + + FF_STRBUF_AUTO_DESTROY totalPretty = ffStrbufCreate(); + ffParseSize(result->total, &totalPretty); + + double bytesPercentage = result->total > 0 ? (double) result->used / (double) result->total * 100.0 : 0; + double fragPercentage = (double) result->fragmentation; + + if(options->moduleArgs.outputFormat.length == 0) + { + ffPrintLogoAndKey(buffer.chars, index, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY); + + ffStrbufClear(&buffer); + ffStrbufSetF(&buffer, "%s / %s (", usedPretty.chars, totalPretty.chars); + ffPercentAppendNum(&buffer, bytesPercentage, options->percent, false, &options->moduleArgs); + ffStrbufAppendS(&buffer, ", "); + ffPercentAppendNum(&buffer, fragPercentage, options->percent, false, &options->moduleArgs); + ffStrbufAppendF(&buffer, " frag) - %s", result->state.chars); + ffStrbufPutTo(&buffer, stdout); + } + else + { + FF_STRBUF_AUTO_DESTROY bytesPercentageNum = ffStrbufCreate(); + ffPercentAppendNum(&bytesPercentageNum, bytesPercentage, options->percent, false, &options->moduleArgs); + FF_STRBUF_AUTO_DESTROY bytesPercentageBar = ffStrbufCreate(); + ffPercentAppendBar(&bytesPercentageBar, bytesPercentage, options->percent, &options->moduleArgs); + + FF_STRBUF_AUTO_DESTROY fragPercentageNum = ffStrbufCreate(); + ffPercentAppendNum(&fragPercentageNum, fragPercentage, options->percent, false, &options->moduleArgs); + FF_STRBUF_AUTO_DESTROY fragPercentageBar = ffStrbufCreate(); + ffPercentAppendBar(&fragPercentageBar, fragPercentage, options->percent, &options->moduleArgs); + + FF_PRINT_FORMAT_CHECKED(buffer.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_ZPOOL_NUM_FORMAT_ARGS, ((FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_STRBUF, &result->name, "name"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->state, "state"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &usedPretty, "size-used"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &totalPretty, "size-total"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &bytesPercentageNum, "size-percentage"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &fragPercentage, "frag-percentage"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &bytesPercentageBar, "size-percentage-bar"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &fragPercentageBar, "frag-percentage-bar"}, + })); + } +} + +void ffPrintZpool(FFZpoolOptions* options) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFZpoolResult)); + + const char* error = ffDetectZpool(&results); + + if (error) + { + ffPrintError(FF_ZPOOL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + return; + } + if(results.length == 0) + { + ffPrintError(FF_ZPOOL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", "No zpool found"); + return; + } + + for(uint32_t i = 0; i < results.length; i++) + { + FFZpoolResult* result = FF_LIST_GET(FFZpoolResult, results, i); + uint8_t index = results.length == 1 ? 0 : (uint8_t) (i + 1); + printZpool(options, result, index); + } + + FF_LIST_FOR_EACH(FFZpoolResult, result, results) + { + ffStrbufDestroy(&result->name); + ffStrbufDestroy(&result->state); + } +} + +bool ffParseZpoolCommandOptions(FFZpoolOptions* options, const char* key, const char* value) +{ + const char* subKey = ffOptionTestPrefix(key, FF_ZPOOL_MODULE_NAME); + if (!subKey) return false; + if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) + return true; + + if (ffPercentParseCommandOptions(key, subKey, value, &options->percent)) + return true; + + return false; +} + +void ffParseZpoolJsonObject(FFZpoolOptions* 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 (ffPercentParseJsonObject(key, val, &options->percent)) + continue; + + ffPrintError(FF_ZPOOL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", key); + } +} + +void ffGenerateZpoolJsonConfig(FFZpoolOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + __attribute__((__cleanup__(ffDestroyZpoolOptions))) FFZpoolOptions defaultOptions; + ffInitZpoolOptions(&defaultOptions); + + ffJsonConfigGenerateModuleArgsConfig(doc, module, &defaultOptions.moduleArgs, &options->moduleArgs); + + ffPercentGenerateJsonConfig(doc, module, defaultOptions.percent, options->percent); +} + +void ffGenerateZpoolJsonResult(FF_MAYBE_UNUSED FFZpoolOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFZpoolResult)); + + const char* error = ffDetectZpool(&results); + 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(FFZpoolResult, zpool, results) + { + 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, "used", zpool->used); + yyjson_mut_obj_add_uint(doc, obj, "total", zpool->total); + yyjson_mut_obj_add_uint(doc, obj, "version", zpool->version); + yyjson_mut_obj_add_uint(doc, obj, "fragmentation", zpool->fragmentation); + } + + FF_LIST_FOR_EACH(FFZpoolResult, battery, results) + { + ffStrbufDestroy(&battery->name); + ffStrbufDestroy(&battery->state); + } +} + +void ffPrintZpoolHelpFormat(void) +{ + FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_ZPOOL_MODULE_NAME, "{3} / {4} ({5}, {6} frag)", FF_ZPOOL_NUM_FORMAT_ARGS, ((const char* []) { + "Zpool name - name", + "Zpool state - state", + "Size used - size-used", + "Size total - size-total", + "Size percentage num - size-percentage", + "Fragmentation percentage num - frag-percentage", + "Size percentage bar - size-percentage-bar", + "Fragmentation percentage bar - frag-percentage-bar", + })); +} + +void ffInitZpoolOptions(FFZpoolOptions* options) +{ + ffOptionInitModuleBaseInfo( + &options->moduleInfo, + FF_ZPOOL_MODULE_NAME, + "Print ZFS storage pools", + ffParseZpoolCommandOptions, + ffParseZpoolJsonObject, + ffPrintZpool, + ffGenerateZpoolJsonResult, + ffPrintZpoolHelpFormat, + ffGenerateZpoolJsonConfig + ); + ffOptionInitModuleArg(&options->moduleArgs, "󱑛"); + options->percent = (FFColorRangeConfig) { 50, 80 }; +} + +void ffDestroyZpoolOptions(FFZpoolOptions* options) +{ + ffOptionDestroyModuleArg(&options->moduleArgs); +} diff --git a/src/modules/zpool/zpool.h b/src/modules/zpool/zpool.h new file mode 100644 index 0000000000..03623e063e --- /dev/null +++ b/src/modules/zpool/zpool.h @@ -0,0 +1,9 @@ +#pragma once + +#include "fastfetch.h" + +#define FF_ZPOOL_MODULE_NAME "Zpool" + +void ffPrintZpool(FFZpoolOptions* options); +void ffInitZpoolOptions(FFZpoolOptions* options); +void ffDestroyZpoolOptions(FFZpoolOptions* options); diff --git a/src/options/modules.c b/src/options/modules.c index 0bc117e3eb..22c6fcf5b1 100644 --- a/src/options/modules.c +++ b/src/options/modules.c @@ -71,6 +71,7 @@ void ffOptionsInitModules(FFOptionsModules* options) ffInitWallpaperOptions(&options->wallpaper); ffInitWeatherOptions(&options->weather); ffInitWifiOptions(&options->wifi); + ffInitZpoolOptions(&options->zpool); } void ffOptionsDestroyModules(FFOptionsModules* options) @@ -143,4 +144,5 @@ void ffOptionsDestroyModules(FFOptionsModules* options) ffDestroyWallpaperOptions(&options->wallpaper); ffDestroyWeatherOptions(&options->weather); ffDestroyWifiOptions(&options->wifi); + ffDestroyZpoolOptions(&options->zpool); } diff --git a/src/options/modules.h b/src/options/modules.h index 1768c8c181..27ec92dc4d 100644 --- a/src/options/modules.h +++ b/src/options/modules.h @@ -72,6 +72,7 @@ typedef struct FFOptionsModules FFWallpaperOptions wallpaper; FFWeatherOptions weather; FFWifiOptions wifi; + FFZpoolOptions zpool; } FFOptionsModules; void ffOptionsInitModules(FFOptionsModules* options); diff --git a/src/util/binary.h b/src/util/binary.h index 2a0e0943de..9ef0ac7e11 100644 --- a/src/util/binary.h +++ b/src/util/binary.h @@ -2,4 +2,4 @@ #include "fastfetch.h" -const char* ffBinaryExtractStrings(const char* file, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata); +const char* ffBinaryExtractStrings(const char* file, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength); diff --git a/src/util/binary_apple.c b/src/util/binary_apple.c index 940228af8e..6efeb5d5a1 100644 --- a/src/util/binary_apple.c +++ b/src/util/binary_apple.c @@ -20,7 +20,7 @@ static inline bool readData(FILE *objFile, void *buf, size_t size, off_t offset) return fread(buf, 1, size, objFile) == size; } -static bool handleMachSection(FILE *objFile, const char *name, off_t offset, size_t size, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata) +static bool handleMachSection(FILE *objFile, const char *name, off_t offset, size_t size, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata, uint32_t minLength) { if (!ffStrEquals(name, "__cstring")) return true; @@ -33,6 +33,7 @@ static bool handleMachSection(FILE *objFile, const char *name, off_t offset, siz const char* p = (const char*) data + off; if (*p == '\0') continue; uint32_t len = (uint32_t) strlen(p); + if (len < minLength) continue; if (*p >= ' ' && *p <= '~') // Ignore control characters { if (!cb(p, len, userdata)) return false; @@ -42,7 +43,7 @@ static bool handleMachSection(FILE *objFile, const char *name, off_t offset, siz return true; } -static const char* dumpMachHeader(FILE *objFile, off_t offset, bool is_64, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata) +static const char* dumpMachHeader(FILE *objFile, off_t offset, bool is_64, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata, uint32_t minLength) { uint32_t ncmds; off_t loadCommandsOffset = offset; @@ -87,7 +88,7 @@ static const char* dumpMachHeader(FILE *objFile, off_t offset, bool is_64, bool if (!readData(objFile, §ion, sizeof(section), (off_t) ((size_t) commandOffset + sizeof(segment) + j * sizeof(section)))) continue; - if (!handleMachSection(objFile, section.sectname, section.offset, section.size, cb, userdata)) + if (!handleMachSection(objFile, section.sectname, section.offset, section.size, cb, userdata, minLength)) return NULL; } } @@ -105,7 +106,7 @@ static const char* dumpMachHeader(FILE *objFile, off_t offset, bool is_64, bool if (!readData(objFile, §ion, sizeof(section), (off_t) ((size_t) commandOffset + sizeof(segment) + j * sizeof(section)))) continue; - if (!handleMachSection(objFile, section.sectname, section.offset, section.size, cb, userdata)) + if (!handleMachSection(objFile, section.sectname, section.offset, section.size, cb, userdata, minLength)) return NULL; } } @@ -116,7 +117,7 @@ static const char* dumpMachHeader(FILE *objFile, off_t offset, bool is_64, bool return NULL; } -static const char* dumpFatHeader(FILE *objFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata) +static const char* dumpFatHeader(FILE *objFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata, uint32_t minLength) { struct fat_header header; if (!readData(objFile, &header, sizeof(header), 0)) @@ -157,14 +158,14 @@ static const char* dumpFatHeader(FILE *objFile, bool (*cb)(const char *str, uint if (magic == MH_MAGIC_64 || magic == MH_MAGIC) { - dumpMachHeader(objFile, machHeaderOffset, magic == MH_MAGIC_64, cb, userdata); + dumpMachHeader(objFile, machHeaderOffset, magic == MH_MAGIC_64, cb, userdata, minLength); return NULL; } } return "Unsupported fat header"; } -const char *ffBinaryExtractStrings(const char *machoFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata) +const char *ffBinaryExtractStrings(const char *machoFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata, uint32_t minLength) { FF_AUTO_CLOSE_FILE FILE *objFile = fopen(machoFile, "rb"); if (objFile == NULL) @@ -180,7 +181,7 @@ const char *ffBinaryExtractStrings(const char *machoFile, bool (*cb)(const char return "Unsupported format or big endian mach-o file"; if (magic == FAT_MAGIC || magic == FAT_MAGIC_64 || magic == FAT_CIGAM || magic == FAT_CIGAM_64) - return dumpFatHeader(objFile, cb, userdata); + return dumpFatHeader(objFile, cb, userdata, minLength); else - return dumpMachHeader(objFile, 0, magic == MH_MAGIC_64, cb, userdata); + return dumpMachHeader(objFile, 0, magic == MH_MAGIC_64, cb, userdata, minLength); } diff --git a/src/util/binary_linux.c b/src/util/binary_linux.c index 11aa2a2096..b3be2b8050 100644 --- a/src/util/binary_linux.c +++ b/src/util/binary_linux.c @@ -23,7 +23,7 @@ struct FFElfData { bool inited; } elfData; -const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata) +const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) { if (!elfData.inited) { @@ -82,6 +82,7 @@ const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* s const char* p = (const char*) data->d_buf + off; if (*p == '\0') continue; uint32_t len = (uint32_t) strlen(p); + if (len < minLength) continue; if (*p >= ' ' && *p <= '~') // Ignore control characters { if (!cb(p, len, userdata)) break; @@ -98,9 +99,9 @@ const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* s #else -const char* ffBinaryExtractStrings(const char* file, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata) +const char* ffBinaryExtractStrings(const char* file, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) { - FF_UNUSED(file, cb, userdata); + FF_UNUSED(file, cb, userdata, minLength); return "Fastfetch was built without libelf support"; } diff --git a/src/util/binary_windows.c b/src/util/binary_windows.c index 6582d9e1df..7f9d842f18 100644 --- a/src/util/binary_windows.c +++ b/src/util/binary_windows.c @@ -8,7 +8,7 @@ #include #include -const char* ffBinaryExtractStrings(const char *peFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata) +const char* ffBinaryExtractStrings(const char *peFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata, uint32_t minLength) { __attribute__((__cleanup__(UnMapAndLoad))) LOADED_IMAGE loadedImage = {}; if (!MapAndLoad(peFile, NULL, &loadedImage, FALSE, TRUE)) @@ -26,6 +26,7 @@ const char* ffBinaryExtractStrings(const char *peFile, bool (*cb)(const char *st const char* p = (const char*) data + off; if (*p == '\0') continue; uint32_t len = (uint32_t) strlen(p); + if (len < minLength) continue; if (*p >= ' ' && *p <= '~') // Ignore control characters { if (!cb(p, len, userdata)) break; diff --git a/tests/format.c b/tests/format.c new file mode 100644 index 0000000000..173a5a298c --- /dev/null +++ b/tests/format.c @@ -0,0 +1,80 @@ +#include "common/format.h" +#include "util/textModifier.h" +#include "fastfetch.h" + +static void verify(const char* format, const char* arg, const char* expected, int lineNo) +{ + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY formatter = ffStrbufCreateStatic(format); + const FFformatarg arguments[] = { + { .type = FF_FORMAT_ARG_TYPE_STRING, arg } + }; + ffParseFormatString(&result, &formatter, 1, arguments); + if (!ffStrbufEqualS(&result, expected)) + { + fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] %s: expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET, lineNo, format, expected, result.chars); + exit(1); + } +} + +#define VERIFY(format, argument, expected) verify((format), (argument), (expected), __LINE__) + +int main(void) +{ + instance.config.display.pipe = true; + + { + VERIFY("output({})", "12345 67890", "output(12345 67890)"); + VERIFY("output({1})", "12345 67890", "output(12345 67890)"); + VERIFY("output({})", "", "output()"); + VERIFY("output({1})", "", "output()"); + } + + { + VERIFY("output({1:20})", "12345 67890", "output(12345 67890)"); + VERIFY("output({1:11})", "12345 67890", "output(12345 67890)"); + VERIFY("output({1:-11})", "12345 67890", "output(12345 67890)"); + VERIFY("output({1:6})", "12345 67890", "output(12345)"); + VERIFY("output({:6})", "12345 67890", "output(12345)"); + VERIFY("output({:-6})", "12345 67890", "output(12345…)"); + VERIFY("output({:0})", "12345 67890", "output()"); + VERIFY("output({:})", "12345 67890", "output()"); + } + + { + VERIFY("output({1<20})", "12345 67890", "output(12345 67890 )"); + VERIFY("output({1<-20})", "12345 67890", "output(12345 67890 )"); + VERIFY("output({1<11})", "12345 67890", "output(12345 67890)"); + VERIFY("output({1<-11})", "12345 67890", "output(12345 67890)"); + VERIFY("output({1<6})", "12345 67890", "output(12345 )"); + VERIFY("output({<6})", "12345 67890", "output(12345 )"); + VERIFY("output({<-6})", "12345 67890", "output(12345…)"); + VERIFY("output({<0})", "12345 67890", "output()"); + VERIFY("output({<})", "12345 67890", "output()"); + } + + { + VERIFY("output({1>20})", "12345 67890", "output( 12345 67890)"); + VERIFY("output({1>-20})", "12345 67890", "output( 12345 67890)"); + VERIFY("output({1>11})", "12345 67890", "output(12345 67890)"); + VERIFY("output({1>-11})", "12345 67890", "output(12345 67890)"); + VERIFY("output({1>6})", "12345 67890", "output(12345 )"); + VERIFY("output({>6})", "12345 67890", "output(12345 )"); + VERIFY("output({>-6})", "12345 67890", "output(12345…)"); + VERIFY("output({>0})", "12345 67890", "output()"); + VERIFY("output({>})", "12345 67890", "output()"); + } + + { + VERIFY("output({1n>20})", "12345 67890", "output({1n>20})"); + VERIFY("output({120})", "12345 67890", "output({120})"); + VERIFY("output({1:11})", "", "output()"); + } + + { + VERIFY("output({1:20}{1<20}{1>20})", "12345 67890", "output(12345 6789012345 67890 12345 67890)"); + } + + //Success + puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET); +}