From 01e78e2179ec391f3a4371de4dc62fe5ae63d7c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 2 Mar 2025 20:29:55 +0800 Subject: [PATCH 01/37] Kmod (NetBSD): add support --- src/util/kmod.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/util/kmod.c b/src/util/kmod.c index 60d1889bf1..defc0ca299 100644 --- a/src/util/kmod.c +++ b/src/util/kmod.c @@ -31,6 +31,50 @@ bool ffKmodLoaded(const char* modName) { return modfind(modName) >= 0; } +#elif __NetBSD__ +#include "util/stringUtils.h" + +#include +#include + +typedef struct FFNbsdModList +{ + int len; + modstat_t mods[]; +} FFNbsdModList; + +bool ffKmodLoaded(const char* modName) +{ + static FFNbsdModList* list = NULL; + + if (list == NULL) + { + struct iovec iov = {}; + + // 初始尝试分配的内存大小 + for (size_t len = 8192;; len = iov.iov_len) + { + iov.iov_len = len; + iov.iov_base = realloc(iov.iov_base, len); + if (modctl(MODCTL_STAT, &iov) < 0) + { + free(iov.iov_base); + return true; // ignore errors + } + + if (len >= iov.iov_len) break; + } + list = (FFNbsdModList*) iov.iov_base; + } + + for (int i = 0; i < list->len; i++) + { + if (ffStrEquals(list->mods[i].ms_name, modName)) + return true; + } + + return false; +} #elif __APPLE__ #include "util/apple/cf_helpers.h" #include From 6e571a31631aac65f4798c5f5a39dd0f4b2356fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 4 Mar 2025 21:13:48 +0800 Subject: [PATCH 02/37] Display (Windows): prefer `DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO_2` --- .../displayserver/displayserver_windows.c | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/detection/displayserver/displayserver_windows.c b/src/detection/displayserver/displayserver_windows.c index 95f435cc94..0773909602 100644 --- a/src/detection/displayserver/displayserver_windows.c +++ b/src/detection/displayserver/displayserver_windows.c @@ -189,26 +189,47 @@ static void detectDisplays(FFDisplayServerResult* ds) if (display) { - DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO advColorInfo = { + DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO_2 advColorInfo2 = { .header = { - .type = DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO, - .size = sizeof(advColorInfo), + .type = DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO_2, + .size = sizeof(advColorInfo2), .adapterId = path->targetInfo.adapterId, .id = path->targetInfo.id, } }; - if (DisplayConfigGetDeviceInfo(&advColorInfo.header) == ERROR_SUCCESS) + if (DisplayConfigGetDeviceInfo(&advColorInfo2.header) == ERROR_SUCCESS) { - if (advColorInfo.advancedColorEnabled) + if (advColorInfo2.highDynamicRangeUserEnabled) display->hdrStatus = FF_DISPLAY_HDR_STATUS_ENABLED; - else if (advColorInfo.advancedColorSupported) + else if (advColorInfo2.highDynamicRangeSupported) display->hdrStatus = FF_DISPLAY_HDR_STATUS_SUPPORTED; else display->hdrStatus = FF_DISPLAY_HDR_STATUS_UNSUPPORTED; - display->bitDepth = (uint8_t) advColorInfo.bitsPerColorChannel; + display->bitDepth = (uint8_t) advColorInfo2.bitsPerColorChannel; } else - display->hdrStatus = FF_DISPLAY_HDR_STATUS_UNKNOWN; + { + DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO advColorInfo = { + .header = { + .type = DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO, + .size = sizeof(advColorInfo), + .adapterId = path->targetInfo.adapterId, + .id = path->targetInfo.id, + } + }; + if (DisplayConfigGetDeviceInfo(&advColorInfo.header) == ERROR_SUCCESS) + { + if (advColorInfo.advancedColorEnabled) + display->hdrStatus = FF_DISPLAY_HDR_STATUS_ENABLED; + else if (advColorInfo.advancedColorSupported) + display->hdrStatus = FF_DISPLAY_HDR_STATUS_SUPPORTED; + else + display->hdrStatus = FF_DISPLAY_HDR_STATUS_UNSUPPORTED; + display->bitDepth = (uint8_t) advColorInfo.bitsPerColorChannel; + } + else + display->hdrStatus = FF_DISPLAY_HDR_STATUS_UNKNOWN; + } if (edidLength > 0) ffEdidGetSerialAndManufactureDate(edidData, &display->serial, &display->manufactureYear, &display->manufactureWeek); } From b569acbb5d45fd440f26073ff2f671c4e7acca15 Mon Sep 17 00:00:00 2001 From: Matheus Castello Date: Tue, 4 Mar 2025 10:57:29 -0300 Subject: [PATCH 03/37] Logo (Builtin): Add Common Torizon OS as a variant --- src/logo/builtin.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/logo/builtin.c b/src/logo/builtin.c index 3e7f81d5d2..b3b39824da 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -1093,6 +1093,16 @@ static const FFlogo C[] = { .colorKeys = FF_COLOR_FG_BLUE, .colorTitle = FF_COLOR_FG_WHITE, }, + // Common Torizon + { + .names = {"common-torizon"}, + .lines = FASTFETCH_DATATEXT_LOGO_TORIZONCORE, + .colors = { + FF_COLOR_FG_LIGHT_WHITE, + FF_COLOR_FG_YELLOW, + FF_COLOR_FG_BLUE + }, + }, // Cosmic DE { .names = {"Cosmic"}, From c1807a11084151140e94d0bb4aaef6b641981538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 4 Mar 2025 23:21:31 +0800 Subject: [PATCH 04/37] CI: remove unnecessary deps --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e8a47a75a2..895c0f4d32 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -575,7 +575,7 @@ jobs: version: '7.6' run: | uname -a - sudo pkg_add -r cmake git pkgconf wayland vulkan-headers vulkan-loader glib2 dconf dbus sqlite3 xfconf imagemagick chafa pulseaudio hwdata + sudo pkg_add -r cmake git pkgconf wayland vulkan-headers vulkan-loader glib2 dconf dbus sqlite3 xfconf imagemagick chafa cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On . cmake --build . --target package --verbose -j4 ./fastfetch --list-features @@ -611,7 +611,7 @@ jobs: version: '10.1' run: | uname -a - sudo pkgin -y install cmake git pkgconf wayland vulkan-headers dconf dbus sqlite3 ImageMagick pulseaudio opencl-headers ocl-icd + sudo pkgin -y install cmake git pkgconf wayland vulkan-headers dconf dbus sqlite3 ImageMagick opencl-headers ocl-icd cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On . cmake --build . --target package --verbose -j4 ./fastfetch --list-features From be89dff12af5760955b34cedbfb707fcaea0314e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 4 Mar 2025 23:22:11 +0800 Subject: [PATCH 05/37] Kmod (NetBSD): add `__attribute__((__packed__))` to ensure memory layout --- src/util/kmod.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/kmod.c b/src/util/kmod.c index defc0ca299..c558481afc 100644 --- a/src/util/kmod.c +++ b/src/util/kmod.c @@ -37,7 +37,7 @@ bool ffKmodLoaded(const char* modName) #include #include -typedef struct FFNbsdModList +typedef struct __attribute__((__packed__)) FFNbsdModList { int len; modstat_t mods[]; From 924c670915c23e33d56c7491dde94667eab0ef4c Mon Sep 17 00:00:00 2001 From: Carter Li Date: Wed, 5 Mar 2025 14:58:45 +0800 Subject: [PATCH 06/37] Packaging: update debian stuff [ci skip] --- debian/changelog | 6 ++++++ debian/files | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 30b84d86ca..18eaa925ed 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +fastfetch (2.38.0) jammy; urgency=medium + + * Update to 2.38.0 + + -- Carter Li Wed, 05 Mar 2025 14:56:24 +0800 + fastfetch (2.37.0) jammy; urgency=medium * Update to 2.37.0 diff --git a/debian/files b/debian/files index f056cc7e5b..475dca9323 100644 --- a/debian/files +++ b/debian/files @@ -1 +1 @@ -fastfetch_2.37.0_source.buildinfo universe/utils optional +fastfetch_2.38.0_source.buildinfo universe/utils optional From c038024108f7d22cd5347916d72fa8f0d968571d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 6 Mar 2025 16:04:22 +0800 Subject: [PATCH 07/37] Wifi (macOS): improve performance of SSID detection on Sequoia Fix #1597 Thanks @torma616 for the information --- src/detection/wifi/wifi_apple.m | 113 +++++++++++--------------------- 1 file changed, 39 insertions(+), 74 deletions(-) diff --git a/src/detection/wifi/wifi_apple.m b/src/detection/wifi/wifi_apple.m index aefe49c881..e6ebe7c88a 100644 --- a/src/detection/wifi/wifi_apple.m +++ b/src/detection/wifi/wifi_apple.m @@ -21,44 +21,41 @@ @interface CWInterface() return CFBridgingRelease(result); } -static NSDictionary* getWifiInfoBySystemProfiler(NSString* ifName) +static bool queryIpconfig(const char* ifName, FFstrbuf* result) { - // Warning: costs about 2s on my machine - static NSArray* spData; - static bool inited; - if (!inited) - { - inited = true; - FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); - if (ffProcessAppendStdOut(&buffer, (char* const[]) { - "system_profiler", - "SPAirPortDataType", - "-xml", - "-detailLevel", - "basic", - NULL - }) != NULL) - return nil; - - spData = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytes:buffer.chars length:buffer.length] - options:NSPropertyListImmutable - format:nil - error:nil]; - } + return ffProcessAppendStdOut(result, (char* const[]) { + "/usr/sbin/ipconfig", + "getsummary", + (char* const) ifName, + NULL + }) == NULL; +} - if (spData) - { - for (NSDictionary* data in spData[0][@"_items"]) - { - for (NSDictionary* inf in data[@"spairport_airport_interfaces"]) - { - if ([ifName isEqualToString:inf[@"_name"]]) - return inf[@"spairport_current_network_information"]; - } - } - } +static bool getWifiInfoByIpconfig(FFstrbuf* ipconfig, const char* prefix, FFstrbuf* result) +{ + // `ipconfig getsummary ` returns a string like this: + // { + // BSSID : + // IPv4 : { + // ... + // } + // IPv6 : { + // ... + // } + // InterfaceType : WiFi + // LinkStatusActive : TRUE + // NetworkID : XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + // SSID : XXXXXX + // Security : WPA2_PSK + // } - return NULL; + const char* start = memmem(ipconfig->chars, ipconfig->length, prefix, strlen(prefix)); + if (!start) return false; + start += strlen(prefix); + const char* end = strchr(start, '\n'); + if (!end) return false; + ffStrbufSetNS(result, (uint32_t) (end - start), start); + return false; } const char* ffDetectWifi(FFlist* result) @@ -93,14 +90,14 @@ @interface CWInterface() continue; NSDictionary* apple = nil; // For getWifiInfoByApple80211 - NSDictionary* sp = nil; // For getWifiInfoBySystemProfiler + FF_STRBUF_AUTO_DESTROY ipconfig = ffStrbufCreate(); if (inf.ssid) // https://developer.apple.com/forums/thread/732431 ffStrbufAppendS(&item->conn.ssid, inf.ssid.UTF8String); else if (apple || (apple = getWifiInfoByApple80211(inf))) ffStrbufAppendS(&item->conn.ssid, [apple[@"SSID_STR"] UTF8String]); - else if (sp || (sp = getWifiInfoBySystemProfiler(inf.interfaceName))) - ffStrbufAppendS(&item->conn.ssid, [sp[@"_name"] UTF8String]); + else if (ipconfig.length || (queryIpconfig(item->inf.description.chars, &ipconfig))) + getWifiInfoByIpconfig(&ipconfig, "\n SSID : ", &item->conn.ssid); else ffStrbufSetStatic(&item->conn.ssid, ""); // https://developer.apple.com/forums/thread/732431 @@ -108,6 +105,8 @@ @interface CWInterface() ffStrbufAppendS(&item->conn.bssid, inf.bssid.UTF8String); else if (apple || (apple = getWifiInfoByApple80211(inf))) ffStrbufAppendS(&item->conn.bssid, [apple[@"BSSID"] UTF8String]); + else if (ipconfig.length || (queryIpconfig(item->inf.description.chars, &ipconfig))) + getWifiInfoByIpconfig(&ipconfig, "\n BSSID : ", &item->conn.bssid); switch(inf.activePHYMode) { @@ -138,22 +137,6 @@ @interface CWInterface() default: if (inf.activePHYMode < 8) ffStrbufAppendF(&item->conn.protocol, "Unknown (%ld)", inf.activePHYMode); - else if (sp || (sp = getWifiInfoBySystemProfiler(inf.interfaceName))) - { - ffStrbufSetS(&item->conn.protocol, [sp[@"spairport_network_phymode"] UTF8String]); - if (ffStrbufStartsWithS(&item->conn.protocol, "802.11")) - { - const char* subProtocol = item->conn.protocol.chars + strlen("802.11"); - if (ffStrEquals(subProtocol, "be")) - ffStrbufSetStatic(&item->conn.protocol, "802.11be (Wi-Fi 7)"); - else if (ffStrEquals(subProtocol, "ax")) - ffStrbufSetStatic(&item->conn.protocol, "802.11ax (Wi-Fi 6)"); - else if (ffStrEquals(subProtocol, "ac")) - ffStrbufSetStatic(&item->conn.protocol, "802.11ac (Wi-Fi 5)"); - else if (ffStrEquals(subProtocol, "n")) - ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)"); - } - } break; } item->conn.signalQuality = (double) (inf.rssiValue >= -50 ? 100 : inf.rssiValue <= -100 ? 0 : (inf.rssiValue + 100) * 2); @@ -266,26 +249,8 @@ @interface CWInterface() } } } - else if (sp || (sp = getWifiInfoBySystemProfiler(inf.interfaceName))) - { - ffStrbufSetS(&item->conn.security, [sp[@"spairport_security_mode"] UTF8String]); - ffStrbufSubstrAfterFirstS(&item->conn.security, "_mode_"); - if (ffStrbufEqualS(&item->conn.security, "none")) - ffStrbufSetStatic(&item->conn.security, "Insecure"); - else - { - ffStrbufReplaceAllC(&item->conn.security, '_', ' '); - if (ffStrbufStartsWithS(&item->conn.security, "wpa")) - { - item->conn.security.chars[0] = 'W'; - item->conn.security.chars[1] = 'P'; - item->conn.security.chars[2] = 'A'; - char* sub = strchr(item->conn.security.chars, ' '); - if (sub && sub[1]) - sub[1] = (char) toupper(sub[1]); - } - } - } + else if (ipconfig.length || (queryIpconfig(item->inf.description.chars, &ipconfig))) + getWifiInfoByIpconfig(&ipconfig, "\n Security : ", &item->conn.security); break; default: ffStrbufAppendF(&item->conn.security, "Unknown (%ld)", inf.security); From 6ef4f62d5b05ef031f09301d7729503a2be3852a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 6 Mar 2025 22:11:36 +0800 Subject: [PATCH 08/37] Terminal (Windows): detect warp version --- src/detection/terminalshell/terminalshell.c | 18 +++--- .../terminalshell/terminalshell_windows.c | 2 +- src/detection/wm/wm_windows.cpp | 2 +- src/util/windows/version.c | 59 ++++++++++++++----- src/util/windows/version.h | 14 ++++- 5 files changed, 71 insertions(+), 24 deletions(-) diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index d3c69d63eb..92bd7294ec 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -20,13 +20,14 @@ #include "util/windows/version.h" #include -static bool getFileVersion(const FFstrbuf* exePath, FFstrbuf* version) +static bool getFileVersion(const FFstrbuf* exePath, const wchar_t* stringName, FFstrbuf* version) { wchar_t exePathW[PATH_MAX]; int len = MultiByteToWideChar(CP_UTF8, 0, exePath->chars, (int)exePath->length, exePathW, ARRAY_SIZE(exePathW)); if (len <= 0) return false; - return ffGetFileVersion(exePathW, version); + return ffGetFileVersion(exePathW, stringName, version); } + #elif __HAIKU__ #include "util/haiku/version.h" #endif @@ -98,7 +99,7 @@ static bool getShellVersionPwsh(FFstrbuf* exe, FFstrbuf* version) } #ifdef _WIN32 - if(getFileVersion(exe, version)) + if(getFileVersion(exe, NULL, version)) { ffStrbufSubstrBeforeLastC(version, '.'); return true; @@ -291,7 +292,7 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath, if(ffStrEqualsIgnCase(exeName, "powershell") || ffStrEqualsIgnCase(exeName, "powershell_ise")) return getShellVersionWinPowerShell(exe, version); - return getFileVersion(exe, version); + return getFileVersion(exe, NULL, version); #endif return false; @@ -737,7 +738,7 @@ static bool getTerminalVersionWindowsTerminal(FFstrbuf* exe, FFstrbuf* version) return true; } - return getFileVersion(exe, version); + return getFileVersion(exe, NULL, version); } static bool getTerminalVersionConEmu(FFstrbuf* exe, FFstrbuf* version) @@ -747,7 +748,7 @@ static bool getTerminalVersionConEmu(FFstrbuf* exe, FFstrbuf* version) if(version->length) return true; - return getFileVersion(exe, version); + return getFileVersion(exe, NULL, version); } #endif @@ -839,6 +840,9 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe if(ffStrbufStartsWithIgnCaseS(processName, "ConEmu")) return getTerminalVersionConEmu(exe, version); + if(ffStrbufIgnCaseEqualS(processName, "warp.exe")) + return getFileVersion(exe, L"ProductVersion", version); + #endif #ifndef _WIN32 @@ -914,7 +918,7 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe #ifdef _WIN32 - return getFileVersion(exe, version); + return getFileVersion(exe, NULL, version); #else diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index 97bfe1820b..68043ab28f 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -87,7 +87,7 @@ static void setShellInfoDetails(FFShellResult* result) if(wcsncmp(module.szModule, L"clink_dll_", strlen("clink_dll_")) == 0) { ffStrbufAppendS(&result->prettyName, " (with Clink "); - ffGetFileVersion(module.szExePath, &result->prettyName); + ffGetFileVersion(module.szExePath, NULL, &result->prettyName); ffStrbufAppendC(&result->prettyName, ')'); break; } diff --git a/src/detection/wm/wm_windows.cpp b/src/detection/wm/wm_windows.cpp index e111968050..62e20a9268 100644 --- a/src/detection/wm/wm_windows.cpp +++ b/src/detection/wm/wm_windows.cpp @@ -79,7 +79,7 @@ const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_MAYBE wchar_t fullPath[MAX_PATH]; wcscpy(fullPath, pPath); wcscat(fullPath, L"\\dwm.exe"); - ffGetFileVersion(fullPath, result); + ffGetFileVersion(fullPath, NULL, result); } CoTaskMemFree(pPath); return NULL; diff --git a/src/util/windows/version.c b/src/util/windows/version.c index e01fe27a14..684a4fcce5 100644 --- a/src/util/windows/version.c +++ b/src/util/windows/version.c @@ -1,28 +1,59 @@ #include "util/windows/version.h" #include "util/mallocHelper.h" +#include "util/windows/unicode.h" #include -bool ffGetFileVersion(const wchar_t* filePath, FFstrbuf* version) +bool ffGetFileVersion(const wchar_t* filePath, const wchar_t* stringName, FFstrbuf* version) { DWORD handle; DWORD size = GetFileVersionInfoSizeW(filePath, &handle); - if(size > 0) + if (size > 0) { - FF_AUTO_FREE void* versionData = malloc(size); - if(GetFileVersionInfoW(filePath, handle, size, versionData)) + FF_AUTO_FREE void *versionData = malloc(size); + if (GetFileVersionInfoW(filePath, handle, size, versionData)) { - VS_FIXEDFILEINFO* verInfo; - UINT len; - if(VerQueryValueW(versionData, L"\\", (void**)&verInfo, &len) && len && verInfo->dwSignature == 0xFEEF04BD) + if (!stringName) { - ffStrbufAppendF(version, "%u.%u.%u.%u", - (unsigned)(( verInfo->dwProductVersionMS >> 16 ) & 0xffff), - (unsigned)(( verInfo->dwProductVersionMS >> 0 ) & 0xffff), - (unsigned)(( verInfo->dwProductVersionLS >> 16 ) & 0xffff), - (unsigned)(( verInfo->dwProductVersionLS >> 0 ) & 0xffff) - ); - return true; + VS_FIXEDFILEINFO* verInfo; + UINT len; + if (VerQueryValueW(versionData, L"\\", (void **)&verInfo, &len) && len && verInfo->dwSignature == 0xFEEF04BD) + { + ffStrbufAppendF(version, "%u.%u.%u.%u", + (unsigned)((verInfo->dwProductVersionMS >> 16) & 0xffff), + (unsigned)((verInfo->dwProductVersionMS >> 0) & 0xffff), + (unsigned)((verInfo->dwProductVersionLS >> 16) & 0xffff), + (unsigned)((verInfo->dwProductVersionLS >> 0) & 0xffff)); + return true; + } + } + else + { + struct + { + WORD language; + WORD codePage; + }* translations; + + UINT translationsLen; + + if (VerQueryValueW(versionData, L"\\VarFileInfo\\Translation", + (void **) &translations, &translationsLen) && + translationsLen >= sizeof(*translations)) + { + wchar_t subBlock[128]; + snwprintf(subBlock, ARRAY_SIZE(subBlock), L"\\StringFileInfo\\%04x%04x\\%ls", + translations[0].language, translations[0].codePage, stringName); + + wchar_t* value; + UINT valueLen; + + if (VerQueryValueW(versionData, subBlock, (void **)&value, &valueLen) && valueLen > 0) + { + ffStrbufSetWS(version, value); + return true; + } + } } } } diff --git a/src/util/windows/version.h b/src/util/windows/version.h index c6206fa5b4..73e7cc1e50 100644 --- a/src/util/windows/version.h +++ b/src/util/windows/version.h @@ -1,3 +1,15 @@ #include "fastfetch.h" -bool ffGetFileVersion(const wchar_t* filePath, FFstrbuf* version); + +/** + * @brief Retrieves a specific version string for a Windows file. + * + * This function gets a version string from a Windows file's version information. + * + * @param filePath The path to the file for which version information is requested. + * @param stringName The name of the specific version string to retrieve (e.g., "FileVersion", "ProductVersion"). + * @param version Pointer to an FFstrbuf where the version string will be stored. + * + * @return true if the version string was successfully retrieved, false otherwise. + */ +bool ffGetFileVersion(const wchar_t* filePath, const wchar_t* stringName, FFstrbuf* version); From b66a009046e1114d2f9c37a2aaa2244e4aa627ac Mon Sep 17 00:00:00 2001 From: WolfTech Innovations Date: Mon, 10 Mar 2025 19:48:39 -0500 Subject: [PATCH 09/37] Logo (Builtin): Change FoxOS to WolfOS (#1534) * Update builtin.c * Update and rename foxos.txt to wolfos.txt * Update builtin.c * Update builtin.c * Update builtin.c * Logo (Builtin): move WolfOS to the correct place * Update wolfos.txt * Update src/logo/builtin.c Co-authored-by: Levvie - she/her <11600822+ItzLevvie@users.noreply.github.com> * Update src/logo/builtin.c Co-authored-by: Levvie - she/her <11600822+ItzLevvie@users.noreply.github.com> --------- Co-authored-by: komaru Co-authored-by: Levvie - she/her <11600822+ItzLevvie@users.noreply.github.com> --- src/logo/ascii/foxos.txt | 8 -------- src/logo/ascii/wolfos.txt | 5 +++++ src/logo/builtin.c | 17 +++++++++-------- 3 files changed, 14 insertions(+), 16 deletions(-) delete mode 100644 src/logo/ascii/foxos.txt create mode 100644 src/logo/ascii/wolfos.txt diff --git a/src/logo/ascii/foxos.txt b/src/logo/ascii/foxos.txt deleted file mode 100644 index 91f114bb0b..0000000000 --- a/src/logo/ascii/foxos.txt +++ /dev/null @@ -1,8 +0,0 @@ - ________ ________ ___ ___ ________ ________ -|\ _____\\ __ \ |\ \ / /|\ __ \|\ ____\ -\ \ \__/\ \ \|\ \ \ \ \/ / | \ \|\ \ \ \___|_ - \ \ __\\ \ \\\ \ \ \ / / \ \ \\\ \ \_____ \ - \ \ \_| \ \ \\\ \ / \/ \ \ \\\ \|____|\ \ - \ \__\ \ \_______\/ /\ \ \ \_______\____\_\ \ - \|__| \|_______/__/ /\ __\ \|_______|\_________\ - |__|/ \|__| \|_________| \ No newline at end of file diff --git a/src/logo/ascii/wolfos.txt b/src/logo/ascii/wolfos.txt new file mode 100644 index 0000000000..f0a7fed32d --- /dev/null +++ b/src/logo/ascii/wolfos.txt @@ -0,0 +1,5 @@ +__ __ _ __ ___ ____ +\ \ / /__ | |/ _|/ _ \/ ___| + \ \ /\ / / _ \| | |_| | | \___ \ + \ V V / (_) | | _| |_| |___) | + \_/\_/ \___/|_|_| \___/|____/ diff --git a/src/logo/builtin.c b/src/logo/builtin.c index b3b39824da..6f938ffb6f 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -1758,14 +1758,6 @@ static const FFlogo F[] = { .colorKeys = FF_COLOR_FG_RED, .colorTitle = FF_COLOR_FG_WHITE, }, - // FoxOS - { - .names = {"FoxOS"}, - .lines = FASTFETCH_DATATEXT_LOGO_FOXOS, - .colors = { - FF_COLOR_FG_YELLOW, - }, - }, // FreeBSD { .names = {"Freebsd"}, @@ -5071,6 +5063,15 @@ static const FFlogo W[] = { .colorKeys = FF_COLOR_FG_CYAN, .colorTitle = FF_COLOR_FG_BLUE, }, + // WolfOS + { + .names = {"WolfOS"}, + .lines = FASTFETCH_DATATEXT_LOGO_WOLFOS, + .colors = { + FF_COLOR_FG_BLUE, + FF_COLOR_FG_GREEN, + }, + }, // LAST {}, }; From 3227e934de83eb4c2b6667dfd81f1de46b3da74d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 14 Mar 2025 09:31:43 +0800 Subject: [PATCH 10/37] Logo (Builtin): add Bredos Fix #1615 --- src/logo/ascii/bredos.txt | 9 +++++++++ src/logo/builtin.c | 10 ++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/logo/ascii/bredos.txt diff --git a/src/logo/ascii/bredos.txt b/src/logo/ascii/bredos.txt new file mode 100644 index 0000000000..d8542fce08 --- /dev/null +++ b/src/logo/ascii/bredos.txt @@ -0,0 +1,9 @@ + ████████████████ + ██████░░░░░░████░░░░██ ████ + ████░░░░████░░░░░░██░░░░░░██░░░░██ + ██░░░░░░░░░░██░░░░░░██░░░░░░░░██░░░░██ + ██░░░░░░░░░░██░░░░░░░░░░░░░░░░██░░░░░░██ +██▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓██ +██▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓▓▓▓██ +██▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░▓▓▓▓▓▓▓▓██ + ██████████████████████████ \ No newline at end of file diff --git a/src/logo/builtin.c b/src/logo/builtin.c index 6f938ffb6f..bea415634f 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -766,6 +766,16 @@ static const FFlogo B[] = { .colorKeys = FF_COLOR_FG_CYAN, .colorTitle = FF_COLOR_FG_GREEN, }, + // BredOS + { + .names = {"Bredos"}, + .lines = FASTFETCH_DATATEXT_LOGO_BREDOS, + .colors = { + FF_COLOR_FG_RGB "198;151;66", //grey + }, + .colorKeys = FF_COLOR_FG_RGB "198;151;66", + .colorTitle = FF_COLOR_FG_RGB "198;151;66", + }, // BSD { .names = {"BSD"}, From 3cd7c6c916ededa801b2496834e520265c448b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 14 Mar 2025 15:30:52 +0800 Subject: [PATCH 11/37] OpenGL: drop OSMesa backend Fix #1618 --- .github/workflows/ci.yml | 20 ++++---- CMakeLists.txt | 5 -- completions/fastfetch.bash | 1 - debian/control | 2 +- doc/json_schema.json | 3 +- src/common/init.c | 3 -- src/data/help.json | 5 +- src/detection/opengl/opengl_linux.c | 74 +---------------------------- src/modules/opengl/opengl.c | 5 -- src/modules/opengl/option.h | 1 - 10 files changed, 15 insertions(+), 104 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 895c0f4d32..d2c18f7903 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: run: uname -a - name: configure project - run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_XCB_RANDR=OFF -DENABLE_XCB=OFF -DENABLE_XRANDR=OFF -DENABLE_X11=OFF -DENABLE_DRM=OFF -DENABLE_DRM_AMDGPU=OFF -DENABLE_GIO=OFF -DENABLE_DCONF=OFF -DENABLE_DBUS=OFF -DENABLE_XFCONF=OFF -DENABLE_SQLITE3=OFF -DENABLE_RPM=OFF -DENABLE_IMAGEMAGICK7=OFF -DENABLE_IMAGEMAGICK6=OFF -DENABLE_CHAFA=OFF -DENABLE_ZLIB=OFF -DENABLE_EGL=OFF -DENABLE_GLX=OFF -DENABLE_OSMESA=OFF -DENABLE_OPENCL=OFF -DENABLE_FREETYPE=OFF -DENABLE_PULSE=OFF -DENABLE_DDCUTIL=OFF -DENABLE_ELF=OFF -DENABLE_DIRECTX_HEADERS=OFF -DENABLE_THREADS=OFF + run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_XCB_RANDR=OFF -DENABLE_XCB=OFF -DENABLE_XRANDR=OFF -DENABLE_X11=OFF -DENABLE_DRM=OFF -DENABLE_DRM_AMDGPU=OFF -DENABLE_GIO=OFF -DENABLE_DCONF=OFF -DENABLE_DBUS=OFF -DENABLE_XFCONF=OFF -DENABLE_SQLITE3=OFF -DENABLE_RPM=OFF -DENABLE_IMAGEMAGICK7=OFF -DENABLE_IMAGEMAGICK6=OFF -DENABLE_CHAFA=OFF -DENABLE_ZLIB=OFF -DENABLE_EGL=OFF -DENABLE_GLX=OFF -DENABLE_OPENCL=OFF -DENABLE_FREETYPE=OFF -DENABLE_PULSE=OFF -DENABLE_DDCUTIL=OFF -DENABLE_ELF=OFF -DENABLE_DIRECTX_HEADERS=OFF -DENABLE_THREADS=OFF - name: build project run: cmake --build . --target package --verbose -j4 @@ -80,7 +80,7 @@ jobs: run: cat /proc/cpuinfo - name: install required packages - run: sudo apt-get update && sudo apt-get install -y libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev directx-headers-dev + run: sudo apt-get update && sudo apt-get install -y libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev directx-headers-dev - name: install linuxbrew packages run: | @@ -149,7 +149,7 @@ jobs: run: cat /proc/cpuinfo - name: install required packages - run: sudo apt-get update && sudo apt-get install -y libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev directx-headers-dev libchafa-dev libddcutil-dev rpm + run: sudo apt-get update && sudo apt-get install -y libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev directx-headers-dev libchafa-dev libddcutil-dev rpm - name: configure project run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr . @@ -208,7 +208,7 @@ jobs: # CMake installed by apt has bug `list sub-command REMOVE_ITEM requires two or more arguments` wget --no-check-certificate https://apt.kitware.com/ubuntu/pool/main/c/cmake/{cmake_3.29.2-0kitware1ubuntu20.04.1_armhf.deb,cmake-data_3.29.2-0kitware1ubuntu20.04.1_all.deb} dpkg -i *.deb - apt-get install -y make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev directx-headers-dev rpm + apt-get install -y make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev directx-headers-dev rpm cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . cmake --build . --target package --verbose -j4 ./fastfetch --list-features @@ -244,7 +244,7 @@ jobs: run: | uname -a apt-get update && apt-get install -y wget - apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev directx-headers-dev rpm + apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev directx-headers-dev rpm cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . cmake --build . --target package --verbose -j4 ./fastfetch --list-features @@ -279,7 +279,7 @@ jobs: githubToken: ${{ github.token }} run: | uname -a - apt-get update && apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libddcutil-dev libchafa-dev libelf-dev directx-headers-dev rpm + apt-get update && apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libddcutil-dev libchafa-dev libelf-dev directx-headers-dev rpm cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . cmake --build . --target package --verbose -j4 ./fastfetch --list-features @@ -314,7 +314,7 @@ jobs: githubToken: ${{ github.token }} run: | uname -a - apt-get update && apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libchafa-dev libelf-dev directx-headers-dev rpm + apt-get update && apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libchafa-dev libelf-dev directx-headers-dev rpm cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . cmake --build . --target package --verbose -j4 ./fastfetch --list-features @@ -349,7 +349,7 @@ jobs: githubToken: ${{ github.token }} run: | uname -a - apt-get update && apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libchafa-dev libelf-dev directx-headers-dev rpm + apt-get update && apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libchafa-dev libelf-dev directx-headers-dev rpm cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . cmake --build . --target package --verbose -j4 ./fastfetch --list-features @@ -504,7 +504,7 @@ jobs: run: | uname -a sudo pkg update - sudo pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl xfce4-conf egl libosmesa opencl ocl-icd v4l_compat chafa + sudo pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl xfce4-conf egl opencl ocl-icd v4l_compat chafa cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On . cmake --build . --target package --verbose -j4 ./fastfetch --list-features @@ -537,7 +537,7 @@ jobs: prepare: | uname -a pkg update - pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl xfce4-conf egl libosmesa opencl ocl-icd v4l_compat chafa libelf + pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl xfce4-conf egl opencl ocl-icd v4l_compat chafa libelf run: | cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On . diff --git a/CMakeLists.txt b/CMakeLists.txt index 9284b746a5..9bc1c4f671 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,7 +74,6 @@ cmake_dependent_option(ENABLE_CHAFA "Enable chafa" ON "ENABLE_IMAGEMAGICK6 OR EN cmake_dependent_option(ENABLE_ZLIB "Enable zlib" ON "ENABLE_IMAGEMAGICK6 OR ENABLE_IMAGEMAGICK7" OFF) cmake_dependent_option(ENABLE_EGL "Enable egl" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR WIN32 OR SunOS OR Haiku" OFF) cmake_dependent_option(ENABLE_GLX "Enable glx" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS" OFF) -cmake_dependent_option(ENABLE_OSMESA "Enable osmesa" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR Haiku" OFF) cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR WIN32 OR ANDROID OR SunOS OR Haiku" OFF) cmake_dependent_option(ENABLE_FREETYPE "Enable freetype" ON "ANDROID" OFF) cmake_dependent_option(ENABLE_PULSE "Enable pulse" ON "LINUX OR SunOS" OFF) @@ -1478,10 +1477,6 @@ else() "GL" ) endif() -ff_lib_enable(OSMESA - "osmesa" - "OSMesa" -) ff_lib_enable(OPENCL "OpenCL" "OpenCL" diff --git a/completions/fastfetch.bash b/completions/fastfetch.bash index 0e7464d6b7..3b48705253 100644 --- a/completions/fastfetch.bash +++ b/completions/fastfetch.bash @@ -97,7 +97,6 @@ __fastfetch_complete_gl() "auto" "egl" "glx" - "osmesa" ) COMPREPLY=($(compgen -W "${__ff_gl_types[*]}" -- "$CURRENT_WORD")) } diff --git a/debian/control b/debian/control index 0f35707a52..e161d94dc1 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: fastfetch Section: universe/utils Priority: optional Maintainer: Carter Li -Build-Depends: libvulkan-dev, libwayland-dev, libxrandr-dev, libxcb-randr0-dev, libdconf-dev, libdbus-1-dev, libmagickcore-dev, libxfconf-0-dev, libsqlite3-dev, librpm-dev, libegl-dev, libglx-dev, libosmesa6-dev, ocl-icd-opencl-dev, libpulse-dev, libdrm-dev, libddcutil-dev, libchafa-dev, directx-headers-dev, pkgconf, cmake (>= 3.12), debhelper (>= 11.2), dh-cmake, dh-cmake-compat (= 1), dh-sequence-cmake, dh-sequence-ctest, ninja-build +Build-Depends: libvulkan-dev, libwayland-dev, libxrandr-dev, libxcb-randr0-dev, libdconf-dev, libdbus-1-dev, libmagickcore-dev, libxfconf-0-dev, libsqlite3-dev, librpm-dev, libegl-dev, libglx-dev, ocl-icd-opencl-dev, libpulse-dev, libdrm-dev, libddcutil-dev, libchafa-dev, directx-headers-dev, pkgconf, cmake (>= 3.12), debhelper (>= 11.2), dh-cmake, dh-cmake-compat (= 1), dh-sequence-cmake, dh-sequence-ctest, ninja-build Standards-Version: 4.0.0 Homepage: https://github.com/fastfetch-cli/fastfetch diff --git a/doc/json_schema.json b/doc/json_schema.json index ae1f3f9d87..eaf5f4c064 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -2563,8 +2563,7 @@ "enum": [ "auto", "egl", - "glx", - "osmesa" + "glx" ], "default": "auto" }, diff --git a/src/common/init.c b/src/common/init.c index 91d30d64cb..1db2aa01d7 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -227,9 +227,6 @@ void ffListFeatures(void) #if FF_HAVE_GLX "glx\n" #endif - #if FF_HAVE_OSMESA - "osmesa\n" - #endif #if FF_HAVE_OPENCL "opencl\n" #endif diff --git a/src/data/help.json b/src/data/help.json index a4a869f5a3..ed422f656c 100644 --- a/src/data/help.json +++ b/src/data/help.json @@ -1419,10 +1419,9 @@ "arg": { "type": "enum", "enum": { - "auto": "Auto detection", + "auto": "Prefer EGL on *nix; prefer platform specific implementation on others", "egl": "EGL", - "glx": "GLX", - "osmesa": "OSMesa" + "glx": "GLX (*nix only)" }, "default": "auto" } diff --git a/src/detection/opengl/opengl_linux.c b/src/detection/opengl/opengl_linux.c index 01491ef666..424cbfe258 100644 --- a/src/detection/opengl/opengl_linux.c +++ b/src/detection/opengl/opengl_linux.c @@ -11,7 +11,7 @@ #endif #endif -#if defined(FF_HAVE_EGL) || defined(FF_HAVE_GLX) || defined(FF_HAVE_OSMESA) +#if defined(FF_HAVE_EGL) || defined(FF_HAVE_GLX) #define FF_HAVE_GL 1 #include "common/library.h" @@ -155,66 +155,6 @@ static const char* detectByGlx(FFOpenGLResult* result) #endif //FF_HAVE_GLX -#ifdef FF_HAVE_OSMESA -#if __has_include() - #include -#else - #include // for sunos -#endif - -typedef struct OSMesaData -{ - FF_LIBRARY_SYMBOL(glGetString) - FF_LIBRARY_SYMBOL(OSMesaGetProcAddress) - FF_LIBRARY_SYMBOL(OSMesaCreateContext) - FF_LIBRARY_SYMBOL(OSMesaMakeCurrent) - FF_LIBRARY_SYMBOL(OSMesaDestroyContext) - - OSMesaContext context; -} OSMesaData; - -static const char* osMesaHandleContext(FFOpenGLResult* result, OSMesaData* data) -{ - unsigned char buffer[FF_OPENGL_BUFFER_WIDTH * FF_OPENGL_BUFFER_HEIGHT * sizeof(uint32_t)]; // 4 bytes per pixel (RGBA) - - if(data->ffOSMesaMakeCurrent(data->context, buffer, GL_UNSIGNED_BYTE, FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT) != GL_TRUE) - return "OSMesaMakeCurrent returned GL_FALSE"; - - ffOpenGLHandleResult(result, data->ffglGetString); - ffStrbufSetF(&result->library, "OSMesa %d.%d.%d", OSMESA_MAJOR_VERSION, OSMESA_MINOR_VERSION, OSMESA_PATCH_VERSION); - return NULL; -} - -static const char* osMesaHandleData(FFOpenGLResult* result, OSMesaData* data) -{ - data->ffglGetString = (void*) data->ffOSMesaGetProcAddress("glGetString"); - if(data->ffglGetString == NULL) - return "OSMesaGetProcAddress(glGetString) returned NULL"; - - data->context = data->ffOSMesaCreateContext(OSMESA_RGBA, NULL); - if(data->context == NULL) - return "OSMesaCreateContext returned NULL"; - - const char* error = osMesaHandleContext(result, data); - data->ffOSMesaDestroyContext(data->context); - return error; -} - -static const char* detectByOsMesa(FFOpenGLResult* result) -{ - OSMesaData data; - - FF_LIBRARY_LOAD(osmesa, "dlopen osmesa failed", "libOSMesa" FF_LIBRARY_EXTENSION, 8); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaGetProcAddress); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaCreateContext); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaMakeCurrent); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaDestroyContext); - - return osMesaHandleData(result, &data); -} - -#endif //FF_HAVE_OSMESA - const char* ffDetectOpenGL(FFOpenGLOptions* options, FFOpenGLResult* result) { #if FF_HAVE_GL @@ -238,15 +178,6 @@ const char* ffDetectOpenGL(FFOpenGLOptions* options, FFOpenGLResult* result) #endif } - if(options->library == FF_OPENGL_LIBRARY_OSMESA) - { - #ifdef FF_HAVE_OSMESA - return detectByOsMesa(result); - #else - return "fastfetch was compiled without osmesa support"; - #endif - } - const char* error = ""; // not NULL dummy value #ifdef FF_HAVE_EGL @@ -259,9 +190,6 @@ const char* ffDetectOpenGL(FFOpenGLOptions* options, FFOpenGLResult* result) error = detectByGlx(result); #endif - //We don't use osmesa in auto mode here, because it is a software implementation, - //that doesn't reflect the opengl supported by the hardware - return error; #else diff --git a/src/modules/opengl/opengl.c b/src/modules/opengl/opengl.c index 8aeeb9c5d2..f6d5398953 100644 --- a/src/modules/opengl/opengl.c +++ b/src/modules/opengl/opengl.c @@ -56,7 +56,6 @@ bool ffParseOpenGLCommandOptions(FFOpenGLOptions* options, const char* key, cons { "auto", FF_OPENGL_LIBRARY_AUTO }, { "egl", FF_OPENGL_LIBRARY_EGL }, { "glx", FF_OPENGL_LIBRARY_GLX }, - { "osmesa", FF_OPENGL_LIBRARY_OSMESA }, {} }); return true; @@ -85,7 +84,6 @@ void ffParseOpenGLJsonObject(FFOpenGLOptions* options, yyjson_val* module) { "auto", FF_OPENGL_LIBRARY_AUTO }, { "egl", FF_OPENGL_LIBRARY_EGL }, { "glx", FF_OPENGL_LIBRARY_GLX }, - { "osmesa", FF_OPENGL_LIBRARY_OSMESA }, {}, }); if (error) @@ -119,9 +117,6 @@ void ffGenerateOpenGLJsonConfig(FFOpenGLOptions* options, yyjson_mut_doc* doc, y case FF_OPENGL_LIBRARY_GLX: yyjson_mut_obj_add_str(doc, module, "library", "glx"); break; - case FF_OPENGL_LIBRARY_OSMESA: - yyjson_mut_obj_add_str(doc, module, "library", "osmesa"); - break; } } } diff --git a/src/modules/opengl/option.h b/src/modules/opengl/option.h index 54fd466c98..bdd4e446a8 100644 --- a/src/modules/opengl/option.h +++ b/src/modules/opengl/option.h @@ -9,7 +9,6 @@ typedef enum __attribute__((__packed__)) FFOpenGLLibrary FF_OPENGL_LIBRARY_AUTO, FF_OPENGL_LIBRARY_EGL, FF_OPENGL_LIBRARY_GLX, - FF_OPENGL_LIBRARY_OSMESA } FFOpenGLLibrary; typedef struct FFOpenGLOptions From bf6d0f871ae49771ea62a851b1ae3435c65fa99c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 14 Mar 2025 15:32:22 +0800 Subject: [PATCH 12/37] Completions (Bash): remove `--lib-*` They are removed long ago --- completions/fastfetch.bash | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/completions/fastfetch.bash b/completions/fastfetch.bash index 3b48705253..b6a0ff6da5 100644 --- a/completions/fastfetch.bash +++ b/completions/fastfetch.bash @@ -358,28 +358,6 @@ __fastfetch_completion() local FF_OPTIONS_PATH=( "-c" "--config" - "--lib-vulkan" - "--lib-wayland" - "--lib-xcb-randr" - "--lib-xcb" - "--lib-xrandr" - "--lib-X11" - "--lib-gio" - "--lib-dconf" - "--lib-dbus" - "--lib-xfconf" - "--lib-sqlite3" - "--lib-rpm" - "--lib-imagemagick" - "--lib-z" - "--lib-chafa" - "--lib-egl" - "--lib-glx" - "--lib-osmesa" - "--lib-opencl" - "--lib-pulse" - "--lib-ddcutil" - "--lib-nm" ) local FF_OPTIONS_LOGO=( From 65c7f68c31ba9f1cc7c0e1280542dc2eb7e9fa60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 14 Mar 2025 15:53:08 +0800 Subject: [PATCH 13/37] Wifi (macOS): remove usage of Apple80211 It's no longer useful in Sequoia --- CMakeLists.txt | 1 - src/detection/wifi/wifi_apple.m | 81 +-------------------------------- 2 files changed, 2 insertions(+), 80 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bc1c4f671..6d1ade90ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1570,7 +1570,6 @@ elseif(APPLE) PRIVATE "-F /System/Library/PrivateFrameworks" PRIVATE "-weak_framework DisplayServices" PRIVATE "-weak_framework MediaRemote" - PRIVATE "-weak_framework Apple80211" ) elseif(WIN32) target_link_libraries(libfastfetch diff --git a/src/detection/wifi/wifi_apple.m b/src/detection/wifi/wifi_apple.m index e6ebe7c88a..082b9966e3 100644 --- a/src/detection/wifi/wifi_apple.m +++ b/src/detection/wifi/wifi_apple.m @@ -4,23 +4,6 @@ #import -struct Apple80211; // https://code.google.com/archive/p/iphone-wireless/wikis/Apple80211.wiki - -// 0 is successful; < 0 is failure -int Apple80211GetInfoCopy(struct Apple80211 *handle, CFDictionaryRef *info) __attribute__((weak_import)); - -@interface CWInterface() -@property (readonly) struct Apple80211* device; -@end - -inline static NSDictionary* getWifiInfoByApple80211(CWInterface* inf) -{ - if (!inf.device || !Apple80211GetInfoCopy) return NULL; - CFDictionaryRef result = NULL; - if (Apple80211GetInfoCopy(inf.device, &result) < 0) return NULL; - return CFBridgingRelease(result); -} - static bool queryIpconfig(const char* ifName, FFstrbuf* result) { return ffProcessAppendStdOut(result, (char* const[]) { @@ -89,13 +72,10 @@ static bool getWifiInfoByIpconfig(FFstrbuf* ipconfig, const char* prefix, FFstrb if(inf.interfaceMode == kCWInterfaceModeNone) continue; - NSDictionary* apple = nil; // For getWifiInfoByApple80211 FF_STRBUF_AUTO_DESTROY ipconfig = ffStrbufCreate(); if (inf.ssid) // https://developer.apple.com/forums/thread/732431 ffStrbufAppendS(&item->conn.ssid, inf.ssid.UTF8String); - else if (apple || (apple = getWifiInfoByApple80211(inf))) - ffStrbufAppendS(&item->conn.ssid, [apple[@"SSID_STR"] UTF8String]); else if (ipconfig.length || (queryIpconfig(item->inf.description.chars, &ipconfig))) getWifiInfoByIpconfig(&ipconfig, "\n SSID : ", &item->conn.ssid); else @@ -103,8 +83,6 @@ static bool getWifiInfoByIpconfig(FFstrbuf* ipconfig, const char* prefix, FFstrb if (inf.bssid) ffStrbufAppendS(&item->conn.bssid, inf.bssid.UTF8String); - else if (apple || (apple = getWifiInfoByApple80211(inf))) - ffStrbufAppendS(&item->conn.bssid, [apple[@"BSSID"] UTF8String]); else if (ipconfig.length || (queryIpconfig(item->inf.description.chars, &ipconfig))) getWifiInfoByIpconfig(&ipconfig, "\n BSSID : ", &item->conn.bssid); @@ -193,63 +171,8 @@ static bool getWifiInfoByIpconfig(FFstrbuf* ipconfig, const char* prefix, FFstrb ffStrbufSetStatic(&item->conn.security, "OWE Transition"); break; case kCWSecurityUnknown: - // Sonoma... - if (apple || (apple = getWifiInfoByApple80211(inf))) - { - NSDictionary* authType = apple[@"AUTH_TYPE"]; - if (authType) - { - // AUTH_LOWER seems useless. `airport` verifies if its value is between 1 and 3, and prints `unknown` if not - - NSNumber* authUpper = authType[@"AUTH_UPPER"]; - if (!authUpper) - ffStrbufSetStatic(&item->conn.security, "Insecure"); - else - { - int authUpperValue = authUpper.intValue; - switch (authUpperValue) - { - case 1: - ffStrbufSetStatic(&item->conn.security, "WPA"); - break; - case 2: - ffStrbufSetStatic(&item->conn.security, "WPA-PSK"); - break; - case 4: - ffStrbufSetStatic(&item->conn.security, "WPA2"); - break; - case 8: - ffStrbufSetStatic(&item->conn.security, "WPA2-PSK"); - break; - case 16: - ffStrbufSetStatic(&item->conn.security, "FT-WPA2-PSK"); - break; - case 32: - ffStrbufSetStatic(&item->conn.security, "LEAP"); - break; - case 64: - ffStrbufSetStatic(&item->conn.security, "802.1X"); - break; - case 128: - ffStrbufSetStatic(&item->conn.security, "FT-WPA2"); - break; - case 256: - ffStrbufSetStatic(&item->conn.security, "WPS"); - break; - case 4096: - ffStrbufSetStatic(&item->conn.security, "WPA3-SAE"); - break; - case 8192: - ffStrbufSetStatic(&item->conn.security, "WPA3-FT-SAE"); - break; - default: // TODO: support more auth types - ffStrbufAppendF(&item->conn.security, "To be supported (%d)", authUpperValue); - break; - } - } - } - } - else if (ipconfig.length || (queryIpconfig(item->inf.description.chars, &ipconfig))) + // Sonoma? + if (ipconfig.length || (queryIpconfig(item->inf.description.chars, &ipconfig))) getWifiInfoByIpconfig(&ipconfig, "\n Security : ", &item->conn.security); break; default: From 8ecd58dd14fdc9dd73f119b2d2e24cbc58960fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 14 Mar 2025 16:11:58 +0800 Subject: [PATCH 14/37] Doc: update changelog [ci skip] --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f80f37bb8e..7444b21891 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +# 2.39.0 + +Changes: +* OSMesa backend for OpenGL detection is removed (#1618) +* Fastfetch no longer tries to use the private framework `Apple80211` to acquire SSID for Wifi module, which is only useful for macOS Sonoma (Wifi, macOS) + +Features: +* Improve accuracy of HDR support on Windows 11 24H2 (Display, Windows) +* Improve performance of SSID detection on macOS Sequoia (Wifi, macOS, #1597) +* Support warp terminal version detection on Windows (Terminal, Windows) + +Logo: +* Add Common Torizon OS +* Change FoxOS to WolfOS +* Add Bredos + # 2.38.0 Bugfixes: From 85654316c7803db907a80139c3802a0a8793fb1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 14 Mar 2025 16:55:07 +0800 Subject: [PATCH 15/37] NetIF (OpenBSD): fix default route detection --- CHANGELOG.md | 1 + src/common/netif/netif_bsd.c | 40 +++++++++++++++++++++++++++++++++++ src/modules/localip/localip.c | 2 +- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7444b21891..f453ed0ba4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Features: * Improve accuracy of HDR support on Windows 11 24H2 (Display, Windows) * Improve performance of SSID detection on macOS Sequoia (Wifi, macOS, #1597) * Support warp terminal version detection on Windows (Terminal, Windows) +* Support default route detection for OpenBSD (LocalIP, OpenBSD) Logo: * Add Common Torizon OS diff --git a/src/common/netif/netif_bsd.c b/src/common/netif/netif_bsd.c index 91a270905b..4e43614f49 100644 --- a/src/common/netif/netif_bsd.c +++ b/src/common/netif/netif_bsd.c @@ -1,6 +1,7 @@ #include "netif.h" #include "common/io/io.h" +#include "util/mallocHelper.h" #include #include @@ -8,6 +9,10 @@ #include #include +#ifdef __OpenBSD__ + #include +#endif + #define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n)) #if defined(__APPLE__) @@ -47,6 +52,39 @@ get_rt_address(struct rt_msghdr *rtm, int desired) bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) { + #if defined(__OpenBSD__) + int mib[6] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_GATEWAY}; + size_t needed; + + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + return false; + + FF_AUTO_FREE char* buf = malloc(needed); + + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) + return false; + + char* lim = buf + needed; + struct rt_msghdr* rtm; + for (char* next = buf; next < lim; next += rtm->rtm_msglen) + { + rtm = (struct rt_msghdr *)next; + struct sockaddr* sa = (struct sockaddr *)(rtm + 1); + + if ((rtm->rtm_flags & RTF_GATEWAY) && (sa->sa_family == AF_INET)) + { + struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(rtm, RTA_IFP); + if (sdl->sdl_family == AF_LINK) + { + assert(sdl->sdl_nlen <= IF_NAMESIZE); + memcpy(iface, sdl->sdl_data, sdl->sdl_nlen); + iface[sdl->sdl_nlen] = '\0'; + *ifIndex = sdl->sdl_index; + return true; + } + } + } + #else //https://github.com/hashPirate/copenheimer-masscan-fork/blob/36f1ed9f7b751a7dccd5ed27874e2e703db7d481/src/rawsock-getif.c#L104 FF_AUTO_CLOSE_FD int pfRoute = socket(PF_ROUTE, SOCK_RAW, AF_INET); @@ -106,5 +144,7 @@ bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) return false; } } + #endif + return false; } diff --git a/src/modules/localip/localip.c b/src/modules/localip/localip.c index 8553809297..9ba08f0278 100644 --- a/src/modules/localip/localip.c +++ b/src/modules/localip/localip.c @@ -525,7 +525,7 @@ void ffInitLocalIpOptions(FFLocalIpOptions* options) ffOptionInitModuleArg(&options->moduleArgs, "󰩟"); options->showType = FF_LOCALIP_TYPE_IPV4_BIT | FF_LOCALIP_TYPE_PREFIX_LEN_BIT - #if !__ANDROID__ /*Permission denied*/ && !__OpenBSD__ /*Report invalid argument for some reason*/ && !__DragonFly__ /*Doesn't work*/ + #if !__ANDROID__ /*Permission denied*/ && !__DragonFly__ /*Doesn't work*/ | FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT #endif ; From 7400f14322ec8aa82ef1ca7a2fe2a0496663e31a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 14 Mar 2025 23:41:09 +0800 Subject: [PATCH 16/37] Netif (DragonFly): use code of OpenBSD --- CHANGELOG.md | 2 +- src/common/netif/netif_bsd.c | 6 +++--- src/modules/localip/localip.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f453ed0ba4..7deb8440c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ Features: * Improve accuracy of HDR support on Windows 11 24H2 (Display, Windows) * Improve performance of SSID detection on macOS Sequoia (Wifi, macOS, #1597) * Support warp terminal version detection on Windows (Terminal, Windows) -* Support default route detection for OpenBSD (LocalIP, OpenBSD) +* Support default route detection on OpenBSD & DragonFly BSD (LocalIP, OpenBSD / DragonFly) Logo: * Add Common Torizon OS diff --git a/src/common/netif/netif_bsd.c b/src/common/netif/netif_bsd.c index 4e43614f49..f85c39a629 100644 --- a/src/common/netif/netif_bsd.c +++ b/src/common/netif/netif_bsd.c @@ -9,7 +9,7 @@ #include #include -#ifdef __OpenBSD__ +#if defined(__OpenBSD__) || defined(__DragonFly__) #include #endif @@ -19,7 +19,7 @@ # define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) #elif defined(__NetBSD__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t)) -#elif defined(__FreeBSD__) +#elif defined(__FreeBSD__) || defined(__DragonFly__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) #elif defined(__OpenBSD__) # define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) @@ -52,7 +52,7 @@ get_rt_address(struct rt_msghdr *rtm, int desired) bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) { - #if defined(__OpenBSD__) + #if defined(__OpenBSD__) || defined(__DragonFly__) int mib[6] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_GATEWAY}; size_t needed; diff --git a/src/modules/localip/localip.c b/src/modules/localip/localip.c index 9ba08f0278..2500ab32d0 100644 --- a/src/modules/localip/localip.c +++ b/src/modules/localip/localip.c @@ -525,7 +525,7 @@ void ffInitLocalIpOptions(FFLocalIpOptions* options) ffOptionInitModuleArg(&options->moduleArgs, "󰩟"); options->showType = FF_LOCALIP_TYPE_IPV4_BIT | FF_LOCALIP_TYPE_PREFIX_LEN_BIT - #if !__ANDROID__ /*Permission denied*/ && !__DragonFly__ /*Doesn't work*/ + #if !__ANDROID__ /*Permission denied*/ | FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT #endif ; From 72afa9ef99449b57328b59249ac4a002346b59b3 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Sat, 15 Mar 2025 19:49:51 +0800 Subject: [PATCH 17/37] Networking (Linux): improve performance by TFO and response compression --- src/common/networking.h | 4 + src/common/networking_linux.c | 540 +++++++++++++++++++++++++++++++--- src/common/time.h | 12 + src/options/display.c | 5 + src/options/display.h | 3 + src/util/debug.h | 28 ++ 6 files changed, 552 insertions(+), 40 deletions(-) create mode 100644 src/util/debug.h diff --git a/src/common/networking.h b/src/common/networking.h index 8122e4eb00..a11114e35b 100644 --- a/src/common/networking.h +++ b/src/common/networking.h @@ -7,6 +7,8 @@ #include #endif +struct addrinfo; + typedef struct FFNetworkingState { #ifdef _WIN32 uintptr_t sockfd; @@ -15,6 +17,7 @@ typedef struct FFNetworkingState { int sockfd; FFstrbuf host; FFstrbuf command; + struct addrinfo* addr; #ifdef FF_HAVE_THREADS FFThreadType thread; @@ -23,6 +26,7 @@ typedef struct FFNetworkingState { uint32_t timeout; bool ipv6; + bool compression; } FFNetworkingState; const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers); diff --git a/src/common/networking_linux.c b/src/common/networking_linux.c index 0bc39d6126..d945ea6840 100644 --- a/src/common/networking_linux.c +++ b/src/common/networking_linux.c @@ -1,5 +1,10 @@ #include "fastfetch.h" #include "common/networking.h" +#include "common/time.h" +#include "common/library.h" +#include "util/stringUtils.h" +#include "util/mallocHelper.h" +#include "util/debug.h" #include #include @@ -7,92 +12,459 @@ #include #include // For FreeBSD #include +#include +#include +// Try to use TCP Fast Open to send data +static const char* tryTcpFastOpen(FFNetworkingState* state) +{ + #if !defined(MSG_FASTOPEN) || !defined(TCP_FASTOPEN) + FF_DEBUG("TCP Fast Open not supported on this system"); + FF_UNUSED(state); + return "TCP Fast Open not supported"; + #else + FF_DEBUG("Attempting to use TCP Fast Open to connect to %s", state->host.chars); + + // Set TCP Fast Open + int qlen = 5; + if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)) != 0) { + FF_DEBUG("Failed to set TCP_FASTOPEN option: %s", strerror(errno)); + } else { + FF_DEBUG("Successfully set TCP_FASTOPEN option, queue length: %d", qlen); + } + + // Set non-blocking mode + int flags = fcntl(state->sockfd, F_GETFL, 0); + FF_DEBUG("Current socket flags: 0x%x", flags); + + if (fcntl(state->sockfd, F_SETFL, flags | O_NONBLOCK) < 0) { + FF_DEBUG("Failed to set non-blocking mode: %s", strerror(errno)); + } else { + FF_DEBUG("Successfully set non-blocking mode"); + } + + // Try to send data using Fast Open + FF_DEBUG("Using sendto() + MSG_FASTOPEN to send %u bytes of data", state->command.length); + ssize_t sent = sendto(state->sockfd, + state->command.chars, + state->command.length, + MSG_FASTOPEN, + state->addr->ai_addr, + state->addr->ai_addrlen); + + // Restore blocking mode + fcntl(state->sockfd, F_SETFL, flags); + + if (sent >= 0 || (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK)) + { + FF_DEBUG("TCP Fast Open succeeded or in progress (sent=%zd, errno=%d: %s)", + sent, errno, sent < 0 ? strerror(errno) : ""); + freeaddrinfo(state->addr); + state->addr = NULL; + ffStrbufDestroy(&state->host); + ffStrbufDestroy(&state->command); + return NULL; + } + + // Fast Open failed + FF_DEBUG("TCP Fast Open failed: %s (errno=%d)", strerror(errno), errno); + return "sendto() failed"; + #endif +} + +// Traditional connect and send function static const char* connectAndSend(FFNetworkingState* state) { const char* ret = NULL; - struct addrinfo* addr; + FF_DEBUG("Using traditional connection method to connect to %s", state->host.chars); + + FF_DEBUG("Attempting connect() to server..."); + if(connect(state->sockfd, state->addr->ai_addr, state->addr->ai_addrlen) == -1) + { + FF_DEBUG("connect() failed: %s (errno=%d)", strerror(errno), errno); + ret = "connect() failed"; + goto error; + } + FF_DEBUG("connect() succeeded"); + + FF_DEBUG("Attempting to send %u bytes of data...", state->command.length); + if(send(state->sockfd, state->command.chars, state->command.length, 0) < 0) + { + FF_DEBUG("send() failed: %s (errno=%d)", strerror(errno), errno); + ret = "send() failed"; + goto error; + } + FF_DEBUG("Data sent successfully"); - if(getaddrinfo(state->host.chars, "80", &(struct addrinfo) { + goto exit; + +error: + FF_DEBUG("Error occurred, closing socket"); + close(state->sockfd); + state->sockfd = -1; + +exit: + FF_DEBUG("Releasing address info and other resources"); + freeaddrinfo(state->addr); + state->addr = NULL; + ffStrbufDestroy(&state->host); + ffStrbufDestroy(&state->command); + + return ret; +} + +FF_THREAD_ENTRY_DECL_WRAPPER(connectAndSend, FFNetworkingState*); + +// Parallel DNS resolution and socket creation +static const char* initNetworkingState(FFNetworkingState* state, const char* host, const char* path, const char* headers) +{ + FF_DEBUG("Initializing network connection state: host=%s, path=%s", host, path); + + // Initialize command and host information + ffStrbufInitS(&state->host, host); + + ffStrbufInitA(&state->command, 64); + ffStrbufAppendS(&state->command, "GET "); + ffStrbufAppendS(&state->command, path); + ffStrbufAppendS(&state->command, " HTTP/1.1\nHost: "); + ffStrbufAppendS(&state->command, host); + ffStrbufAppendS(&state->command, "\r\n"); + + // Add extra optimized HTTP headers + ffStrbufAppendS(&state->command, "Connection: close\r\n"); // Explicitly tell the server we don't need to keep the connection + + // If compression needs to be enabled + if (state->compression) { + FF_DEBUG("Enabling HTTP content compression"); + ffStrbufAppendS(&state->command, "Accept-Encoding: gzip\r\n"); + } + + ffStrbufAppendS(&state->command, headers); + ffStrbufAppendS(&state->command, "\r\n"); + + #ifdef FF_HAVE_THREADS + state->thread = 0; + FF_DEBUG("Thread ID initialized to 0"); + #endif + + const char* ret = NULL; + + struct addrinfo hints = { .ai_family = state->ipv6 ? AF_INET6 : AF_INET, .ai_socktype = SOCK_STREAM, - }, &addr) != 0) + .ai_flags = AI_NUMERICSERV + }; + + FF_DEBUG("Resolving address: %s (%s)", host, state->ipv6 ? "IPv6" : "IPv4"); + // Use AI_NUMERICSERV flag to indicate the service is a numeric port, reducing parsing time + + if(getaddrinfo(host, "80", &hints, &state->addr) != 0) { + FF_DEBUG("getaddrinfo() failed: %s", gai_strerror(errno)); ret = "getaddrinfo() failed"; goto error; } + FF_DEBUG("Address resolution successful"); - state->sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + FF_DEBUG("Creating socket"); + state->sockfd = socket(state->addr->ai_family, state->addr->ai_socktype, state->addr->ai_protocol); if(state->sockfd == -1) { - freeaddrinfo(addr); + FF_DEBUG("socket() failed: %s (errno=%d)", strerror(errno), errno); ret = "socket() failed"; goto error; } + FF_DEBUG("Socket creation successful: fd=%d", state->sockfd); + + int flag = 1; + #ifdef TCP_NODELAY + // Disable Nagle's algorithm to reduce small packet transmission delay + if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)) != 0) { + FF_DEBUG("Failed to set TCP_NODELAY: %s", strerror(errno)); + } else { + FF_DEBUG("Successfully disabled Nagle's algorithm"); + } + #endif + + #ifdef TCP_QUICKACK + // Set TCP_QUICKACK option to avoid delayed acknowledgments + if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_QUICKACK, &flag, sizeof(flag)) != 0) { + FF_DEBUG("Failed to set TCP_QUICKACK: %s", strerror(errno)); + } else { + FF_DEBUG("Successfully enabled TCP quick acknowledgment"); + } + #endif if (state->timeout > 0) { + FF_DEBUG("Setting connection timeout: %u ms", state->timeout); FF_MAYBE_UNUSED uint32_t sec = state->timeout / 1000; if (sec == 0) sec = 1; #ifdef TCP_CONNECTIONTIMEOUT + FF_DEBUG("Using TCP_CONNECTIONTIMEOUT: %u seconds", sec); setsockopt(state->sockfd, IPPROTO_TCP, TCP_CONNECTIONTIMEOUT, &sec, sizeof(sec)); #elif defined(TCP_KEEPINIT) + FF_DEBUG("Using TCP_KEEPINIT: %u seconds", sec); setsockopt(state->sockfd, IPPROTO_TCP, TCP_KEEPINIT, &sec, sizeof(sec)); #elif defined(TCP_USER_TIMEOUT) + FF_DEBUG("Using TCP_USER_TIMEOUT: %u milliseconds", state->timeout); setsockopt(state->sockfd, IPPROTO_TCP, TCP_USER_TIMEOUT, &state->timeout, sizeof(state->timeout)); + #else + FF_DEBUG("Current platform does not support TCP connection timeout"); #endif } - if(connect(state->sockfd, addr->ai_addr, addr->ai_addrlen) == -1) + return NULL; + +error: + FF_DEBUG("Error occurred during initialization"); + if (state->addr != NULL) { + FF_DEBUG("Releasing address information"); + freeaddrinfo(state->addr); + state->addr = NULL; + } + + if (state->sockfd > 0) + { + FF_DEBUG("Closing socket: fd=%d", state->sockfd); close(state->sockfd); - freeaddrinfo(addr); - ret = "connect() failed"; - goto error; + state->sockfd = -1; } + return ret; +} - freeaddrinfo(addr); +#ifdef FF_HAVE_ZLIB +#include - if(send(state->sockfd, state->command.chars, state->command.length, 0) < 0) +struct FFZlibLibrary +{ + FF_LIBRARY_SYMBOL(inflateInit2_) + FF_LIBRARY_SYMBOL(inflate) + FF_LIBRARY_SYMBOL(inflateEnd) + FF_LIBRARY_SYMBOL(inflateGetHeader) + + bool inited; +} zlibData; + +static const char* loadZlibLibrary(void) +{ + if (!zlibData.inited) { - close(state->sockfd); - ret = "send() failed"; - goto error; + zlibData.inited = true; + FF_LIBRARY_LOAD(zlib, "dlopen libz failed", "libz" FF_LIBRARY_EXTENSION, 2) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateInit2_) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflate) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateEnd) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateGetHeader) + zlib = NULL; // don't auto dlclose } + return zlibData.ffinflateEnd == NULL ? "Failed to load libz" : NULL; +} - goto exit; +// Try to pre-read gzip header to determine uncompressed size +static uint32_t guessGzipOutputSize(const void* data, uint32_t dataSize) +{ + // gzip file format: http://www.zlib.org/rfc-gzip.html + if (dataSize < 10 || ((const uint8_t*)data)[0] != 0x1f || ((const uint8_t*)data)[1] != 0x8b) + return 0; -error: - state->sockfd = -1; + // Uncompressed size in gzip format is stored in the last 4 bytes, but only valid if data is less than 4GB + if (dataSize > 18) { + // Get ISIZE value from the end of file (little endian) + const uint8_t* tail = (const uint8_t*)data + dataSize - 4; + uint32_t uncompressedSize = tail[0] | (tail[1] << 8) | (tail[2] << 16) | (tail[3] << 24); -exit: - ffStrbufDestroy(&state->host); - ffStrbufDestroy(&state->command); + // For valid gzip files, this value is the length of the uncompressed data modulo 2^32 + if (uncompressedSize > 0) { + FF_DEBUG("Read uncompressed size from GZIP trailer: %u bytes", uncompressedSize); + // Add some margin to the estimated size for safety + return uncompressedSize + 64; + } + } - return ret; + // If unable to get size from trailer or size is 0, use estimated value + // Typically, text data compression ratio is between 3-5x, we use the larger value + uint32_t estimatedSize = dataSize * 5; + FF_DEBUG("Unable to read exact uncompressed size, estimated as 5x of compressed data: %u bytes", estimatedSize); + return estimatedSize; } -FF_THREAD_ENTRY_DECL_WRAPPER(connectAndSend, FFNetworkingState*); +// Decompress gzip content +static bool decompressGzip(FFstrbuf* buffer, char* headerEnd) +{ + // Calculate header size + uint32_t headerSize = (uint32_t) (headerEnd - buffer->chars); + + *headerEnd = '\0'; // Replace delimiter with null character for easier processing + // Ensure Content-Encoding is in response headers, not in response body + bool hasGzip = strcasestr(buffer->chars, "\nContent-Encoding: gzip") != NULL; + *headerEnd = '\r'; // Restore delimiter + + if (!hasGzip) { + FF_DEBUG("No gzip compressed content detected, skipping decompression"); + return true; + } + + FF_DEBUG("Gzip compressed content detected, preparing for decompression"); + + const char* bodyStart = headerEnd + 4; // Skip delimiter + + // Calculate compressed content size + uint32_t compressedSize = buffer->length - headerSize - 4; + + if (compressedSize <= 0) { + // No content to decompress + FF_DEBUG("Compressed content size is 0, skipping decompression"); + return true; + } + + // Check if content is actually in gzip format (gzip header magic is 0x1f 0x8b) + if (compressedSize < 2 || (uint8_t)bodyStart[0] != 0x1f || (uint8_t)bodyStart[1] != 0x8b) { + FF_DEBUG("Content is not valid gzip format, skipping decompression"); + return false; + } + + // Predict uncompressed size + uint32_t estimatedSize = guessGzipOutputSize(bodyStart, compressedSize); + + // Create decompression buffer with estimated size + FF_STRBUF_AUTO_DESTROY decompressedBuffer = ffStrbufCreateA(estimatedSize > 0 ? estimatedSize : compressedSize * 5); + FF_DEBUG("Created decompression buffer: %u bytes", decompressedBuffer.allocated); + + // Initialize decompression + z_stream zs = { + .zalloc = Z_NULL, + .zfree = Z_NULL, + .opaque = Z_NULL, + .avail_in = (uInt)compressedSize, + .next_in = (Bytef*)bodyStart, + .avail_out = (uInt)ffStrbufGetFree(&decompressedBuffer), + .next_out = (Bytef*)decompressedBuffer.chars, + }; + + // Initialize decompression engine + if (zlibData.ffinflateInit2_(&zs, 16 + MAX_WBITS, ZLIB_VERSION, (int)sizeof(z_stream)) != Z_OK) { + FF_DEBUG("Failed to initialize decompression engine"); + return false; + } + uInt availableOut = zs.avail_out; + + // Perform decompression + int result = zlibData.ffinflate(&zs, Z_FINISH); + + // If output buffer is insufficient, try to extend buffer + while (result == Z_BUF_ERROR || (result != Z_STREAM_END && zs.avail_out == 0)) + { + FF_DEBUG("Output buffer insufficient, trying to extend"); + + // Save already decompressed data amount + uint32_t alreadyDecompressed = (uint32_t)(availableOut - zs.avail_out); + decompressedBuffer.length += alreadyDecompressed; + decompressedBuffer.chars[decompressedBuffer.length] = '\0'; // Ensure null-terminated string + + ffStrbufEnsureFree(&decompressedBuffer, decompressedBuffer.length / 2); + + // Set output parameters to point to new buffer + zs.avail_out = (uInt)ffStrbufGetFree(&decompressedBuffer); + zs.next_out = (Bytef*)(decompressedBuffer.chars + decompressedBuffer.length); + availableOut = zs.avail_out; + + // Decompress again + result = zlibData.ffinflate(&zs, Z_FINISH); + } + + zlibData.ffinflateEnd(&zs); + + // Calculate decompressed size + uint32_t decompressedSize = (uint32_t)(availableOut - zs.avail_out); + decompressedBuffer.length += decompressedSize; + decompressedBuffer.chars[decompressedSize] = '\0'; // Ensure null-terminated string + FF_DEBUG("Successfully decompressed %u bytes compressed data to %u bytes", compressedSize, decompressedBuffer.length); + + // Modify Content-Length header and remove Content-Encoding header + FF_STRBUF_AUTO_DESTROY newBuffer = ffStrbufCreateA(headerSize + decompressedSize + 64); + + char* line = NULL; + size_t len = 0; + while (ffStrbufGetline(&line, &len, buffer)) + { + if (ffStrStartsWithIgnCase(line, "Content-Encoding:")) + { + continue; + } + else if (ffStrStartsWithIgnCase(line, "Content-Length:")) + { + ffStrbufAppendF(&newBuffer, "Content-Length: %u\r\n", decompressedSize); + continue; + } + else if (line[0] == '\r') + { + ffStrbufAppendS(&newBuffer, "\r\n"); + ffStrbufGetlineRestore(&line, &len, buffer); + break; + } + + ffStrbufAppendS(&newBuffer, line); + ffStrbufAppendC(&newBuffer, '\n'); + } + + ffStrbufAppend(&newBuffer, &decompressedBuffer); + ffStrbufDestroy(buffer); + ffStrbufInitMove(buffer, &newBuffer); + + return true; +} +#endif const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers) { - ffStrbufInitS(&state->host, host); + FF_DEBUG("Preparing to send HTTP request: host=%s, path=%s", host, path); - ffStrbufInitA(&state->command, 64); - ffStrbufAppendS(&state->command, "GET "); - ffStrbufAppendS(&state->command, path); - ffStrbufAppendS(&state->command, " HTTP/1.1\nHost: "); - ffStrbufAppendS(&state->command, host); - ffStrbufAppendS(&state->command, "\r\n"); - ffStrbufAppendS(&state->command, headers); - ffStrbufAppendS(&state->command, "\r\n"); + // Initialize with compression disabled by default + state->compression = false; + + #ifdef FF_HAVE_ZLIB + const char* zlibError = loadZlibLibrary(); + // Only enable compression if zlib library is successfully loaded + if (zlibError == NULL) + { + state->compression = true; + FF_DEBUG("Successfully loaded zlib library, compression enabled"); + } else { + FF_DEBUG("Failed to load zlib library, compression disabled: %s", zlibError); + } + #else + FF_DEBUG("zlib not supported at build time, compression disabled"); + #endif + + const char* initResult = initNetworkingState(state, host, path, headers); + if (initResult != NULL) { + FF_DEBUG("Initialization failed: %s", initResult); + return initResult; + } + FF_DEBUG("Network state initialization successful"); + + const char* tfoResult = tryTcpFastOpen(state); + if (tfoResult == NULL) { + FF_DEBUG("TCP Fast Open succeeded or in progress"); + return NULL; + } + FF_DEBUG("TCP Fast Open unavailable or failed: %s, trying traditional connection", tfoResult); #ifdef FF_HAVE_THREADS if (instance.config.general.multithreading) { + FF_DEBUG("Multithreading mode enabled, creating connection thread"); state->thread = ffThreadCreate(connectAndSendThreadMain, state); - return state->thread ? NULL : "ffThreadCreate(connectAndSend) failed"; + if (state->thread) { + FF_DEBUG("Thread creation successful: thread=%p", (void*)state->thread); + return NULL; + } + FF_DEBUG("Thread creation failed"); + } else { + FF_DEBUG("Multithreading mode disabled, connecting in main thread"); } #endif @@ -101,37 +473,125 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer) { + FF_DEBUG("Preparing to receive HTTP response"); uint32_t timeout = state->timeout; #ifdef FF_HAVE_THREADS - if (instance.config.general.multithreading) + if (state->thread) { - if (!ffThreadJoin(state->thread, timeout)) + FF_DEBUG("Connection thread is running, waiting for it to complete (timeout=%u ms)", timeout); + if (!ffThreadJoin(state->thread, timeout)) { + FF_DEBUG("Thread join failed or timed out"); return "ffThreadJoin() failed or timeout"; + } + FF_DEBUG("Thread completed successfully"); + state->thread = 0; } #endif if(state->sockfd == -1) + { + FF_DEBUG("Invalid socket, HTTP request might have failed"); return "ffNetworkingSendHttpRequest() failed"; + } if(timeout > 0) { + FF_DEBUG("Setting receive timeout: %u ms", timeout); struct timeval timev; timev.tv_sec = timeout / 1000; timev.tv_usec = (__typeof__(timev.tv_usec)) ((timeout % 1000) * 1000); //milliseconds to microseconds setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev)); } - uint32_t recvStart; + // Set larger initial receive buffer instead of small repeated receives + int rcvbuf = 65536; // 64KB + setsockopt(state->sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); + + FF_DEBUG("Starting data reception"); + FF_MAYBE_UNUSED int recvCount = 0; + uint32_t contentLength = 0; + char* headerEnd = NULL; + do { - recvStart = buffer->length; + size_t availSpace = ffStrbufGetFree(buffer); + FF_DEBUG("Data reception loop #%d, current buffer size: %u, available space: %zu", + ++recvCount, buffer->length, availSpace); + ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, ffStrbufGetFree(buffer), 0); - if (received <= 0) break; + + if (received <= 0) { + if (received == 0) { + FF_DEBUG("Connection closed (received=0)"); + } else { + FF_DEBUG("Reception failed: %s (errno=%d)", strerror(errno), errno); + } + break; + } + buffer->length += (uint32_t) received; buffer->chars[buffer->length] = '\0'; - } while (ffStrbufGetFree(buffer) > 0 && strstr(buffer->chars + recvStart, "\r\n\r\n") == NULL); + FF_DEBUG("Successfully received %zd bytes of data, total: %u bytes", received, buffer->length); + + // Check if HTTP header end marker is found + if (headerEnd == NULL) { + headerEnd = memmem(buffer->chars, buffer->length, "\r\n\r\n", 4); + if (headerEnd != NULL) { + FF_DEBUG("Found HTTP header end marker, position: %ld", (long)(headerEnd - buffer->chars)); + + // Check for Content-Length header to pre-allocate enough memory + const char* clHeader = strcasestr(buffer->chars, "Content-Length:"); + if (clHeader) { + contentLength = (uint32_t) strtoul(clHeader + 16, NULL, 10); + if (contentLength > 0) { + FF_DEBUG("Detected Content-Length: %u, pre-allocating buffer", contentLength); + // Ensure buffer is large enough, adding header size and some margin + ffStrbufEnsureFree(buffer, contentLength + 16); + FF_DEBUG("Extended receive buffer to %u bytes", buffer->length); + } + } + } + } + } while (ffStrbufGetFree(buffer) > 0); + + FF_DEBUG("Closing socket: fd=%d", state->sockfd); close(state->sockfd); - if (buffer->length == 0) return "Empty server response received"; - return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n") ? NULL : "Invalid response"; + state->sockfd = -1; + + if (buffer->length == 0) { + FF_DEBUG("Server response is empty"); + return "Empty server response received"; + } + + if (headerEnd == NULL) { + FF_DEBUG("No HTTP header end marker found"); + return "No HTTP header end found"; + } + if (contentLength > 0 && buffer->length != contentLength + (uint32_t)(headerEnd - buffer->chars) + 4) { + FF_DEBUG("Received content length mismatches: %u != %u", buffer->length, contentLength + (uint32_t)(headerEnd - buffer->chars) + 4); + return "Content length mismatch"; + } + + if (ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n")) { + FF_DEBUG("Received valid HTTP 200 response, content %u bytes, total %u bytes", contentLength, buffer->length); + } else { + FF_DEBUG("Invalid response: %.40s...", buffer->chars); + return "Invalid response"; + } + + // If compression was used, try to decompress + #ifdef FF_HAVE_ZLIB + if (state->compression) { + FF_DEBUG("Content received, checking if compressed"); + if (!decompressGzip(buffer, headerEnd)) { + FF_DEBUG("Decompression failed or invalid compression format"); + return "Failed to decompress or invalid format"; + } else { + FF_DEBUG("Decompression successful or no decompression needed, total length after decompression: %u bytes", buffer->length); + } + } + #endif + + return NULL; } diff --git a/src/common/time.h b/src/common/time.h index a041209059..0745574bbb 100644 --- a/src/common/time.h +++ b/src/common/time.h @@ -82,6 +82,18 @@ static inline const char* ffTimeToShortStr(uint64_t msec) return buf; } +// Not thread-safe +static inline const char* ffTimeToTimeStr(uint64_t msec) +{ + if (msec == 0) return ""; + time_t tsec = (time_t) (msec / 1000); + + static char buf[32]; + strftime(buf, sizeof(buf), "%T", localtime(&tsec)); + sprintf(buf + __builtin_strlen("00:00:00"), ".%03u", (unsigned) (msec % 1000)); + return buf; +} + #ifdef _WIN32 #pragma GCC diagnostic pop #endif diff --git a/src/options/display.c b/src/options/display.c index 247abfc6fc..61393a91ba 100644 --- a/src/options/display.c +++ b/src/options/display.c @@ -317,6 +317,10 @@ bool ffOptionsParseDisplayCommandLine(FFOptionsDisplay* options, const char* key options->pipe = ffOptionParseBoolean(value); else if(ffStrEqualsIgnCase(key, "--show-errors")) options->showErrors = ffOptionParseBoolean(value); + #ifndef NDEBUG + else if(ffStrEqualsIgnCase(key, "--debug")) + options->debugMode = ffOptionParseBoolean(value); + #endif else if(ffStrEqualsIgnCase(key, "--disable-linewrap")) options->disableLinewrap = ffOptionParseBoolean(value); else if(ffStrEqualsIgnCase(key, "--hide-cursor")) @@ -505,6 +509,7 @@ void ffOptionsInitDisplay(FFOptionsDisplay* options) options->disableLinewrap = !options->pipe; #else options->disableLinewrap = false; + options->debugMode = false; #endif options->hideCursor = false; diff --git a/src/options/display.h b/src/options/display.h index f96f304bec..74c4ecb3bb 100644 --- a/src/options/display.h +++ b/src/options/display.h @@ -32,6 +32,9 @@ typedef struct FFOptionsDisplay int32_t stat; // <0: disable stat; 0: no threshold; >0: threshold in ms bool pipe; //disables all escape sequences bool showErrors; + #ifndef NDEBUG + bool debugMode; + #endif bool disableLinewrap; bool hideCursor; FFSizeBinaryPrefixType sizeBinaryPrefix; diff --git a/src/util/debug.h b/src/util/debug.h new file mode 100644 index 0000000000..9e9d2e7101 --- /dev/null +++ b/src/util/debug.h @@ -0,0 +1,28 @@ +#include "common/time.h" + +static inline const char* ffFindFileName(const char* file) +{ + const char* lastSlash = __builtin_strrchr(file, + #ifdef _WIN32 + '\\' + #else + '/' + #endif + ); + if (lastSlash != NULL) + return lastSlash + 1; + return file; +} + +#ifndef NDEBUG + #define FF_DEBUG_PRINT(file_, line_, format_, ...) \ + do { \ + if (instance.config.display.debugMode) \ + fprintf(stderr, "[%s%4d, %s] " format_ "\n", ffFindFileName(file_), line_, ffTimeToTimeStr(ffTimeGetNow()), ##__VA_ARGS__); \ + } while (0) +#else + #define FF_DEBUG_PRINT(file_, line_, format_, ...) \ + do { } while (0) +#endif + +#define FF_DEBUG(format, ...) FF_DEBUG_PRINT(__FILE__, __LINE__, format, ##__VA_ARGS__) From 840434e709a4939c9a1eaf1ddaf1344aab62d03b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sat, 15 Mar 2025 00:46:55 +0800 Subject: [PATCH 18/37] Netif (Windows): use `GetIpForwardTable2` to support IPv6 --- src/common/netif/netif.c | 2 +- src/common/netif/netif_windows.c | 31 ++++++++++++++++++++----------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/common/netif/netif.c b/src/common/netif/netif.c index ec317454b8..da5a838594 100644 --- a/src/common/netif/netif.c +++ b/src/common/netif/netif.c @@ -3,7 +3,7 @@ #ifndef _WIN32 #include #else - #define IF_NAMESIZE 16 + #define IF_NAMESIZE 0 #endif bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex); diff --git a/src/common/netif/netif_windows.c b/src/common/netif/netif_windows.c index 8607ed4866..6042dd8d6e 100644 --- a/src/common/netif/netif_windows.c +++ b/src/common/netif/netif_windows.c @@ -1,27 +1,36 @@ #include "netif.h" #include "util/mallocHelper.h" +#include // AF_INET6, IN6_IS_ADDR_UNSPECIFIED #include -bool ffNetifGetDefaultRouteImpl(FF_MAYBE_UNUSED char* iface/* unsupported */, uint32_t* ifIndex) +bool ffNetifGetDefaultRouteImpl(FF_MAYBE_UNUSED char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) { - ULONG size = 0; - if (GetIpForwardTable(NULL, &size, TRUE) != ERROR_INSUFFICIENT_BUFFER) - return false; + PMIB_IPFORWARD_TABLE2 pIpForwardTable = NULL; + DWORD result = GetIpForwardTable2(AF_UNSPEC, &pIpForwardTable); - FF_AUTO_FREE MIB_IPFORWARDTABLE* pIpForwardTable = (MIB_IPFORWARDTABLE*) malloc(size); - if (GetIpForwardTable(pIpForwardTable, &size, TRUE) != ERROR_SUCCESS) + if (result != NO_ERROR) return false; - for (uint32_t i = 0; i < pIpForwardTable->dwNumEntries; ++i) + bool foundDefault = false; + + for (ULONG i = 0; i < pIpForwardTable->NumEntries; ++i) { - MIB_IPFORWARDROW* ipForwardRow = &pIpForwardTable->table[i]; - if (ipForwardRow->dwForwardDest == 0 && ipForwardRow->dwForwardMask == 0) + MIB_IPFORWARD_ROW2* row = &pIpForwardTable->Table[i]; + + if ((row->DestinationPrefix.PrefixLength == 0) && + ((row->DestinationPrefix.Prefix.Ipv4.sin_family == AF_INET && + row->DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr == 0) || + (row->DestinationPrefix.Prefix.Ipv6.sin6_family == AF_INET6 && + IN6_IS_ADDR_UNSPECIFIED(&row->DestinationPrefix.Prefix.Ipv6.sin6_addr)))) { - *ifIndex = ipForwardRow->dwForwardIfIndex; + *ifIndex = row->InterfaceIndex; + foundDefault = true; break; } } - return true; + FreeMibTable(pIpForwardTable); + + return foundDefault; } From 244625d26a8eff1d2ad0ad4ddeb0dce06b2155e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sat, 15 Mar 2025 23:35:58 +0800 Subject: [PATCH 19/37] Networking (Windows): apply the same optimizations of *nix's --- CMakeLists.txt | 1 + src/common/networking.h | 5 + src/common/networking_common.c | 188 ++++++++++++++++++++++++++++++ src/common/networking_linux.c | 187 +----------------------------- src/common/networking_windows.c | 197 +++++++++++++++++++++++++++++--- src/util/debug.h | 12 +- 6 files changed, 379 insertions(+), 211 deletions(-) create mode 100644 src/common/networking_common.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d1ade90ea..6ac15a7835 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -364,6 +364,7 @@ set(LIBFASTFETCH_SRC src/common/library.c src/common/modules.c src/common/netif/netif.c + src/common/networking_common.c src/common/option.c src/common/parsing.c src/common/printing.c diff --git a/src/common/networking.h b/src/common/networking.h index a11114e35b..338827eaa6 100644 --- a/src/common/networking.h +++ b/src/common/networking.h @@ -31,3 +31,8 @@ typedef struct FFNetworkingState { const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers); const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer); + +#ifdef FF_HAVE_ZLIB +const char* ffNetworkingLoadZlibLibrary(void); +bool ffNetworkingDecompressGzip(FFstrbuf* buffer, char* headerEnd); +#endif diff --git a/src/common/networking_common.c b/src/common/networking_common.c new file mode 100644 index 0000000000..769860ff7f --- /dev/null +++ b/src/common/networking_common.c @@ -0,0 +1,188 @@ +#include "fastfetch.h" +#include "common/library.h" +#include "common/networking.h" +#include "util/stringUtils.h" +#include "util/debug.h" + +#ifdef FF_HAVE_ZLIB +#include + +struct FFZlibLibrary +{ + FF_LIBRARY_SYMBOL(inflateInit2_) + FF_LIBRARY_SYMBOL(inflate) + FF_LIBRARY_SYMBOL(inflateEnd) + FF_LIBRARY_SYMBOL(inflateGetHeader) + + bool inited; +} zlibData; + +const char* ffNetworkingLoadZlibLibrary(void) +{ + if (!zlibData.inited) + { + zlibData.inited = true; + FF_LIBRARY_LOAD(zlib, "dlopen libz failed", "libz" FF_LIBRARY_EXTENSION, 2) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateInit2_) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflate) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateEnd) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateGetHeader) + zlib = NULL; // don't auto dlclose + } + return zlibData.ffinflateEnd == NULL ? "Failed to load libz" : NULL; +} + +// Try to pre-read gzip header to determine uncompressed size +static uint32_t guessGzipOutputSize(const void* data, uint32_t dataSize) +{ + // gzip file format: http://www.zlib.org/rfc-gzip.html + if (dataSize < 10 || ((const uint8_t*)data)[0] != 0x1f || ((const uint8_t*)data)[1] != 0x8b) + return 0; + + // Uncompressed size in gzip format is stored in the last 4 bytes, but only valid if data is less than 4GB + if (dataSize > 18) { + // Get ISIZE value from the end of file (little endian) + const uint8_t* tail = (const uint8_t*)data + dataSize - 4; + uint32_t uncompressedSize = tail[0] | (tail[1] << 8) | (tail[2] << 16) | (tail[3] << 24); + + // For valid gzip files, this value is the length of the uncompressed data modulo 2^32 + if (uncompressedSize > 0) { + FF_DEBUG("Read uncompressed size from GZIP trailer: %u bytes", uncompressedSize); + // Add some margin to the estimated size for safety + return uncompressedSize + 64; + } + } + + // If unable to get size from trailer or size is 0, use estimated value + // Typically, text data compression ratio is between 3-5x, we use the larger value + uint32_t estimatedSize = dataSize * 5; + FF_DEBUG("Unable to read exact uncompressed size, estimated as 5x of compressed data: %u bytes", estimatedSize); + return estimatedSize; +} + +// Decompress gzip content +bool ffNetworkingDecompressGzip(FFstrbuf* buffer, char* headerEnd) +{ + // Calculate header size + uint32_t headerSize = (uint32_t) (headerEnd - buffer->chars); + + *headerEnd = '\0'; // Replace delimiter with null character for easier processing + // Ensure Content-Encoding is in response headers, not in response body + bool hasGzip = strcasestr(buffer->chars, "\nContent-Encoding: gzip") != NULL; + *headerEnd = '\r'; // Restore delimiter + + if (!hasGzip) { + FF_DEBUG("No gzip compressed content detected, skipping decompression"); + return true; + } + + FF_DEBUG("Gzip compressed content detected, preparing for decompression"); + + const char* bodyStart = headerEnd + 4; // Skip delimiter + + // Calculate compressed content size + uint32_t compressedSize = buffer->length - headerSize - 4; + + if (compressedSize <= 0) { + // No content to decompress + FF_DEBUG("Compressed content size is 0, skipping decompression"); + return true; + } + + // Check if content is actually in gzip format (gzip header magic is 0x1f 0x8b) + if (compressedSize < 2 || (uint8_t)bodyStart[0] != 0x1f || (uint8_t)bodyStart[1] != 0x8b) { + FF_DEBUG("Content is not valid gzip format, skipping decompression"); + return false; + } + + // Predict uncompressed size + uint32_t estimatedSize = guessGzipOutputSize(bodyStart, compressedSize); + + // Create decompression buffer with estimated size + FF_STRBUF_AUTO_DESTROY decompressedBuffer = ffStrbufCreateA(estimatedSize > 0 ? estimatedSize : compressedSize * 5); + FF_DEBUG("Created decompression buffer: %u bytes", decompressedBuffer.allocated); + + // Initialize decompression + z_stream zs = { + .zalloc = Z_NULL, + .zfree = Z_NULL, + .opaque = Z_NULL, + .avail_in = (uInt)compressedSize, + .next_in = (Bytef*)bodyStart, + .avail_out = (uInt)ffStrbufGetFree(&decompressedBuffer), + .next_out = (Bytef*)decompressedBuffer.chars, + }; + + // Initialize decompression engine + if (zlibData.ffinflateInit2_(&zs, 16 + MAX_WBITS, ZLIB_VERSION, (int)sizeof(z_stream)) != Z_OK) { + FF_DEBUG("Failed to initialize decompression engine"); + return false; + } + uInt availableOut = zs.avail_out; + + // Perform decompression + int result = zlibData.ffinflate(&zs, Z_FINISH); + + // If output buffer is insufficient, try to extend buffer + while (result == Z_BUF_ERROR || (result != Z_STREAM_END && zs.avail_out == 0)) + { + FF_DEBUG("Output buffer insufficient, trying to extend"); + + // Save already decompressed data amount + uint32_t alreadyDecompressed = (uint32_t)(availableOut - zs.avail_out); + decompressedBuffer.length += alreadyDecompressed; + decompressedBuffer.chars[decompressedBuffer.length] = '\0'; // Ensure null-terminated string + + ffStrbufEnsureFree(&decompressedBuffer, decompressedBuffer.length / 2); + + // Set output parameters to point to new buffer + zs.avail_out = (uInt)ffStrbufGetFree(&decompressedBuffer); + zs.next_out = (Bytef*)(decompressedBuffer.chars + decompressedBuffer.length); + availableOut = zs.avail_out; + + // Decompress again + result = zlibData.ffinflate(&zs, Z_FINISH); + } + + zlibData.ffinflateEnd(&zs); + + // Calculate decompressed size + uint32_t decompressedSize = (uint32_t)(availableOut - zs.avail_out); + decompressedBuffer.length += decompressedSize; + decompressedBuffer.chars[decompressedSize] = '\0'; // Ensure null-terminated string + FF_DEBUG("Successfully decompressed %u bytes compressed data to %u bytes", compressedSize, decompressedBuffer.length); + + // Modify Content-Length header and remove Content-Encoding header + FF_STRBUF_AUTO_DESTROY newBuffer = ffStrbufCreateA(headerSize + decompressedSize + 64); + + char* line = NULL; + size_t len = 0; + while (ffStrbufGetline(&line, &len, buffer)) + { + if (ffStrStartsWithIgnCase(line, "Content-Encoding:")) + { + continue; + } + else if (ffStrStartsWithIgnCase(line, "Content-Length:")) + { + ffStrbufAppendF(&newBuffer, "Content-Length: %u\r\n", decompressedSize); + continue; + } + else if (line[0] == '\r') + { + ffStrbufAppendS(&newBuffer, "\r\n"); + ffStrbufGetlineRestore(&line, &len, buffer); + break; + } + + ffStrbufAppendS(&newBuffer, line); + ffStrbufAppendC(&newBuffer, '\n'); + } + + ffStrbufAppend(&newBuffer, &decompressedBuffer); + ffStrbufDestroy(buffer); + ffStrbufInitMove(buffer, &newBuffer); + + return true; +} +#endif // FF_HAVE_ZLIB diff --git a/src/common/networking_linux.c b/src/common/networking_linux.c index d945ea6840..76ec71dbfa 100644 --- a/src/common/networking_linux.c +++ b/src/common/networking_linux.c @@ -235,189 +235,6 @@ static const char* initNetworkingState(FFNetworkingState* state, const char* hos return ret; } -#ifdef FF_HAVE_ZLIB -#include - -struct FFZlibLibrary -{ - FF_LIBRARY_SYMBOL(inflateInit2_) - FF_LIBRARY_SYMBOL(inflate) - FF_LIBRARY_SYMBOL(inflateEnd) - FF_LIBRARY_SYMBOL(inflateGetHeader) - - bool inited; -} zlibData; - -static const char* loadZlibLibrary(void) -{ - if (!zlibData.inited) - { - zlibData.inited = true; - FF_LIBRARY_LOAD(zlib, "dlopen libz failed", "libz" FF_LIBRARY_EXTENSION, 2) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateInit2_) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflate) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateEnd) - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateGetHeader) - zlib = NULL; // don't auto dlclose - } - return zlibData.ffinflateEnd == NULL ? "Failed to load libz" : NULL; -} - -// Try to pre-read gzip header to determine uncompressed size -static uint32_t guessGzipOutputSize(const void* data, uint32_t dataSize) -{ - // gzip file format: http://www.zlib.org/rfc-gzip.html - if (dataSize < 10 || ((const uint8_t*)data)[0] != 0x1f || ((const uint8_t*)data)[1] != 0x8b) - return 0; - - // Uncompressed size in gzip format is stored in the last 4 bytes, but only valid if data is less than 4GB - if (dataSize > 18) { - // Get ISIZE value from the end of file (little endian) - const uint8_t* tail = (const uint8_t*)data + dataSize - 4; - uint32_t uncompressedSize = tail[0] | (tail[1] << 8) | (tail[2] << 16) | (tail[3] << 24); - - // For valid gzip files, this value is the length of the uncompressed data modulo 2^32 - if (uncompressedSize > 0) { - FF_DEBUG("Read uncompressed size from GZIP trailer: %u bytes", uncompressedSize); - // Add some margin to the estimated size for safety - return uncompressedSize + 64; - } - } - - // If unable to get size from trailer or size is 0, use estimated value - // Typically, text data compression ratio is between 3-5x, we use the larger value - uint32_t estimatedSize = dataSize * 5; - FF_DEBUG("Unable to read exact uncompressed size, estimated as 5x of compressed data: %u bytes", estimatedSize); - return estimatedSize; -} - -// Decompress gzip content -static bool decompressGzip(FFstrbuf* buffer, char* headerEnd) -{ - // Calculate header size - uint32_t headerSize = (uint32_t) (headerEnd - buffer->chars); - - *headerEnd = '\0'; // Replace delimiter with null character for easier processing - // Ensure Content-Encoding is in response headers, not in response body - bool hasGzip = strcasestr(buffer->chars, "\nContent-Encoding: gzip") != NULL; - *headerEnd = '\r'; // Restore delimiter - - if (!hasGzip) { - FF_DEBUG("No gzip compressed content detected, skipping decompression"); - return true; - } - - FF_DEBUG("Gzip compressed content detected, preparing for decompression"); - - const char* bodyStart = headerEnd + 4; // Skip delimiter - - // Calculate compressed content size - uint32_t compressedSize = buffer->length - headerSize - 4; - - if (compressedSize <= 0) { - // No content to decompress - FF_DEBUG("Compressed content size is 0, skipping decompression"); - return true; - } - - // Check if content is actually in gzip format (gzip header magic is 0x1f 0x8b) - if (compressedSize < 2 || (uint8_t)bodyStart[0] != 0x1f || (uint8_t)bodyStart[1] != 0x8b) { - FF_DEBUG("Content is not valid gzip format, skipping decompression"); - return false; - } - - // Predict uncompressed size - uint32_t estimatedSize = guessGzipOutputSize(bodyStart, compressedSize); - - // Create decompression buffer with estimated size - FF_STRBUF_AUTO_DESTROY decompressedBuffer = ffStrbufCreateA(estimatedSize > 0 ? estimatedSize : compressedSize * 5); - FF_DEBUG("Created decompression buffer: %u bytes", decompressedBuffer.allocated); - - // Initialize decompression - z_stream zs = { - .zalloc = Z_NULL, - .zfree = Z_NULL, - .opaque = Z_NULL, - .avail_in = (uInt)compressedSize, - .next_in = (Bytef*)bodyStart, - .avail_out = (uInt)ffStrbufGetFree(&decompressedBuffer), - .next_out = (Bytef*)decompressedBuffer.chars, - }; - - // Initialize decompression engine - if (zlibData.ffinflateInit2_(&zs, 16 + MAX_WBITS, ZLIB_VERSION, (int)sizeof(z_stream)) != Z_OK) { - FF_DEBUG("Failed to initialize decompression engine"); - return false; - } - uInt availableOut = zs.avail_out; - - // Perform decompression - int result = zlibData.ffinflate(&zs, Z_FINISH); - - // If output buffer is insufficient, try to extend buffer - while (result == Z_BUF_ERROR || (result != Z_STREAM_END && zs.avail_out == 0)) - { - FF_DEBUG("Output buffer insufficient, trying to extend"); - - // Save already decompressed data amount - uint32_t alreadyDecompressed = (uint32_t)(availableOut - zs.avail_out); - decompressedBuffer.length += alreadyDecompressed; - decompressedBuffer.chars[decompressedBuffer.length] = '\0'; // Ensure null-terminated string - - ffStrbufEnsureFree(&decompressedBuffer, decompressedBuffer.length / 2); - - // Set output parameters to point to new buffer - zs.avail_out = (uInt)ffStrbufGetFree(&decompressedBuffer); - zs.next_out = (Bytef*)(decompressedBuffer.chars + decompressedBuffer.length); - availableOut = zs.avail_out; - - // Decompress again - result = zlibData.ffinflate(&zs, Z_FINISH); - } - - zlibData.ffinflateEnd(&zs); - - // Calculate decompressed size - uint32_t decompressedSize = (uint32_t)(availableOut - zs.avail_out); - decompressedBuffer.length += decompressedSize; - decompressedBuffer.chars[decompressedSize] = '\0'; // Ensure null-terminated string - FF_DEBUG("Successfully decompressed %u bytes compressed data to %u bytes", compressedSize, decompressedBuffer.length); - - // Modify Content-Length header and remove Content-Encoding header - FF_STRBUF_AUTO_DESTROY newBuffer = ffStrbufCreateA(headerSize + decompressedSize + 64); - - char* line = NULL; - size_t len = 0; - while (ffStrbufGetline(&line, &len, buffer)) - { - if (ffStrStartsWithIgnCase(line, "Content-Encoding:")) - { - continue; - } - else if (ffStrStartsWithIgnCase(line, "Content-Length:")) - { - ffStrbufAppendF(&newBuffer, "Content-Length: %u\r\n", decompressedSize); - continue; - } - else if (line[0] == '\r') - { - ffStrbufAppendS(&newBuffer, "\r\n"); - ffStrbufGetlineRestore(&line, &len, buffer); - break; - } - - ffStrbufAppendS(&newBuffer, line); - ffStrbufAppendC(&newBuffer, '\n'); - } - - ffStrbufAppend(&newBuffer, &decompressedBuffer); - ffStrbufDestroy(buffer); - ffStrbufInitMove(buffer, &newBuffer); - - return true; -} -#endif - const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers) { FF_DEBUG("Preparing to send HTTP request: host=%s, path=%s", host, path); @@ -426,7 +243,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho state->compression = false; #ifdef FF_HAVE_ZLIB - const char* zlibError = loadZlibLibrary(); + const char* zlibError = ffNetworkingLoadZlibLibrary(); // Only enable compression if zlib library is successfully loaded if (zlibError == NULL) { @@ -584,7 +401,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf #ifdef FF_HAVE_ZLIB if (state->compression) { FF_DEBUG("Content received, checking if compressed"); - if (!decompressGzip(buffer, headerEnd)) { + if (!ffNetworkingDecompressGzip(buffer, headerEnd)) { FF_DEBUG("Decompression failed or invalid compression format"); return "Failed to decompress or invalid format"; } else { diff --git a/src/common/networking_windows.c b/src/common/networking_windows.c index 305f558642..2210c8feae 100644 --- a/src/common/networking_windows.c +++ b/src/common/networking_windows.c @@ -4,37 +4,68 @@ //Must be included after #include "fastfetch.h" #include "common/networking.h" +#include "util/stringUtils.h" +#include "util/debug.h" static LPFN_CONNECTEX ConnectEx; static const char* initWsaData(WSADATA* wsaData) { - if(WSAStartup(MAKEWORD(2, 2), wsaData) != 0) + FF_DEBUG("Initializing WinSock"); + if(WSAStartup(MAKEWORD(2, 2), wsaData) != 0) { + FF_DEBUG("WSAStartup() failed"); return "WSAStartup() failed"; + } - if(LOBYTE(wsaData->wVersion) != 2 || HIBYTE(wsaData->wVersion) != 2) + if(LOBYTE(wsaData->wVersion) != 2 || HIBYTE(wsaData->wVersion) != 2) { + FF_DEBUG("Invalid wsaData version found: %d.%d", LOBYTE(wsaData->wVersion), HIBYTE(wsaData->wVersion)); return "Invalid wsaData version found"; + } //Dummy socket needed for WSAIoctl SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0); - if(sockfd == INVALID_SOCKET) + if(sockfd == INVALID_SOCKET) { + FF_DEBUG("socket(AF_INET, SOCK_STREAM) failed"); return "socket(AF_INET, SOCK_STREAM) failed"; + } DWORD dwBytes; GUID guid = WSAID_CONNECTEX; if(WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &ConnectEx, sizeof(ConnectEx), - &dwBytes, NULL, NULL) != 0) + &dwBytes, NULL, NULL) != 0) { + FF_DEBUG("WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER) failed"); return "WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER) failed"; + } closesocket(sockfd); + FF_DEBUG("WinSock initialized successfully"); return NULL; } const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers) { + FF_DEBUG("Preparing to send HTTP request: host=%s, path=%s", host, path); + + // Initialize with compression disabled by default + state->compression = false; + + #ifdef FF_HAVE_ZLIB + const char* zlibError = ffNetworkingLoadZlibLibrary(); + // Only enable compression if zlib library is successfully loaded + if (zlibError == NULL) + { + state->compression = true; + FF_DEBUG("Successfully loaded zlib library, compression enabled"); + } else { + FF_DEBUG("Failed to load zlib library, compression disabled: %s", zlibError); + } + #else + FF_DEBUG("zlib not supported at build time, compression disabled"); + #endif + static WSADATA wsaData; if (wsaData.wVersion == 0) { @@ -42,27 +73,54 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho if (error != NULL) { wsaData.wVersion = (WORD) -1; + FF_DEBUG("WinSock initialization failed: %s", error); return error; } } else if (wsaData.wVersion == (WORD) -1) + { + FF_DEBUG("WinSock initialization previously failed"); return "initWsaData() failed before"; + } struct addrinfo* addr; - - if(getaddrinfo(host, "80", &(struct addrinfo) { + struct addrinfo hints = { .ai_family = state->ipv6 ? AF_INET6 : AF_INET, .ai_socktype = SOCK_STREAM, - }, &addr) != 0) + .ai_flags = AI_NUMERICSERV, + }; + + FF_DEBUG("Resolving address: %s (%s)", host, state->ipv6 ? "IPv6" : "IPv4"); + if(getaddrinfo(host, "80", &hints, &addr) != 0) + { + FF_DEBUG("getaddrinfo() failed"); return "getaddrinfo() failed"; + } state->sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if(state->sockfd == INVALID_SOCKET) { + FF_DEBUG("socket() failed"); freeaddrinfo(addr); return "socket() failed"; } + #ifdef TCP_NODELAY + // Enable TCP_NODELAY to disable Nagle's algorithm + DWORD flag = 1; + if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag)) != 0) { + FF_DEBUG("Failed to set TCP_NODELAY: %d", WSAGetLastError()); + } else { + FF_DEBUG("Successfully disabled Nagle's algorithm"); + } + #endif + + // Set timeout if needed + if (state->timeout > 0) { + FF_DEBUG("Setting connection timeout: %u ms", state->timeout); + setsockopt(state->sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&state->timeout, sizeof(state->timeout)); + } + //ConnectEx requires the socket to be initially bound if((state->ipv6 ? bind(state->sockfd, (SOCKADDR *) &(struct sockaddr_in6) { @@ -74,44 +132,69 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho .sin_addr.s_addr = INADDR_ANY, }, sizeof(struct sockaddr_in))) != 0) { + FF_DEBUG("bind() failed: %d", WSAGetLastError()); closesocket(state->sockfd); freeaddrinfo(addr); state->sockfd = INVALID_SOCKET; return "bind() failed"; } + // Initialize overlapped structure for asynchronous I/O + memset(&state->overlapped, 0, sizeof(OVERLAPPED)); + + // Build HTTP command FF_STRBUF_AUTO_DESTROY command = ffStrbufCreateA(64); ffStrbufAppendS(&command, "GET "); ffStrbufAppendS(&command, path); ffStrbufAppendS(&command, " HTTP/1.1\nHost: "); ffStrbufAppendS(&command, host); ffStrbufAppendS(&command, "\r\n"); + ffStrbufAppendS(&command, "Connection: close\r\n"); // Explicitly request connection closure + + // Add compression support if enabled + if (state->compression) { + FF_DEBUG("Enabling HTTP content compression"); + ffStrbufAppendS(&command, "Accept-Encoding: gzip\r\n"); + } + ffStrbufAppendS(&command, headers); ffStrbufAppendS(&command, "\r\n"); - BOOL result = ConnectEx(state->sockfd, addr->ai_addr, (int)addr->ai_addrlen, command.chars, command.length, NULL, &state->overlapped); + FF_DEBUG("Using ConnectEx to send %u bytes of data", command.length); + BOOL result = ConnectEx(state->sockfd, addr->ai_addr, (int)addr->ai_addrlen, + command.chars, command.length, NULL, &state->overlapped); + freeaddrinfo(addr); if(!result && WSAGetLastError() != WSA_IO_PENDING) { + FF_DEBUG("ConnectEx() failed: %d", WSAGetLastError()); closesocket(state->sockfd); state->sockfd = INVALID_SOCKET; return "ConnectEx() failed"; } + // No need to cleanup state fields here since we need them in the receive function return NULL; } const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer) { + FF_DEBUG("Preparing to receive HTTP response"); + if (state->sockfd == INVALID_SOCKET) + { + FF_DEBUG("Invalid socket, HTTP request might have failed"); return "ffNetworkingSendHttpRequest() failed"; + } uint32_t timeout = state->timeout; if (timeout > 0) { - if (WaitForSingleObject((HANDLE) state->sockfd, timeout) != WAIT_OBJECT_0) + FF_DEBUG("WaitForSingleObject with timeout: %u ms", timeout); + if (WaitForSingleObject((HANDLE)state->sockfd, timeout) != WAIT_OBJECT_0) { + FF_DEBUG("WaitForSingleObject failed or timed out"); CancelIo((HANDLE) state->sockfd); closesocket(state->sockfd); return "WaitForSingleObject(state->sockfd) failed or timeout"; @@ -121,26 +204,102 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf DWORD transfer, flags; if (!WSAGetOverlappedResult(state->sockfd, &state->overlapped, &transfer, TRUE, &flags)) { + FF_DEBUG("WSAGetOverlappedResult failed: %d", WSAGetLastError()); closesocket(state->sockfd); return "WSAGetOverlappedResult() failed"; } if(timeout > 0) { - //https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-setsockopt - setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &timeout, sizeof(timeout)); + FF_DEBUG("Setting receive timeout: %u ms", timeout); + setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); } - uint32_t recvStart; + // Set larger receive buffer for better performance + int rcvbuf = 65536; // 64KB + setsockopt(state->sockfd, SOL_SOCKET, SO_RCVBUF, (const char*)&rcvbuf, sizeof(rcvbuf)); + + FF_DEBUG("Starting data reception"); + FF_MAYBE_UNUSED int recvCount = 0; + uint32_t contentLength = 0; + char* headerEnd = NULL; + do { - recvStart = buffer->length; - ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, (int) ffStrbufGetFree(buffer), 0); - if (received <= 0) break; - buffer->length = recvStart + (uint32_t) received; + size_t availSpace = ffStrbufGetFree(buffer); + FF_DEBUG("Data reception loop #%d, current buffer size: %u, available space: %zu", + ++recvCount, buffer->length, availSpace); + + ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, (int)ffStrbufGetFree(buffer), 0); + + if (received <= 0) { + if (received == 0) { + FF_DEBUG("Connection closed (received=0)"); + } else { + FF_DEBUG("Reception failed: %d", WSAGetLastError()); + } + break; + } + + buffer->length += (uint32_t) received; buffer->chars[buffer->length] = '\0'; - } while (ffStrbufGetFree(buffer) > 0 && strstr(buffer->chars + recvStart, "\r\n\r\n") == NULL); + FF_DEBUG("Successfully received %zd bytes of data, total: %u bytes", received, buffer->length); + + // Check if HTTP header end marker is found + if (headerEnd == NULL) { + headerEnd = strstr(buffer->chars, "\r\n\r\n"); + if (headerEnd != NULL) { + FF_DEBUG("Found HTTP header end marker, position: %ld", (long)(headerEnd - buffer->chars)); + + // Check for Content-Length header to pre-allocate enough memory + const char* clHeader = strcasestr(buffer->chars, "Content-Length:"); + if (clHeader) { + contentLength = (uint32_t) strtoul(clHeader + 16, NULL, 10); + if (contentLength > 0) { + FF_DEBUG("Detected Content-Length: %u, pre-allocating buffer", contentLength); + // Ensure buffer is large enough, adding header size and some margin + ffStrbufEnsureFree(buffer, contentLength + 16); + FF_DEBUG("Extended receive buffer to %u bytes", buffer->allocated); + } + } + } + } + } while (ffStrbufGetFree(buffer) > 0); + + FF_DEBUG("Closing socket: fd=%u", (unsigned)state->sockfd); closesocket(state->sockfd); - if (buffer->length == 0) return "Empty server response received"; - return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n") ? NULL : "Invalid response"; + state->sockfd = INVALID_SOCKET; + + if (buffer->length == 0) { + FF_DEBUG("Server response is empty"); + return "Empty server response received"; + } + + if (headerEnd == NULL) { + FF_DEBUG("No HTTP header end marker found"); + return "No HTTP header end found"; + } + + if (ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n")) { + FF_DEBUG("Received valid HTTP 200 response, content length: %u bytes, total length: %u bytes", + contentLength, buffer->length); + } else { + FF_DEBUG("Invalid response: %.40s...", buffer->chars); + return "Invalid response"; + } + + // If compression was used, try to decompress + #ifdef FF_HAVE_ZLIB + if (state->compression) { + FF_DEBUG("Content received, checking if compressed"); + if (!ffNetworkingDecompressGzip(buffer, headerEnd)) { + FF_DEBUG("Decompression failed or invalid compression format"); + return "Failed to decompress or invalid format"; + } else { + FF_DEBUG("Decompression successful or no decompression needed, total length after decompression: %u bytes", buffer->length); + } + } + #endif + + return NULL; } diff --git a/src/util/debug.h b/src/util/debug.h index 9e9d2e7101..70a990a68f 100644 --- a/src/util/debug.h +++ b/src/util/debug.h @@ -2,13 +2,11 @@ static inline const char* ffFindFileName(const char* file) { - const char* lastSlash = __builtin_strrchr(file, - #ifdef _WIN32 - '\\' - #else - '/' - #endif - ); + const char* lastSlash = __builtin_strrchr(file, '/'); + #ifdef _WIN32 + if (lastSlash == NULL) + lastSlash = __builtin_strrchr(file, '\\'); + #endif if (lastSlash != NULL) return lastSlash + 1; return file; From 99415a3841e7dafcdc0c2adb192a14a2382a038b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sat, 15 Mar 2025 23:40:40 +0800 Subject: [PATCH 20/37] Networking: move files to a sub folder --- CMakeLists.txt | 20 +++++++++---------- src/common/{ => networking}/networking.h | 0 .../{ => networking}/networking_common.c | 2 +- .../{ => networking}/networking_linux.c | 7 +++---- .../{ => networking}/networking_windows.c | 7 +++---- src/detection/publicip/publicip.c | 2 +- src/detection/weather/weather.c | 1 + src/detection/weather/weather.h | 2 +- 8 files changed, 20 insertions(+), 21 deletions(-) rename src/common/{ => networking}/networking.h (100%) rename src/common/{ => networking}/networking_common.c (99%) rename src/common/{ => networking}/networking_linux.c (98%) rename src/common/{ => networking}/networking_windows.c (98%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ac15a7835..6883288238 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -364,7 +364,7 @@ set(LIBFASTFETCH_SRC src/common/library.c src/common/modules.c src/common/netif/netif.c - src/common/networking_common.c + src/common/networking/networking_common.c src/common/option.c src/common/parsing.c src/common/printing.c @@ -493,7 +493,7 @@ if(LINUX) src/common/dbus.c src/common/io/io_unix.c src/common/netif/netif_linux.c - src/common/networking_linux.c + src/common/networking/networking_linux.c src/common/processing_linux.c src/detection/battery/battery_linux.c src/detection/bios/bios_linux.c @@ -574,7 +574,7 @@ elseif(ANDROID) list(APPEND LIBFASTFETCH_SRC src/common/io/io_unix.c src/common/netif/netif_linux.c - src/common/networking_linux.c + src/common/networking/networking_linux.c src/common/processing_linux.c src/detection/battery/battery_android.c src/detection/bios/bios_android.c @@ -640,7 +640,7 @@ elseif(FreeBSD) src/common/dbus.c src/common/io/io_unix.c src/common/netif/netif_bsd.c - src/common/networking_linux.c + src/common/networking/networking_linux.c src/common/processing_linux.c src/common/sysctl.c src/detection/battery/battery_bsd.c @@ -731,7 +731,7 @@ elseif(NetBSD) src/common/dbus.c src/common/io/io_unix.c src/common/netif/netif_bsd.c - src/common/networking_linux.c + src/common/networking/networking_linux.c src/common/processing_linux.c src/common/sysctl.c src/detection/battery/battery_nbsd.c @@ -813,7 +813,7 @@ elseif(OpenBSD) src/common/dbus.c src/common/io/io_unix.c src/common/netif/netif_bsd.c - src/common/networking_linux.c + src/common/networking/networking_linux.c src/common/processing_linux.c src/common/sysctl.c src/detection/battery/battery_obsd.c @@ -894,7 +894,7 @@ elseif(APPLE) list(APPEND LIBFASTFETCH_SRC src/common/io/io_unix.c src/common/netif/netif_bsd.c - src/common/networking_linux.c + src/common/networking/networking_linux.c src/common/processing_linux.c src/common/sysctl.c src/detection/battery/battery_apple.c @@ -965,7 +965,7 @@ elseif(WIN32) list(APPEND LIBFASTFETCH_SRC src/common/io/io_windows.c src/common/netif/netif_windows.c - src/common/networking_windows.c + src/common/networking/networking_windows.c src/common/processing_windows.c src/detection/battery/battery_windows.c src/detection/bios/bios_windows.c @@ -1040,7 +1040,7 @@ elseif(SunOS) src/common/dbus.c src/common/io/io_unix.c src/common/netif/netif_bsd.c - src/common/networking_linux.c + src/common/networking/networking_linux.c src/common/processing_linux.c src/detection/battery/battery_nosupport.c src/detection/bios/bios_windows.c @@ -1121,7 +1121,7 @@ elseif(Haiku) src/common/dbus.c src/common/io/io_unix.c src/common/netif/netif_haiku.c - src/common/networking_linux.c + src/common/networking/networking_linux.c src/common/processing_linux.c src/detection/battery/battery_haiku.c src/detection/bios/bios_windows.c diff --git a/src/common/networking.h b/src/common/networking/networking.h similarity index 100% rename from src/common/networking.h rename to src/common/networking/networking.h diff --git a/src/common/networking_common.c b/src/common/networking/networking_common.c similarity index 99% rename from src/common/networking_common.c rename to src/common/networking/networking_common.c index 769860ff7f..2c2de07c7f 100644 --- a/src/common/networking_common.c +++ b/src/common/networking/networking_common.c @@ -1,6 +1,6 @@ #include "fastfetch.h" #include "common/library.h" -#include "common/networking.h" +#include "common/networking/networking.h" #include "util/stringUtils.h" #include "util/debug.h" diff --git a/src/common/networking_linux.c b/src/common/networking/networking_linux.c similarity index 98% rename from src/common/networking_linux.c rename to src/common/networking/networking_linux.c index 76ec71dbfa..6402e394e8 100644 --- a/src/common/networking_linux.c +++ b/src/common/networking/networking_linux.c @@ -1,5 +1,5 @@ #include "fastfetch.h" -#include "common/networking.h" +#include "common/networking/networking.h" #include "common/time.h" #include "common/library.h" #include "util/stringUtils.h" @@ -331,9 +331,8 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf char* headerEnd = NULL; do { - size_t availSpace = ffStrbufGetFree(buffer); - FF_DEBUG("Data reception loop #%d, current buffer size: %u, available space: %zu", - ++recvCount, buffer->length, availSpace); + FF_DEBUG("Data reception loop #%d, current buffer size: %u, available space: %u", + ++recvCount, buffer->length, ffStrbufGetFree(buffer)); ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, ffStrbufGetFree(buffer), 0); diff --git a/src/common/networking_windows.c b/src/common/networking/networking_windows.c similarity index 98% rename from src/common/networking_windows.c rename to src/common/networking/networking_windows.c index 2210c8feae..92ce0375ab 100644 --- a/src/common/networking_windows.c +++ b/src/common/networking/networking_windows.c @@ -3,7 +3,7 @@ //Must be included after #include "fastfetch.h" -#include "common/networking.h" +#include "common/networking/networking.h" #include "util/stringUtils.h" #include "util/debug.h" @@ -225,9 +225,8 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf char* headerEnd = NULL; do { - size_t availSpace = ffStrbufGetFree(buffer); - FF_DEBUG("Data reception loop #%d, current buffer size: %u, available space: %zu", - ++recvCount, buffer->length, availSpace); + FF_DEBUG("Data reception loop #%d, current buffer size: %u, available space: %u", + ++recvCount, buffer->length, ffStrbufGetFree(buffer)); ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, (int)ffStrbufGetFree(buffer), 0); diff --git a/src/detection/publicip/publicip.c b/src/detection/publicip/publicip.c index aa5f224193..fd5bed2322 100644 --- a/src/detection/publicip/publicip.c +++ b/src/detection/publicip/publicip.c @@ -1,5 +1,5 @@ #include "publicip.h" -#include "common/networking.h" +#include "common/networking/networking.h" #define FF_UNITIALIZED ((const char*)(uintptr_t) -1) static FFNetworkingState states[2]; diff --git a/src/detection/weather/weather.c b/src/detection/weather/weather.c index c340db6369..8f1a2accba 100644 --- a/src/detection/weather/weather.c +++ b/src/detection/weather/weather.c @@ -1,4 +1,5 @@ #include "weather.h" +#include "common/networking/networking.h" #define FF_UNITIALIZED ((const char*)(uintptr_t) -1) static FFNetworkingState state; diff --git a/src/detection/weather/weather.h b/src/detection/weather/weather.h index 55a5786281..24dde33bba 100644 --- a/src/detection/weather/weather.h +++ b/src/detection/weather/weather.h @@ -1,6 +1,6 @@ #pragma once -#include "common/networking.h" +#include "fastfetch.h" void ffPrepareWeather(FFWeatherOptions* options); const char* ffDetectWeather(FFWeatherOptions* options, FFstrbuf* result); From 2ce511c4d348e64cef3c269b9bbccdf20a1f1693 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Sun, 16 Mar 2025 09:39:46 +0800 Subject: [PATCH 21/37] Networking (Linux): handle a case when `sendto()` reports `EINPROGRESS` --- src/common/networking/networking_linux.c | 37 ++++++++++-------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/src/common/networking/networking_linux.c b/src/common/networking/networking_linux.c index 6402e394e8..079b7d9ff8 100644 --- a/src/common/networking/networking_linux.c +++ b/src/common/networking/networking_linux.c @@ -33,32 +33,19 @@ static const char* tryTcpFastOpen(FFNetworkingState* state) FF_DEBUG("Successfully set TCP_FASTOPEN option, queue length: %d", qlen); } - // Set non-blocking mode - int flags = fcntl(state->sockfd, F_GETFL, 0); - FF_DEBUG("Current socket flags: 0x%x", flags); - - if (fcntl(state->sockfd, F_SETFL, flags | O_NONBLOCK) < 0) { - FF_DEBUG("Failed to set non-blocking mode: %s", strerror(errno)); - } else { - FF_DEBUG("Successfully set non-blocking mode"); - } - // Try to send data using Fast Open FF_DEBUG("Using sendto() + MSG_FASTOPEN to send %u bytes of data", state->command.length); ssize_t sent = sendto(state->sockfd, state->command.chars, state->command.length, - MSG_FASTOPEN, + MSG_FASTOPEN | MSG_DONTWAIT, state->addr->ai_addr, state->addr->ai_addrlen); - // Restore blocking mode - fcntl(state->sockfd, F_SETFL, flags); - - if (sent >= 0 || (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK)) + if (sent >= 0 || (errno == EAGAIN || errno == EWOULDBLOCK)) { - FF_DEBUG("TCP Fast Open succeeded or in progress (sent=%zd, errno=%d: %s)", - sent, errno, sent < 0 ? strerror(errno) : ""); + FF_DEBUG("TCP Fast Open %s (sent=%zd, errno=%d: %s)", errno == 0 ? "succeeded" : "was in progress", + sent, errno, strerror(errno)); freeaddrinfo(state->addr); state->addr = NULL; ffStrbufDestroy(&state->host); @@ -66,9 +53,17 @@ static const char* tryTcpFastOpen(FFNetworkingState* state) return NULL; } - // Fast Open failed - FF_DEBUG("TCP Fast Open failed: %s (errno=%d)", strerror(errno), errno); - return "sendto() failed"; + if (errno == EINPROGRESS) + { + FF_DEBUG("TCP Fast Open cookie is not available locally"); + return "sendto() reports EINPROGRESS"; + } + else + { + // Fast Open failed + FF_DEBUG("TCP Fast Open failed: %s (errno=%d)", strerror(errno), errno); + return "sendto() failed"; + } #endif } @@ -334,7 +329,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf FF_DEBUG("Data reception loop #%d, current buffer size: %u, available space: %u", ++recvCount, buffer->length, ffStrbufGetFree(buffer)); - ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, ffStrbufGetFree(buffer), 0); + ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, ffStrbufGetFree(buffer), MSG_WAITALL); if (received <= 0) { if (received == 0) { From dfc9bc265cf4399d3b26d88a6fee102be10161c9 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Sun, 16 Mar 2025 09:43:08 +0800 Subject: [PATCH 22/37] Networking: silence compiler warnings --- src/common/networking/networking_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/networking/networking_common.c b/src/common/networking/networking_common.c index 2c2de07c7f..45e95b401c 100644 --- a/src/common/networking/networking_common.c +++ b/src/common/networking/networking_common.c @@ -43,7 +43,7 @@ static uint32_t guessGzipOutputSize(const void* data, uint32_t dataSize) if (dataSize > 18) { // Get ISIZE value from the end of file (little endian) const uint8_t* tail = (const uint8_t*)data + dataSize - 4; - uint32_t uncompressedSize = tail[0] | (tail[1] << 8) | (tail[2] << 16) | (tail[3] << 24); + uint32_t uncompressedSize = (uint32_t) tail[0] | ((uint32_t) tail[1] << 8u) | ((uint32_t) tail[2] << 16u) | ((uint32_t) tail[3] << 24u); // For valid gzip files, this value is the length of the uncompressed data modulo 2^32 if (uncompressedSize > 0) { From dbc6f70f192cfbb382b96bdfba047424dd34bb65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 16 Mar 2025 10:09:33 +0800 Subject: [PATCH 23/37] Networking (Windows): fix libz loading; use TCP_FASTOPEN if available --- src/common/networking/networking_common.c | 8 +++++++- src/common/networking/networking_windows.c | 11 ++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/common/networking/networking_common.c b/src/common/networking/networking_common.c index 45e95b401c..df04eb3f0d 100644 --- a/src/common/networking/networking_common.c +++ b/src/common/networking/networking_common.c @@ -22,7 +22,13 @@ const char* ffNetworkingLoadZlibLibrary(void) if (!zlibData.inited) { zlibData.inited = true; - FF_LIBRARY_LOAD(zlib, "dlopen libz failed", "libz" FF_LIBRARY_EXTENSION, 2) + FF_LIBRARY_LOAD(zlib, "dlopen libz failed", + #ifdef _WIN32 + "zlib1" + #else + "libz" + #endif + FF_LIBRARY_EXTENSION, 2) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateInit2_) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflate) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(zlib, zlibData, inflateEnd) diff --git a/src/common/networking/networking_windows.c b/src/common/networking/networking_windows.c index 92ce0375ab..93ca13f6c2 100644 --- a/src/common/networking/networking_windows.c +++ b/src/common/networking/networking_windows.c @@ -105,9 +105,9 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho return "socket() failed"; } + DWORD flag = 1; #ifdef TCP_NODELAY // Enable TCP_NODELAY to disable Nagle's algorithm - DWORD flag = 1; if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag)) != 0) { FF_DEBUG("Failed to set TCP_NODELAY: %d", WSAGetLastError()); } else { @@ -115,6 +115,15 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho } #endif + #ifdef TCP_FASTOPEN + // Set TCP Fast Open + if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_FASTOPEN, (char*)&flag, sizeof(flag)) != 0) { + FF_DEBUG("Failed to set TCP_FASTOPEN option: %d", WSAGetLastError()); + } else { + FF_DEBUG("Successfully set TCP_FASTOPEN option"); + } + #endif + // Set timeout if needed if (state->timeout > 0) { FF_DEBUG("Setting connection timeout: %u ms", state->timeout); From 96e9e61437713bb46573de4aec41ac697865ad7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 16 Mar 2025 10:38:04 +0800 Subject: [PATCH 24/37] Networking (Windows): print readable error message --- CMakeLists.txt | 1 + src/common/networking/networking_windows.c | 12 +++++----- src/util/debug.h | 2 ++ src/util/debug_windows.c | 28 ++++++++++++++++++++++ 4 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 src/util/debug_windows.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 6883288238..08c01ade21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1034,6 +1034,7 @@ elseif(WIN32) src/util/windows/version.c src/util/platform/FFPlatform_windows.c src/util/binary_windows.c + src/util/debug_windows.c ) elseif(SunOS) list(APPEND LIBFASTFETCH_SRC diff --git a/src/common/networking/networking_windows.c b/src/common/networking/networking_windows.c index 93ca13f6c2..33a46c13b6 100644 --- a/src/common/networking/networking_windows.c +++ b/src/common/networking/networking_windows.c @@ -109,7 +109,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho #ifdef TCP_NODELAY // Enable TCP_NODELAY to disable Nagle's algorithm if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag)) != 0) { - FF_DEBUG("Failed to set TCP_NODELAY: %d", WSAGetLastError()); + FF_DEBUG("Failed to set TCP_NODELAY: %s", ffDebugWin32Error((DWORD) WSAGetLastError())); } else { FF_DEBUG("Successfully disabled Nagle's algorithm"); } @@ -118,7 +118,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho #ifdef TCP_FASTOPEN // Set TCP Fast Open if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_FASTOPEN, (char*)&flag, sizeof(flag)) != 0) { - FF_DEBUG("Failed to set TCP_FASTOPEN option: %d", WSAGetLastError()); + FF_DEBUG("Failed to set TCP_FASTOPEN option: %s", ffDebugWin32Error((DWORD) WSAGetLastError())); } else { FF_DEBUG("Successfully set TCP_FASTOPEN option"); } @@ -141,7 +141,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho .sin_addr.s_addr = INADDR_ANY, }, sizeof(struct sockaddr_in))) != 0) { - FF_DEBUG("bind() failed: %d", WSAGetLastError()); + FF_DEBUG("bind() failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError())); closesocket(state->sockfd); freeaddrinfo(addr); state->sockfd = INVALID_SOCKET; @@ -177,7 +177,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho if(!result && WSAGetLastError() != WSA_IO_PENDING) { - FF_DEBUG("ConnectEx() failed: %d", WSAGetLastError()); + FF_DEBUG("ConnectEx() failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError())); closesocket(state->sockfd); state->sockfd = INVALID_SOCKET; return "ConnectEx() failed"; @@ -213,7 +213,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf DWORD transfer, flags; if (!WSAGetOverlappedResult(state->sockfd, &state->overlapped, &transfer, TRUE, &flags)) { - FF_DEBUG("WSAGetOverlappedResult failed: %d", WSAGetLastError()); + FF_DEBUG("WSAGetOverlappedResult failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError())); closesocket(state->sockfd); return "WSAGetOverlappedResult() failed"; } @@ -243,7 +243,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf if (received == 0) { FF_DEBUG("Connection closed (received=0)"); } else { - FF_DEBUG("Reception failed: %d", WSAGetLastError()); + FF_DEBUG("Reception failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError())); } break; } diff --git a/src/util/debug.h b/src/util/debug.h index 70a990a68f..9e6bf7228c 100644 --- a/src/util/debug.h +++ b/src/util/debug.h @@ -24,3 +24,5 @@ static inline const char* ffFindFileName(const char* file) #endif #define FF_DEBUG(format, ...) FF_DEBUG_PRINT(__FILE__, __LINE__, format, ##__VA_ARGS__) + +const char* ffDebugWin32Error(unsigned long errorCode); diff --git a/src/util/debug_windows.c b/src/util/debug_windows.c new file mode 100644 index 0000000000..e6bf3f7865 --- /dev/null +++ b/src/util/debug_windows.c @@ -0,0 +1,28 @@ +#include "debug.h" + +#include + +const char* ffDebugWin32Error(DWORD errorCode) +{ + static char buffer[256]; + + DWORD len = FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + (DWORD) errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buffer, + sizeof(buffer), + NULL); + + if (len == 0) { + snprintf(buffer, sizeof(buffer), "Unknown error code (%lu)", errorCode); + } else { + // Remove trailing newline + if (buffer[len - 1] == '\n') buffer[len - 1] = '\0'; + if (buffer[len - 2] == '\r') buffer[len - 2] = '\0'; + snprintf(buffer + len - 2, sizeof(buffer) - len + 2, " (%lu)", errorCode); + } + + return buffer; +} From 672d4ad543ebea801172d46ffab2b88955a955e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 16 Mar 2025 18:32:39 +0800 Subject: [PATCH 25/37] Completion (Bash): Use `fastfetch --help-raw` for auto-completion Fix #1619 --- completions/fastfetch.bash | 524 +++++++++---------------------------- 1 file changed, 119 insertions(+), 405 deletions(-) diff --git a/completions/fastfetch.bash b/completions/fastfetch.bash index b6a0ff6da5..681046ddf6 100644 --- a/completions/fastfetch.bash +++ b/completions/fastfetch.bash @@ -1,407 +1,121 @@ -__fastfetch_complete_help() -{ - local __ff_helps=( - "color" - "format" - "config" - "os-format" - "host-format" - "kernel-format" - "uptime-format" - "processes-format" - "packages-format" - "shell-format" - "display-format" - "de-format" - "wm-format" - "wmtheme-format" - "theme-format" - "icons-format" - "font-format" - "cursor-format" - "terminal-format" - "terminalfont-format" - "cpu-format" - "cpuusage-format" - "gpu-format" - "memory-format" - "swap-format" - "disk-format" - "battery-format" - "poweradapter-format" - "locale-format" - "localip-format" - "publicip-format" - "player-format" - "media-format" - "datetime-format" - "date-format" - "time-format" - "vulkan-format" - "opengl-format" - "opencl-format" - "bluetooth-format" - ) - COMPREPLY=($(compgen -W "${__ff_helps[*]}" -- "$CURRENT_WORD")) +#!/usr/bin/env bash + +_fastfetch() { + # Use Bash built-in variables directly + local cur="${COMP_WORDS[COMP_CWORD]}" + local prev="${COMP_WORDS[COMP_CWORD-1]}" + + # Check if Python is available + if ! command -v python3 &>/dev/null; then + return + fi + + # Handle standard completion cases + case "$prev" in + --color|--color-keys|--color-title|--color-output|--color-separator|--*-color|--*-key-color|--*-output-color|--logo-color-[1-9]|--percent-color-*|--temp-color-*) + local -a colors=("black" "red" "green" "yellow" "blue" "magenta" "cyan" "white" "default") + COMPREPLY=($(compgen -W "${colors[*]}" -- "$cur")) + return + ;; + --logo|-l) + local -a logos + readarray -t logos < <(fastfetch --list-logos autocompletion 2>/dev/null) + logos+=("none" "small") + COMPREPLY=($(compgen -W "${logos[*]}" -- "$cur")) + return + ;; + --config|-c) + local -a presets + readarray -t presets < <(fastfetch --list-presets autocompletion 2>/dev/null) + presets+=("none") + COMPREPLY=($(compgen -W "${presets[*]}" -- "$cur")) + # Also allow file path completion + if type _filedir &>/dev/null; then + _filedir + elif type compgen &>/dev/null; then + COMPREPLY+=($(compgen -f -- "$cur")) + fi + return + ;; + --structure|-s) + # Get all module names in lowercase only + local -a structures + readarray -t structures < <(fastfetch --list-modules autocompletion 2>/dev/null | cut -d':' -f1 | tr '[:upper:]' '[:lower:]') + COMPREPLY=($(compgen -W "${structures[*]}" -- "$cur")) + return + ;; + --help|-h) + local -a modules + readarray -t modules < <(fastfetch --list-modules autocompletion 2>/dev/null) + # Convert to lowercase and keep only module names + local -a module_names=() + for module in "${modules[@]}"; do + module_names+=($(echo "$module" | cut -d':' -f1 | tr '[:upper:]' '[:lower:]')-format) + done + module_names+=("format" "color") + COMPREPLY=($(compgen -W "${module_names[*]}" -- "$cur")) + return + ;; + --format) + COMPREPLY=($(compgen -W "json default" -- "$cur")) + return + ;; + --*-format) + # Format string completion, handle spaces + return + ;; + --*path*|--*file*|--gen-config*|--*data*) + # File path completion + if type _filedir &>/dev/null; then + _filedir + elif type compgen &>/dev/null; then + COMPREPLY=($(compgen -f -- "$cur")) + fi + return + ;; + esac + + # If not a special option, generate all possible options + if [[ "$cur" == -* ]]; then + local -a opts + readarray -t opts < <(python3 - "$cur" <<'EOF' +import json +import sys +import subprocess + +def main(current): + try: + # Use fastfetch --help-raw to get option data + output = subprocess.check_output(['fastfetch', '--help-raw'], stderr=subprocess.DEVNULL) + data = json.loads(output) + + for category in data.values(): + for flag in category: + if flag.get("pseudo", False): + continue + + if "short" in flag: + print(f"-{flag['short']}") + + if "long" in flag: + if flag["long"] == "logo-color-[1-9]": + for i in range(1, 10): + print(f"--logo-color-{i}") + else: + print(f"--{flag['long']}") + except Exception: + # If error occurs, return no options + pass + +if __name__ == "__main__": + main(sys.argv[1]) +EOF +) + COMPREPLY=($(compgen -W "${opts[*]}" -- "$cur")) + fi + + return 0 } -__fastfetch_complete_bool() -{ - COMPREPLY=($(compgen -W "true false" -- "$CURRENT_WORD")) -} - -__fastfetch_complete_string() -{ - if [[ $CURRENT_WORD != "" ]]; then - COMPREPLY=("$CURRENT_WORD") - fi -} - -__fastfetch_complete_path() -{ - COMPREPLY=($(compgen -A file -- "$CURRENT_WORD")) -} - -__fastfetch_complete_logo() -{ - COMPREPLY=($(compgen -W "$(fastfetch --list-logos autocompletion)" -- "$CURRENT_WORD")) -} - -__fastfetch_complete_logo_type() -{ - local __ff_logo_types=( - "auto" - "builtin" - "file" - "raw" - "sixel" - "kitty" - "chafa" - ) - COMPREPLY=($(compgen -W "${__ff_logo_types[*]}" -- "$CURRENT_WORD")) -} - -__fastfetch_complete_binary_prefix() -{ - local __ff_size_binary_prefixes=( - "iec" - "si" - "jedec" - ) - COMPREPLY=($(compgen -W "${__ff_size_binary_prefixes[*]}" -- "$CURRENT_WORD")) -} - -__fastfetch_complete_gl() -{ - local __ff_gl_types=( - "auto" - "egl" - "glx" - ) - COMPREPLY=($(compgen -W "${__ff_gl_types[*]}" -- "$CURRENT_WORD")) -} - -__fastfetch_complete_option() -{ - local FF_OPTIONS_ALL=( - "${FF_OPTIONS_BOOL[@]}" - "${FF_OPTIONS_STRING[@]}" - "${FF_OPTIONS_PATH[@]}" - "${FF_OPTIONS_LOGO[@]}" - "${FF_OPTIONS_LOGO_TYPE[@]}" - "${FF_OPTIONS_BINARY_PREFIX[@]}" - "${FF_OPTIONS_OPENGL[@]}" - ) - - if [[ $WORD_COUND -lt 3 ]]; then - FF_OPTIONS_ALL+=( - "${FF_OPTIONS_SINGLE[@]}" - "${FF_OPTIONS_HELP[@]}" - ) - fi - - for ff_word in ${COMP_WORDS[@]}; do - if [[ $ff_word == $CURRENT_WORD ]]; then - break - fi - - FF_OPTIONS_ALL=("${FF_OPTIONS_ALL[@]/$ff_word}") - done - - COMPREPLY=($(compgen -W "${FF_OPTIONS_ALL[*]}" -- "$CURRENT_WORD")) -} - -__fastfetch_previous_matches() -{ - for ff_option in "$@"; do - if [[ $ff_option == "$PREVIOUS_WORD" ]]; then - return 0 - fi - done - return 1 -} - -__fastfetch_completion() -{ - local CURRENT_WORD="${COMP_WORDS[$COMP_CWORD]}" - local PREVIOUS_WORD="${COMP_WORDS[$COMP_CWORD - 1]}" - local WORD_COUND="${#COMP_WORDS[@]}" - - local FF_OPTIONS_SINGLE=( - "-v" - "--version" - "--list-logos" - "--list-modules" - "--list-presets" - "--list-features" - "--print-logos" - "--print-config-system" - "--print-config-user" - "--print-structure" - "--gen-config" - "--gen-config-force" - ) - - local FF_OPTIONS_HELP=( - "-h" - "--help" - ) - - local FF_OPTIONS_BOOL=( - "-r" - "--show-errors" - "--logo-print-remaining" - "--multithreading" - "--stat" - "--allow-slow-operations" - "--disable-linewrap" - "--hide-cursor" - "--cpu-temp" - "--gpu-temp" - "--battery-temp" - "--display-precise-refresh-rate" - "--localip-show-ipv4" - "--localip-show-ipv6" - "--localip-show-flags" - "--localip-show-loop" - "--localip-show-mtu" - "--localip-show-speed" - "--localip-name-prefix" - "--localip-compact-type" - "--escape-bedrock" - "--pipe" - "--title-fqdn" - "--disk-folders" - "--disk-show-external" - "--disk-show-hidden" - "--disk-show-subvolumes" - "--gpu-hide-integrated" - "--gpu-hide-discrete" - "--gpu-force-method" - "--disk-show-unknown" - "--bluetooth-show-disconnected" - ) - - local FF_OPTIONS_STRING=( - "--logo-type" - "--logo-padding" - "--logo-padding-left" - "--logo-padding-right" - "--logo-padding-top" - "--logo-color-1" - "--logo-color-2" - "--logo-color-3" - "--logo-color-4" - "--logo-color-5" - "--logo-color-6" - "--logo-color-7" - "--logo-color-8" - "--logo-color-9" - "--logo-width" - "--logo-height" - "--color" - "--color-keys" - "--color-title" - "--display-compact-type" - "--separator" - "-s" - "--structure" - "--player-name" - "--percent-type" - "--publicip-url" - "--publicip-timeout" - "--weather-output-format" - "--weather-timeout" - "--os-key" - "--os-format" - "--os-key-color" - "--host-key" - "--host-format" - "--host-key-color" - "--kernel-key" - "--kernel-format" - "--kernel-key-color" - "--uptime-key" - "--uptime-format" - "--uptime-key-color" - "--processes-key" - "--processes-format" - "--processes-key-color" - "--packages-key" - "--packages-format" - "--packages-key-color" - "--shell-key" - "--shell-format" - "--shell-key-color" - "--display-key" - "--display-format" - "--display-key-color" - "--de-key" - "--de-format" - "--de-key-color" - "--wm-key" - "--wm-format" - "--wm-key-color" - "--wmtheme-key" - "--wmtheme-format" - "--wmtheme-key-color" - "--theme-key" - "--theme-format" - "--theme-key-color" - "--icons-key" - "--icons-format" - "--icons-key-color" - "--font-key" - "--font-format" - "--font-key-color" - "--cursor-key" - "--cursor-format" - "--cursor-key-color" - "--terminal-key" - "--terminal-format" - "--terminal-key-color" - "--terminalfont-key" - "--terminalfont-format" - "--terminalfont-key-color" - "--cpu-key" - "--cpu-format" - "--cpu-key-color" - "--cpu-useage-key" - "--cpu-useage-format" - "--cpu-useage-key-color" - "--gpu-key" - "--gpu-format" - "--gpu-key-color" - "--memory-key" - "--memory-format" - "--memory-key-color" - "--swap-key" - "--swap-format" - "--swap-key-color" - "--disk-key" - "--disk-format" - "--disk-key-color" - "--battery-key" - "--battery-format" - "--battery-key-color" - "--poweradapter-key" - "--poweradapter-format" - "--poweradapter-key-color" - "--locale-key" - "--locale-format" - "--locale-key-color" - "--localip-key" - "--localip-format" - "--localip-key-color" - "--publicip-key" - "--publicip-format" - "--publicip-key-color" - "--wifi-key" - "--wifi-format" - "--wifi-key-color" - "--weather-key" - "--weather-format" - "--weather-key-color" - "--player-key" - "--player-format" - "--player-key-color" - "--media-key" - "--media-format" - "--media-key-color" - "--datetime-key" - "--datetime-format" - "--datetime-key-color" - "--date-key" - "--date-format" - "--date-key-color" - "--time-key" - "--time-format" - "--time-key-color" - "--vulkan-key" - "--vulkan-format" - "--vulkan-key-color" - "--opengl-key" - "--opengl-format" - "--opengl-key-color" - "--opencl-key" - "--opencl-format" - "--opencl-key-color" - "--users-key" - "--users-format" - "--users-key-color" - "--users-myself-only" - "--bluetooth-key" - "--bluetooth-format" - "--bluetooth-key-color" - ) - - local FF_OPTIONS_PATH=( - "-c" - "--config" - ) - - local FF_OPTIONS_LOGO=( - "-l" - "--logo" - ) - - local FF_OPTIONS_LOGO_TYPE=( - "--logo-type" - ) - - local FF_OPTIONS_BINARY_PREFIX=( - "--binary-prefix" - ) - - local FF_OPTIONS_OPENGL=( - "--opengl-type" - ) - - if __fastfetch_previous_matches "${FF_OPTIONS_SINGLE[@]}"; then - return - elif [[ $WORD_COUND -gt 3 && ( ${COMP_WORDS[$COMP_CWORD - 2]} == "--help" || ${COMP_WORDS[$COMP_CWORD - 2]} == "-h" ) ]]; then - return - elif [[ $CURRENT_WORD == "-"* ]]; then - __fastfetch_complete_option - elif __fastfetch_previous_matches "${FF_OPTIONS_HELP[@]}"; then - __fastfetch_complete_help - elif __fastfetch_previous_matches "${FF_OPTIONS_BOOL[@]}"; then - __fastfetch_complete_bool - elif __fastfetch_previous_matches "${FF_OPTIONS_STRING[@]}"; then - __fastfetch_complete_string - elif __fastfetch_previous_matches "${FF_OPTIONS_PATH[@]}"; then - __fastfetch_complete_path - elif __fastfetch_previous_matches "${FF_OPTIONS_LOGO[@]}"; then - __fastfetch_complete_logo - elif __fastfetch_previous_matches "${FF_OPTIONS_LOGO_TYPE[@]}"; then - __fastfetch_complete_logo_type - elif __fastfetch_previous_matches "${FF_OPTIONS_BINARY_PREFIX[@]}"; then - __fastfetch_complete_binary_prefix - elif __fastfetch_previous_matches "${FF_OPTIONS_OPENGL[@]}"; then - __fastfetch_complete_gl - else - __fastfetch_complete_option - fi -} - -complete -F __fastfetch_completion fastfetch +# Register completion +complete -F _fastfetch fastfetch From 60eaa326a298651248642790ec7759c5ae3c2139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 17 Mar 2025 09:07:30 +0800 Subject: [PATCH 26/37] Networking (BSD): support TCP_FASTOPEN --- src/common/networking/networking_linux.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/common/networking/networking_linux.c b/src/common/networking/networking_linux.c index 079b7d9ff8..30a3569073 100644 --- a/src/common/networking/networking_linux.c +++ b/src/common/networking/networking_linux.c @@ -18,7 +18,7 @@ // Try to use TCP Fast Open to send data static const char* tryTcpFastOpen(FFNetworkingState* state) { - #if !defined(MSG_FASTOPEN) || !defined(TCP_FASTOPEN) + #if !defined(TCP_FASTOPEN) || (defined(__linux__) && !defined(MSG_FASTOPEN)) FF_DEBUG("TCP Fast Open not supported on this system"); FF_UNUSED(state); return "TCP Fast Open not supported"; @@ -34,14 +34,20 @@ static const char* tryTcpFastOpen(FFNetworkingState* state) } // Try to send data using Fast Open + #ifdef __APPLE__ + ssize_t sent = 0; + #else FF_DEBUG("Using sendto() + MSG_FASTOPEN to send %u bytes of data", state->command.length); ssize_t sent = sendto(state->sockfd, state->command.chars, state->command.length, - MSG_FASTOPEN | MSG_DONTWAIT, + #ifdef MSG_FASTOPEN + MSG_FASTOPEN | + #endif + MSG_DONTWAIT, state->addr->ai_addr, state->addr->ai_addrlen); - + #endif if (sent >= 0 || (errno == EAGAIN || errno == EWOULDBLOCK)) { FF_DEBUG("TCP Fast Open %s (sent=%zd, errno=%d: %s)", errno == 0 ? "succeeded" : "was in progress", From b775fe74865d451e7017d23d8613c1873f795986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 17 Mar 2025 15:59:39 +0800 Subject: [PATCH 27/37] Networking (macOS): support TFO --- src/common/networking/networking_linux.c | 83 ++++++++++++++++++++---- 1 file changed, 72 insertions(+), 11 deletions(-) diff --git a/src/common/networking/networking_linux.c b/src/common/networking/networking_linux.c index 30a3569073..caf91f668f 100644 --- a/src/common/networking/networking_linux.c +++ b/src/common/networking/networking_linux.c @@ -7,6 +7,7 @@ #include "util/debug.h" #include +#include #include #include #include @@ -25,18 +26,35 @@ static const char* tryTcpFastOpen(FFNetworkingState* state) #else FF_DEBUG("Attempting to use TCP Fast Open to connect to %s", state->host.chars); + #ifndef __APPLE__ // On macOS, TCP_FASTOPEN doesn't seem to be needed // Set TCP Fast Open - int qlen = 5; - if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)) != 0) { + #ifdef __linux__ + int flag = 5; // the queue length of pending packets + #else + int flag = 1; // enable TCP Fast Open + #endif + if (setsockopt(state->sockfd, IPPROTO_TCP, + #ifdef __APPLE__ + // https://github.com/rust-lang/libc/pull/3135 + 0x218 // TCP_FASTOPEN_FORCE_ENABLE + #else + TCP_FASTOPEN + #endif + , &flag, sizeof(flag)) != 0) { FF_DEBUG("Failed to set TCP_FASTOPEN option: %s", strerror(errno)); + return "setsockopt(TCP_FASTOPEN) failed"; } else { - FF_DEBUG("Successfully set TCP_FASTOPEN option, queue length: %d", qlen); + #ifdef __linux__ + FF_DEBUG("Successfully set TCP_FASTOPEN option, queue length: %d", flag); + #elif defined(__APPLE__) + FF_DEBUG("Successfully set TCP_FASTOPEN_FORCE_ENABLE option"); + #else + FF_DEBUG("Successfully set TCP_FASTOPEN option"); + #endif } + #endif - // Try to send data using Fast Open - #ifdef __APPLE__ - ssize_t sent = 0; - #else + #ifndef __APPLE__ FF_DEBUG("Using sendto() + MSG_FASTOPEN to send %u bytes of data", state->command.length); ssize_t sent = sendto(state->sockfd, state->command.chars, @@ -47,6 +65,28 @@ static const char* tryTcpFastOpen(FFNetworkingState* state) MSG_DONTWAIT, state->addr->ai_addr, state->addr->ai_addrlen); + #else + if (fcntl(state->sockfd, F_SETFL, O_NONBLOCK) == -1) { + FF_DEBUG("fcntl(F_SETFL) failed: %s", strerror(errno)); + return "fcntl(F_SETFL) failed"; + } + FF_DEBUG("Using connectx() to send %u bytes of data", state->command.length); + // Use connectx to establish connection and send data in one call + size_t sent; + if (connectx(state->sockfd, + &(sa_endpoints_t) { + .sae_dstaddr = state->addr->ai_addr, + .sae_dstaddrlen = state->addr->ai_addrlen, + }, + SAE_ASSOCID_ANY, CONNECT_DATA_IDEMPOTENT, + &(struct iovec) { + .iov_base = state->command.chars, + .iov_len = state->command.length, + }, 1, &sent, NULL) != 0) sent = 0; + if (fcntl(state->sockfd, F_SETFL, 0) == -1) { + FF_DEBUG("fcntl(F_SETFL) failed: %s", strerror(errno)); + return "fcntl(F_SETFL) failed"; + } #endif if (sent >= 0 || (errno == EAGAIN || errno == EWOULDBLOCK)) { @@ -68,7 +108,11 @@ static const char* tryTcpFastOpen(FFNetworkingState* state) { // Fast Open failed FF_DEBUG("TCP Fast Open failed: %s (errno=%d)", strerror(errno), errno); + #ifdef __APPLE__ + return "connectx() failed"; + #else return "sendto() failed"; + #endif } #endif } @@ -313,6 +357,26 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf return "ffNetworkingSendHttpRequest() failed"; } + // Set larger initial receive buffer instead of small repeated receives + int rcvbuf = 65536; // 64KB + setsockopt(state->sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); + + #ifdef __APPLE__ + // poll for the socket to be readable. + // Because of the non-blocking connectx() call, the connection might not be established yet + FF_DEBUG("Using poll() to check if socket is readable"); + if (poll(&(struct pollfd) { + .fd = state->sockfd, + .events = POLLIN + }, 1, timeout > 0 ? (int) timeout : -1) == -1) + { + FF_DEBUG("poll() failed: %s (errno=%d)", strerror(errno), errno); + close(state->sockfd); + state->sockfd = -1; + return "poll() failed"; + } + FF_DEBUG("Socket is readable, proceeding to receive data"); + #else if(timeout > 0) { FF_DEBUG("Setting receive timeout: %u ms", timeout); @@ -321,10 +385,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf timev.tv_usec = (__typeof__(timev.tv_usec)) ((timeout % 1000) * 1000); //milliseconds to microseconds setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev)); } - - // Set larger initial receive buffer instead of small repeated receives - int rcvbuf = 65536; // 64KB - setsockopt(state->sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); + #endif FF_DEBUG("Starting data reception"); FF_MAYBE_UNUSED int recvCount = 0; From 5f43f6399029bd4507644a250d61c8dd2dfe2956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 17 Mar 2025 22:54:47 +0800 Subject: [PATCH 28/37] Networking (Windows): more verbose log --- src/common/networking/networking_windows.c | 43 ++++++++++++++-------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/common/networking/networking_windows.c b/src/common/networking/networking_windows.c index 33a46c13b6..9a61b73533 100644 --- a/src/common/networking/networking_windows.c +++ b/src/common/networking/networking_windows.c @@ -115,15 +115,6 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho } #endif - #ifdef TCP_FASTOPEN - // Set TCP Fast Open - if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_FASTOPEN, (char*)&flag, sizeof(flag)) != 0) { - FF_DEBUG("Failed to set TCP_FASTOPEN option: %s", ffDebugWin32Error((DWORD) WSAGetLastError())); - } else { - FF_DEBUG("Successfully set TCP_FASTOPEN option"); - } - #endif - // Set timeout if needed if (state->timeout > 0) { FF_DEBUG("Setting connection timeout: %u ms", state->timeout); @@ -169,18 +160,40 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho ffStrbufAppendS(&command, headers); ffStrbufAppendS(&command, "\r\n"); + #ifdef TCP_FASTOPEN + // Set TCP Fast Open + flag = 1; + if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_FASTOPEN, (char*)&flag, sizeof(flag)) != 0) { + FF_DEBUG("Failed to set TCP_FASTOPEN option: %s", ffDebugWin32Error((DWORD) WSAGetLastError())); + } else { + FF_DEBUG("Successfully set TCP_FASTOPEN option"); + } + #endif + FF_DEBUG("Using ConnectEx to send %u bytes of data", command.length); + DWORD sent = 0; BOOL result = ConnectEx(state->sockfd, addr->ai_addr, (int)addr->ai_addrlen, - command.chars, command.length, NULL, &state->overlapped); + command.chars, command.length, &sent, &state->overlapped); freeaddrinfo(addr); - if(!result && WSAGetLastError() != WSA_IO_PENDING) + if(!result) { - FF_DEBUG("ConnectEx() failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError())); - closesocket(state->sockfd); - state->sockfd = INVALID_SOCKET; - return "ConnectEx() failed"; + if (WSAGetLastError() != WSA_IO_PENDING) + { + FF_DEBUG("ConnectEx() failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError())); + closesocket(state->sockfd); + state->sockfd = INVALID_SOCKET; + return "ConnectEx() failed"; + } + else + { + FF_DEBUG("ConnectEx() pending"); + } + } + else + { + FF_DEBUG("ConnectEx() succeeded, sent %u bytes of data", (unsigned) sent); } // No need to cleanup state fields here since we need them in the receive function From dbc5a7961e5e8501a07d3c20748653d23e17067b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 17 Mar 2025 23:13:44 +0800 Subject: [PATCH 29/37] Networking (Linux): don't generate SIGPIPE --- src/common/networking/networking_linux.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/common/networking/networking_linux.c b/src/common/networking/networking_linux.c index caf91f668f..4c3dc34353 100644 --- a/src/common/networking/networking_linux.c +++ b/src/common/networking/networking_linux.c @@ -57,14 +57,17 @@ static const char* tryTcpFastOpen(FFNetworkingState* state) #ifndef __APPLE__ FF_DEBUG("Using sendto() + MSG_FASTOPEN to send %u bytes of data", state->command.length); ssize_t sent = sendto(state->sockfd, - state->command.chars, - state->command.length, + state->command.chars, + state->command.length, #ifdef MSG_FASTOPEN - MSG_FASTOPEN | + MSG_FASTOPEN | #endif - MSG_DONTWAIT, - state->addr->ai_addr, - state->addr->ai_addrlen); + #ifdef MSG_NOSIGNAL + MSG_NOSIGNAL | + #endif + MSG_DONTWAIT, + state->addr->ai_addr, + state->addr->ai_addrlen); #else if (fcntl(state->sockfd, F_SETFL, O_NONBLOCK) == -1) { FF_DEBUG("fcntl(F_SETFL) failed: %s", strerror(errno)); From 28bc97b92b094ce21dea068f126fd00004f34003 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Tue, 18 Mar 2025 09:51:11 +0800 Subject: [PATCH 30/37] Networking (Linux): remove unnecessary variables --- src/common/networking/networking.h | 1 - src/common/networking/networking_linux.c | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/common/networking/networking.h b/src/common/networking/networking.h index 338827eaa6..ca856e2e03 100644 --- a/src/common/networking/networking.h +++ b/src/common/networking/networking.h @@ -15,7 +15,6 @@ typedef struct FFNetworkingState { OVERLAPPED overlapped; #else int sockfd; - FFstrbuf host; FFstrbuf command; struct addrinfo* addr; diff --git a/src/common/networking/networking_linux.c b/src/common/networking/networking_linux.c index 4c3dc34353..1f3881ae45 100644 --- a/src/common/networking/networking_linux.c +++ b/src/common/networking/networking_linux.c @@ -24,7 +24,7 @@ static const char* tryTcpFastOpen(FFNetworkingState* state) FF_UNUSED(state); return "TCP Fast Open not supported"; #else - FF_DEBUG("Attempting to use TCP Fast Open to connect to %s", state->host.chars); + FF_DEBUG("Attempting to use TCP Fast Open to connect"); #ifndef __APPLE__ // On macOS, TCP_FASTOPEN doesn't seem to be needed // Set TCP Fast Open @@ -97,7 +97,6 @@ static const char* tryTcpFastOpen(FFNetworkingState* state) sent, errno, strerror(errno)); freeaddrinfo(state->addr); state->addr = NULL; - ffStrbufDestroy(&state->host); ffStrbufDestroy(&state->command); return NULL; } @@ -124,7 +123,7 @@ static const char* tryTcpFastOpen(FFNetworkingState* state) static const char* connectAndSend(FFNetworkingState* state) { const char* ret = NULL; - FF_DEBUG("Using traditional connection method to connect to %s", state->host.chars); + FF_DEBUG("Using traditional connection method to connect"); FF_DEBUG("Attempting connect() to server..."); if(connect(state->sockfd, state->addr->ai_addr, state->addr->ai_addrlen) == -1) @@ -155,7 +154,6 @@ static const char* connectAndSend(FFNetworkingState* state) FF_DEBUG("Releasing address info and other resources"); freeaddrinfo(state->addr); state->addr = NULL; - ffStrbufDestroy(&state->host); ffStrbufDestroy(&state->command); return ret; @@ -169,8 +167,6 @@ static const char* initNetworkingState(FFNetworkingState* state, const char* hos FF_DEBUG("Initializing network connection state: host=%s, path=%s", host, path); // Initialize command and host information - ffStrbufInitS(&state->host, host); - ffStrbufInitA(&state->command, 64); ffStrbufAppendS(&state->command, "GET "); ffStrbufAppendS(&state->command, path); From 3489cc8fe8d460e5df91a4e45e781c61382d6fae Mon Sep 17 00:00:00 2001 From: Carter Li Date: Tue, 18 Mar 2025 09:56:35 +0800 Subject: [PATCH 31/37] Networking: don't enable HTTP compression if server doesn't support it --- src/common/networking/networking.h | 2 +- src/common/networking/networking_linux.c | 33 +++++++++++++--------- src/common/networking/networking_windows.c | 33 +++++++++++++--------- src/detection/publicip/publicip.c | 3 ++ 4 files changed, 43 insertions(+), 28 deletions(-) diff --git a/src/common/networking/networking.h b/src/common/networking/networking.h index ca856e2e03..0a86d6c685 100644 --- a/src/common/networking/networking.h +++ b/src/common/networking/networking.h @@ -25,7 +25,7 @@ typedef struct FFNetworkingState { uint32_t timeout; bool ipv6; - bool compression; + bool compression; // if true, HTTP content compression will be enabled if supported } FFNetworkingState; const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers); diff --git a/src/common/networking/networking_linux.c b/src/common/networking/networking_linux.c index 1f3881ae45..1f9dc6fb34 100644 --- a/src/common/networking/networking_linux.c +++ b/src/common/networking/networking_linux.c @@ -283,22 +283,29 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho { FF_DEBUG("Preparing to send HTTP request: host=%s, path=%s", host, path); - // Initialize with compression disabled by default - state->compression = false; + if (state->compression) + { + FF_DEBUG("Compression enabled, checking if zlib is available"); - #ifdef FF_HAVE_ZLIB - const char* zlibError = ffNetworkingLoadZlibLibrary(); - // Only enable compression if zlib library is successfully loaded - if (zlibError == NULL) + #ifdef FF_HAVE_ZLIB + const char* zlibError = ffNetworkingLoadZlibLibrary(); + // Only enable compression if zlib library is successfully loaded + if (zlibError == NULL) + { + FF_DEBUG("Successfully loaded zlib library, compression enabled"); + } else { + FF_DEBUG("Failed to load zlib library, compression disabled: %s", zlibError); + state->compression = false; + } + #else + FF_DEBUG("zlib not supported at build time, compression disabled"); + state->compression = false; + #endif + } + else { - state->compression = true; - FF_DEBUG("Successfully loaded zlib library, compression enabled"); - } else { - FF_DEBUG("Failed to load zlib library, compression disabled: %s", zlibError); + FF_DEBUG("Compression disabled"); } - #else - FF_DEBUG("zlib not supported at build time, compression disabled"); - #endif const char* initResult = initNetworkingState(state, host, path, headers); if (initResult != NULL) { diff --git a/src/common/networking/networking_windows.c b/src/common/networking/networking_windows.c index 9a61b73533..3e353167d7 100644 --- a/src/common/networking/networking_windows.c +++ b/src/common/networking/networking_windows.c @@ -49,22 +49,27 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho { FF_DEBUG("Preparing to send HTTP request: host=%s, path=%s", host, path); - // Initialize with compression disabled by default - state->compression = false; - - #ifdef FF_HAVE_ZLIB - const char* zlibError = ffNetworkingLoadZlibLibrary(); - // Only enable compression if zlib library is successfully loaded - if (zlibError == NULL) + if (state->compression) { - state->compression = true; - FF_DEBUG("Successfully loaded zlib library, compression enabled"); - } else { - FF_DEBUG("Failed to load zlib library, compression disabled: %s", zlibError); + #ifdef FF_HAVE_ZLIB + const char* zlibError = ffNetworkingLoadZlibLibrary(); + // Only enable compression if zlib library is successfully loaded + if (zlibError == NULL) + { + FF_DEBUG("Successfully loaded zlib library, compression enabled"); + } else { + FF_DEBUG("Failed to load zlib library, compression disabled: %s", zlibError); + state->compression = false; + } + #else + FF_DEBUG("zlib not supported at build time, compression disabled"); + state->compression = false; + #endif + } + else + { + FF_DEBUG("Compression disabled"); } - #else - FF_DEBUG("zlib not supported at build time, compression disabled"); - #endif static WSADATA wsaData; if (wsaData.wVersion == 0) diff --git a/src/detection/publicip/publicip.c b/src/detection/publicip/publicip.c index fd5bed2322..1c952415b9 100644 --- a/src/detection/publicip/publicip.c +++ b/src/detection/publicip/publicip.c @@ -19,7 +19,10 @@ void ffPreparePublicIp(FFPublicIpOptions* options) state->ipv6 = options->ipv6; if (options->url.length == 0) + { + state->compression = true; *status = ffNetworkingSendHttpRequest(state, options->ipv6 ? "v6.ipinfo.io" : "ipinfo.io", "/json", NULL); + } else { FF_STRBUF_AUTO_DESTROY host = ffStrbufCreateCopy(&options->url); From 8851821484393df4e2dea49d43fd219fdf39d66c Mon Sep 17 00:00:00 2001 From: Carter Li Date: Tue, 18 Mar 2025 10:20:23 +0800 Subject: [PATCH 32/37] Networking: don't enable TFO if server doesn't support it --- src/common/networking/networking.h | 1 + src/common/networking/networking_linux.c | 57 ++++++++++++++-------- src/common/networking/networking_windows.c | 20 +++++--- src/detection/publicip/publicip.c | 1 + 4 files changed, 54 insertions(+), 25 deletions(-) diff --git a/src/common/networking/networking.h b/src/common/networking/networking.h index 0a86d6c685..6662f3117d 100644 --- a/src/common/networking/networking.h +++ b/src/common/networking/networking.h @@ -26,6 +26,7 @@ typedef struct FFNetworkingState { uint32_t timeout; bool ipv6; bool compression; // if true, HTTP content compression will be enabled if supported + bool tfo; // if true, TCP Fast Open will be attempted first, and fallback to traditional connection if it fails } FFNetworkingState; const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers); diff --git a/src/common/networking/networking_linux.c b/src/common/networking/networking_linux.c index 1f9dc6fb34..b019caaf9f 100644 --- a/src/common/networking/networking_linux.c +++ b/src/common/networking/networking_linux.c @@ -19,7 +19,17 @@ // Try to use TCP Fast Open to send data static const char* tryTcpFastOpen(FFNetworkingState* state) { - #if !defined(TCP_FASTOPEN) || (defined(__linux__) && !defined(MSG_FASTOPEN)) + if (!state->tfo) + { + #ifndef __APPLE__ + FF_DEBUG("TCP Fast Open disabled, skipping"); + return "TCP Fast Open disabled"; + #else + FF_DEBUG("TCP Fast Open disabled, using connectx() to send data"); + #endif + } + + #if (!defined(__APPLE__) && !defined(TCP_FASTOPEN)) || (defined(__linux__) && !defined(MSG_FASTOPEN)) FF_DEBUG("TCP Fast Open not supported on this system"); FF_UNUSED(state); return "TCP Fast Open not supported"; @@ -81,7 +91,7 @@ static const char* tryTcpFastOpen(FFNetworkingState* state) .sae_dstaddr = state->addr->ai_addr, .sae_dstaddrlen = state->addr->ai_addrlen, }, - SAE_ASSOCID_ANY, CONNECT_DATA_IDEMPOTENT, + SAE_ASSOCID_ANY, state->tfo ? CONNECT_DATA_IDEMPOTENT : 0, &(struct iovec) { .iov_base = state->command.chars, .iov_len = state->command.length, @@ -91,31 +101,40 @@ static const char* tryTcpFastOpen(FFNetworkingState* state) return "fcntl(F_SETFL) failed"; } #endif - if (sent >= 0 || (errno == EAGAIN || errno == EWOULDBLOCK)) + if (sent > 0 || (errno == EAGAIN || errno == EWOULDBLOCK + #ifdef __APPLE__ + // On macOS EINPROGRESS means the connection cannot be completed immediately + // On Linux, it means the TFO cookie is not available locally + || errno == EINPROGRESS + #endif + )) { - FF_DEBUG("TCP Fast Open %s (sent=%zd, errno=%d: %s)", errno == 0 ? "succeeded" : "was in progress", - sent, errno, strerror(errno)); + FF_DEBUG( + #ifdef __APPLE__ + "connectx()" + #else + "sendto()" + #endif + " %s (sent=%zd, errno=%d: %s)", errno == 0 ? "succeeded" : "was in progress", + sent, errno, strerror(errno)); freeaddrinfo(state->addr); state->addr = NULL; ffStrbufDestroy(&state->command); return NULL; } - if (errno == EINPROGRESS) - { - FF_DEBUG("TCP Fast Open cookie is not available locally"); - return "sendto() reports EINPROGRESS"; - } - else - { - // Fast Open failed - FF_DEBUG("TCP Fast Open failed: %s (errno=%d)", strerror(errno), errno); + FF_DEBUG( #ifdef __APPLE__ - return "connectx() failed"; + "connectx()" #else - return "sendto() failed"; + "sendto()" #endif - } + " failed: %s (errno=%d)", strerror(errno), errno); + #ifdef __APPLE__ + return "connectx() failed"; + #else + return "sendto() failed"; + #endif #endif } @@ -316,10 +335,10 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho const char* tfoResult = tryTcpFastOpen(state); if (tfoResult == NULL) { - FF_DEBUG("TCP Fast Open succeeded or in progress"); + FF_DEBUG("TryTcpFastOpen succeeded or in progress"); return NULL; } - FF_DEBUG("TCP Fast Open unavailable or failed: %s, trying traditional connection", tfoResult); + FF_DEBUG("TryTcpFastOpen failed: %s, trying traditional connection", tfoResult); #ifdef FF_HAVE_THREADS if (instance.config.general.multithreading) diff --git a/src/common/networking/networking_windows.c b/src/common/networking/networking_windows.c index 3e353167d7..65a3b0d66d 100644 --- a/src/common/networking/networking_windows.c +++ b/src/common/networking/networking_windows.c @@ -166,12 +166,19 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho ffStrbufAppendS(&command, "\r\n"); #ifdef TCP_FASTOPEN - // Set TCP Fast Open - flag = 1; - if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_FASTOPEN, (char*)&flag, sizeof(flag)) != 0) { - FF_DEBUG("Failed to set TCP_FASTOPEN option: %s", ffDebugWin32Error((DWORD) WSAGetLastError())); - } else { - FF_DEBUG("Successfully set TCP_FASTOPEN option"); + if (state->tfo) + { + // Set TCP Fast Open + flag = 1; + if (setsockopt(state->sockfd, IPPROTO_TCP, TCP_FASTOPEN, (char*)&flag, sizeof(flag)) != 0) { + FF_DEBUG("Failed to set TCP_FASTOPEN option: %s", ffDebugWin32Error((DWORD) WSAGetLastError())); + } else { + FF_DEBUG("Successfully set TCP_FASTOPEN option"); + } + } + else + { + FF_DEBUG("TCP Fast Open disabled"); } #endif @@ -181,6 +188,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho command.chars, command.length, &sent, &state->overlapped); freeaddrinfo(addr); + addr = NULL; if(!result) { diff --git a/src/detection/publicip/publicip.c b/src/detection/publicip/publicip.c index 1c952415b9..a568f5d707 100644 --- a/src/detection/publicip/publicip.c +++ b/src/detection/publicip/publicip.c @@ -21,6 +21,7 @@ void ffPreparePublicIp(FFPublicIpOptions* options) if (options->url.length == 0) { state->compression = true; + state->tfo = true; *status = ffNetworkingSendHttpRequest(state, options->ipv6 ? "v6.ipinfo.io" : "ipinfo.io", "/json", NULL); } else From e18c261054dde7a640a03f8308e6b74ac4e1b8d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 18 Mar 2025 14:12:43 +0800 Subject: [PATCH 33/37] Networking (FreeBSD): support fast path without TFO enabled --- src/common/networking/networking_linux.c | 102 +++++++++++------------ 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/src/common/networking/networking_linux.c b/src/common/networking/networking_linux.c index b019caaf9f..731ca2d1a2 100644 --- a/src/common/networking/networking_linux.c +++ b/src/common/networking/networking_linux.c @@ -16,68 +16,65 @@ #include #include -// Try to use TCP Fast Open to send data -static const char* tryTcpFastOpen(FFNetworkingState* state) +static const char* tryNonThreadingFastPath(FFNetworkingState* state) { - if (!state->tfo) - { - #ifndef __APPLE__ - FF_DEBUG("TCP Fast Open disabled, skipping"); - return "TCP Fast Open disabled"; - #else - FF_DEBUG("TCP Fast Open disabled, using connectx() to send data"); - #endif - } - - #if (!defined(__APPLE__) && !defined(TCP_FASTOPEN)) || (defined(__linux__) && !defined(MSG_FASTOPEN)) - FF_DEBUG("TCP Fast Open not supported on this system"); - FF_UNUSED(state); - return "TCP Fast Open not supported"; - #else - FF_DEBUG("Attempting to use TCP Fast Open to connect"); + #if defined(TCP_FASTOPEN) || __APPLE__ - #ifndef __APPLE__ // On macOS, TCP_FASTOPEN doesn't seem to be needed - // Set TCP Fast Open - #ifdef __linux__ - int flag = 5; // the queue length of pending packets - #else - int flag = 1; // enable TCP Fast Open - #endif - if (setsockopt(state->sockfd, IPPROTO_TCP, - #ifdef __APPLE__ - // https://github.com/rust-lang/libc/pull/3135 - 0x218 // TCP_FASTOPEN_FORCE_ENABLE - #else - TCP_FASTOPEN + if (!state->tfo) + { + #ifdef __linux__ + // Linux doesn't support sendto() on unconnected sockets + FF_DEBUG("TCP Fast Open disabled, skipping"); + return "TCP Fast Open disabled"; #endif - , &flag, sizeof(flag)) != 0) { - FF_DEBUG("Failed to set TCP_FASTOPEN option: %s", strerror(errno)); - return "setsockopt(TCP_FASTOPEN) failed"; - } else { + } + else + { + FF_DEBUG("Attempting to use TCP Fast Open to connect"); + + #ifndef __APPLE__ // On macOS, TCP_FASTOPEN doesn't seem to be needed + // Set TCP Fast Open #ifdef __linux__ - FF_DEBUG("Successfully set TCP_FASTOPEN option, queue length: %d", flag); - #elif defined(__APPLE__) - FF_DEBUG("Successfully set TCP_FASTOPEN_FORCE_ENABLE option"); + int flag = 5; // the queue length of pending packets #else - FF_DEBUG("Successfully set TCP_FASTOPEN option"); + int flag = 1; // enable TCP Fast Open + #endif + if (setsockopt(state->sockfd, IPPROTO_TCP, + #ifdef __APPLE__ + // https://github.com/rust-lang/libc/pull/3135 + 0x218 // TCP_FASTOPEN_FORCE_ENABLE + #else + TCP_FASTOPEN + #endif + , &flag, sizeof(flag)) != 0) { + FF_DEBUG("Failed to set TCP_FASTOPEN option: %s", strerror(errno)); + return "setsockopt(TCP_FASTOPEN) failed"; + } else { + #ifdef __linux__ + FF_DEBUG("Successfully set TCP_FASTOPEN option, queue length: %d", flag); + #elif defined(__APPLE__) + FF_DEBUG("Successfully set TCP_FASTOPEN_FORCE_ENABLE option"); + #else + FF_DEBUG("Successfully set TCP_FASTOPEN option"); + #endif + } #endif } - #endif #ifndef __APPLE__ - FF_DEBUG("Using sendto() + MSG_FASTOPEN to send %u bytes of data", state->command.length); + FF_DEBUG("Using sendto() + MSG_DONTWAIT to send %u bytes of data", state->command.length); ssize_t sent = sendto(state->sockfd, - state->command.chars, - state->command.length, + state->command.chars, + state->command.length, #ifdef MSG_FASTOPEN - MSG_FASTOPEN | + MSG_FASTOPEN | #endif #ifdef MSG_NOSIGNAL - MSG_NOSIGNAL | + MSG_NOSIGNAL | #endif - MSG_DONTWAIT, - state->addr->ai_addr, - state->addr->ai_addrlen); + MSG_DONTWAIT, + state->addr->ai_addr, + state->addr->ai_addrlen); #else if (fcntl(state->sockfd, F_SETFL, O_NONBLOCK) == -1) { FF_DEBUG("fcntl(F_SETFL) failed: %s", strerror(errno)); @@ -135,6 +132,9 @@ static const char* tryTcpFastOpen(FFNetworkingState* state) #else return "sendto() failed"; #endif + #else + FF_UNUSED(state); + return "TFO support is not available"; #endif } @@ -333,12 +333,12 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho } FF_DEBUG("Network state initialization successful"); - const char* tfoResult = tryTcpFastOpen(state); + const char* tfoResult = tryNonThreadingFastPath(state); if (tfoResult == NULL) { - FF_DEBUG("TryTcpFastOpen succeeded or in progress"); + FF_DEBUG("TryNonThreadingFastPath() succeeded or in progress"); return NULL; } - FF_DEBUG("TryTcpFastOpen failed: %s, trying traditional connection", tfoResult); + FF_DEBUG("TryNonThreadingFastPath() failed: %s, trying traditional connection", tfoResult); #ifdef FF_HAVE_THREADS if (instance.config.general.multithreading) From ef5d12395b8a78613e02b39d410d0b7e361d80e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 18 Mar 2025 16:25:35 +0800 Subject: [PATCH 34/37] Release: v2.39.0 --- CHANGELOG.md | 2 ++ CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7deb8440c2..0352c6a662 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ Features: * Improve performance of SSID detection on macOS Sequoia (Wifi, macOS, #1597) * Support warp terminal version detection on Windows (Terminal, Windows) * Support default route detection on OpenBSD & DragonFly BSD (LocalIP, OpenBSD / DragonFly) +* Improve bash completion script +* Improve performance of networking (PublicIP / Weather) Logo: * Add Common Torizon OS diff --git a/CMakeLists.txt b/CMakeLists.txt index 08c01ade21..6ab1d4ae45 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.38.0 + VERSION 2.39.0 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" From d33b5d60b6f5db72dbf4d23f8b642d60429844b3 Mon Sep 17 00:00:00 2001 From: apocelipes Date: Tue, 18 Mar 2025 19:51:51 +0800 Subject: [PATCH 35/37] Display (Linux): fix the error check for ffReadFileData (#1622) --- src/detection/displayserver/linux/drm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/detection/displayserver/linux/drm.c b/src/detection/displayserver/linux/drm.c index 1fe274b479..c62c544e55 100644 --- a/src/detection/displayserver/linux/drm.c +++ b/src/detection/displayserver/linux/drm.c @@ -30,7 +30,7 @@ static const char* drmParseSysfs(FFDisplayServerResult* result) char buf; ffStrbufAppendS(&drmDir, "/enabled"); - if (!ffReadFileData(drmDir.chars, sizeof(buf), &buf) || buf != 'e') { + if (ffReadFileData(drmDir.chars, sizeof(buf), &buf) <= 0 || buf != 'e') { /* read failed or enabled != "enabled" */ ffStrbufSubstrBefore(&drmDir, drmDirWithDnameLength); ffStrbufAppendS(&drmDir, "/status"); From 0d2ccb5fe5b1dce34c71a5b8381ed12c1411a26a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 19 Mar 2025 14:47:07 +0800 Subject: [PATCH 36/37] Logo (Builtin): add NetBSD2 Ref: https://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/sysutils/fastfetch/patches/patch-src_logo_ascii_netbsd2.txt?rev=1.1;content-type=text%2Fplain --- CHANGELOG.md | 1 + src/logo/ascii/netbsd2.txt | 20 ++++++++++++++++++++ src/logo/builtin.c | 12 ++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 src/logo/ascii/netbsd2.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 0352c6a662..6af55cf1ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Logo: * Add Common Torizon OS * Change FoxOS to WolfOS * Add Bredos +* Add NetBSD2 # 2.38.0 diff --git a/src/logo/ascii/netbsd2.txt b/src/logo/ascii/netbsd2.txt new file mode 100644 index 0000000000..81777a466d --- /dev/null +++ b/src/logo/ascii/netbsd2.txt @@ -0,0 +1,20 @@ + __,gnnnOCCCCCOObaau,_ +$2 _._ $1__,gnnCCCCCCCCOPF"'' +$2 (N\\$1XCbngg,._____.,gnnndCCCCCCCCCCCCF"___,,,,___ +$2 \N\\$1XCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCOOOOPYvv. +$2 \N\\$1XCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCPF"'' +$2 \N\\$1XCCCCCCCCCCCCCCCCCCCCCCCCCOF"' +$2 \N\\$1XCCCCCCCCCCCCCCCCCCCCOF"' +$2 \N\\$1XCCCCCCCCCCCCCCCPF"' +$2 \N\\$1\"PCOCCCOCCFP"" +$2 \N\\ + \N\\ + \N\\ + \NN\ + \NN\ + \NNA. + \NNA, + \NNN, + \NNN\ + \NNN\ + \NNNA \ No newline at end of file diff --git a/src/logo/builtin.c b/src/logo/builtin.c index bea415634f..19dffec73e 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -3148,6 +3148,18 @@ static const FFlogo N[] = { .colorKeys = FF_COLOR_FG_RED, .colorTitle = FF_COLOR_FG_WHITE, }, + // NetBSD2 + { + .names = {"NetBSD2"}, + .lines = FASTFETCH_DATATEXT_LOGO_NETBSD2, + .type = FF_LOGO_LINE_TYPE_ALTER_BIT, + .colors = { + FF_COLOR_FG_RED, + FF_COLOR_FG_WHITE, + }, + .colorKeys = FF_COLOR_FG_RED, + .colorTitle = FF_COLOR_FG_WHITE, + }, // NetBSD Small { .names = {"NetBSD_small"}, From b662db452f07d4d4c7bb86fd81fed59f2856dc95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 19 Mar 2025 14:59:59 +0800 Subject: [PATCH 37/37] Packages (Linux): add pkgsrc detection We will not add pkgsrc detection for macOS unless someone can verify it. Ref: https://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/sysutils/fastfetch/patches/patch-src_detection_packages_packages__linux.c?rev=1.1;content-type=text%2Fplain --- CHANGELOG.md | 1 + src/detection/packages/packages_linux.c | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6af55cf1ca..b412be171b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Features: * Support default route detection on OpenBSD & DragonFly BSD (LocalIP, OpenBSD / DragonFly) * Improve bash completion script * Improve performance of networking (PublicIP / Weather) +* Support pkgsrc package manager detection on Linux (Packages, Linux) Logo: * Add Common Torizon OS diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index ab677ccfce..43ed96e6c7 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -574,6 +574,7 @@ static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts, if (!(options->disabled & FF_PACKAGES_FLAG_PACSTALL_BIT)) packageCounts->pacstall += getNumElements(baseDir, "/var/lib/pacstall/metadata", false); if (!(options->disabled & FF_PACKAGES_FLAG_QI_BIT)) packageCounts->qi += getNumStrings(baseDir, "/var/qi/installed_packages.list", "\n", "qi"); if (!(options->disabled & FF_PACKAGES_FLAG_PISI_BIT)) packageCounts->pisi += getNumElements(baseDir, "/var/lib/pisi/package", true); + if (!(options->disabled & FF_PACKAGES_FLAG_PKGSRC_BIT)) packageCounts->pkgsrc += getNumElements(baseDir, "/usr/pkg/pkgdb", DT_DIR); } static void getPackageCountsRegular(FFstrbuf* baseDir, FFPackagesResult* packageCounts, FFPackagesOptions* options)