diff --git a/CHANGELOG.md b/CHANGELOG.md index faf5354d6..e57c6d662 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +# 2.22.0 + +Features: +* Small performance improvements (Terminal, Editor) +* Improve arm32 and loongarch support (CPU, Linux) +* Ignore the parent process if env `$FFTS_IGNORE_PARENT` is set to `1` (Shell) +* Add code name of Apple M4 (CPU, Linux) +* Add ethernet speed rate detection support (LocalIP) +* Add zsh completion script +* Add Linglong package manager detection support (Packages, Linux) + +Bugfixes: +* Fix building on macOS 10.14 +* Fix tmux in linux TTY (Colors) +* Fix hang in WSL when custom format is used (Disk, Linux) +* Fix `/proc/loadavg` parsing (Loadavg, Linux) +* Disable use of `LC_NUMERIC` locale settings to fix parsing of decimal numbers +* Fix possible segfault (DiskIO, Linux) +* Honor `preciseRefreshRate` in custom format (Display) + +Logos: +* Add Lingmo OS +* Add Sleeper OS + # 2.21.3 Bugfixes: diff --git a/CMakeLists.txt b/CMakeLists.txt index e403b2376..1abe943d2 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.21.3 + VERSION 2.22.0 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" @@ -69,7 +69,7 @@ cmake_dependent_option(ENABLE_FREETYPE "Enable freetype" ON "ANDROID" OFF) cmake_dependent_option(ENABLE_PULSE "Enable pulse" ON "LINUX OR SunOS" OFF) cmake_dependent_option(ENABLE_DDCUTIL "Enable ddcutil" ON "LINUX" OFF) cmake_dependent_option(ENABLE_DIRECTX_HEADERS "Enable DirectX headers for WSL" ON "LINUX" OFF) -cmake_dependent_option(ENABLE_ELF "Enable libelf" ON "LINUX OR FreeBSD OR SunOS" OFF) +cmake_dependent_option(ENABLE_ELF "Enable libelf" ON "LINUX OR FreeBSD OR ANDROID" OFF) cmake_dependent_option(ENABLE_THREADS "Enable multithreading" ON "Threads_FOUND" OFF) option(ENABLE_SYSTEM_YYJSON "Use system provided (instead of fastfetch embedded) yyjson library" OFF) @@ -401,6 +401,7 @@ set(LIBFASTFETCH_SRC src/util/base64.c src/util/FFlist.c src/util/FFstrbuf.c + src/util/path.c src/util/platform/FFPlatform.c src/util/smbiosHelper.c ) @@ -481,7 +482,7 @@ if(LINUX) src/detection/wmtheme/wmtheme_linux.c src/detection/camera/camera_linux.c src/util/platform/FFPlatform_unix.c - src/util/linux/elf.c + src/util/binary_linux.c ) elseif(ANDROID) list(APPEND LIBFASTFETCH_SRC @@ -543,6 +544,7 @@ elseif(ANDROID) src/detection/wmtheme/wmtheme_nosupport.c src/detection/camera/camera_android.c src/util/platform/FFPlatform_unix.c + src/util/binary_linux.c ) elseif(FreeBSD) list(APPEND LIBFASTFETCH_SRC @@ -621,7 +623,7 @@ elseif(FreeBSD) src/detection/wmtheme/wmtheme_linux.c src/detection/camera/camera_linux.c src/util/platform/FFPlatform_unix.c - src/util/linux/elf.c + src/util/binary_linux.c ) elseif(APPLE) list(APPEND LIBFASTFETCH_SRC @@ -687,6 +689,7 @@ elseif(APPLE) src/util/apple/cf_helpers.c src/util/apple/osascript.m src/util/platform/FFPlatform_unix.c + src/util/binary_apple.c ) elseif(WIN32) list(APPEND LIBFASTFETCH_SRC @@ -753,6 +756,7 @@ elseif(WIN32) src/util/windows/unicode.c src/util/windows/wmi.cpp src/util/platform/FFPlatform_windows.c + src/util/binary_windows.c ) elseif(SunOS) list(APPEND LIBFASTFETCH_SRC @@ -829,7 +833,7 @@ elseif(SunOS) src/detection/wmtheme/wmtheme_linux.c src/detection/camera/camera_nosupport.c src/util/platform/FFPlatform_unix.c - src/util/linux/elf.c + src/util/binary_linux.c ) endif() @@ -1135,6 +1139,7 @@ elseif(WIN32) PRIVATE "setupapi" PRIVATE "hid" PRIVATE "wtsapi32" + PRIVATE "imagehlp" ) elseif(FreeBSD) target_link_libraries(libfastfetch @@ -1332,6 +1337,12 @@ install( RENAME "${CMAKE_PROJECT_NAME}" ) +install( + FILES "${CMAKE_SOURCE_DIR}/completions/${CMAKE_PROJECT_NAME}.zsh" + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/zsh/site-functions" + RENAME "_${CMAKE_PROJECT_NAME}" +) + install( FILES "${CMAKE_SOURCE_DIR}/completions/${CMAKE_PROJECT_NAME}.fish" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/fish/vendor_completions.d" diff --git a/README.md b/README.md index cd25100d8..036e028e6 100644 --- a/README.md +++ b/README.md @@ -30,17 +30,23 @@ There are [screenshots on different platforms](https://github.com/fastfetch-cli/ Some distros packaged an outdated fastfetch version. Older version receive no support, so please try always to use the latest version. * Ubuntu: [`ppa:zhangsongcui3371/fastfetch`](https://launchpad.net/~zhangsongcui3371/+archive/ubuntu/fastfetch) (for Ubuntu 22.04 or newer) -* Debian: `sudo apt install fastfetch` (for Debian 13 or newer) +* Debian: `apt install fastfetch` (for Debian 13 or newer) * Debian / Ubuntu: Download `fastfetch-linux-.deb` from [Github release page](https://github.com/fastfetch-cli/fastfetch/releases/latest) and double-click it (for Ubuntu 20.04 or newer and Debian 11 or newer). -* Arch Linux: `sudo pacman -S fastfetch` -* Fedora: `sudo dnf install fastfetch` -* Gentoo: `sudo emerge --ask app-misc/fastfetch` +* Arch Linux: `pacman -S fastfetch` +* Fedora: `dnf install fastfetch` +* Gentoo: `emerge --ask app-misc/fastfetch` * Alpine: `apk add --upgrade fastfetch` * NixOS: `nix-shell -p fastfetch` -* openSUSE: `sudo zypper install fastfetch` -* ALT Linux: `sudo apt-get install fastfetch` +* openSUSE: `zypper install fastfetch` +* ALT Linux: `apt-get install fastfetch` +* Exherbo: `cave resolve --execute app-misc/fastfetch` +* GNU Guix: `guix install fastfetch` +* Solus: `eopkg install fastfetch` +* Slackware: `sbopkg -i fastfetch` +* Void Linux: `xbps-install fastfetch` +* Venom Linux: `scratch install fastfetch` -Replace sudo with doas depending on what you use. +You may need `sudo`, `doas` or `sup` to run these commands. [See also if fastfetch has been packaged for your favorite Linux distro](#Packaging). @@ -54,8 +60,9 @@ If fastfetch is not packaged for your distro or an outdated version is packaged, ### Windows * [scoop](https://scoop.sh/#/apps?q=fastfetch): `scoop install fastfetch` +* [Chocolatey](https://community.chocolatey.org/packages/fastfetch): `choco install fastfetch` * [winget](https://github.com/microsoft/winget-pkgs/tree/master/manifests/f/Fastfetch-cli/Fastfetch): `winget install fastfetch` -* [MSYS2 MinGW](https://github.com/msys2/MINGW-packages/tree/master/mingw-w64-fastfetch): `pacman -S mingw-w64---fastfetch` +* [MSYS2 MinGW](https://packages.msys2.org/base/mingw-w64-fastfetch): `pacman -S mingw-w64---fastfetch` You may also download the program directly from [the GitHub releases page](https://github.com/fastfetch-cli/fastfetch/releases/latest) in the form of an archive file. diff --git a/completions/fastfetch.zsh b/completions/fastfetch.zsh new file mode 100644 index 000000000..924e4a338 --- /dev/null +++ b/completions/fastfetch.zsh @@ -0,0 +1,97 @@ +#compdef fastfetch + +function _fastfetch() { + local state + + local -a opts + opts=(${(f)"$( + python <modules") + elif type == "config": + print(f"{command_prefix}:presets:->presets") + elif type == "enum": + temp: str = " ".join(flag["arg"]["enum"]) + print(f'{command_prefix}:type:( {temp} )') + elif type == "logo": + print(f"{command_prefix}:logo:->logo") + elif type == "structure": + print(f"{command_prefix}:structure:->structure") + elif type == "path": + print(f"{command_prefix}:path:_files -/") + else: + print(f"{command_prefix}:") + else: + print(f"{command_prefix}") + + +if __name__ == "__main__": + try: + main() + except Exception: + sys.exit(1) +EOF + )"}) + + _arguments -C "$opts[@]" + + case $state in + modules) + local -a modules=( ${(f)"$(fastfetch --list-modules autocompletion)"} ) + modules=( ${(L)^modules%%:*}-format format color ) + _describe 'module' modules + ;; + presets) + local -a presets=( + ${$(fastfetch --list-presets autocompletion):#.*} + "none:Disable loading config file" + ) + _describe 'preset' presets + ;; + structure) + local -a structures=( ${(f)"$(fastfetch --list-modules autocompletion)"} ) + _describe 'structure' structures + ;; + logo) + local -a logos=( + $(fastfetch --list-logos autocompletion) + "none:Don't print logo" + "small:Print small ascii logo if available" + ) + _describe 'logo' logos + ;; + esac +} + +_fastfetch "$@" diff --git a/debian/changelog b/debian/changelog index 9274e6956..fd559ffe9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +fastfetch (2.21.3) jammy; urgency=medium + + * Update to 2.21.3 + + -- Carter Li Thu, 15 Aug 2024 16:14:52 +0800 + fastfetch (2.21.2) jammy; urgency=medium * Update to 2.21.2 diff --git a/debian/files b/debian/files index b4af41394..a2ab3b200 100644 --- a/debian/files +++ b/debian/files @@ -1 +1 @@ -fastfetch_2.21.2_source.buildinfo universe/utils optional +fastfetch_2.21.3_source.buildinfo universe/utils optional diff --git a/presets/examples/23.jsonc b/presets/examples/23.jsonc index c075b741b..effc39a7b 100644 --- a/presets/examples/23.jsonc +++ b/presets/examples/23.jsonc @@ -11,45 +11,51 @@ }, "separator": "" }, - "modules": [ + "modules": [ { "type": "kernel", - "key": "Vanilla ", + "key": "[_Kernel___> ", "keyColor": "blue" }, { "type": "packages", + "outputColor": "white", "key": " [_Packages_> ", "keyColor": "green" }, { "type": "localip", + "outputColor": "white", "key": " [_Local_IP_> ", "keyColor": "green" }, - { - "type": "cpu", - "key": " [_CPU______> ", - "keyColor": "magenta" - }, { "type": "memory", + "format": "[{3}] {1} / {2}", "key": " [_RAM______> ", "keyColor": "magenta" }, { "type": "swap", + "format": "[{3}] {1} / {2}", "key": " [_SWAP_____> ", "keyColor": "magenta" }, { "type": "disk", + "format": "[{3}] {1} / {2} {9}", "key": " [_Disk_____> ", "keyColor": "magenta" }, + { + "type": "battery", + "format": "[{4}] {5}", + "key": " [_Battery__> ", + "keyColor": "magenta" + }, "break", { - "type":"colors", + "type": "colors", "paddingLeft": 9, "symbol": "circle" } diff --git a/src/common/init.c b/src/common/init.c index b4d5c1721..2da9f3027 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -54,10 +54,6 @@ void ffInitInstance(void) #else // Never use `setlocale(LC_ALL, "")` setlocale(LC_TIME, ""); - setlocale(LC_NUMERIC, ""); - #ifdef LC_MESSAGES - setlocale(LC_MESSAGES, ""); - #endif #endif initState(&instance.state); diff --git a/src/detection/cpu/cpu.c b/src/detection/cpu/cpu.c index 68d23ac88..dc27da241 100644 --- a/src/detection/cpu/cpu.c +++ b/src/detection/cpu/cpu.c @@ -36,6 +36,7 @@ const char* ffCPUAppleCodeToName(uint32_t code) case 6030: return "Apple M3 Pro"; case 6031: case 6034: return "Apple M3 Max"; + case 8132: return "Apple M4"; default: return "Apple Silicon"; } } diff --git a/src/detection/cpu/cpu.h b/src/detection/cpu/cpu.h index e27a437af..dd2d178ed 100644 --- a/src/detection/cpu/cpu.h +++ b/src/detection/cpu/cpu.h @@ -30,3 +30,30 @@ typedef struct FFCPUResult const char* ffDetectCPU(const FFCPUOptions* options, FFCPUResult* cpu); const char* ffCPUAppleCodeToName(uint32_t code); + + +#if defined(__x86_64__) || defined(__i386__) + +#include + +// WARNING: CPUID may report frequencies of efficient cores +inline static const char* ffCPUDetectSpeedByCpuid(FFCPUResult* cpu) +{ + uint32_t base = 0, max = 0, bus = 0, unused = 0; + if (!__get_cpuid(0x16, &base, &max, &bus, &unused)) + return "Unsupported instruction"; + + // cpuid returns 0 MHz when hyper-v is enabled + if (base) cpu->frequencyBase = base; + if (max) cpu->frequencyMax = max; + return NULL; +} + +#else + +inline static const char* ffCPUDetectSpeedByCpuid(FF_MAYBE_UNUSED FFCPUResult* cpu) +{ + return "Unsupported platform"; +} + +#endif diff --git a/src/detection/cpu/cpu_bsd.c b/src/detection/cpu/cpu_bsd.c index 92773dcdc..0b17d595d 100644 --- a/src/detection/cpu/cpu_bsd.c +++ b/src/detection/cpu/cpu_bsd.c @@ -39,6 +39,8 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) } } + ffCPUDetectSpeedByCpuid(cpu); + for (uint16_t i = 0; i < cpu->coresLogical; ++i) { ffStrbufClear(&buffer); diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index 5b948150c..7e9d92f7a 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -137,36 +137,52 @@ static void detectArmName(FILE* cpuinfo, FFCPUResult* cpu, uint32_t implId) } #endif -static const char* parseCpuInfo(FILE* cpuinfo, FFCPUResult* cpu, FFstrbuf* physicalCoresBuffer, FFstrbuf* cpuMHz, FFstrbuf* cpuIsa, FFstrbuf* cpuUarch, FFstrbuf* cpuImplementer) +static const char* parseCpuInfo( + FF_MAYBE_UNUSED FILE* cpuinfo, + FF_MAYBE_UNUSED FFCPUResult* cpu, + FF_MAYBE_UNUSED FFstrbuf* physicalCoresBuffer, + FF_MAYBE_UNUSED FFstrbuf* cpuMHz, + FF_MAYBE_UNUSED FFstrbuf* cpuIsa, + FF_MAYBE_UNUSED FFstrbuf* cpuUarch, + FF_MAYBE_UNUSED FFstrbuf* cpuImplementer) { FF_AUTO_FREE char* line = NULL; size_t len = 0; while(getline(&line, &len, cpuinfo) != -1) { - //Stop after the first CPU - if(*line == '\0' || *line == '\n') + //Stop after reasonable information is acquired + if((*line == '\0' || *line == '\n') + #if __arm__ || __loongarch__ + && cpu->name.length > 0 // #1202 #1204 + #endif + ) break; (void)( - ffParsePropLine(line, "model name :", &cpu->name) || - ffParsePropLine(line, "vendor_id :", &cpu->vendor) || - ffParsePropLine(line, "cpu cores :", physicalCoresBuffer) || - ffParsePropLine(line, "cpu MHz :", cpuMHz) || - ffParsePropLine(line, "isa :", cpuIsa) || - ffParsePropLine(line, "uarch :", cpuUarch) || + // arm64 doesn't have "model name"; arm32 does have "model name" but its value is not useful. + // "Hardware" should always be used in this case + #if !(__arm__ || __aarch64__) + (cpu->name.length == 0 && ffParsePropLine(line, "model name :", &cpu->name)) || + (cpu->vendor.length == 0 && ffParsePropLine(line, "vendor_id :", &cpu->vendor)) || + (physicalCoresBuffer->length == 0 && ffParsePropLine(line, "cpu cores :", physicalCoresBuffer)) || + (cpuMHz->length == 0 && ffParsePropLine(line, "cpu MHz :", cpuMHz)) || + #endif - #if __arm__ || __aarch64__ - (cpu->vendor.length == 0 && ffParsePropLine(line, "CPU implementer :", cpuImplementer)) || + #if !(__x86_64__ || __i386__ || __arm__ || __aarch64__) + (cpuIsa->length == 0 && ffParsePropLine(line, "isa :", cpuIsa)) || + (cpuUarch->length == 0 && ffParsePropLine(line, "uarch :", cpuUarch)) || #endif - #if __ANDROID__ + + #if __arm__ || __aarch64__ + (cpuImplementer->length == 0 && ffParsePropLine(line, "CPU implementer :", cpuImplementer)) || (cpu->name.length == 0 && ffParsePropLine(line, "Hardware :", &cpu->name)) || //For Android devices #endif #if __powerpc__ || __powerpc - (cpu->name.length == 0 && ffParsePropLine(line, "cpu :", &cpu->name)) || //For POWER + (cpu->name.length == 0 && ffParsePropLine(line, "cpu :", &cpu->name)) || //For POWER #endif #if __mips__ - (cpu->name.length == 0 && ffParsePropLine(line, "cpu model :", &cpu->name)) || //For MIPS + (cpu->name.length == 0 && ffParsePropLine(line, "cpu model :", &cpu->name)) || //For MIPS #endif false ); @@ -265,16 +281,19 @@ static double detectCPUTemp(void) { if( ffStrbufFirstIndexS(&value->name, "cpu") < value->name.length || - ffStrbufCompS(&value->name, "k10temp") == 0 || - ffStrbufCompS(&value->name, "coretemp") == 0 + ffStrbufEqualS(&value->name, "k10temp") || + ffStrbufEqualS(&value->name, "coretemp") ) return value->value; } return FF_CPU_TEMP_UNSET; } -static void parseIsa(FFstrbuf* cpuIsa) +FF_MAYBE_UNUSED static void parseIsa(FFstrbuf* cpuIsa) { + // Always use the last part of the ISA string. Ref: #590 #1204 + ffStrbufSubstrAfterLastC(cpuIsa, ' '); + if(ffStrbufStartsWithS(cpuIsa, "rv")) { // RISC-V ISA string example: "rv64imafdch_zicsr_zifencei". @@ -290,13 +309,9 @@ static void parseIsa(FFstrbuf* cpuIsa) } // The final ISA output of the above example is "rv64gch". } - if(ffStrbufStartsWithS(cpuIsa, "mips")) - { - ffStrbufSubstrAfterLastC(cpuIsa, ' '); - } } -void detectAsahi(FFCPUResult* cpu) +FF_MAYBE_UNUSED static void detectAsahi(FFCPUResult* cpu) { // In Asahi Linux, reading /proc/device-tree/compatible gives // information on the device model. It consists of 3 NUL terminated @@ -340,9 +355,12 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) cpu->coresOnline = (uint16_t) get_nprocs(); cpu->coresPhysical = (uint16_t) ffStrbufToUInt(&physicalCoresBuffer, cpu->coresLogical); + // Ref https://github.com/fastfetch-cli/fastfetch/issues/1194#issuecomment-2295058252 + ffCPUDetectSpeedByCpuid(cpu); if (!detectFrequency(cpu, options) || cpu->frequencyBase == 0) cpu->frequencyBase = (uint32_t) ffStrbufToUInt(&cpuMHz, 0); + #if !(__x86_64__ || __i386__ || __arm__ || __aarch64__) if(cpuUarch.length > 0) { if(cpu->name.length > 0) @@ -357,6 +375,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) ffStrbufAppendC(&cpu->name, ' '); ffStrbufAppend(&cpu->name, &cpuIsa); } + #endif #if __arm__ || __aarch64__ uint32_t cpuImplementer = (uint32_t) strtoul(cpuImplementerStr.chars, NULL, 16); diff --git a/src/detection/cpu/cpu_sunos.c b/src/detection/cpu/cpu_sunos.c index 67954ee30..e10dcb898 100644 --- a/src/detection/cpu/cpu_sunos.c +++ b/src/detection/cpu/cpu_sunos.c @@ -29,10 +29,10 @@ const char* ffDetectCPUImpl(FF_MAYBE_UNUSED const FFCPUOptions* options, FFCPURe kstat_named_t* kn = kstat_data_lookup(ks, "vendor_id"); ffStrbufSetNS(&cpu->vendor, KSTAT_NAMED_STR_BUFLEN(kn) - 1, KSTAT_NAMED_STR_PTR(kn)); } - { - kstat_named_t* kn = kstat_data_lookup(ks, "clock_MHz"); + ffCPUDetectSpeedByCpuid(cpu); + kstat_named_t* kn = kstat_data_lookup(ks, "clock_MHz"); + if (kn->value.ui32 > cpu->frequencyBase) cpu->frequencyBase = kn->value.ui32; - } ks = kstat_lookup(kc, "unix", -1, "system_misc"); if (ks && kstat_read(kc, ks, NULL) >= 0) diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c index fa96bb6ff..411ffe6f7 100644 --- a/src/detection/cpu/cpu_windows.c +++ b/src/detection/cpu/cpu_windows.c @@ -54,31 +54,6 @@ typedef struct FFSmbiosProcessorInfo static_assert(offsetof(FFSmbiosProcessorInfo, ThreadEnabled) == 0x30, "FFSmbiosProcessorInfo: Wrong struct alignment"); -#if defined(__x86_64__) || defined(__i386__) - -#include - -inline static const char* detectSpeedByCpuid(FFCPUResult* cpu) -{ - uint32_t base = 0, max = 0, bus = 0, unused = 0; - if (!__get_cpuid(0x16, &base, &max, &bus, &unused)) - return "Unsupported instruction"; - - // cpuid returns 0 MHz when hyper-v is enabled - if (base) cpu->frequencyBase = base; - if (max) cpu->frequencyMax = max; - return NULL; -} - -#else - -inline static const char* detectSpeedByCpuid(FF_MAYBE_UNUSED FFCPUResult* cpu) -{ - return "Unsupported platform"; -} - -#endif - static const char* detectMaxSpeedBySmbios(FFCPUResult* cpu) { const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable(); @@ -193,7 +168,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) if (error) return error; - detectSpeedByCpuid(cpu); + ffCPUDetectSpeedByCpuid(cpu); if (options->showPeCoreCount) detectCoreTypes(cpu); if (cpu->frequencyMax == 0) diff --git a/src/detection/disk/disk_linux.c b/src/detection/disk/disk_linux.c index 31b90adca..3836f6eb8 100644 --- a/src/detection/disk/disk_linux.c +++ b/src/detection/disk/disk_linux.c @@ -72,7 +72,7 @@ static bool isPhysicalDevice(const struct mntent* device) ) return false; // https://source.android.com/docs/core/ota/apex?hl=zh-cn - if(ffStrStartsWith(device->mnt_dir, "/apex/")) + if(ffStrStartsWith(device->mnt_dir, "/apex/")) return false; #endif // __ANDROID__ @@ -240,8 +240,16 @@ static void detectStats(FFDisk* disk) disk->bytesAvailable = fs.f_bavail * fs.f_frsize; disk->bytesUsed = 0; // To be filled in ./disk.c - disk->filesTotal = (uint32_t) fs.f_files; - disk->filesUsed = (uint32_t) (disk->filesTotal - fs.f_ffree); + if (fs.f_files >= fs.f_ffree) + { + disk->filesTotal = (uint32_t) fs.f_files; + disk->filesUsed = (uint32_t) (disk->filesTotal - fs.f_ffree); + } + else + { + // Windows filesystem in WSL + disk->filesTotal = disk->filesUsed = 0; + } if(fs.f_flag & ST_RDONLY) disk->type |= FF_DISK_VOLUME_TYPE_READONLY_BIT; diff --git a/src/detection/diskio/diskio_linux.c b/src/detection/diskio/diskio_linux.c index f34345da9..456ff39d5 100644 --- a/src/detection/diskio/diskio_linux.c +++ b/src/detection/diskio/diskio_linux.c @@ -37,24 +37,23 @@ const char* ffDiskIOGetIoCounters(FFlist* result, FFDiskIOOptions* options) if (!ffPathExists(pathSysBlock, FF_PATHTYPE_DIRECTORY)) continue; - FFDiskIOResult* device = (FFDiskIOResult*) ffListAdd(result); - ffStrbufInit(&device->name); + FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate(); { snprintf(pathSysBlock, PATH_MAX, "/sys/block/%s/device/vendor", devName); - if (ffAppendFileBuffer(pathSysBlock, &device->name)) + if (ffAppendFileBuffer(pathSysBlock, &name)) { - ffStrbufTrimRightSpace(&device->name); - if (device->name.length > 0) - ffStrbufAppendC(&device->name, ' '); + ffStrbufTrimRightSpace(&name); + if (name.length > 0) + ffStrbufAppendC(&name, ' '); } snprintf(pathSysBlock, PATH_MAX, "/sys/block/%s/device/model", devName); - ffAppendFileBuffer(pathSysBlock, &device->name); - ffStrbufTrimRightSpace(&device->name); + ffAppendFileBuffer(pathSysBlock, &name); + ffStrbufTrimRightSpace(&name); - if (device->name.length == 0) - ffStrbufSetS(&device->name, devName); + if (name.length == 0) + ffStrbufSetS(&name, devName); else if (ffStrStartsWith(devName, "nvme")) { int devid, nsid; @@ -69,17 +68,13 @@ const char* ffDiskIOGetIoCounters(FFlist* result, FFDiskIOOptions* options) if (multiNs) { // In Asahi Linux, there are multiple namespaces for the same NVMe drive. - ffStrbufAppendF(&device->name, " - %d", nsid); + ffStrbufAppendF(&name, " - %d", nsid); } } } - if (options->namePrefix.length && !ffStrbufStartsWith(&device->name, &options->namePrefix)) - { - ffStrbufDestroy(&device->name); - result->length--; + if (options->namePrefix.length && !ffStrbufStartsWith(&name, &options->namePrefix)) continue; - } } // I/Os merges sectors ticks ... @@ -94,6 +89,8 @@ const char* ffDiskIOGetIoCounters(FFlist* result, FFDiskIOOptions* options) continue; } + FFDiskIOResult* device = (FFDiskIOResult*) ffListAdd(result); + ffStrbufInitMove(&device->name, &name); ffStrbufInitF(&device->devPath, "/dev/%s", devName); device->bytesRead = sectorRead * 512; device->bytesWritten = sectorWritten * 512; diff --git a/src/detection/displayserver/displayserver_apple.c b/src/detection/displayserver/displayserver_apple.c index d05eb8b1e..ab2abe5e2 100644 --- a/src/detection/displayserver/displayserver_apple.c +++ b/src/detection/displayserver/displayserver_apple.c @@ -89,10 +89,12 @@ static void detectDisplays(FFDisplayServerResult* ds) display->bitDepth = (uint8_t) bitDepth; } + #ifdef MAC_OS_X_VERSION_10_15 if (CoreDisplay_Display_IsHDRModeEnabled) { display->hdrEnabled = CoreDisplay_Display_IsHDRModeEnabled(screen); } + #endif } CGDisplayModeRelease(mode); } diff --git a/src/detection/editor/editor.c b/src/detection/editor/editor.c index f71544d60..9ae00eea8 100644 --- a/src/detection/editor/editor.c +++ b/src/detection/editor/editor.c @@ -1,17 +1,44 @@ #include "editor.h" #include "common/processing.h" +#include "common/library.h" #include "util/stringUtils.h" +#include "util/path.h" +#include "util/binary.h" #include #ifdef _WIN32 -#include static inline char* realpath(const char* restrict file_name, char* restrict resolved_name) { return _fullpath(resolved_name, file_name, _MAX_PATH); } #endif +static bool extractNvimVersionFromBinary(const char* str, uint32_t len, void* userdata) +{ + if (len < strlen("NVIM v0.0.0")) return true; + if (!ffStrStartsWith(str, "NVIM v")) return true; + ffStrbufSetS((FFstrbuf*) userdata, str + strlen("NVIM v")); + return false; +} + +static bool extractVimVersionFromBinary(const char* str, uint32_t len, void* userdata) +{ + if (len < strlen("VIM - Vi IMproved 0.0")) return true; + if (!ffStrStartsWith(str, "VIM - Vi IMproved ")) return true; + ffStrbufSetS((FFstrbuf*) userdata, str + strlen("VIM - Vi IMproved ")); + ffStrbufSubstrBeforeFirstC(userdata, ' '); + return false; +} + +static bool extractNanoVersionFromBinary(const char* str, uint32_t len, void* userdata) +{ + if (len < strlen("GNU nano 0.0")) return true; + if (!ffStrStartsWith(str, "GNU nano ")) return true; + ffStrbufSetS((FFstrbuf*) userdata, str + strlen("GNU nano ")); + return false; +} + const char* ffDetectEditor(FFEditorResult* result) { ffStrbufSetS(&result->name, getenv("VISUAL")); @@ -26,41 +53,26 @@ const char* ffDetectEditor(FFEditorResult* result) return "$VISUAL or $EDITOR not set"; } - if (!instance.config.general.detectVersion) return NULL; - - #ifndef _WIN32 - if (result->name.chars[0] != '/') + if (ffIsAbsolutePath(result->name.chars)) + ffStrbufSet(&result->path, &result->name); + else { - if (ffProcessAppendStdOut(&result->path, (char* const[]){ - FASTFETCH_TARGET_DIR_USR "/bin/which", - result->name.chars, - NULL, - }) != NULL || result->path.length == 0) - return NULL; + const char* error = ffFindExecutableInPath(result->name.chars, &result->path); + if (error) return NULL; } - #else - if (!(result->name.length > 3 && ffCharIsEnglishAlphabet(result->name.chars[0]) && result->name.chars[1] == ':' && result->name.chars[2] == '\\')) + { - char buf[32]; - uint32_t len = GetSystemDirectoryA(buf, sizeof(buf)); - if (len < strlen("C:\\WINDOWS\\system32")) return NULL; - strncpy(buf + len, "\\where.exe", sizeof(buf) - len); - if (ffProcessAppendStdOut(&result->path, (char* const[]){ - buf, - result->name.chars, - NULL, - }) != NULL || result->path.length == 0) + char buf[PATH_MAX + 1]; + if (!realpath(result->path.chars, buf)) return NULL; - } - #endif - else - ffStrbufSet(&result->path, &result->name); - char buf[PATH_MAX + 1]; - if (!realpath(result->path.chars, buf)) - return NULL; + // WIN32: Should we handle scoop shim exe here? - ffStrbufSetS(&result->path, buf); + #ifdef __linux__ + if (!ffStrEndsWith(buf, "/snap")) + #endif + ffStrbufSetS(&result->path, buf); + } { uint32_t index = ffStrbufLastIndexC(&result->path, @@ -82,6 +94,17 @@ const char* ffDetectEditor(FFEditorResult* result) #endif } + if (!instance.config.general.detectVersion) return NULL; + + if (ffStrbufEqualS(&result->exe, "nvim")) + ffBinaryExtractStrings(result->path.chars, extractNvimVersionFromBinary, &result->version); + else if (ffStrbufEqualS(&result->exe, "vim")) + ffBinaryExtractStrings(result->path.chars, extractVimVersionFromBinary, &result->version); + else if (ffStrbufEqualS(&result->exe, "nano")) + ffBinaryExtractStrings(result->path.chars, extractNanoVersionFromBinary, &result->version); + + if (result->version.length > 0) return NULL; + const char* param = NULL; if ( ffStrbufEqualS(&result->exe, "nano") || diff --git a/src/detection/initsystem/initsystem_linux.c b/src/detection/initsystem/initsystem_linux.c index a16254595..d135dce87 100644 --- a/src/detection/initsystem/initsystem_linux.c +++ b/src/detection/initsystem/initsystem_linux.c @@ -1,6 +1,6 @@ #include "initsystem.h" #include "common/processing.h" -#include "util/linux/elf.h" +#include "util/binary.h" #include FF_MAYBE_UNUSED static bool elfExtractStringsCallBack(const char* str, uint32_t len, void* data) @@ -49,7 +49,7 @@ const char* ffDetectInitSystem(FFInitSystemResult* result) #if __linux__ && !__ANDROID__ if (ffStrbufEqualS(&result->name, "systemd")) { - ffElfExtractStrings(result->exe.chars, elfExtractStringsCallBack, &result->version); + ffBinaryExtractStrings(result->exe.chars, elfExtractStringsCallBack, &result->version); if (result->version.length == 0) { if (ffProcessAppendStdOut(&result->version, (char* const[]) { diff --git a/src/detection/loadavg/loadavg_linux.c b/src/detection/loadavg/loadavg_linux.c index 47085a744..9e8b091c5 100644 --- a/src/detection/loadavg/loadavg_linux.c +++ b/src/detection/loadavg/loadavg_linux.c @@ -9,12 +9,12 @@ const char* ffDetectLoadavg(double result[3]) // Don't use syscall for container compatibility. #620 char buf[64]; - ssize_t nRead = ffReadFileData("/proc/uptime", sizeof(buf) - 1, buf); + ssize_t nRead = ffReadFileData("/proc/loadavg", sizeof(buf) - 1, buf); if (nRead > 0) { buf[nRead] = '\0'; - if (sscanf(buf, "%lf %lf %lf", &result[0], &result[1], &result[2]) == 3) + if (sscanf(buf, "%lf%lf%lf", &result[0], &result[1], &result[2]) == 3) return NULL; } diff --git a/src/detection/locale/locale_linux.c b/src/detection/locale/locale_linux.c index 4b5123059..34c1fb52f 100644 --- a/src/detection/locale/locale_linux.c +++ b/src/detection/locale/locale_linux.c @@ -8,19 +8,9 @@ void ffDetectLocale(FFstrbuf* result) if(result->length > 0) return; - ffStrbufAppendS(result, getenv("LC_MESSAGES")); - if(result->length > 0) - return; - ffStrbufAppendS(result, getenv("LANG")); if(result->length > 0) return; - #ifdef LC_MESSAGES - ffStrbufAppendS(result, setlocale(LC_MESSAGES, NULL)); - if(result->length > 0) - return; - #endif - ffStrbufAppendS(result, setlocale(LC_TIME, NULL)); } diff --git a/src/detection/localip/localip.h b/src/detection/localip/localip.h index 4b4162604..859d5340f 100644 --- a/src/detection/localip/localip.h +++ b/src/detection/localip/localip.h @@ -9,6 +9,7 @@ typedef struct FFLocalIpResult FFstrbuf ipv6; FFstrbuf mac; int32_t mtu; + int32_t speed; bool defaultRoute; } FFLocalIpResult; diff --git a/src/detection/localip/localip_linux.c b/src/detection/localip/localip_linux.c index 9939bbcd2..bdd91ac09 100644 --- a/src/detection/localip/localip_linux.c +++ b/src/detection/localip/localip_linux.c @@ -12,7 +12,13 @@ #include #include +#ifdef __linux__ +#include +#include +#endif + #if defined(__FreeBSD__) || defined(__APPLE__) +#include #include #else #include @@ -40,6 +46,7 @@ static void addNewIp(FFlist* list, const char* name, const char* addr, int type, ffStrbufInit(&ip->mac); ip->defaultRoute = defaultRoute; ip->mtu = -1; + ip->speed = -1; } switch (type) @@ -172,9 +179,388 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) { struct ifreq ifr; strncpy(ifr.ifr_name, iface->name.chars, IFNAMSIZ - 1); + if (ioctl(sockfd, SIOCGIFMTU, &ifr) == 0) iface->mtu = (int32_t) ifr.ifr_mtu; + #ifdef __linux__ + struct ethtool_cmd edata = { .cmd = ETHTOOL_GSET }; + ifr.ifr_data = (void*) &edata; + if (ioctl(sockfd, SIOCETHTOOL, &ifr) == 0) + iface->speed = (edata.speed_hi << 16) | edata.speed; // ethtool_cmd_speed is not available on Android + #elif __FreeBSD__ || __APPLE__ + struct ifmediareq ifmr = {}; + strncpy(ifmr.ifm_name, iface->name.chars, IFNAMSIZ - 1); + if (ioctl(sockfd, SIOCGIFMEDIA, &ifmr) == 0) + { + switch (IFM_SUBTYPE(ifmr.ifm_active)) + { + #ifdef IFM_HPNA_1 + case IFM_HPNA_1: + #endif + iface->speed = 1; break; + #ifdef IFM_1000_CX + case IFM_1000_CX: + #endif + #ifdef IFM_1000_CX_SGMII + case IFM_1000_CX_SGMII: + #endif + #ifdef IFM_1000_KX + case IFM_1000_KX: + #endif + #ifdef IFM_1000_LX + case IFM_1000_LX: + #endif + #ifdef IFM_1000_SGMII + case IFM_1000_SGMII: + #endif + #ifdef IFM_1000_SX + case IFM_1000_SX: + #endif + #ifdef IFM_1000_T + case IFM_1000_T: + #endif + iface->speed = 1000; break; + #ifdef IFM_100G_AUI2 + case IFM_100G_AUI2: + #endif + #ifdef IFM_100G_AUI2_AC + case IFM_100G_AUI2_AC: + #endif + #ifdef IFM_100G_AUI4 + case IFM_100G_AUI4: + #endif + #ifdef IFM_100G_AUI4_AC + case IFM_100G_AUI4_AC: + #endif + #ifdef IFM_100G_CAUI2 + case IFM_100G_CAUI2: + #endif + #ifdef IFM_100G_CAUI2_AC + case IFM_100G_CAUI2_AC: + #endif + #ifdef IFM_100G_CAUI4 + case IFM_100G_CAUI4: + #endif + #ifdef IFM_100G_CAUI4_AC + case IFM_100G_CAUI4_AC: + #endif + #ifdef IFM_100G_CP2 + case IFM_100G_CP2: + #endif + #ifdef IFM_100G_CR4 + case IFM_100G_CR4: + #endif + #ifdef IFM_100G_CR_PAM4 + case IFM_100G_CR_PAM4: + #endif + #ifdef IFM_100G_DR + case IFM_100G_DR: + #endif + #ifdef IFM_100G_KR2_PAM4 + case IFM_100G_KR2_PAM4: + #endif + #ifdef IFM_100G_KR4 + case IFM_100G_KR4: + #endif + #ifdef IFM_100G_KR_PAM4 + case IFM_100G_KR_PAM4: + #endif + #ifdef IFM_100G_LR4 + case IFM_100G_LR4: + #endif + #ifdef IFM_100G_SR2 + case IFM_100G_SR2: + #endif + #ifdef IFM_100G_SR4 + case IFM_100G_SR4: + #endif + iface->speed = 100000; break; + #ifdef IFM_100_FX + case IFM_100_FX: + #endif + #ifdef IFM_100_SGMII + case IFM_100_SGMII: + #endif + #ifdef IFM_100_T + case IFM_100_T: + #endif + #ifdef IFM_100_T2 + case IFM_100_T2: + #endif + #ifdef IFM_100_T4 + case IFM_100_T4: + #endif + #ifdef IFM_100_TX + case IFM_100_TX: + #endif + #ifdef IFM_100_VG + case IFM_100_VG: + #endif + iface->speed = 100; break; + #ifdef IFM_10G_AOC + case IFM_10G_AOC: + #endif + #ifdef IFM_10G_CR1 + case IFM_10G_CR1: + #endif + #ifdef IFM_10G_CX4 + case IFM_10G_CX4: + #endif + #ifdef IFM_10G_ER + case IFM_10G_ER: + #endif + #ifdef IFM_10G_KR + case IFM_10G_KR: + #endif + #ifdef IFM_10G_KX4 + case IFM_10G_KX4: + #endif + #ifdef IFM_10G_LR + case IFM_10G_LR: + #endif + #ifdef IFM_10G_LRM + case IFM_10G_LRM: + #endif + #ifdef IFM_10G_SFI + case IFM_10G_SFI: + #endif + #ifdef IFM_10G_SR + case IFM_10G_SR: + #endif + #ifdef IFM_10G_T + case IFM_10G_T: + #endif + #ifdef IFM_10G_TWINAX + case IFM_10G_TWINAX: + #endif + #ifdef IFM_10G_TWINAX_LONG + case IFM_10G_TWINAX_LONG: + #endif + iface->speed = 10000; break; + #ifdef IFM_10_2 + case IFM_10_2: + #endif + #ifdef IFM_10_5 + case IFM_10_5: + #endif + #ifdef IFM_10_FL + case IFM_10_FL: + #endif + #ifdef IFM_10_STP + case IFM_10_STP: + #endif + #ifdef IFM_10_T + case IFM_10_T: + #endif + iface->speed = 10; break; + #ifdef IFM_200G_AUI4 + case IFM_200G_AUI4: + #endif + #ifdef IFM_200G_AUI4_AC + case IFM_200G_AUI4_AC: + #endif + #ifdef IFM_200G_AUI8 + case IFM_200G_AUI8: + #endif + #ifdef IFM_200G_AUI8_AC + case IFM_200G_AUI8_AC: + #endif + #ifdef IFM_200G_CR4_PAM4 + case IFM_200G_CR4_PAM4: + #endif + #ifdef IFM_200G_DR4 + case IFM_200G_DR4: + #endif + #ifdef IFM_200G_FR4 + case IFM_200G_FR4: + #endif + #ifdef IFM_200G_KR4_PAM4 + case IFM_200G_KR4_PAM4: + #endif + #ifdef IFM_200G_LR4 + case IFM_200G_LR4: + #endif + #ifdef IFM_200G_SR4 + case IFM_200G_SR4: + #endif + iface->speed = 200000; break; + #ifdef IFM_20G_KR2 + case IFM_20G_KR2: + #endif + iface->speed = 20000; break; + #ifdef IFM_2500_KX + case IFM_2500_KX: + #endif + #ifdef IFM_2500_SX + case IFM_2500_SX: + #endif + #ifdef IFM_2500_T + case IFM_2500_T: + #endif + #ifdef IFM_2500_X + case IFM_2500_X: + #endif + iface->speed = 2500; break; + #ifdef IFM_25G_ACC + case IFM_25G_ACC: + #endif + #ifdef IFM_25G_AOC + case IFM_25G_AOC: + #endif + #ifdef IFM_25G_AUI + case IFM_25G_AUI: + #endif + #ifdef IFM_25G_CR + case IFM_25G_CR: + #endif + #ifdef IFM_25G_CR1 + case IFM_25G_CR1: + #endif + #ifdef IFM_25G_CR_S + case IFM_25G_CR_S: + #endif + #ifdef IFM_25G_KR + case IFM_25G_KR: + #endif + #ifdef IFM_25G_KR1 + case IFM_25G_KR1: + #endif + #ifdef IFM_25G_KR_S + case IFM_25G_KR_S: + #endif + #ifdef IFM_25G_LR + case IFM_25G_LR: + #endif + #ifdef IFM_25G_PCIE + case IFM_25G_PCIE: + #endif + #ifdef IFM_25G_SR + case IFM_25G_SR: + #endif + #ifdef IFM_25G_T + case IFM_25G_T: + #endif + iface->speed = 25000; break; + #ifdef IFM_400G_AUI8 + case IFM_400G_AUI8: + #endif + #ifdef IFM_400G_AUI8_AC + case IFM_400G_AUI8_AC: + #endif + #ifdef IFM_400G_DR4 + case IFM_400G_DR4: + #endif + #ifdef IFM_400G_FR8 + case IFM_400G_FR8: + #endif + #ifdef IFM_400G_LR8 + case IFM_400G_LR8: + #endif + iface->speed = 400000; break; + #ifdef IFM_40G_CR4 + case IFM_40G_CR4: + #endif + #ifdef IFM_40G_ER4 + case IFM_40G_ER4: + #endif + #ifdef IFM_40G_KR4 + case IFM_40G_KR4: + #endif + #ifdef IFM_40G_LR4 + case IFM_40G_LR4: + #endif + #ifdef IFM_40G_SR4 + case IFM_40G_SR4: + #endif + #ifdef IFM_40G_XLAUI + case IFM_40G_XLAUI: + #endif + #ifdef IFM_40G_XLAUI_AC + case IFM_40G_XLAUI_AC: + #endif + #ifdef IFM_40G_XLPPI + case IFM_40G_XLPPI: + #endif + #ifdef IFM_40G_LM4 + case IFM_40G_LM4: + #endif + iface->speed = 40000; break; + #ifdef IFM_5000_KR + case IFM_5000_KR: + #endif + #ifdef IFM_5000_KR1 + case IFM_5000_KR1: + #endif + #ifdef IFM_5000_KR_S + case IFM_5000_KR_S: + #endif + #ifdef IFM_5000_T + case IFM_5000_T: + #endif + iface->speed = 5000; break; + #ifdef IFM_50G_AUI1 + case IFM_50G_AUI1: + #endif + #ifdef IFM_50G_AUI1_AC + case IFM_50G_AUI1_AC: + #endif + #ifdef IFM_50G_AUI2 + case IFM_50G_AUI2: + #endif + #ifdef IFM_50G_AUI2_AC + case IFM_50G_AUI2_AC: + #endif + #ifdef IFM_50G_CP + case IFM_50G_CP: + #endif + #ifdef IFM_50G_CR2 + case IFM_50G_CR2: + #endif + #ifdef IFM_50G_FR + case IFM_50G_FR: + #endif + #ifdef IFM_50G_KR2 + case IFM_50G_KR2: + #endif + #ifdef IFM_50G_KR_PAM4 + case IFM_50G_KR_PAM4: + #endif + #ifdef IFM_50G_LAUI2 + case IFM_50G_LAUI2: + #endif + #ifdef IFM_50G_LAUI2_AC + case IFM_50G_LAUI2_AC: + #endif + #ifdef IFM_50G_LR + case IFM_50G_LR: + #endif + #ifdef IFM_50G_LR2 + case IFM_50G_LR2: + #endif + #ifdef IFM_50G_PCIE + case IFM_50G_PCIE: + #endif + #ifdef IFM_50G_SR + case IFM_50G_SR: + #endif + #ifdef IFM_50G_SR2 + case IFM_50G_SR2: + #endif + #ifdef IFM_50G_KR4 + case IFM_50G_KR4: + #endif + iface->speed = 50000; break; + #ifdef IFM_56G_R4 + case IFM_56G_R4: + #endif + iface->speed = 56000; break; + default: + iface->speed = -1; + } + } + #endif + #ifdef __sun if ((options->showType & FF_LOCALIP_TYPE_MAC_BIT) && ioctl(sockfd, SIOCGIFHWADDR, &ifr) == 0) { diff --git a/src/detection/localip/localip_windows.c b/src/detection/localip/localip_windows.c index 8ac766660..c378100f9 100644 --- a/src/detection/localip/localip_windows.c +++ b/src/detection/localip/localip_windows.c @@ -18,6 +18,7 @@ static void addNewIp(FFlist* list, const char* name, const char* addr, int type, ffStrbufInit(&ip->ipv6); ffStrbufInit(&ip->mac); ip->defaultRoute = defaultRoute; + ip->speed = -1; ip->mtu = -1; } else @@ -150,7 +151,11 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) } if (!newIp) - FF_LIST_GET(FFLocalIpResult, *results, results->length - 1)->mtu = (int32_t) adapter->Mtu; + { + FFLocalIpResult* result = FF_LIST_GET(FFLocalIpResult, *results, results->length - 1); + result->speed = (int32_t) (adapter->ReceiveLinkSpeed / 1000); + result->mtu = (int32_t) adapter->Mtu; + } } return NULL; diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h index fd0cbec0f..feffb0cea 100644 --- a/src/detection/packages/packages.h +++ b/src/detection/packages/packages.h @@ -17,6 +17,7 @@ typedef struct FFPackagesResult uint32_t guixHome; uint32_t guixSystem; uint32_t guixUser; + uint32_t linglong; uint32_t lpkg; uint32_t lpkgbuild; uint32_t nixDefault; diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 5af0226a3..ab07ff07b 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -471,6 +471,7 @@ static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts, { packageCounts->guixSystem += getGuixPackages(baseDir, "/run/current-system/profile"); } + if (!(options->disabled & FF_PACKAGES_FLAG_LINGLONG_BIT)) packageCounts->linglong += getNumElements(baseDir, "/var/lib/linglong/repo/refs/heads/main", DT_DIR); } static void getPackageCountsRegular(FFstrbuf* baseDir, FFPackagesResult* packageCounts, FFPackagesOptions* options) diff --git a/src/detection/terminalfont/terminalfont_linux.c b/src/detection/terminalfont/terminalfont_linux.c index 271243354..8dd15ee7a 100644 --- a/src/detection/terminalfont/terminalfont_linux.c +++ b/src/detection/terminalfont/terminalfont_linux.c @@ -8,7 +8,7 @@ #include "detection/displayserver/displayserver.h" #include "util/mallocHelper.h" #include "util/stringUtils.h" -#include "util/linux/elf.h" +#include "util/binary.h" static const char* getSystemMonospaceFont(void) { @@ -315,7 +315,7 @@ static void detectSt(FFTerminalFontResult* terminalFont, const FFTerminalResult* { ffStrbufClear(&font); - const char* error = ffElfExtractStrings(terminal->exePath.chars, elfExtractStringsCallBack, &font); + const char* error = ffBinaryExtractStrings(terminal->exePath.chars, elfExtractStringsCallBack, &font); if (error) { ffStrbufAppendS(&terminalFont->error, error); diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index 4d0adf414..4073d7aef 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -3,6 +3,7 @@ #include "common/processing.h" #include "common/properties.h" #include "util/stringUtils.h" +#include "util/binary.h" #include #ifdef __FreeBSD__ @@ -250,8 +251,24 @@ FF_MAYBE_UNUSED static bool getTerminalVersionTermux(FFstrbuf* version) return version->length > 0; } -FF_MAYBE_UNUSED static bool getTerminalVersionGnome(FFstrbuf* version) +static bool extractGnomeTerminalVersion(const char *str, FF_MAYBE_UNUSED uint32_t len, void *userdata) { + if (!ffCharIsDigit(str[0])) return true; + int count = 0; + sscanf(str, "%*d.%*d.%*d%n", &count); + if (count == 0) return true; + ffStrbufSetS((FFstrbuf*) userdata, str); + return false; +} + +FF_MAYBE_UNUSED static bool getTerminalVersionGnome(FFstrbuf* exe, FFstrbuf* version) +{ + if (exe->chars[0] == '/') + { + ffBinaryExtractStrings(exe->chars, extractGnomeTerminalVersion, version); + if (version->length) return true; + } + if(ffProcessAppendStdOut(version, (char* const[]){ "gnome-terminal", "--version", @@ -590,7 +607,7 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe #if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) if(ffStrbufStartsWithIgnCaseS(processName, "gnome-terminal")) - return getTerminalVersionGnome(version); + return getTerminalVersionGnome(exe, version); if(ffStrbufIgnCaseEqualS(processName, "konsole")) return getTerminalVersionKonsole(exe, version); diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index 30e131904..18be4f3b8 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -384,6 +384,14 @@ const FFShellResult* ffDetectShell() result.tty = -1; pid_t ppid = getppid(); + + const char* ignoreParent = getenv("FFTS_IGNORE_PARENT"); + if (ignoreParent && ffStrEquals(ignoreParent, "1")) + { + FF_STRBUF_AUTO_DESTROY _ = ffStrbufCreate(); + ffProcessGetBasicInfoLinux(ppid, &_, &ppid, NULL); + } + ppid = getShellInfo(&result, ppid); getUserShellFromEnv(&result); setShellInfoDetails(&result); diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index f1e27d63e..ecac82e1f 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -5,6 +5,7 @@ #include "util/mallocHelper.h" #include "util/windows/registry.h" #include "util/windows/unicode.h" +#include "util/stringUtils.h" #include #include @@ -345,6 +346,10 @@ const FFShellResult* ffDetectShell(void) if(!ffProcessGetInfoWindows(0, &ppid, NULL, NULL, NULL, NULL, NULL)) return &result; + const char* ignoreParent = getenv("FFTS_IGNORE_PARENT"); + if (ignoreParent && ffStrEquals(ignoreParent, "1")) + ffProcessGetInfoWindows(ppid, &ppid, NULL, NULL, NULL, NULL, NULL); + ppid = getShellInfo(&result, ppid); if (result.processName.length > 0) diff --git a/src/detection/version/version.c b/src/detection/version/version.c index 5c1fb0ae4..fe87e2e77 100644 --- a/src/detection/version/version.c +++ b/src/detection/version/version.c @@ -1,8 +1,8 @@ #include "version.h" -#if defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) || defined(__amd64) +#if defined(__x86_64__) #define FF_ARCHITECTURE "x86_64" -#elif defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(__i586__) || defined(__i586) || defined(__i686__) || defined(__i686) +#elif defined(__i386__) #define FF_ARCHITECTURE "i386" #elif defined(__aarch64__) #define FF_ARCHITECTURE "aarch64" @@ -16,6 +16,8 @@ #define FF_ARCHITECTURE "riscv" #elif defined(__s390x__) #define FF_ARCHITECTURE "s390x" +#elif defined(__loongarch__) + #define FF_ARCHITECTURE "loongarch" #else #define FF_ARCHITECTURE "unknown" #endif diff --git a/src/logo/ascii/lingmo.txt b/src/logo/ascii/lingmo.txt new file mode 100644 index 000000000..23651b458 --- /dev/null +++ b/src/logo/ascii/lingmo.txt @@ -0,0 +1,20 @@ + ..',;;;;;;,,.. + ..,;;;;;;;;;;;;;;;;,.. + .';;;;;;;;;;;;;;;;;;;;;;;;,. + .;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;' + .;;;;;;;;;$2clodddol:$1;;;;;;;;;;;;;;;;. + ';;;;;;;$2ckXWWWNNWWWN0d:$1;;;;;;;;;;;;;;' + ';;;;;;$2cOWWKxl:$1;;;:$2okXWXx$1;;;;;;;;;;;;;;, + ;;;;;;$2:XWWo$1;;;;;;;;;;;$2ONNx$1;;;;;;;;;;;;;;. +,;;;;;;$2xWMk$1;;;;;;;;;;;;;$20NX:$1;;;;;;;;;;;;;; +;;;;;;;$2xMMk$1;;;;;;;;;;;;;$2oXNdcloooolc:$1;;;;; +,;;;;;;$2:NMWl$1;;;;;;;;;;;;$2ckodKK00000Oc$1;;;;; +.;;;;;;;$2cKMWOo:$1;;;;;$2cox0Xccdl:$1;;;;;:;;;;;' + ;;;;;;;;;$2o0WWNK000KXXKkodKKo$1;;;;;;;;;;;; + ,;;;;;;;;;$2:loxxxxdlc$1;;;;$2xKKxc$1;;;;;;;;; + ,;;;;;;;;;;;;;;;;;;;;;;;$2ckK0o;$1;;;;;; + ;;;;;;;;;;;;;;;;;;;;;;;;$2:l$1;;;;;;. + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;. + ;;;;;;;;;;;;;;;;;;;;;;. + ';;;;;;;;;;;;, + ';;'. diff --git a/src/logo/ascii/sleeperos.txt b/src/logo/ascii/sleeperos.txt new file mode 100644 index 000000000..4fdf57945 --- /dev/null +++ b/src/logo/ascii/sleeperos.txt @@ -0,0 +1,16 @@ + ______________ +/P$2aooooooooooooa$19\ +H$2EEEEEEEEEEEEEEEE$1H +H$2EEEEEEEEEEEEEEEE$1H +\baaaaaaap;$2ZZZZ$1,/* + x%"$2ZZZZ$1,%^ + >7'$2ZZZZ$1,%" + /7'$2ZZZZ$1/>' + ,//$2ZZZZ$1,// + ,%<$2ZZZZ$1,%< + >%^$2ZZZZ$1x%^_____ +4>'$2ZZZZ$1---------9b +H$2EEEEEEEEEEEEEEEE$1H +H$2EEEEEEEEEEEEEEEE$1H +Yq$2^************^$1pY + `````````````` diff --git a/src/logo/ascii/sleeperos_small.txt b/src/logo/ascii/sleeperos_small.txt new file mode 100644 index 000000000..13c18f3cb --- /dev/null +++ b/src/logo/ascii/sleeperos_small.txt @@ -0,0 +1,7 @@ + _____ +[$2BBBBB$1] +`^^7$2&$1/` + ,/$2&$1/` +,/$2&$1Z__ +[$2BBBBB$1] +`^^^^^` diff --git a/src/logo/builtin.c b/src/logo/builtin.c index 19ad305ef..58f8348e0 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -2332,6 +2332,17 @@ static const FFlogo L[] = { .colorKeys = FF_COLOR_FG_GREEN, .colorTitle = FF_COLOR_FG_YELLOW, }, + // Lingmo OS + { + .names = {"Lingmo", "lingmo", "LingmoOS", "lingmoos"}, + .lines = FASTFETCH_DATATEXT_LOGO_LINGMO, + .colors = { + FF_COLOR_FG_BLUE, + FF_COLOR_FG_WHITE, + }, + .colorKeys = FF_COLOR_FG_BLUE, + .colorTitle = FF_COLOR_FG_BLUE, + }, // Linspire { .names = {"Linspire", "Lindows"}, @@ -3959,6 +3970,25 @@ static const FFlogo S[] = { FF_COLOR_FG_WHITE, }, }, + // SleeperOS + { + .names = {"SleeperOS"}, + .lines = FASTFETCH_DATATEXT_LOGO_SLEEPEROS, + .colors = { + FF_COLOR_FG_CYAN, + FF_COLOR_FG_WHITE, + } + }, + // SleeperOS + { + .names = {"SleeperOS_small"}, + .type = FF_LOGO_LINE_TYPE_SMALL_BIT, + .lines = FASTFETCH_DATATEXT_LOGO_SLEEPEROS_SMALL, + .colors = { + FF_COLOR_FG_CYAN, + FF_COLOR_FG_WHITE, + } + }, // Slitaz { .names = {"Slitaz"}, diff --git a/src/modules/colors/colors.c b/src/modules/colors/colors.c index 55192eae5..7da162136 100644 --- a/src/modules/colors/colors.c +++ b/src/modules/colors/colors.c @@ -57,7 +57,8 @@ void ffPrintColors(FFColorsOptions* options) if (options->symbol == FF_COLORS_SYMBOL_BACKGROUND) { const char* term = getenv("TERM"); - if (term && ffStrEquals(term, "linux")) + // Should be "linux", however some terminal mulitplexer overrides $TERM + if (term && !ffStrStartsWith(term, "xterm")) ffStrbufAppendS(&result, "\e[5m"); } #endif diff --git a/src/modules/display/display.c b/src/modules/display/display.c index 8d73a67ce..c180d338a 100644 --- a/src/modules/display/display.c +++ b/src/modules/display/display.c @@ -140,10 +140,21 @@ void ffPrintDisplay(FFDisplayOptions* options) { double ppi = sqrt(result->width * result->width + result->height * result->height) / inch; + char refreshRate[16]; + if(result->refreshRate > 0) + { + if(options->preciseRefreshRate) + snprintf(refreshRate, sizeof(refreshRate), "%g", ((int) (result->refreshRate * 1000 + 0.5)) / 1000.0); + else + snprintf(refreshRate, sizeof(refreshRate), "%i", (uint32_t) (result->refreshRate + 0.5)); + } + else + refreshRate[0] = 0; + FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_DISPLAY_NUM_FORMAT_ARGS, ((FFformatarg[]) { {FF_FORMAT_ARG_TYPE_UINT, &result->width, "width"}, {FF_FORMAT_ARG_TYPE_UINT, &result->height, "height"}, - {FF_FORMAT_ARG_TYPE_DOUBLE, &result->refreshRate, "refresh-rate"}, + {FF_FORMAT_ARG_TYPE_STRING, refreshRate, "refresh-rate"}, {FF_FORMAT_ARG_TYPE_UINT, &result->scaledWidth, "scaled-width"}, {FF_FORMAT_ARG_TYPE_UINT, &result->scaledHeight, "scaled-height"}, {FF_FORMAT_ARG_TYPE_STRBUF, &result->name, "name"}, diff --git a/src/modules/localip/localip.c b/src/modules/localip/localip.c index 0238b82e6..2a5db4914 100644 --- a/src/modules/localip/localip.c +++ b/src/modules/localip/localip.c @@ -5,7 +5,7 @@ #include "util/stringUtils.h" #define FF_LOCALIP_DISPLAY_NAME "Local IP" -#define FF_LOCALIP_NUM_FORMAT_ARGS 6 +#define FF_LOCALIP_NUM_FORMAT_ARGS 7 #pragma GCC diagnostic ignored "-Wsign-conversion" static int sortIps(const FFLocalIpResult* left, const FFLocalIpResult* right) @@ -111,6 +111,14 @@ void ffPrintLocalIp(FFLocalIpOptions* options) } else { + FF_STRBUF_AUTO_DESTROY speedStr = ffStrbufCreate(); + if (ip->speed > 0) + { + if (ip->speed >= 1000) + ffStrbufSetF(&speedStr, "%g Gbps", ip->speed / 1000.0); + else + ffStrbufSetF(&speedStr, "%u Mbps", (unsigned) ip->speed); + } FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_LOCALIP_NUM_FORMAT_ARGS, ((FFformatarg[]){ {FF_FORMAT_ARG_TYPE_STRBUF, &ip->ipv4, "ipv4"}, {FF_FORMAT_ARG_TYPE_STRBUF, &ip->ipv6, "ipv6"}, @@ -118,6 +126,7 @@ void ffPrintLocalIp(FFLocalIpOptions* options) {FF_FORMAT_ARG_TYPE_STRBUF, &ip->name, "ifname"}, {FF_FORMAT_ARG_TYPE_BOOL, &ip->defaultRoute, "is-default-route"}, {FF_FORMAT_ARG_TYPE_INT, &ip->mtu, "mtu"}, + {FF_FORMAT_ARG_TYPE_STRBUF, &speedStr, "speed"}, })); } ++index; @@ -376,6 +385,7 @@ void ffGenerateLocalIpJsonResult(FF_MAYBE_UNUSED FFLocalIpOptions* options, yyjs yyjson_mut_obj_add_strbuf(doc, obj, "mac", &ip->mac); yyjson_mut_obj_add_strbuf(doc, obj, "name", &ip->name); yyjson_mut_obj_add_int(doc, obj, "mtu", ip->mtu); + yyjson_mut_obj_add_int(doc, obj, "speed", ip->speed); } FF_LIST_FOR_EACH(FFLocalIpResult, ip, results) @@ -396,6 +406,7 @@ void ffPrintLocalIpHelpFormat(void) "Interface name - ifname", "Is default route - is-default-route", "MTU size in bytes - mtu", + "Link speed (formatted) - speed", })); } diff --git a/src/modules/packages/option.h b/src/modules/packages/option.h index 2456ce85a..de28caa33 100644 --- a/src/modules/packages/option.h +++ b/src/modules/packages/option.h @@ -31,6 +31,7 @@ typedef enum FFPackagesFlags FF_PACKAGES_FLAG_LPKG_BIT = 1 << 21, FF_PACKAGES_FLAG_LPKGBUILD_BIT = 1 << 22, FF_PACKAGES_FLAG_GUIX_BIT = 1 << 23, + FF_PACKAGES_FLAG_LINGLONG_BIT = 1 << 24, } FFPackagesFlags; typedef struct FFPackagesOptions diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index 175a365ab..8593a9f46 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -4,7 +4,7 @@ #include "modules/packages/packages.h" #include "util/stringUtils.h" -#define FF_PACKAGES_NUM_FORMAT_ARGS 36 +#define FF_PACKAGES_NUM_FORMAT_ARGS 37 void ffPrintPackages(FFPackagesOptions* options) { @@ -71,6 +71,7 @@ void ffPrintPackages(FFPackagesOptions* options) FF_PRINT_PACKAGE_NAME(guixSystem, "guix-system") FF_PRINT_PACKAGE_NAME(guixUser, "guix-user") FF_PRINT_PACKAGE_NAME(guixHome, "guix-home") + FF_PRINT_PACKAGE(linglong) putchar('\n'); } @@ -113,6 +114,7 @@ void ffPrintPackages(FFPackagesOptions* options) {FF_FORMAT_ARG_TYPE_UINT, &counts.guixSystem, "guix-system"}, {FF_FORMAT_ARG_TYPE_UINT, &counts.guixUser, "guix-user"}, {FF_FORMAT_ARG_TYPE_UINT, &counts.guixHome, "guix-home"}, + {FF_FORMAT_ARG_TYPE_UINT, &counts.linglong, "linglong"}, {FF_FORMAT_ARG_TYPE_UINT, &nixAll, "nix-all"}, {FF_FORMAT_ARG_TYPE_UINT, &flatpakAll, "flatpak-all"}, {FF_FORMAT_ARG_TYPE_UINT, &brewAll, "brew-all"}, @@ -171,6 +173,7 @@ bool ffParsePackagesCommandOptions(FFPackagesOptions* options, const char* key, case 'L': if (false); FF_TEST_PACKAGE_NAME(LPKG) FF_TEST_PACKAGE_NAME(LPKGBUILD) + FF_TEST_PACKAGE_NAME(LINGLONG) break; case 'M': if (false); FF_TEST_PACKAGE_NAME(MACPORTS) @@ -279,6 +282,7 @@ void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module) case 'L': if (false); FF_TEST_PACKAGE_NAME(LPKG) FF_TEST_PACKAGE_NAME(LPKGBUILD) + FF_TEST_PACKAGE_NAME(LINGLONG) break; case 'M': if (false); FF_TEST_PACKAGE_NAME(MACPORTS) @@ -356,6 +360,7 @@ void ffGeneratePackagesJsonConfig(FFPackagesOptions* options, yyjson_mut_doc* do FF_TEST_PACKAGE_NAME(AM) FF_TEST_PACKAGE_NAME(SORCERY) FF_TEST_PACKAGE_NAME(GUIX) + FF_TEST_PACKAGE_NAME(LINGLONG) #undef FF_TEST_PACKAGE_NAME } } @@ -406,12 +411,13 @@ void ffGeneratePackagesJsonResult(FF_MAYBE_UNUSED FFPackagesOptions* options, yy FF_APPEND_PACKAGE_COUNT(guixSystem) FF_APPEND_PACKAGE_COUNT(guixUser) FF_APPEND_PACKAGE_COUNT(guixHome) + FF_APPEND_PACKAGE_COUNT(linglong) yyjson_mut_obj_add_strbuf(doc, obj, "pacmanBranch", &counts.pacmanBranch); } void ffPrintPackagesHelpFormat(void) { - FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_PACKAGES_MODULE_NAME, "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (eopkg), {8} (xbps), {9} (nix-system), {10} (nix-user), {11} (nix-default), {12} (apk), {13} (pkg), {14} (flatpak-system), {15} (flatpack-user), {16} (snap), {17} (brew), {18} (brew-cask), {19} (MacPorts), {20} (scoop), {21} (choco), {22} (pkgtool), {23} (paludis), {24} (winget), {25} (opkg), {26} (am), {27} (sorcery), {28} (lpkg), {29} (lpkgbuild), {30} (guix-system), {31} (guix-user), {32} (guix-home)", FF_PACKAGES_NUM_FORMAT_ARGS, ((const char* []) { + FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_PACKAGES_MODULE_NAME, "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (eopkg), {8} (xbps), {9} (nix-system), {10} (nix-user), {11} (nix-default), {12} (apk), {13} (pkg), {14} (flatpak-system), {15} (flatpack-user), {16} (snap), {17} (brew), {18} (brew-cask), {19} (MacPorts), {20} (scoop), {21} (choco), {22} (pkgtool), {23} (paludis), {24} (winget), {25} (opkg), {26} (am), {27} (sorcery), {28} (lpkg), {29} (lpkgbuild), {30} (guix-system), {31} (guix-user), {32} (guix-home), {33} (linglong)", FF_PACKAGES_NUM_FORMAT_ARGS, ((const char* []) { "Number of all packages - all", "Number of pacman packages - pacman", "Pacman branch on manjaro - pacman-branch", @@ -444,6 +450,7 @@ void ffPrintPackagesHelpFormat(void) "Number of guix-system packages - guix-system", "Number of guix-user packages - guix-user", "Number of guix-home packages - guix-home", + "Number of linglong packages - linglong", "Total number of all nix packages - nix-all", "Total number of all flatpak app packages - flatpak-all", "Total number of all brew packages - brew-all", diff --git a/src/util/linux/elf.h b/src/util/binary.h similarity index 61% rename from src/util/linux/elf.h rename to src/util/binary.h index 4cfdaeb45..2a0e0943d 100644 --- a/src/util/linux/elf.h +++ b/src/util/binary.h @@ -2,4 +2,4 @@ #include "fastfetch.h" -const char* ffElfExtractStrings(const char* elfFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata); +const char* ffBinaryExtractStrings(const char* file, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata); diff --git a/src/util/binary_apple.c b/src/util/binary_apple.c new file mode 100644 index 000000000..940228af8 --- /dev/null +++ b/src/util/binary_apple.c @@ -0,0 +1,186 @@ +#include "binary.h" +#include "common/io/io.h" +#include "util/stringUtils.h" +#include "util/mallocHelper.h" + +#include +#include +#include +#include +#include +#include + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // swap_fat_arch + +// Ref: https://github.com/AlexDenisov/segment_dumper/blob/master/main.c + +static inline bool readData(FILE *objFile, void *buf, size_t size, off_t offset) +{ + fseek(objFile, offset, SEEK_SET); + return fread(buf, 1, size, objFile) == size; +} + +static bool handleMachSection(FILE *objFile, const char *name, off_t offset, size_t size, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata) +{ + if (!ffStrEquals(name, "__cstring")) return true; + + FF_AUTO_FREE char* data = (char*) malloc(size); + if (!readData(objFile, data, size, offset)) + return true; + + for (size_t off = 0; off < size; ++off) + { + const char* p = (const char*) data + off; + if (*p == '\0') continue; + uint32_t len = (uint32_t) strlen(p); + if (*p >= ' ' && *p <= '~') // Ignore control characters + { + if (!cb(p, len, userdata)) return false; + } + off += len; + } + return true; +} + +static const char* dumpMachHeader(FILE *objFile, off_t offset, bool is_64, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata) +{ + uint32_t ncmds; + off_t loadCommandsOffset = offset; + + if (is_64) + { + struct mach_header_64 header; + if (!readData(objFile, &header, sizeof(header), offset)) + return "read mach header failed"; + + ncmds = header.ncmds; + loadCommandsOffset += sizeof(header); + } + else + { + struct mach_header header; + if (!readData(objFile, &header, sizeof(header), offset)) + return "read mach header failed"; + + ncmds = header.ncmds; + loadCommandsOffset += sizeof(header); + } + + off_t commandOffset = loadCommandsOffset; + struct load_command cmd = {}; + for (uint32_t i = 0U; i < ncmds; i++, commandOffset += cmd.cmdsize) + { + if (!readData(objFile, &cmd, sizeof(cmd), commandOffset)) + continue; + + if (cmd.cmd == LC_SEGMENT_64) + { + struct segment_command_64 segment; + if (!readData(objFile, &segment, sizeof(segment), commandOffset)) + continue; + + if (!ffStrEquals(segment.segname, "__TEXT")) continue; + + for (uint32_t j = 0U; j < segment.nsects; j++) + { + struct section_64 section; + if (!readData(objFile, §ion, sizeof(section), (off_t) ((size_t) commandOffset + sizeof(segment) + j * sizeof(section)))) + continue; + + if (!handleMachSection(objFile, section.sectname, section.offset, section.size, cb, userdata)) + return NULL; + } + } + else if (cmd.cmd == LC_SEGMENT) + { + struct segment_command segment; + if (!readData(objFile, &segment, sizeof(segment), commandOffset)) + continue; + + if (!ffStrEquals(segment.segname, "__TEXT")) continue; + + for (uint32_t j = 0; j < segment.nsects; j++) + { + struct section section; + if (!readData(objFile, §ion, sizeof(section), (off_t) ((size_t) commandOffset + sizeof(segment) + j * sizeof(section)))) + continue; + + if (!handleMachSection(objFile, section.sectname, section.offset, section.size, cb, userdata)) + return NULL; + } + } + + return NULL; + } + + return NULL; +} + +static const char* dumpFatHeader(FILE *objFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata) +{ + struct fat_header header; + if (!readData(objFile, &header, sizeof(header), 0)) + return "read fat header failed"; + + bool needSwap = header.magic == FAT_CIGAM || header.magic == FAT_CIGAM_64; + + if (needSwap) swap_fat_header(&header, NX_UnknownByteOrder); + + for (uint32_t i = 0U; i < header.nfat_arch; i++) + { + off_t machHeaderOffset = 0; + if (header.magic == FAT_MAGIC) + { + struct fat_arch arch; + if (!readData(objFile, &arch, sizeof(arch), (off_t) (sizeof(header) + i * sizeof(arch)))) + continue; + + if (needSwap) + swap_fat_arch(&arch, 1, NX_UnknownByteOrder); + machHeaderOffset = (off_t)arch.offset; + } + else + { + struct fat_arch_64 arch; + if (!readData(objFile, &arch, sizeof(arch), (off_t) (sizeof(header) + i * sizeof(arch)))) + continue; + + if (needSwap) + swap_fat_arch_64(&arch, 1, NX_UnknownByteOrder); + + machHeaderOffset = (off_t)arch.offset; + } + + uint32_t magic; + if (!readData(objFile, &magic, sizeof(magic), machHeaderOffset)) + continue; + + if (magic == MH_MAGIC_64 || magic == MH_MAGIC) + { + dumpMachHeader(objFile, machHeaderOffset, magic == MH_MAGIC_64, cb, userdata); + return NULL; + } + } + return "Unsupported fat header"; +} + +const char *ffBinaryExtractStrings(const char *machoFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata) +{ + FF_AUTO_CLOSE_FILE FILE *objFile = fopen(machoFile, "rb"); + if (objFile == NULL) + return "File could not be opened"; + + uint32_t magic; + if (!readData(objFile, &magic, sizeof(magic), 0)) + return "read magic number failed"; + + // MH_CIGAM and MH_CIGAM_64 seem to be no longer used, as `swap_mach_header` is marked as deprecated. + // However FAT_CIGAM and FAT_CIGAM_64 are still used (/usr/bin/vim). + if (magic != MH_MAGIC && magic != MH_MAGIC_64 && magic != FAT_CIGAM && magic != FAT_CIGAM_64 && magic != FAT_MAGIC && magic != FAT_MAGIC_64) + return "Unsupported format or big endian mach-o file"; + + if (magic == FAT_MAGIC || magic == FAT_MAGIC_64 || magic == FAT_CIGAM || magic == FAT_CIGAM_64) + return dumpFatHeader(objFile, cb, userdata); + else + return dumpMachHeader(objFile, 0, magic == MH_MAGIC_64, cb, userdata); +} diff --git a/src/util/binary_linux.c b/src/util/binary_linux.c new file mode 100644 index 000000000..11aa2a209 --- /dev/null +++ b/src/util/binary_linux.c @@ -0,0 +1,107 @@ +#include "binary.h" + +#if defined(FF_HAVE_ELF) || defined(__sun) + +#include "common/io/io.h" +#include "common/library.h" +#include "util/stringUtils.h" + +#include +#include + +struct FFElfData { + FF_LIBRARY_SYMBOL(elf_version) + FF_LIBRARY_SYMBOL(elf_begin) + FF_LIBRARY_SYMBOL(elf_getshdrstrndx) + FF_LIBRARY_SYMBOL(elf_nextscn) + FF_LIBRARY_SYMBOL(elf64_getshdr) + FF_LIBRARY_SYMBOL(elf32_getshdr) + FF_LIBRARY_SYMBOL(elf_getdata) + FF_LIBRARY_SYMBOL(elf_strptr) + FF_LIBRARY_SYMBOL(elf_end) + + bool inited; +} elfData; + +const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata) +{ + if (!elfData.inited) + { + elfData.inited = true; + FF_LIBRARY_LOAD(libelf, &instance.config.library.libelf, "dlopen libelf" FF_LIBRARY_EXTENSION " failed", "libelf" FF_LIBRARY_EXTENSION, 1); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_version) + if (elfData.ffelf_version(EV_CURRENT) == EV_NONE) return "elf_version() failed"; + + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_begin) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_getshdrstrndx) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_nextscn) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf64_getshdr) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf32_getshdr) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_getdata) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_strptr) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_end) + + libelf = NULL; + } + + if (elfData.ffelf_end == NULL) + return "load libelf failed"; + + FF_AUTO_CLOSE_FD int fd = open(elfFile, O_RDONLY, 0); + if (fd < 0) return "open() failed"; + + Elf* elf = elfData.ffelf_begin(fd, ELF_C_READ, NULL); + if (elf == NULL) return "elf_begin() failed"; + + size_t shstrndx = 0; + if (elfData.ffelf_getshdrstrndx(elf, &shstrndx) < 0) + { + elfData.ffelf_end(elf); + return "elf_getshdrstrndx() failed"; + } + + Elf_Scn* scn = NULL; + while ((scn = elfData.ffelf_nextscn(elf, scn)) != NULL) + { + Elf64_Shdr* shdr64 = elfData.ffelf64_getshdr(scn); + Elf32_Shdr* shdr32 = NULL; + if (shdr64 == NULL) + { + shdr32 = elfData.ffelf32_getshdr(scn); + if (shdr32 == NULL) continue; + } + + const char* name = elfData.ffelf_strptr(elf, shstrndx, shdr64 ? shdr64->sh_name : shdr32->sh_name); + if (name == NULL || !ffStrEquals(name, ".rodata")) continue; + + Elf_Data* data = elfData.ffelf_getdata(scn, NULL); + if (data == NULL) continue; + + for (size_t off = 0; off < data->d_size; ++off) + { + const char* p = (const char*) data->d_buf + off; + if (*p == '\0') continue; + uint32_t len = (uint32_t) strlen(p); + if (*p >= ' ' && *p <= '~') // Ignore control characters + { + if (!cb(p, len, userdata)) break; + } + off += len; + } + + break; + } + + elfData.ffelf_end(elf); + return NULL; +} + +#else + +const char* ffBinaryExtractStrings(const char* file, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata) +{ + FF_UNUSED(file, cb, userdata); + return "Fastfetch was built without libelf support"; +} + +#endif diff --git a/src/util/binary_windows.c b/src/util/binary_windows.c new file mode 100644 index 000000000..6582d9e1d --- /dev/null +++ b/src/util/binary_windows.c @@ -0,0 +1,39 @@ +#include "binary.h" +#include "common/io/io.h" +#include "util/stringUtils.h" +#include "util/mallocHelper.h" + +#include +#include +#include +#include + +const char* ffBinaryExtractStrings(const char *peFile, bool (*cb)(const char *str, uint32_t len, void *userdata), void *userdata) +{ + __attribute__((__cleanup__(UnMapAndLoad))) LOADED_IMAGE loadedImage = {}; + if (!MapAndLoad(peFile, NULL, &loadedImage, FALSE, TRUE)) + return "File could not be loaded"; + + for (ULONG i = 0; i < loadedImage.NumberOfSections; ++i) + { + PIMAGE_SECTION_HEADER section = &loadedImage.Sections[i]; + if ((section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) && ffStrEquals((const char*) section->Name, ".rdata")) + { + uint8_t *data = (uint8_t *) loadedImage.MappedAddress + section->PointerToRawData; + + for (size_t off = 0; off < section->SizeOfRawData; ++off) + { + const char* p = (const char*) data + off; + if (*p == '\0') continue; + uint32_t len = (uint32_t) strlen(p); + if (*p >= ' ' && *p <= '~') // Ignore control characters + { + if (!cb(p, len, userdata)) break; + } + off += len; + } + } + } + + return NULL; +} diff --git a/src/util/linux/elf.c b/src/util/linux/elf.c deleted file mode 100644 index 4e65c15db..000000000 --- a/src/util/linux/elf.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "elf.h" - -#ifdef FF_HAVE_ELF - -#include "common/io/io.h" -#include "common/library.h" -#include "util/stringUtils.h" - -#include -#include - -const char* ffElfExtractStrings(const char* elfFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata) -{ - FF_LIBRARY_LOAD(libelf, &instance.config.library.libelf, "dlopen libelf" FF_LIBRARY_EXTENSION " failed", "libelf" FF_LIBRARY_EXTENSION, 1); - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libelf, elf_version) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libelf, elf_begin) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libelf, elf_getshdrstrndx) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libelf, elf_nextscn) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libelf, elf64_getshdr) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libelf, elf32_getshdr) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libelf, elf_getdata) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libelf, elf_strptr) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libelf, elf_end) - - if (ffelf_version(EV_CURRENT) == EV_NONE) return "elf_version() failed"; - - FF_AUTO_CLOSE_FD int fd = open(elfFile, O_RDONLY, 0); - if (fd < 0) return "open() failed"; - - Elf* elf = ffelf_begin(fd, ELF_C_READ, NULL); - if (elf == NULL) return "elf_begin() failed"; - - size_t shstrndx = 0; - if (ffelf_getshdrstrndx(elf, &shstrndx) < 0) - { - ffelf_end(elf); - return "elf_getshdrstrndx() failed"; - } - - Elf_Scn* scn = NULL; - while ((scn = ffelf_nextscn(elf, scn)) != NULL) - { - Elf64_Shdr* shdr64 = ffelf64_getshdr(scn); - Elf32_Shdr* shdr32 = NULL; - if (shdr64 == NULL) - { - shdr32 = ffelf32_getshdr(scn); - if (shdr32 == NULL) continue; - } - - const char* name = ffelf_strptr(elf, shstrndx, shdr64 ? shdr64->sh_name : shdr32->sh_name); - if (name == NULL || !ffStrEquals(name, ".rodata")) continue; - - Elf_Data* data = ffelf_getdata(scn, NULL); - if (data == NULL) continue; - - for (size_t off = 0; off < data->d_size; ++off) - { - const char* p = (const char*) data->d_buf + off; - if (*p == '\0') continue; - uint32_t len = (uint32_t) strlen(p); - if (*p >= ' ' && *p <= '~') // Ignore control characters - { - if (!cb(p, len, userdata)) break; - } - off += len; - } - - break; - } - - ffelf_end(elf); - return NULL; -} - -#else - -const char* ffElfExtractStrings(const char* elfFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata) -{ - FF_UNUSED(elfFile, cb, userdata); - return "Fastfetch was built without libelf support"; -} - -#endif diff --git a/src/util/path.c b/src/util/path.c new file mode 100644 index 000000000..5a0b5964e --- /dev/null +++ b/src/util/path.c @@ -0,0 +1,59 @@ +#include "path.h" + +#include "common/io/io.h" +#include "util/stringUtils.h" + +const char* ffFindExecutableInPath(const char* name, FFstrbuf* result) +{ + char* path = getenv("PATH"); + if(!path) + return "$PATH not set"; + + #ifdef _WIN32 + const bool appendExe = !ffStrEndsWithIgnCase(name, ".exe"); + #endif + + for (char* token = path; *token; path = token + 1) + { + token = strchr(path, + #ifdef _WIN32 + ';' + #else + ':' + #endif + ); + if (!token) token = path + strlen(path); + + ffStrbufSetNS(result, (uint32_t)(token - path), path); + ffStrbufEnsureEndsWithC(result, + #ifdef _WIN32 + '\\' + #else + '/' + #endif + ); + ffStrbufAppendS(result, name); + #ifdef _WIN32 + if (appendExe) ffStrbufAppendS(result, ".exe"); + if (!ffPathExists(result->chars, FF_PATHTYPE_FILE)) + continue; + #else + struct stat st; + if (stat(result->chars, &st) < 0 || !(st.st_mode & S_IXUSR)) + continue; + #endif + + return NULL; + } + ffStrbufClear(result); + return "Executable not found"; +} + +bool ffIsAbsolutePath(const char* path) +{ + #ifdef _WIN32 + return ffCharIsEnglishAlphabet(path[0]) && path[1] == ':' && (path[2] == '\\' || path[2] == '/'); + #else + return path[0] == '/'; + #endif +} diff --git a/src/util/path.h b/src/util/path.h new file mode 100644 index 000000000..7ff77631b --- /dev/null +++ b/src/util/path.h @@ -0,0 +1,6 @@ +#pragma once + +#include "fastfetch.h" + +const char* ffFindExecutableInPath(const char* name, FFstrbuf* result); +bool ffIsAbsolutePath(const char* path);