diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d6eb8d76c..94f3a74b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -377,7 +377,7 @@ jobs: with: msystem: CLANG64 update: true - install: git mingw-w64-clang-x86_64-7zip mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-clang mingw-w64-clang-x86_64-vulkan-loader mingw-w64-clang-x86_64-opencl-icd + install: git mingw-w64-clang-x86_64-7zip mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-clang mingw-w64-clang-x86_64-vulkan-loader mingw-w64-clang-x86_64-vulkan-headers mingw-w64-clang-x86_64-opencl-icd - name: print msys version run: uname -a @@ -439,7 +439,7 @@ jobs: with: msystem: CLANG32 update: true - install: git mingw-w64-clang-i686-7zip mingw-w64-clang-i686-cmake mingw-w64-clang-i686-clang mingw-w64-clang-i686-vulkan-loader mingw-w64-clang-i686-opencl-icd + install: git mingw-w64-clang-i686-7zip mingw-w64-clang-i686-cmake mingw-w64-clang-i686-clang mingw-w64-clang-i686-vulkan-loader mingw-w64-clang-i686-vulkan-headers mingw-w64-clang-i686-opencl-icd - name: print msys version run: uname -a diff --git a/.gitignore b/.gitignore index 6a7a19529..9af64d1cd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ -**/build/ -**/.vscode/ -**/.cache/ -**/.kdev4/ -**/.DS_Store -/.vs +build/ +.vs/ +.vscode/ +.cache/ +.kdev4/ +.DS_Store cscope.* tags fastfetch.kdev4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cb31dd9c..6c534c9c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +# 2.8.10 + +Changes: +* Use MS-DOS device name as mountFrom result, instead of useless GUID volume name (Windows, Disk) +* Some adjustments to Terminal detection (Terminal, Windows) + * Don't pretty print CMD + * Print conhost as Windows Console + * Don't detect `wininit` as Terminal + +Bugfixes: +* Don't display 0.00 GHz (CPU, FreeBSD) +* Don't detect manufactor of Qualcomm as ARM (CPU, Android) +* Ignore `chezmoi` (Terminal, Linux) +* Trim trailing possible whitespaces (PublicIP) +* Fix detection compatibility for KDE 6 (Font, Linux) +* Always use Metal API to detect vmem size (GPU, macOS) + +Features: +* Improve stability; print more useful error message; avoid misuse (PublicIP / Weather) + +Logo: +* Fix color of Arco Linux + # 2.8.9 Bugfixes: diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c3100abb..64a87ad3d 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.8.9 + VERSION 2.8.10 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" diff --git a/debian/changelog b/debian/changelog index 80ba492cb..8515ae964 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +fastfetch (2.8.9) jammy; urgency=medium + + * Update to 2.8.9 + + -- Carter Li Fri, 15 Mar 2024 10:49:42 +0800 + fastfetch (2.8.8) jammy; urgency=medium * Update to 2.8.8 diff --git a/debian/files b/debian/files index 094800a84..b621bfa1f 100644 --- a/debian/files +++ b/debian/files @@ -1 +1 @@ -fastfetch_2.8.8_source.buildinfo universe/utils optional +fastfetch_2.8.9_source.buildinfo universe/utils optional diff --git a/presets/examples/12.jsonc b/presets/examples/12.jsonc new file mode 100644 index 000000000..eca55ab7f --- /dev/null +++ b/presets/examples/12.jsonc @@ -0,0 +1,104 @@ +{ + "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", + "logo": { + "type": "none" + }, + "display": { + "separator": "-> " + }, + "modules": [ + { + "type": "title", + "format": " {6}{7}{8}" + }, + "break", + { + "type": "custom", + "format": "┌───────────────────────────── \u001b[1mSystem Information\u001b[0m ─────────────────────────────┐" // `\u001b` is `\033`, or `\e` + }, + "break", + { + "key": "  OS ", + "keyColor": "red", + "type": "os" + }, + { + "key": "  Machine ", + "keyColor": "green", + "type": "host" + }, + { + "key": "  Kernel ", + "keyColor": "magenta", + "type": "kernel" + }, + { + "key": " 󰅐 Uptime ", + "keyColor": "red", + "type": "uptime" + }, + { + "key": " 󰍹 Resolution ", + "keyColor": "yellow", + "type": "display", + "compactType": "original-with-refresh-rate" + }, + { + "key": "  WM ", + "keyColor": "blue", + "type": "wm" + }, + { + "key": "  DE ", + "keyColor": "green", + "type": "de" + }, + { + "key": "  Shell ", + "keyColor": "cyan", + "type": "shell" + }, + { + "key": "  Terminal ", + "keyColor": "red", + "type": "terminal" + }, + { + "key": "  CPU ", + "keyColor": "yellow", + "type": "cpu" + }, + { + "key": " ﬙ GPU ", + "keyColor": "blue", + "type": "gpu" + }, + { + "key": " 󰑭 Memory ", + "keyColor": "magenta", + "type": "memory" + }, + { + "key": " 󰩟 Local IP ", + "keyColor": "red", + "type": "localip", + "compact": true + }, + { + "key": " 󰩠 Public IP ", + "keyColor": "cyan", + "type": "publicip" + }, + "break", + { + "type": "custom", + "format": "└──────────────────────────────────────────────────────────────────────────────┘" // `\u001b` is `\033`, or `\e` + }, + "break", + { + "type": "colors", + "paddingLeft": 34, + "symbol": "circle" + } + ] +} diff --git a/src/common/font.c b/src/common/font.c index 2e89f1c69..80a67144c 100644 --- a/src/common/font.c +++ b/src/common/font.c @@ -2,6 +2,7 @@ #include "common/font.h" #include +#include void ffFontInit(FFfont* font) { @@ -66,59 +67,33 @@ void ffFontInitQt(FFfont* font, const char* data) //See https://doc.qt.io/qt-5/qfont.html#toString //Family - while(*data != ',' && *data != '\0') - { - ffStrbufAppendC(&font->name, *data); - ++data; - } - if(*data != '\0') - ++data; + data = ffStrbufAppendSUntilC(&font->name, data, ','); ffStrbufTrim(&font->name, ' '); + if (!data) goto exit; + data++; //Size - while(*data != ',' && *data != '\0') - { - ffStrbufAppendC(&font->size, *data); - ++data; - } - if(*data != '\0') - ++data; + data = ffStrbufAppendSUntilC(&font->size, data, ','); ffStrbufTrim(&font->size, ' '); - - #define FF_FONT_QT_SKIP_VALUE \ - while(*data != ',' && *data != '\0') \ - ++data; \ - if(*data != '\0') \ - ++data; - - FF_FONT_QT_SKIP_VALUE //Pixel size - FF_FONT_QT_SKIP_VALUE //Style hint - FF_FONT_QT_SKIP_VALUE //Font weight - FF_FONT_QT_SKIP_VALUE //Font style - FF_FONT_QT_SKIP_VALUE //Underline - FF_FONT_QT_SKIP_VALUE //Strike out - FF_FONT_QT_SKIP_VALUE //Fixed pitch - FF_FONT_QT_SKIP_VALUE //Always 0 - - #undef FF_FONT_QT_SKIP_VALUE - - while(*data != '\0') + if (!data) goto exit; + data++; + + //Style + data = strrchr(data, ','); + if (!data) goto exit; + data++; + if (isalpha(*data)) { - while(*data == ' ') - ++data; - - if(*data == '\0') - break; - - FFstrbuf* style = ffListAdd(&font->styles); - ffStrbufInit(style); - while(*data != ' ' && *data != '\0') + do { - ffStrbufAppendC(style, *data); - ++data; - } + FFstrbuf* style = ffListAdd(&font->styles); + ffStrbufInit(style); + data = ffStrbufAppendSUntilC(style, data, ' '); + if (data) data++; + } while (data); } +exit: fontInitPretty(font); } diff --git a/src/common/networking.h b/src/common/networking.h index 1fb4589d9..cc8285cc5 100644 --- a/src/common/networking.h +++ b/src/common/networking.h @@ -20,15 +20,9 @@ typedef struct FFNetworkingState { FFThreadType thread; #endif #endif -} FFNetworkingState; -bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers); -bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout); + uint32_t timeout; +} FFNetworkingState; -static inline bool ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, const char* headers, FFstrbuf* buffer) -{ - FFNetworkingState state; - if(ffNetworkingSendHttpRequest(&state, host, path, headers)) - return ffNetworkingRecvHttpResponse(&state, buffer, timeout); - return false; -} +const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers); +const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer); diff --git a/src/common/networking_linux.c b/src/common/networking_linux.c index 37c796276..7a8054918 100644 --- a/src/common/networking_linux.c +++ b/src/common/networking_linux.c @@ -5,30 +5,50 @@ #include #include #include +#include // For FreeBSD +#include -static void connectAndSend(FFNetworkingState* state) +static const char* connectAndSend(FFNetworkingState* state) { - struct addrinfo hints = { - .ai_family = AF_INET, - .ai_socktype = SOCK_STREAM, - }; - + const char* ret = NULL; struct addrinfo* addr; - if(getaddrinfo(state->host.chars, "80", &hints, &addr) != 0) + if(getaddrinfo(state->host.chars, "80", &(struct addrinfo) { + .ai_family = AF_INET, + .ai_socktype = SOCK_STREAM, + }, &addr) != 0) + { + ret = "getaddrinfo() failed"; goto error; + } state->sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if(state->sockfd == -1) { freeaddrinfo(addr); + ret = "socket() failed"; goto error; } + if (state->timeout > 0) + { + FF_MAYBE_UNUSED uint32_t sec = state->timeout / 1000; + if (sec == 0) sec = 1; + + #ifdef TCP_CONNECTIONTIMEOUT + setsockopt(state->sockfd, IPPROTO_TCP, TCP_CONNECTIONTIMEOUT, &sec, sizeof(sec)); + #elif defined(TCP_KEEPINIT) + setsockopt(state->sockfd, IPPROTO_TCP, TCP_KEEPINIT, &sec, sizeof(sec)); + #elif defined(TCP_USER_TIMEOUT) + setsockopt(state->sockfd, IPPROTO_TCP, TCP_USER_TIMEOUT, &state->timeout, sizeof(state->timeout)); + #endif + } + if(connect(state->sockfd, addr->ai_addr, addr->ai_addrlen) == -1) { close(state->sockfd); freeaddrinfo(addr); + ret = "connect() failed"; goto error; } @@ -37,6 +57,7 @@ static void connectAndSend(FFNetworkingState* state) if(send(state->sockfd, state->command.chars, state->command.length, 0) < 0) { close(state->sockfd); + ret = "send() failed"; goto error; } @@ -48,11 +69,13 @@ static void connectAndSend(FFNetworkingState* state) exit: ffStrbufDestroy(&state->host); ffStrbufDestroy(&state->command); + + return ret; } FF_THREAD_ENTRY_DECL_WRAPPER(connectAndSend, FFNetworkingState*); -bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers) +const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers) { ffStrbufInitS(&state->host, host); @@ -66,22 +89,30 @@ bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, con ffStrbufAppendS(&state->command, "\r\n"); #ifdef FF_HAVE_THREADS + if (instance.config.general.multithreading) + { state->thread = ffThreadCreate(connectAndSendThreadMain, state); - return !!state->thread; - #else - connectAndSend(state); - return state->sockfd != -1; + return state->thread ? NULL : "ffThreadCreate(connectAndSend) failed"; + } #endif + + return connectAndSend(state); } -bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout) +const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer) { + uint32_t timeout = state->timeout; + #ifdef FF_HAVE_THREADS + if (instance.config.general.multithreading) + { if (!ffThreadJoin(state->thread, timeout)) - return false; + return "ffThreadJoin() failed or timeout"; + } #endif + if(state->sockfd == -1) - return false; + return "ffNetworkingSendHttpRequest() failed"; if(timeout > 0) { @@ -91,14 +122,15 @@ bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, ui setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev)); } - ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, ffStrbufGetFree(buffer), 0); - - if(received > 0) - { + uint32_t recvStart; + do { + recvStart = buffer->length; + ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, ffStrbufGetFree(buffer), 0); + if (received <= 0) break; buffer->length += (uint32_t) received; buffer->chars[buffer->length] = '\0'; - } + } while (ffStrbufGetFree(buffer) > 0 && strstr(buffer->chars + recvStart, "\r\n\r\n") == NULL); close(state->sockfd); - return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n"); + return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n") ? NULL : "Invalid response"; } diff --git a/src/common/networking_windows.c b/src/common/networking_windows.c index 739b27600..27a884216 100644 --- a/src/common/networking_windows.c +++ b/src/common/networking_windows.c @@ -33,21 +33,20 @@ static const char* initWsaData(WSADATA* wsaData) return NULL; } -bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers) +const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, const char* path, const char* headers) { static WSADATA wsaData; if (wsaData.wVersion == 0) { - if (initWsaData(&wsaData) != NULL) + const char* error = initWsaData(&wsaData); + if (error != NULL) { wsaData.wVersion = (WORD) -1; - return false; + return error; } } else if (wsaData.wVersion == (WORD) -1) - return false; - - memset(state, 0, sizeof(*state)); + return "initWsaData() failed before"; struct addrinfo* addr; @@ -55,26 +54,27 @@ bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, con .ai_family = AF_INET, .ai_socktype = SOCK_STREAM, }, &addr) != 0) - return false; + return "getaddrinfo() failed"; state->sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if(state->sockfd == INVALID_SOCKET) { freeaddrinfo(addr); - return false; + return "socket() failed"; } { //ConnectEx requires the socket to be initially bound - struct sockaddr_in addr = { + if(bind(state->sockfd, (SOCKADDR *) &(struct sockaddr_in) { .sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY, .sin_port = 0, - }; - if(bind(state->sockfd, (SOCKADDR *)&addr, sizeof(addr)) != 0) + }, sizeof(struct sockaddr_in)) != 0) { - printf("bind %d\n", WSAGetLastError()); - return false; + closesocket(state->sockfd); + freeaddrinfo(addr); + state->sockfd = INVALID_SOCKET; + return "bind() failed"; } } @@ -93,35 +93,52 @@ bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, con if(!result && WSAGetLastError() != WSA_IO_PENDING) { closesocket(state->sockfd); - return false; + freeaddrinfo(addr); + state->sockfd = INVALID_SOCKET; + return "ConnectEx() failed"; } - return true; + return NULL; } -bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout) +const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer) { + if (state->sockfd == INVALID_SOCKET) + return "ffNetworkingSendHttpRequest() failed"; + + uint32_t timeout = state->timeout; + if (timeout > 0) + { + if (WaitForSingleObject((HANDLE) state->sockfd, timeout) != WAIT_OBJECT_0) + { + CancelIo((HANDLE) state->sockfd); + closesocket(state->sockfd); + return "WaitForSingleObject(state->sockfd) failed or timeout"; + } + } + DWORD transfer, flags; if (!WSAGetOverlappedResult(state->sockfd, &state->overlapped, &transfer, TRUE, &flags)) { closesocket(state->sockfd); - return false; + 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)); + setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &timeout, sizeof(timeout)); } - ssize_t received = recv(state->sockfd, buffer->chars + buffer->length, (int)ffStrbufGetFree(buffer), 0); - - if(received > 0) - { - buffer->length += (uint32_t) received; + uint32_t recvStart; + 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; buffer->chars[buffer->length] = '\0'; - } + } while (ffStrbufGetFree(buffer) > 0 && strstr(buffer->chars + recvStart, "\r\n\r\n") == NULL); closesocket(state->sockfd); - return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n"); + return ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n") ? NULL : "Invalid response"; } diff --git a/src/detection/cpu/cpu_bsd.c b/src/detection/cpu/cpu_bsd.c index e1d647cd3..70623cbff 100644 --- a/src/detection/cpu/cpu_bsd.c +++ b/src/detection/cpu/cpu_bsd.c @@ -11,7 +11,8 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) cpu->coresLogical = cpu->coresPhysical; cpu->coresOnline = cpu->coresPhysical; - cpu->frequencyBase = ffSysctlGetInt("hw.clockrate", 0) / 1000.0; + int clockRate = ffSysctlGetInt("hw.clockrate", 0); + cpu->frequencyBase = clockRate <= 0 ? 0.0/0.0 : clockRate / 1000.0; cpu->temperature = FF_CPU_TEMP_UNSET; if (options->temp) diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index 673f0cbdb..acce51ca8 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -17,7 +17,10 @@ static void detectAndroid(FFCPUResult* cpu) { if (cpu->name.length == 0) + { ffSettingsGetAndroidProperty("ro.soc.model", &cpu->name); + ffStrbufClear(&cpu->vendor); // We usually detect the vendor of CPU core as ARM, but instead we want the vendor of SOC + } if (cpu->vendor.length == 0) { if (!ffSettingsGetAndroidProperty("ro.soc.manufacturer", &cpu->vendor)) diff --git a/src/detection/disk/disk_windows.c b/src/detection/disk/disk_windows.c index 24a1651ba..cf951f294 100644 --- a/src/detection/disk/disk_windows.c +++ b/src/detection/disk/disk_windows.c @@ -15,7 +15,7 @@ const char* ffDetectDisksImpl(FFlist* disks) for(uint32_t i = 0; i < length; i++) { - const wchar_t* mountpoint = buf + i; + wchar_t* mountpoint = buf + i; UINT driveType = GetDriveTypeW(mountpoint); if(driveType == DRIVE_NO_ROOT_DIR) @@ -25,13 +25,6 @@ const char* ffDetectDisksImpl(FFlist* disks) } FFDisk* disk = ffListAdd(disks); - ffStrbufInitWS(&disk->mountpoint, mountpoint); - - wchar_t volumeName[64]; - if(GetVolumeNameForVolumeMountPointW(mountpoint, volumeName, sizeof(volumeName) / sizeof(*volumeName))) - ffStrbufInitWS(&disk->mountFrom, volumeName); - else - ffStrbufInit(&disk->mountFrom); if(!GetDiskFreeSpaceExW( mountpoint, @@ -78,6 +71,17 @@ const char* ffDetectDisksImpl(FFlist* disks) disk->type |= FF_DISK_VOLUME_TYPE_READONLY_BIT; } + ffStrbufInitWS(&disk->mountpoint, mountpoint); + if (mountpoint[2] == L'\\' && mountpoint[3] == L'\0') + { + wchar_t volumeName[MAX_PATH + 1]; + mountpoint[2] = L'\0'; + if(QueryDosDeviceW(mountpoint, volumeName, sizeof(volumeName) / sizeof(*volumeName))) + ffStrbufInitWS(&disk->mountFrom, volumeName); + else + ffStrbufInit(&disk->mountFrom); + } + //Unsupported disk->filesUsed = 0; disk->filesTotal = 0; diff --git a/src/detection/gpu/gpu_apple.c b/src/detection/gpu/gpu_apple.c index 6113dfce6..6bc0ee48e 100644 --- a/src/detection/gpu/gpu_apple.c +++ b/src/detection/gpu/gpu_apple.c @@ -63,10 +63,6 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) ffStrbufInit(&gpu->driver); // Ok for both Apple and Intel ffCfDictGetString(properties, CFSTR("CFBundleIdentifier"), &gpu->driver); - int vram; // Supported on Intel - if(!ffCfDictGetInt(properties, CFSTR("VRAM,totalMB"), &vram)) - gpu->dedicated.total = (uint64_t) vram * 1024 * 1024; - if(ffCfDictGetInt(properties, CFSTR("gpu-core-count"), &gpu->coreCount)) // For Apple gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; diff --git a/src/detection/publicip/publicip.c b/src/detection/publicip/publicip.c index 4c1cdc42f..b5038e66e 100644 --- a/src/detection/publicip/publicip.c +++ b/src/detection/publicip/publicip.c @@ -1,23 +1,35 @@ #include "publicip.h" #include "common/networking.h" +#define FF_UNITIALIZED ((const char*)(uintptr_t) -1) static FFNetworkingState state; -static int status = -1; +static const char* status = FF_UNITIALIZED; void ffPreparePublicIp(FFPublicIpOptions* options) { - if (status != -1) + if (status != FF_UNITIALIZED) { fputs("Error: this module can only be used once due to internal limitations\n", stderr); exit(1); } + state.timeout = options->timeout; + if (options->url.length == 0) status = ffNetworkingSendHttpRequest(&state, "ipinfo.io", "/json", NULL); else { FF_STRBUF_AUTO_DESTROY host = ffStrbufCreateCopy(&options->url); - ffStrbufSubstrAfterFirstS(&host, "://"); + uint32_t hostStartIndex = ffStrbufFirstIndexS(&host, "://"); + if (hostStartIndex < host.length) + { + if (hostStartIndex != 4 || !ffStrbufStartsWithIgnCaseS(&host, "http")) + { + fputs("Error: only http: protocol is supported. Use `Command` module with `curl` if needed\n", stderr); + exit(1); + } + ffStrbufSubstrAfter(&host, hostStartIndex + (strlen("://") - 1)); + } uint32_t pathStartIndex = ffStrbufFirstIndexC(&host, '/'); FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); @@ -41,18 +53,21 @@ static inline void wrapYyjsonFree(yyjson_doc** doc) const char* ffDetectPublicIp(FFPublicIpOptions* options, FFPublicIpResult* result) { - if (status == -1) + if (status == FF_UNITIALIZED) ffPreparePublicIp(options); - if (status == 0) - return "Failed to connect to an IP detection server"; + if (status != NULL) + return status; FF_STRBUF_AUTO_DESTROY response = ffStrbufCreateA(4096); - bool success = ffNetworkingRecvHttpResponse(&state, &response, options->timeout); - if (success) ffStrbufSubstrAfterFirstS(&response, "\r\n\r\n"); + const char* error = ffNetworkingRecvHttpResponse(&state, &response); + if (error == NULL) + ffStrbufSubstrAfterFirstS(&response, "\r\n\r\n"); + else + return error; - if (!success || response.length == 0) - return "Failed to receive the server response"; + if (response.length == 0) + return "Empty server response received"; if (options->url.length == 0) { @@ -69,5 +84,6 @@ const char* ffDetectPublicIp(FFPublicIpOptions* options, FFPublicIpResult* resul ffStrbufDestroy(&result->ip); ffStrbufInitMove(&result->ip, &response); + ffStrbufTrimRightSpace(&result->ip); return NULL; } diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index fc29113f4..d62d7275e 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -280,6 +280,7 @@ static pid_t getTerminalInfo(FFTerminalResult* result, pid_t pid) ffStrEquals(name, "xonsh") || // works in Linux but not in macOS because kernel returns `Python` in this case ffStrEquals(name, "login") || ffStrEquals(name, "sshd") || + ffStrEquals(name, "chezmoi") || // #762 #ifdef __linux__ ffStrStartsWith(name, "flatpak-") || // #707 #endif diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index cce353c29..bd92f0829 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -143,7 +143,7 @@ static void setShellInfoDetails(FFShellResult* result) ffStrbufSetS(&result->prettyName, "Windows PowerShell ISE"); else if(ffStrbufIgnCaseEqualS(&result->prettyName, "cmd")) { - ffStrbufClear(&result->prettyName); + ffStrbufSetS(&result->prettyName, "CMD"); FF_AUTO_CLOSE_FD HANDLE snapshot = NULL; while(!(snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, result->pid)) && GetLastError() == ERROR_BAD_LENGTH) {} @@ -156,15 +156,13 @@ static void setShellInfoDetails(FFShellResult* result) { if(wcsncmp(module.szModule, L"clink_dll_", strlen("clink_dll_")) == 0) { - ffStrbufAppendS(&result->prettyName, "CMD (with Clink "); + ffStrbufAppendS(&result->prettyName, " (with Clink "); getProductVersion(module.szExePath, &result->prettyName); ffStrbufAppendC(&result->prettyName, ')'); break; } } } - if(result->prettyName.length == 0) - ffStrbufAppendS(&result->prettyName, "Command Prompt"); } else if(ffStrbufIgnCaseEqualS(&result->prettyName, "nu")) ffStrbufSetS(&result->prettyName, "nushell"); @@ -313,7 +311,8 @@ static uint32_t getTerminalInfo(FFTerminalResult* result, uint32_t pid) ffStrbufSubstrBefore(&result->prettyName, result->prettyName.length - 4); if(ffStrbufIgnCaseEqualS(&result->prettyName, "sihost") || - ffStrbufIgnCaseEqualS(&result->prettyName, "explorer") + ffStrbufIgnCaseEqualS(&result->prettyName, "explorer") || + ffStrbufIgnCaseEqualS(&result->prettyName, "wininit") ) { // A CUI program created by Windows Explorer will spawn a conhost as its child. // However the conhost process is just a placeholder; @@ -344,7 +343,7 @@ static void setTerminalInfoDetails(FFTerminalResult* result) : "Windows Terminal" ); else if(ffStrbufIgnCaseEqualS(&result->prettyName, "conhost")) - ffStrbufSetStatic(&result->prettyName, "Console Window Host"); + ffStrbufSetStatic(&result->prettyName, "Windows Console"); else if(ffStrbufIgnCaseEqualS(&result->prettyName, "Code")) ffStrbufSetStatic(&result->prettyName, "Visual Studio Code"); else if(ffStrbufIgnCaseEqualS(&result->prettyName, "explorer")) diff --git a/src/detection/weather/weather.c b/src/detection/weather/weather.c index 94bd6b5b6..de3281348 100644 --- a/src/detection/weather/weather.c +++ b/src/detection/weather/weather.c @@ -1,16 +1,19 @@ #include "weather.h" +#define FF_UNITIALIZED ((const char*)(uintptr_t) -1) static FFNetworkingState state; -static int status = -1; +static const char* status = FF_UNITIALIZED; void ffPrepareWeather(FFWeatherOptions* options) { - if (status != -1) + if (status != FF_UNITIALIZED) { fputs("Error: this module can only be used once due to internal limitations\n", stderr); exit(1); } + state.timeout = options->timeout; + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateS("/"); if (options->location.length) ffStrbufAppend(&path, &options->location); @@ -21,18 +24,24 @@ void ffPrepareWeather(FFWeatherOptions* options) const char* ffDetectWeather(FFWeatherOptions* options, FFstrbuf* result) { - if(status == -1) + if(status == FF_UNITIALIZED) ffPrepareWeather(options); - if(status == 0) - return "Failed to connect to 'wttr.in'"; + if(status != NULL) + return status; ffStrbufEnsureFree(result, 4095); - bool success = ffNetworkingRecvHttpResponse(&state, result, options->timeout); - if (success) ffStrbufSubstrAfterFirstS(result, "\r\n\r\n"); + const char* error = ffNetworkingRecvHttpResponse(&state, result); + if (error == NULL) + { + ffStrbufSubstrAfterFirstS(result, "\r\n\r\n"); + ffStrbufTrimRightSpace(result); + } + else + return error; - if(!success || result->length == 0) - return "Failed to receive the server response"; + if(result->length == 0) + return "Empty server response received"; return NULL; } diff --git a/src/logo/ascii/arco_small.txt b/src/logo/ascii/arco_small.txt index cafcc5441..45da66921 100644 --- a/src/logo/ascii/arco_small.txt +++ b/src/logo/ascii/arco_small.txt @@ -1,4 +1,4 @@ -${c1} A + A ooo ooooo ooooooo diff --git a/src/logo/builtin.c b/src/logo/builtin.c index 45e61e8aa..2b06782c5 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -413,7 +413,7 @@ static const FFlogo A[] = { .lines = FASTFETCH_DATATEXT_LOGO_ARCO, .colors = { FF_COLOR_FG_BLUE, - FF_COLOR_FG_GREEN, + FF_COLOR_FG_WHITE, }, .colorKeys = FF_COLOR_FG_BLUE, .colorTitle = FF_COLOR_FG_BLUE, @@ -425,7 +425,7 @@ static const FFlogo A[] = { .lines = FASTFETCH_DATATEXT_LOGO_ARCO_SMALL, .colors = { FF_COLOR_FG_BLUE, - FF_COLOR_FG_GREEN, + FF_COLOR_FG_WHITE, }, .colorKeys = FF_COLOR_FG_BLUE, .colorTitle = FF_COLOR_FG_BLUE, diff --git a/src/modules/disk/disk.c b/src/modules/disk/disk.c index b5137fa95..20c53f980 100644 --- a/src/modules/disk/disk.c +++ b/src/modules/disk/disk.c @@ -422,8 +422,16 @@ void ffGenerateDiskJsonResult(FFDiskOptions* options, yyjson_mut_doc* doc, yyjso yyjson_mut_obj_add_uint(doc, bytes, "used", item->bytesUsed); yyjson_mut_val* files = yyjson_mut_obj_add_obj(doc, obj, "files"); - yyjson_mut_obj_add_uint(doc, files, "total", item->filesTotal); - yyjson_mut_obj_add_uint(doc, files, "used", item->filesUsed); + if (item->filesTotal == 0 && item->filesUsed == 0) + { + yyjson_mut_obj_add_null(doc, files, "total"); + yyjson_mut_obj_add_null(doc, files, "used"); + } + else + { + yyjson_mut_obj_add_uint(doc, files, "total", item->filesTotal); + yyjson_mut_obj_add_uint(doc, files, "used", item->filesUsed); + } yyjson_mut_obj_add_strbuf(doc, obj, "filesystem", &item->filesystem); yyjson_mut_obj_add_strbuf(doc, obj, "mountpoint", &item->mountpoint); diff --git a/src/util/FFstrbuf.c b/src/util/FFstrbuf.c index d3c8bb8ec..5c045c190 100644 --- a/src/util/FFstrbuf.c +++ b/src/util/FFstrbuf.c @@ -159,16 +159,17 @@ void ffStrbufAppendVF(FFstrbuf* strbuf, const char* format, va_list arguments) strbuf->length += (uint32_t) written; } -void ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char until) +const char* ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char until) { if(value == NULL) - return; + return NULL; char* end = strchr(value, until); if(end == NULL) ffStrbufAppendS(strbuf, value); else ffStrbufAppendNS(strbuf, (uint32_t) (end - value), value); + return end; } void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...) diff --git a/src/util/FFstrbuf.h b/src/util/FFstrbuf.h index d26e1fff5..bf3550594 100644 --- a/src/util/FFstrbuf.h +++ b/src/util/FFstrbuf.h @@ -44,7 +44,7 @@ void ffStrbufAppendNS(FFstrbuf* strbuf, uint32_t length, const char* value); void ffStrbufAppendTransformS(FFstrbuf* strbuf, const char* value, int(*transformFunc)(int)); FF_C_PRINTF(2, 3) void ffStrbufAppendF(FFstrbuf* strbuf, const char* format, ...); void ffStrbufAppendVF(FFstrbuf* strbuf, const char* format, va_list arguments); -void ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char until); +const char* ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char until); void ffStrbufPrependNS(FFstrbuf* strbuf, uint32_t length, const char* value); diff --git a/src/util/windows/version.rc b/src/util/windows/version.rc index e130a65e9..789c0c103 100644 --- a/src/util/windows/version.rc +++ b/src/util/windows/version.rc @@ -1,54 +1,36 @@ -// -// Include the necessary resources -// +#ifdef RC_INVOKED + #include #include #include #include "fastfetch_config.h" -#ifdef RC_INVOKED - -// -// Set up debug information -// -#if DEBUG -#define VER_DEBUG VS_FF_DEBUG -#else -#define VER_DEBUG 0 -#endif - #define FF_TO_STR1(str) #str #define FF_TO_STR(str) FF_TO_STR1(str) CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "manifest.xml" -// ------- version info ------------------------------------------------------- - VS_VERSION_INFO VERSIONINFO -FILEVERSION FASTFETCH_PROJECT_VERSION_MAJOR,FASTFETCH_PROJECT_VERSION_MINOR,FASTFETCH_PROJECT_VERSION_PATCH,FASTFETCH_PROJECT_VERSION_TWEAK_NUM -PRODUCTVERSION FASTFETCH_PROJECT_VERSION_MAJOR,FASTFETCH_PROJECT_VERSION_MINOR,FASTFETCH_PROJECT_VERSION_PATCH,FASTFETCH_PROJECT_VERSION_TWEAK_NUM -FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -FILEFLAGS (VER_DEBUG|VS_FF_PRERELEASE) -FILEOS VOS_NT -FILETYPE VFT_APP -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", FASTFETCH_PROJECT_HOMEPAGE_URL - VALUE "FileDescription", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME) " - " FASTFETCH_PROJECT_DESCRIPTION - VALUE "FileVersion", FASTFETCH_PROJECT_VERSION FASTFETCH_PROJECT_VERSION_TWEAK - VALUE "InternalName", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME) ".exe" - VALUE "LegalCopyright", FASTFETCH_PROJECT_LICENSE - VALUE "OriginalFilename", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME) ".exe" - VALUE "ProductName", FASTFETCH_PROJECT_NAME - VALUE "ProductVersion", FASTFETCH_PROJECT_VERSION FASTFETCH_PROJECT_VERSION_TWEAK - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0409,1252 - END -END + FILEVERSION FASTFETCH_PROJECT_VERSION_MAJOR,FASTFETCH_PROJECT_VERSION_MINOR,FASTFETCH_PROJECT_VERSION_PATCH,FASTFETCH_PROJECT_VERSION_TWEAK_NUM + PRODUCTVERSION FASTFETCH_PROJECT_VERSION_MAJOR,FASTFETCH_PROJECT_VERSION_MINOR,FASTFETCH_PROJECT_VERSION_PATCH,FASTFETCH_PROJECT_VERSION_TWEAK_NUM + FILEOS VOS_NT + FILETYPE VFT_APP +{ + BLOCK "StringFileInfo" { + BLOCK "040904b0" { + VALUE "Comments", FASTFETCH_PROJECT_DESCRIPTION + VALUE "FileDescription", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME) + VALUE "FileVersion", FASTFETCH_PROJECT_VERSION FASTFETCH_PROJECT_VERSION_TWEAK + VALUE "InternalName", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME) + VALUE "LegalCopyright", FASTFETCH_PROJECT_LICENSE + VALUE "OriginalFilename", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME) ".exe" + VALUE "ProductName", FASTFETCH_PROJECT_NAME " - " FASTFETCH_PROJECT_DESCRIPTION + VALUE "ProductVersion", FASTFETCH_PROJECT_VERSION FASTFETCH_PROJECT_VERSION_TWEAK + VALUE "CompanyName", FASTFETCH_PROJECT_HOMEPAGE_URL + } + } + BLOCK "VarFileInfo" { + VALUE "Translation", 0x0409, 1200 + } +} #endif