From 4860bc35d67a908946677d1f613218988304f38f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 20 Aug 2024 16:37:54 +0800 Subject: [PATCH] Editor: improve performance of version detection --- CMakeLists.txt | 1 + src/detection/editor/editor.c | 77 ++++++++++++++++++++++------------- src/util/linux/elf.c | 63 +++++++++++++++++++--------- src/util/path.c | 56 +++++++++++++++++++++++++ src/util/path.h | 6 +++ 5 files changed, 154 insertions(+), 49 deletions(-) create mode 100644 src/util/path.c create mode 100644 src/util/path.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 52be34625..52e0cb3c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ) diff --git a/src/detection/editor/editor.c b/src/detection/editor/editor.c index f71544d60..a69e6c5c9 100644 --- a/src/detection/editor/editor.c +++ b/src/detection/editor/editor.c @@ -1,17 +1,45 @@ #include "editor.h" #include "common/processing.h" #include "util/stringUtils.h" +#include "util/path.h" +#include "util/linux/elf.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 +#if __linux__ || __FreeBSD__ +static bool extractNvimVersion(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 extractVimVersion(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 extractNanoVersion(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; +} +#endif + const char* ffDetectEditor(FFEditorResult* result) { ffStrbufSetS(&result->name, getenv("VISUAL")); @@ -26,35 +54,15 @@ 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 (ffProcessAppendStdOut(&result->path, (char* const[]){ - FASTFETCH_TARGET_DIR_USR "/bin/which", - result->name.chars, - NULL, - }) != NULL || result->path.length == 0) - 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) - return NULL; - } - #endif - else + if (ffIsAbsolutePath(result->name.chars)) ffStrbufSet(&result->path, &result->name); + else + { + const char* error = ffFindExecutableInPath(result->name.chars, &result->path); + if (error) return error; + } + + if (!instance.config.general.detectVersion) return NULL; char buf[PATH_MAX + 1]; if (!realpath(result->path.chars, buf)) @@ -82,6 +90,17 @@ const char* ffDetectEditor(FFEditorResult* result) #endif } + #if __linux__ || __FreeBSD__ + if (ffStrbufEqualS(&result->exe, "nvim")) + ffElfExtractStrings(buf, extractNvimVersion, &result->version); + else if (ffStrbufEqualS(&result->exe, "vim")) + ffElfExtractStrings(buf, extractVimVersion, &result->version); + else if (ffStrbufEqualS(&result->exe, "nano")) + ffElfExtractStrings(buf, extractNanoVersion, &result->version); + + if (result->version.length > 0) return NULL; + #endif + const char* param = NULL; if ( ffStrbufEqualS(&result->exe, "nano") || diff --git a/src/util/linux/elf.c b/src/util/linux/elf.c index 4e65c15db..1af384354 100644 --- a/src/util/linux/elf.c +++ b/src/util/linux/elf.c @@ -9,49 +9,72 @@ #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* 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 (!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"; - if (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 = ffelf_begin(fd, ELF_C_READ, NULL); + Elf* elf = elfData.ffelf_begin(fd, ELF_C_READ, NULL); if (elf == NULL) return "elf_begin() failed"; size_t shstrndx = 0; - if (ffelf_getshdrstrndx(elf, &shstrndx) < 0) + if (elfData.ffelf_getshdrstrndx(elf, &shstrndx) < 0) { - ffelf_end(elf); + elfData.ffelf_end(elf); return "elf_getshdrstrndx() failed"; } Elf_Scn* scn = NULL; - while ((scn = ffelf_nextscn(elf, scn)) != NULL) + while ((scn = elfData.ffelf_nextscn(elf, scn)) != NULL) { - Elf64_Shdr* shdr64 = ffelf64_getshdr(scn); + Elf64_Shdr* shdr64 = elfData.ffelf64_getshdr(scn); Elf32_Shdr* shdr32 = NULL; if (shdr64 == NULL) { - shdr32 = ffelf32_getshdr(scn); + shdr32 = elfData.ffelf32_getshdr(scn); if (shdr32 == NULL) continue; } - const char* name = ffelf_strptr(elf, shstrndx, shdr64 ? shdr64->sh_name : shdr32->sh_name); + 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 = ffelf_getdata(scn, NULL); + Elf_Data* data = elfData.ffelf_getdata(scn, NULL); if (data == NULL) continue; for (size_t off = 0; off < data->d_size; ++off) @@ -69,7 +92,7 @@ const char* ffElfExtractStrings(const char* elfFile, bool (*cb)(const char* str, break; } - ffelf_end(elf); + elfData.ffelf_end(elf); return NULL; } diff --git a/src/util/path.c b/src/util/path.c new file mode 100644 index 000000000..e47cbe768 --- /dev/null +++ b/src/util/path.c @@ -0,0 +1,56 @@ +#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 = NULL; (token = strchr(path, + #ifdef _WIN32 + ';' + #else + ':' + #endif + )) != NULL; path = token + 1) + { + 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);