2022-10-14 16:28:03 +08:00
|
|
|
|
#include "fastfetch.h"
|
2026-01-06 09:48:38 +08:00
|
|
|
|
#include "common/io.h"
|
|
|
|
|
|
#include "common/processing.h"
|
|
|
|
|
|
#include "common/properties.h"
|
|
|
|
|
|
#include "common/path.h"
|
2026-05-28 15:58:08 +08:00
|
|
|
|
#include "common/strutil.h"
|
2026-01-06 09:48:38 +08:00
|
|
|
|
#include "common/binary.h"
|
2022-10-14 16:28:03 +08:00
|
|
|
|
|
2023-12-15 14:29:51 +08:00
|
|
|
|
#include <ctype.h>
|
2025-10-19 09:41:01 +08:00
|
|
|
|
#include <stdint.h>
|
2023-05-27 21:47:18 +08:00
|
|
|
|
|
2026-08-26 23:12:25 +08:00
|
|
|
|
#if _WIN32
|
2026-07-03 11:03:55 +08:00
|
|
|
|
#include "common/windows/registry.h"
|
2026-04-15 16:16:46 +08:00
|
|
|
|
#include "common/windows/version.h"
|
|
|
|
|
|
#include <windows.h>
|
2022-10-15 22:35:04 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getFileVersion(const FFstrbuf* exePath, const wchar_t* stringName, FFstrbuf* version) {
|
2025-03-30 16:38:54 +08:00
|
|
|
|
wchar_t exePathW[PATH_MAX + 1];
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(exePathW, (ULONG) sizeof(exePathW), nullptr, exePath->chars, (ULONG) exePath->length + 1))) {
|
2026-03-11 14:55:09 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2025-03-06 22:11:36 +08:00
|
|
|
|
return ffGetFileVersion(exePathW, stringName, version);
|
2022-10-15 22:35:04 +08:00
|
|
|
|
}
|
2025-03-06 22:11:36 +08:00
|
|
|
|
|
2025-02-17 21:22:12 +08:00
|
|
|
|
#elif __HAIKU__
|
2026-04-15 16:16:46 +08:00
|
|
|
|
#include "common/haiku/version.h"
|
2026-05-22 13:37:48 +08:00
|
|
|
|
#elif __APPLE__
|
|
|
|
|
|
#include "common/apple/version.h"
|
2022-10-15 22:35:04 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getExeVersionRaw(FFstrbuf* exe, FFstrbuf* version) {
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return ffProcessAppendStdOut(version, (char* const[]) { exe->chars, "--version", nullptr }) == nullptr && version->length > 0;
|
2022-10-14 16:28:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getExeVersionGeneral(FFstrbuf* exe, FFstrbuf* version) {
|
|
|
|
|
|
if (!getExeVersionRaw(exe, version)) {
|
2023-01-11 16:34:37 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-01-11 16:34:37 +08:00
|
|
|
|
|
2022-10-14 16:28:03 +08:00
|
|
|
|
ffStrbufSubstrAfterFirstC(version, ' ');
|
2023-01-11 16:34:37 +08:00
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, ' ');
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return version->length > 0;
|
2022-10-14 16:28:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
static bool extractBashVersion(const char* line, [[maybe_unused]] uint32_t len, void* userdata) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (!ffStrStartsWith(line, "@(#)Bash version ")) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-08-27 14:06:45 +08:00
|
|
|
|
const char* start = line + strlen("@(#)Bash version ");
|
|
|
|
|
|
const char* end = strchr(start, '(');
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (!end) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-08-27 14:06:45 +08:00
|
|
|
|
ffStrbufSetNS((FFstrbuf*) userdata, (uint32_t) (end - start), start);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getShellVersionBash(FFstrbuf* exe, FFstrbuf* version) {
|
2025-11-25 09:09:58 +08:00
|
|
|
|
ffBinaryExtractStrings(exe->chars, extractBashVersion, version, (uint32_t) strlen("@(#)Bash version 0.0.0(0) release GNU"));
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (version->length > 0) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-08-27 14:06:45 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (!getExeVersionRaw(exe, version)) {
|
2023-01-11 16:34:37 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-01-11 16:34:37 +08:00
|
|
|
|
|
|
|
|
|
|
// GNU bash, version 5.1.16(1)-release (x86_64-pc-msys)\nCopyright...
|
2024-08-27 14:06:45 +08:00
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, '('); // GNU bash, version 5.1.16
|
2026-03-29 09:28:49 +08:00
|
|
|
|
ffStrbufSubstrAfterLastC(version, ' '); // 5.1.16
|
2023-01-11 16:34:37 +08:00
|
|
|
|
return true;
|
2022-10-14 16:28:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getShellVersionFish(FFstrbuf* exe, FFstrbuf* version) {
|
|
|
|
|
|
if (!getExeVersionRaw(exe, version)) {
|
2023-01-11 16:34:37 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-01-11 16:34:37 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
// fish, version 4.0.2-1 (Built by MSYS2 project) // version can be localized if LC_ALL is set
|
|
|
|
|
|
if (version->length < strlen("fish, v") || !ffStrbufStartsWithS(version, "fish")) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-07-10 21:47:31 +08:00
|
|
|
|
uint32_t index = ffStrbufFirstIndexC(version, ' '); // skip "fish,"
|
2026-08-04 21:14:48 +08:00
|
|
|
|
while (index + 1 < version->length && !ffCharIsDigit(version->chars[index + 1])) {
|
2026-08-02 22:02:54 +08:00
|
|
|
|
index = ffStrbufNextIndexC(version, index + 1, ' '); // skip "version"
|
2026-08-04 21:14:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (index + 1 >= version->length) {
|
|
|
|
|
|
return false;
|
2026-08-02 22:02:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-19 09:41:01 +08:00
|
|
|
|
ffStrbufSubstrAfter(version, index);
|
2025-08-19 17:25:04 +08:00
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, ' ');
|
2023-01-11 16:34:37 +08:00
|
|
|
|
return true;
|
2022-10-27 16:30:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getShellVersionPwsh(FFstrbuf* exe, FFstrbuf* version) {
|
2024-12-19 11:00:16 +08:00
|
|
|
|
// Requires manually setting $POWERSHELL_VERSION
|
|
|
|
|
|
// $env:POWERSHELL_VERSION = $PSVersionTable.PSVersion.ToString(); fastfetch.exe
|
|
|
|
|
|
const char* env = getenv("POWERSHELL_VERSION");
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (env) {
|
2024-12-19 11:00:16 +08:00
|
|
|
|
ffStrbufSetS(version, env);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#ifdef _WIN32
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (getFileVersion(exe, nullptr, version)) {
|
2023-01-11 16:34:37 +08:00
|
|
|
|
ffStrbufSubstrBeforeLastC(version, '.');
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#endif
|
2022-10-15 22:35:04 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (!getExeVersionRaw(exe, version)) {
|
2023-01-11 16:34:37 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-01-11 16:34:37 +08:00
|
|
|
|
|
2022-10-15 22:35:04 +08:00
|
|
|
|
ffStrbufSubstrAfterLastC(version, ' ');
|
2023-01-11 16:34:37 +08:00
|
|
|
|
return true;
|
2022-10-15 22:35:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getShellVersionKsh(FFstrbuf* exe, FFstrbuf* version) {
|
2026-09-25 11:33:46 +08:00
|
|
|
|
ffStrbufClear(version);
|
2024-10-04 15:58:17 +08:00
|
|
|
|
|
2026-09-25 11:33:46 +08:00
|
|
|
|
// ksh93 has used both stderr and stdout for --version, and the exit
|
|
|
|
|
|
// status is not consistent across releases. Parse any captured output
|
|
|
|
|
|
// before trying the next probe.
|
|
|
|
|
|
for (int attempt = 0; attempt < 4; ++attempt) {
|
2026-09-25 11:34:13 +08:00
|
|
|
|
ffStrbufClear(version);
|
2026-09-25 11:33:46 +08:00
|
|
|
|
if (attempt < 2 && (ffStrbufEndsWithS(exe, "mksh") || ffStrbufEndsWithS(exe, "pdksh") || ffStrbufEndsWithS(exe, "oksh")))
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
if (attempt == 0) {
|
|
|
|
|
|
ffProcessAppendStdErr(version, (char* const[]) { exe->chars, "--version", nullptr });
|
|
|
|
|
|
} else if (attempt == 1) {
|
|
|
|
|
|
ffProcessAppendStdOut(version, (char* const[]) { exe->chars, "--version", nullptr });
|
|
|
|
|
|
} else if (attempt == 2) {
|
|
|
|
|
|
ffProcessAppendStdOut(version, (char* const[]) {
|
|
|
|
|
|
exe->chars,
|
|
|
|
|
|
"-c",
|
|
|
|
|
|
"printf '%s\\n' \"${KSH_VERSION:-${SH_VERSION:-}}\"",
|
|
|
|
|
|
nullptr
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
ffProcessAppendStdOut(version, (char* const[]) {
|
|
|
|
|
|
exe->chars,
|
|
|
|
|
|
"-c",
|
|
|
|
|
|
"printf '%s\\n' \"${.sh.version}\"",
|
|
|
|
|
|
nullptr
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char* p = version->chars;
|
|
|
|
|
|
while (*p && isspace((unsigned char) *p))
|
|
|
|
|
|
++p;
|
|
|
|
|
|
if (!*p)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
// @(#)PD KSH v5.2.14, @(#)MIRBSD KSH R59, @(#)LEGACY KSH R59
|
|
|
|
|
|
if (ffStrStartsWith(p, "@(#)")) {
|
|
|
|
|
|
const char* marker = strstr(p, " KSH ");
|
|
|
|
|
|
if (!marker)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
const char* start = marker + strlen(" KSH ");
|
|
|
|
|
|
while (*start && isspace((unsigned char) *start))
|
|
|
|
|
|
++start;
|
|
|
|
|
|
const char* end = start;
|
|
|
|
|
|
while (*end && !isspace((unsigned char) *end))
|
|
|
|
|
|
++end;
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t length = (uint32_t) (end - start);
|
|
|
|
|
|
if (length > 1 && start[0] == 'v' && ffCharIsDigit(start[1])) {
|
|
|
|
|
|
++start;
|
|
|
|
|
|
--length;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (length == 0 || (!ffCharIsDigit(start[0]) && !(start[0] == 'R' && length > 1 && ffCharIsDigit(start[1]))))
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
ffStrbufSetNS(version, length, start);
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2023-07-20 16:11:02 +08:00
|
|
|
|
|
2026-09-25 11:33:46 +08:00
|
|
|
|
const char* core = nullptr;
|
|
|
|
|
|
if (ffStrStartsWith(p, "version")) {
|
|
|
|
|
|
// ksh93 self-doc: version sh (AT&T [Labs] Research) ...
|
|
|
|
|
|
const char* selfDoc = strstr(p, " sh (");
|
|
|
|
|
|
const char* end = selfDoc ? strchr(selfDoc, ')') : nullptr;
|
|
|
|
|
|
if (!end)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
core = end + 1;
|
|
|
|
|
|
} else if (ffStrStartsWith(p, "Version ")) {
|
|
|
|
|
|
// $KSH_VERSION / ${.sh.version}: Version <features> <version> <date>
|
|
|
|
|
|
core = p + strlen("Version ");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
while (*core && isspace((unsigned char) *core))
|
|
|
|
|
|
++core;
|
|
|
|
|
|
|
|
|
|
|
|
const char* token = core;
|
|
|
|
|
|
while (*token) {
|
|
|
|
|
|
while (*token && isspace((unsigned char) *token))
|
|
|
|
|
|
++token;
|
|
|
|
|
|
if (!*token)
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
const char* end = token;
|
|
|
|
|
|
while (*end && !isspace((unsigned char) *end))
|
|
|
|
|
|
++end;
|
|
|
|
|
|
uint32_t length = (uint32_t) (end - token);
|
|
|
|
|
|
|
|
|
|
|
|
bool valid = false;
|
|
|
|
|
|
if (length >= 2 && token[0] == '9' && token[1] == '3') {
|
|
|
|
|
|
valid = true;
|
|
|
|
|
|
for (uint32_t i = 2; i < length; ++i) {
|
|
|
|
|
|
char c = token[i];
|
|
|
|
|
|
if (!(ffCharIsDigit(c) || ffCharIsEnglishAlphabet(c) || c == '+' || c == '.' || c == '_' || c == '/' || c == '-')) {
|
|
|
|
|
|
valid = false;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (length >= 5 && token[0] == 'M' && token[1] == '-') {
|
|
|
|
|
|
for (uint32_t i = 2; i + 2 < length; ++i) {
|
|
|
|
|
|
if (token[i] == '/' && token[i + 1] == '9' && token[i + 2] == '3') {
|
|
|
|
|
|
valid = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (length > 0 && ffCharIsDigit(token[0]) && (memchr(token, '.', length) || memchr(token, '-', length))) {
|
|
|
|
|
|
valid = true;
|
|
|
|
|
|
} else if (length > 1 && token[0] == 'v' && ffCharIsDigit(token[1])) {
|
|
|
|
|
|
valid = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (valid) {
|
|
|
|
|
|
uint32_t resultLength = length;
|
|
|
|
|
|
bool date = length >= 10 &&
|
|
|
|
|
|
ffCharIsDigit(token[0]) && ffCharIsDigit(token[1]) &&
|
|
|
|
|
|
ffCharIsDigit(token[2]) && ffCharIsDigit(token[3]) && token[4] == '-' &&
|
|
|
|
|
|
ffCharIsDigit(token[5]) && ffCharIsDigit(token[6]) && token[7] == '-' &&
|
|
|
|
|
|
ffCharIsDigit(token[8]) && ffCharIsDigit(token[9]);
|
|
|
|
|
|
if (date) {
|
|
|
|
|
|
const char* next = end;
|
|
|
|
|
|
while (*next && isspace((unsigned char) *next))
|
|
|
|
|
|
++next;
|
|
|
|
|
|
const char* nextEnd = next;
|
|
|
|
|
|
while (*nextEnd && !isspace((unsigned char) *nextEnd))
|
|
|
|
|
|
++nextEnd;
|
|
|
|
|
|
uint32_t nextLength = (uint32_t) (nextEnd - next);
|
|
|
|
|
|
if (nextLength > 0 && nextLength <= 2) {
|
|
|
|
|
|
bool suffix = true;
|
|
|
|
|
|
for (uint32_t i = 0; i < nextLength; ++i) {
|
|
|
|
|
|
if (!ffCharIsEnglishAlphabet(next[i]) && next[i] != '+') {
|
|
|
|
|
|
suffix = false;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (suffix)
|
|
|
|
|
|
resultLength = (uint32_t) (nextEnd - token);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char* start = token;
|
|
|
|
|
|
if (resultLength > 1 && start[0] == 'v' && ffCharIsDigit(start[1])) {
|
|
|
|
|
|
++start;
|
|
|
|
|
|
--resultLength;
|
|
|
|
|
|
}
|
|
|
|
|
|
ffStrbufSetNS(version, resultLength, start);
|
|
|
|
|
|
return version->length > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
token = end;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ffStrbufClear(version);
|
2025-12-01 10:04:17 +08:00
|
|
|
|
return false;
|
2023-07-20 16:11:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getShellVersionOksh(FFstrbuf* exe, FFstrbuf* version) {
|
2025-12-01 10:04:17 +08:00
|
|
|
|
// Homebrew version
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdOut(version, (char* const[]) { exe->chars, "-c", "echo $OKSH_VERSION", nullptr }) != nullptr) {
|
2023-07-20 16:11:02 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-07-20 16:11:02 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
// oksh 7.3
|
2023-07-20 16:11:02 +08:00
|
|
|
|
ffStrbufSubstrAfterFirstC(version, ' ');
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getShellVersionOils(FFstrbuf* exe, FFstrbuf* version) {
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdOut(version, (char* const[]) { exe->chars, "--version", nullptr }) != nullptr) {
|
2023-11-15 16:30:46 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-11-15 16:30:46 +08:00
|
|
|
|
|
|
|
|
|
|
// Oils 0.18.0 https://www.oilshell.org/...
|
|
|
|
|
|
ffStrbufSubstrAfterFirstC(version, ' ');
|
|
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, '\t');
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getShellVersionNushell(FFstrbuf* exe, FFstrbuf* version) {
|
2023-09-27 20:05:55 +08:00
|
|
|
|
ffStrbufSetS(version, getenv("NU_VERSION"));
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (version->length) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return getExeVersionRaw(exe, version); // 0.73.0
|
2023-09-27 20:05:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-02 17:44:55 +08:00
|
|
|
|
static bool extractBusyboxVersion(const char* line, uint32_t len, void* userdata) {
|
|
|
|
|
|
if (!ffStrStartsWith(line, "BusyBox v")) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 18:29:10 +08:00
|
|
|
|
line += strlen("BusyBox v");
|
|
|
|
|
|
len -= (uint32_t) strlen("BusyBox v");
|
|
|
|
|
|
const char* space = memchr(line, ' ', len);
|
|
|
|
|
|
if (space) {
|
|
|
|
|
|
len = (uint32_t) (space - line);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ffStrbufSetNS((FFstrbuf*) userdata, len, line);
|
2026-06-02 17:44:55 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getShellVersionAsh(FFstrbuf* exe, FFstrbuf* version) {
|
2026-06-02 17:44:55 +08:00
|
|
|
|
ffBinaryExtractStrings(exe->chars, extractBusyboxVersion, version, (uint32_t) strlen("BusyBox v0.0.0"));
|
2026-08-26 08:50:08 +08:00
|
|
|
|
if (version->length > 0) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2026-06-02 17:44:55 +08:00
|
|
|
|
|
2026-04-17 18:28:25 +08:00
|
|
|
|
const char* error = ffStrbufEndsWithS(exe, "busybox")
|
2026-07-10 18:08:04 +08:00
|
|
|
|
? ffProcessAppendStdErr(version, (char* const[]) { exe->chars, "ash", "--help", nullptr })
|
|
|
|
|
|
: ffProcessAppendStdErr(version, (char* const[]) { exe->chars, "--help", nullptr });
|
|
|
|
|
|
if (error != nullptr) {
|
2024-01-13 23:15:47 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-01-13 23:15:47 +08:00
|
|
|
|
|
|
|
|
|
|
// BusyBox v1.36.1 (2023-11-07 18:53:09 UTC) multi-call binary...
|
|
|
|
|
|
ffStrbufSubstrAfterFirstC(version, ' ');
|
|
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, ' ');
|
|
|
|
|
|
ffStrbufTrimLeft(version, 'v');
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
static bool getShellVersionXonsh([[maybe_unused]] FFstrbuf* exe, FFstrbuf* version) {
|
2024-01-18 15:27:31 +08:00
|
|
|
|
ffStrbufSetS(version, getenv("XONSH_VERSION"));
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (version->length) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-01-18 15:27:31 +08:00
|
|
|
|
|
2026-01-09 16:12:56 +08:00
|
|
|
|
// exe is python here
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdErr(version, (char* const[]) { "xonsh", "--version", nullptr }) != nullptr) {
|
2024-01-18 15:27:31 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-01-18 15:27:31 +08:00
|
|
|
|
|
|
|
|
|
|
// xonsh/0.14.1
|
|
|
|
|
|
ffStrbufSubstrAfterFirstC(version, '/');
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
static bool extractZshVersion(const char* line, [[maybe_unused]] uint32_t len, void* userdata) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (!ffStrStartsWith(line, "zsh-")) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-08-27 14:06:45 +08:00
|
|
|
|
const char* start = line + strlen("zsh-");
|
|
|
|
|
|
const char* end = strchr(start, '-');
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (!end) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-08-27 14:06:45 +08:00
|
|
|
|
|
|
|
|
|
|
ffStrbufSetNS((FFstrbuf*) userdata, (uint32_t) (end - start), start);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getShellVersionZsh(FFstrbuf* exe, FFstrbuf* version) {
|
2025-11-25 09:09:58 +08:00
|
|
|
|
ffBinaryExtractStrings(exe->chars, extractZshVersion, version, (uint32_t) strlen("zsh-0.0-0"));
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (version->length) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-08-27 14:06:45 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
return getExeVersionGeneral(exe, version); // zsh 5.9 (arm-apple-darwin21.3.0)
|
2024-08-27 14:06:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-10-15 21:44:28 +08:00
|
|
|
|
#ifdef _WIN32
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getShellVersionWinPowerShell(FFstrbuf* exe, FFstrbuf* version) {
|
2024-12-19 11:00:16 +08:00
|
|
|
|
const char* env = getenv("POWERSHELL_VERSION");
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (env) {
|
2024-12-19 11:00:16 +08:00
|
|
|
|
ffStrbufSetS(version, env);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 18:08:04 +08:00
|
|
|
|
FF_AUTO_CLOSE_FD HANDLE hKey = nullptr;
|
|
|
|
|
|
if (ffRegOpenSubkeyForRead(ffRegGetRootKeyHandle(HKEY_LOCAL_MACHINE), L"SOFTWARE\\Microsoft\\PowerShell\\3\\PowerShellEngine", &hKey, nullptr) && ffRegReadStrbuf(hKey, L"PowerShellVersion", version, nullptr)) {
|
2026-07-03 11:03:55 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Extremely slow
|
2026-07-10 18:08:04 +08:00
|
|
|
|
return ffProcessAppendStdOut(version, (char* const[]) { exe->chars, "-NoLogo", "-NoProfile", "-Command", "$PSVersionTable.PSVersion.ToString()", nullptr }) == nullptr;
|
2022-10-15 21:44:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version) {
|
|
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "sh")) { // #849
|
2024-05-03 08:36:06 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-05-03 08:36:06 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "bash")) {
|
2025-11-25 09:09:58 +08:00
|
|
|
|
return getShellVersionBash(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "zsh")) {
|
2025-11-25 09:09:58 +08:00
|
|
|
|
return getShellVersionZsh(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "fish")) {
|
2023-01-11 16:34:37 +08:00
|
|
|
|
return getShellVersionFish(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "pwsh")) {
|
2023-01-11 16:34:37 +08:00
|
|
|
|
return getShellVersionPwsh(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "csh") || ffStrEqualsIgnCase(exeName, "tcsh")) {
|
|
|
|
|
|
return getExeVersionGeneral(exe, version); // tcsh 6.24.07 (Astron) 2022-12-21 (aarch64-apple-darwin) options wide,nls,dl,al,kan,sm,rh,color,filec
|
|
|
|
|
|
}
|
|
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "nu")) {
|
2023-09-27 20:05:55 +08:00
|
|
|
|
return getShellVersionNushell(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2026-09-25 11:33:46 +08:00
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "ksh") || ffStrEqualsIgnCase(exeName, "ksh93") || ffStrEqualsIgnCase(exeName, "mksh") || ffStrEqualsIgnCase(exeName, "pdksh")) {
|
2023-07-20 16:11:02 +08:00
|
|
|
|
return getShellVersionKsh(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "oksh")) {
|
2023-07-20 16:11:02 +08:00
|
|
|
|
return getShellVersionOksh(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "oil.ovm")) {
|
2023-11-15 16:30:46 +08:00
|
|
|
|
return getShellVersionOils(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "elvish")) {
|
2023-11-15 16:30:46 +08:00
|
|
|
|
return getExeVersionRaw(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2026-04-17 18:28:25 +08:00
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "ash") || ffStrEqualsIgnCase(exeName, "busybox")) {
|
2024-01-13 23:15:47 +08:00
|
|
|
|
return getShellVersionAsh(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "xonsh")) {
|
2024-01-18 15:27:31 +08:00
|
|
|
|
return getShellVersionXonsh(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "brush")) {
|
2025-10-19 09:42:36 +08:00
|
|
|
|
return getExeVersionGeneral(exe, version); // brush 0.2.23 (git:2835487)
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2022-10-15 21:44:28 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
if (ffStrEqualsIgnCase(exeName, "powershell") || ffStrEqualsIgnCase(exeName, "powershell_ise")) {
|
2023-01-11 16:34:37 +08:00
|
|
|
|
return getShellVersionWinPowerShell(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2022-10-15 21:44:28 +08:00
|
|
|
|
|
2026-07-10 18:08:04 +08:00
|
|
|
|
return getFileVersion(exe, nullptr, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#endif
|
2024-08-03 08:17:14 +08:00
|
|
|
|
|
|
|
|
|
|
return false;
|
2022-10-14 16:28:03 +08:00
|
|
|
|
}
|
2023-01-10 19:24:59 +08:00
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionTermux(FFstrbuf* version) {
|
2023-01-11 15:03:16 +08:00
|
|
|
|
ffStrbufSetS(version, getenv("TERMUX_VERSION"));
|
2023-01-11 16:34:37 +08:00
|
|
|
|
return version->length > 0;
|
2023-01-11 15:03:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
static bool extractGeneralVersion(const char* str, [[maybe_unused]] uint32_t len, void* userdata) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (!ffCharIsDigit(str[0])) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-08-17 14:06:34 +08:00
|
|
|
|
int count = 0;
|
|
|
|
|
|
sscanf(str, "%*d.%*d.%*d%n", &count);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (count == 0) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-08-17 14:06:34 +08:00
|
|
|
|
ffStrbufSetS((FFstrbuf*) userdata, str);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionGnome(FFstrbuf* exe, FFstrbuf* version) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffIsAbsolutePath(exe->chars)) {
|
2024-08-29 16:40:33 +08:00
|
|
|
|
ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0"));
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (version->length) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-08-17 14:06:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdOut(version, (char* const[]) { "gnome-terminal", "--version", nullptr })) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2023-01-11 15:03:42 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
// # GNOME Terminal 3.46.7 using VTE 0.70.2 +BIDI +GNUTLS +ICU +SYSTEMD
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstS(version, "Terminal "))
|
|
|
|
|
|
return false;
|
2023-01-11 15:03:42 +08:00
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, ' ');
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return version->length > 0;
|
2023-10-08 09:24:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionXfce4Terminal(FFstrbuf* exe, FFstrbuf* version) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffIsAbsolutePath(exe->chars)) {
|
2024-08-29 16:40:33 +08:00
|
|
|
|
ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0"));
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (version->length) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-08-29 16:40:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
return getExeVersionGeneral(exe, version); // xfce4-terminal 1.0.4 (Xfce 4.18)...
|
2024-08-29 16:40:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionKgx(FFstrbuf* version) {
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdOut(version, (char* const[]) { "kgx", "--version", nullptr })) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2023-10-08 09:24:13 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
// # KGX 45.0 using VTE 0.74.0 +BIDI +GNUTLS +ICU +SYSTEMD
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstS(version, "KGX "))
|
|
|
|
|
|
return false;
|
2023-10-08 09:24:13 +08:00
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, ' ');
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return version->length > 0;
|
2023-01-11 15:03:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionKonsole(FFstrbuf* exe, FFstrbuf* version) {
|
2023-01-11 15:56:24 +08:00
|
|
|
|
const char* konsoleVersion = getenv("KONSOLE_VERSION");
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (konsoleVersion) {
|
|
|
|
|
|
// 221201
|
2026-09-25 11:34:13 +08:00
|
|
|
|
char* end;
|
|
|
|
|
|
long major = strtol(konsoleVersion, &end, 10);
|
|
|
|
|
|
if (end != konsoleVersion && *end == '\0' && major >= 0) {
|
2023-01-11 15:56:24 +08:00
|
|
|
|
long patch = major % 100;
|
|
|
|
|
|
major /= 100;
|
|
|
|
|
|
long minor = major % 100;
|
|
|
|
|
|
major /= 100;
|
|
|
|
|
|
ffStrbufSetF(version, "%ld.%ld.%ld", major, minor, patch);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-11 08:51:41 +08:00
|
|
|
|
// Likely TDE konsole. See #2319
|
|
|
|
|
|
if (!getExeVersionRaw(exe, version)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return ffStrbufSubstrAfterLastC(version, ' ');
|
2023-01-11 15:14:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionFoot(FFstrbuf* exe, FFstrbuf* version) {
|
2024-09-20 15:33:30 +08:00
|
|
|
|
uint32_t major = 0, minor = 0, patch = 0;
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffGetTerminalResponse("\e[>c", 3, "\e[>1;%2u%2u%2u;0c", &major, &minor, &patch) == nullptr) {
|
2024-09-20 15:33:30 +08:00
|
|
|
|
ffStrbufSetF(version, "%u.%u.%u", major, minor, patch);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (!getExeVersionRaw(exe, version)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2023-03-09 11:08:02 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
// foot version: 1.13.1 -pgo +ime -graphemes -assertions
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstS(version, "version: "))
|
|
|
|
|
|
return false;
|
2023-03-09 11:08:02 +08:00
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, ' ');
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return version->length > 0;
|
2023-03-09 11:08:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionMateTerminal(FFstrbuf* exe, FFstrbuf* version) {
|
2024-09-07 15:23:42 +08:00
|
|
|
|
ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0"));
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (version->length > 0) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-09-07 15:23:42 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (!getExeVersionRaw(exe, version)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2023-06-11 13:28:02 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
// MATE Terminal 1.26.1
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterLastC(version, ' '))
|
|
|
|
|
|
return false;
|
2023-06-11 13:28:02 +08:00
|
|
|
|
return version->length > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionCockpit(FFstrbuf* exe, FFstrbuf* version) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (!getExeVersionRaw(exe, version)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2023-07-07 09:40:04 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
// Version: 295\n...
|
2023-07-07 09:40:04 +08:00
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, '\n');
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstC(version, ' '))
|
|
|
|
|
|
return false;
|
2023-07-07 09:40:04 +08:00
|
|
|
|
return version->length > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionXterm(FFstrbuf* exe, FFstrbuf* version) {
|
2024-07-15 10:05:35 +08:00
|
|
|
|
ffStrbufSetS(version, getenv("XTERM_VERSION"));
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (!version->length) {
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdOut(version, (char* const[]) { exe->chars, "-v", nullptr })) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2024-07-15 10:05:35 +08:00
|
|
|
|
}
|
2023-07-09 22:15:03 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
// xterm(273)
|
2023-07-09 22:15:03 +08:00
|
|
|
|
ffStrbufTrimRight(version, ')');
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstC(version, '('))
|
|
|
|
|
|
return false;
|
2023-07-09 22:15:03 +08:00
|
|
|
|
return version->length > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionBlackbox(FFstrbuf* exe, FFstrbuf* version) {
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdOut(version, (char* const[]) { exe->chars, "--version", nullptr })) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2024-02-01 09:02:36 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
// BlackBox version 0.14.0 (flatpak)
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstS(version, "version "))
|
|
|
|
|
|
return false;
|
2024-02-01 09:14:06 +08:00
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, ' ');
|
2024-02-01 09:02:36 +08:00
|
|
|
|
return version->length > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionUrxvt([[maybe_unused]] FFstrbuf* exe, FFstrbuf* version) {
|
2026-03-30 10:27:23 +08:00
|
|
|
|
if (ffProcessAppendStdErr(version, (char* const[]) { "urxvt", // Don't use exe because of urxvtd
|
2026-03-29 09:28:49 +08:00
|
|
|
|
"-invalid",
|
2026-07-10 18:08:04 +08:00
|
|
|
|
nullptr })) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2023-10-24 15:47:53 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
// urxvt: "invalid": unknown or malformed option.
|
|
|
|
|
|
// rxvt-unicode (urxvt) v9.31 - released: 2023-01-02
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstS(version, "(urxvt) v"))
|
|
|
|
|
|
return false;
|
2023-10-24 15:47:53 +08:00
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, ' ');
|
|
|
|
|
|
|
|
|
|
|
|
return version->length > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionSt([[maybe_unused]] FFstrbuf* exe, FFstrbuf* version) {
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdErr(version, (char* const[]) { exe->chars, "-v", nullptr })) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2023-10-24 15:57:40 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
// st 0.9
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstC(version, ' '))
|
|
|
|
|
|
return false;
|
2023-10-24 15:57:40 +08:00
|
|
|
|
|
|
|
|
|
|
return version->length > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionLxterminal(FFstrbuf* exe, FFstrbuf* version) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (!getExeVersionRaw(exe, version)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2024-03-26 08:42:48 +00:00
|
|
|
|
// lxterminal 0.3.2
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstC(version, ' '))
|
|
|
|
|
|
return false;
|
2024-03-26 08:42:48 +00:00
|
|
|
|
return version->length > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionWeston([[maybe_unused]] FFstrbuf* exe, FFstrbuf* version) {
|
2024-03-28 03:12:46 +00:00
|
|
|
|
// weston-terminal doesn't report a version, use weston version instead
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdOut(version, (char* const[]) { "weston", "--version", nullptr })) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2024-03-28 03:12:46 +00:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
// weston 8.0.0
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstC(version, ' '))
|
|
|
|
|
|
return false;
|
2024-03-28 03:12:46 +00:00
|
|
|
|
|
|
|
|
|
|
return version->length > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool extractKmsconVersion(const char* str, [[maybe_unused]] uint32_t len, void* userdata) {
|
2026-06-11 09:20:52 +08:00
|
|
|
|
if (!ffStrStartsWith(str, "v")) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
sscanf(str, "v%*d.%*d.%*d%n", &count);
|
|
|
|
|
|
if (count == 0) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
ffStrbufSetNS((FFstrbuf*) userdata, (uint32_t) count - 1, str + 1);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionKmscon(FFstrbuf* exe, FFstrbuf* version) {
|
2026-06-11 09:20:52 +08:00
|
|
|
|
if (ffIsAbsolutePath(exe->chars)) {
|
|
|
|
|
|
ffBinaryExtractStrings(exe->chars, extractKmsconVersion, version, (uint32_t) strlen("v0.0.0"));
|
|
|
|
|
|
if (version->length) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!getExeVersionRaw(exe, version)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// kmscon version v10.0.0
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterLastC(version, ' '))
|
|
|
|
|
|
return false;
|
2026-06-11 09:20:52 +08:00
|
|
|
|
return version->length > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getTerminalVersionContour(FFstrbuf* exe, FFstrbuf* version) {
|
2023-09-02 07:40:23 -04:00
|
|
|
|
const char* env = getenv("TERMINAL_VERSION_STRING");
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (env && *env) {
|
2023-09-02 07:40:23 -04:00
|
|
|
|
ffStrbufAppendS(version, env);
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return version->length > 0;
|
2023-09-02 07:40:23 -04:00
|
|
|
|
}
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (!getExeVersionRaw(exe, version)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2023-09-02 07:40:23 -04:00
|
|
|
|
// Contour Terminal Emulator 0.3.12.262
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterLastC(version, ' '))
|
|
|
|
|
|
return false;
|
2023-09-02 07:40:23 -04:00
|
|
|
|
return version->length > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getTerminalVersionScreen(FFstrbuf* exe, FFstrbuf* version) {
|
|
|
|
|
|
if (!getExeVersionRaw(exe, version)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2024-01-22 14:33:55 +08:00
|
|
|
|
// Screen version 4.09.01 (GNU) 20-Aug-23
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstS(version, "Screen version "))
|
|
|
|
|
|
return false;
|
2024-01-22 14:33:55 +08:00
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, ' ');
|
|
|
|
|
|
return version->length > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getTerminalVersionTmux(FFstrbuf* exe, FFstrbuf* version) {
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdOut(version, (char* const[]) { exe->chars, "-V", nullptr }) != nullptr) {
|
2024-02-26 23:33:30 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-02-26 23:33:30 +08:00
|
|
|
|
|
|
|
|
|
|
// tmux 3.4
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstC(version, ' '))
|
|
|
|
|
|
return false;
|
2024-02-26 23:33:30 +08:00
|
|
|
|
return version->length > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getTerminalVersionZellij(FFstrbuf* exe, FFstrbuf* version) {
|
|
|
|
|
|
if (!getExeVersionRaw(exe, version)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2024-03-02 18:32:31 +08:00
|
|
|
|
|
|
|
|
|
|
// zellij 0.39.2
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstC(version, ' '))
|
|
|
|
|
|
return false;
|
2024-03-02 18:32:31 +08:00
|
|
|
|
return version->length > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getTerminalVersionZed(FFstrbuf* exe, FFstrbuf* version) {
|
2024-07-16 14:53:14 +08:00
|
|
|
|
FF_STRBUF_AUTO_DESTROY cli = ffStrbufCreateCopy(exe);
|
|
|
|
|
|
ffStrbufSubstrBeforeLastC(&cli, '/');
|
|
|
|
|
|
ffStrbufAppendS(&cli, "/cli"
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
".exe"
|
|
|
|
|
|
#endif
|
2024-07-16 14:53:14 +08:00
|
|
|
|
);
|
|
|
|
|
|
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdOut(version, (char* const[]) { cli.chars, "--version", nullptr }) != nullptr) {
|
2024-07-16 14:53:14 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-07-16 14:53:14 +08:00
|
|
|
|
|
|
|
|
|
|
// Zed 0.142.6 – /Applications/Zed.app
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufStartsWithS(version, "Zed "))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
ffStrbufSubstrAfter(version, (uint32_t) strlen("Zed ") - 1);
|
2024-07-16 14:53:14 +08:00
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, ' ');
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return version->length > 0;
|
2024-07-16 14:53:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
static bool extractSshdVersion(const char* str, [[maybe_unused]] uint32_t len, void* userdata) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (!ffStrStartsWith(str, "OpenSSH_") || !ffCharIsDigit(str[strlen("OpenSSH_")])) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2025-11-25 10:33:57 +08:00
|
|
|
|
str += strlen("OpenSSH_");
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
sscanf(str, "%*d.%*dp%*d%n", &count);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (count == 0) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2025-11-25 10:33:57 +08:00
|
|
|
|
ffStrbufSetS((FFstrbuf*) userdata, str);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getTerminalVersionSshd(FFstrbuf* exe, FFstrbuf* version) {
|
2025-11-25 10:33:57 +08:00
|
|
|
|
FF_STRBUF_AUTO_DESTROY exePath = ffStrbufCreate();
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffIsAbsolutePath(exe->chars)) {
|
2025-11-25 10:33:57 +08:00
|
|
|
|
ffStrbufSet(&exePath, exe);
|
2026-07-10 18:08:04 +08:00
|
|
|
|
} else if (ffFindExecutableInPath("sshd", &exePath) != nullptr) {
|
2025-11-25 10:33:57 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2025-11-25 10:33:57 +08:00
|
|
|
|
|
|
|
|
|
|
ffBinaryExtractStrings(exePath.chars, extractSshdVersion, version, (uint32_t) strlen("OpenSSH0.0"));
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (version->length) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2025-11-25 10:33:57 +08:00
|
|
|
|
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (ffProcessAppendStdErr(version, (char* const[]) { exePath.chars, "-V", nullptr }) != nullptr) {
|
2025-11-25 10:33:57 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2025-11-25 10:33:57 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufStartsWithS(version, "unknown ")) { // `unknown option -- V` (ancient OpenSSH version)
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstC(version, '\n'))
|
|
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2025-11-25 10:33:57 +08:00
|
|
|
|
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufStartsWithS(version, "OpenSSH_"))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
ffStrbufSubstrAfter(version, (uint32_t) strlen("OpenSSH_") - 1);
|
|
|
|
|
|
|
2025-11-25 10:33:57 +08:00
|
|
|
|
// OpenSSH_10.0p2 Ubuntu-5ubuntu5, OpenSSL 3.5.3 16 Sep 2025
|
|
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, ',');
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return version->length > 0;
|
2025-11-25 10:33:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-17 23:18:32 +08:00
|
|
|
|
#ifndef _WIN32
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getTerminalVersionKitty(FFstrbuf* exe, FFstrbuf* version) {
|
2026-04-15 16:16:46 +08:00
|
|
|
|
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__GNU__)
|
2024-08-08 14:58:56 +08:00
|
|
|
|
char buffer[1024] = {};
|
|
|
|
|
|
if (
|
2026-04-15 16:16:46 +08:00
|
|
|
|
#if __linux__ || __GNU__
|
2024-10-18 16:15:48 +08:00
|
|
|
|
ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib64/kitty/kitty/constants.py", ARRAY_SIZE(buffer) - 1, buffer) ||
|
|
|
|
|
|
ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib/kitty/kitty/constants.py", ARRAY_SIZE(buffer) - 1, buffer)
|
2026-04-15 16:16:46 +08:00
|
|
|
|
#else
|
2026-08-26 23:12:25 +08:00
|
|
|
|
ffReadFileData(FF_PATH_PKG_BASE "/share/kitty/kitty/constants.py", ARRAY_SIZE(buffer) - 1, buffer)
|
2026-04-15 16:16:46 +08:00
|
|
|
|
#endif
|
2026-03-29 09:28:49 +08:00
|
|
|
|
) {
|
2024-08-08 14:58:56 +08:00
|
|
|
|
// Starts from version 0.17.0
|
|
|
|
|
|
// https://github.com/kovidgoyal/kitty/blob/master/kitty/constants.py#L25
|
2024-10-18 16:15:48 +08:00
|
|
|
|
const char* p = memmem(buffer, ARRAY_SIZE(buffer) - 1, "version: Version = Version(", strlen("version: Version = Version("));
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (p) {
|
2024-08-08 14:58:56 +08:00
|
|
|
|
p += strlen("version: Version = Version(");
|
|
|
|
|
|
int major, minor, patch;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (sscanf(p, "%d,%d,%d", &major, &minor, &patch) == 3) {
|
2024-08-08 14:58:56 +08:00
|
|
|
|
ffStrbufSetF(version, "%d.%d.%d", major, minor, patch);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-04-15 16:16:46 +08:00
|
|
|
|
#elif __APPLE__
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (ffGetAppNameAndVersion(exe->chars, nullptr, version) && version->length > 0) {
|
2026-05-22 13:37:48 +08:00
|
|
|
|
return true;
|
2025-01-02 14:37:10 +08:00
|
|
|
|
}
|
2026-04-15 16:16:46 +08:00
|
|
|
|
#endif
|
2024-08-08 14:58:56 +08:00
|
|
|
|
|
2024-07-22 09:22:01 +08:00
|
|
|
|
char versionHex[64];
|
2024-06-22 15:42:56 +08:00
|
|
|
|
// https://github.com/fastfetch-cli/fastfetch/discussions/1030#discussioncomment-9845233
|
|
|
|
|
|
if (ffGetTerminalResponse(
|
2026-03-29 09:28:49 +08:00
|
|
|
|
"\eP+q6b697474792d71756572792d76657273696f6e\e\\", // kitty-query-version
|
|
|
|
|
|
1,
|
|
|
|
|
|
"\eP1+r%*[^=]=%63[^\e]\e\\\\",
|
2026-07-10 18:08:04 +08:00
|
|
|
|
versionHex) == nullptr) {
|
2024-06-22 15:42:56 +08:00
|
|
|
|
// decode hex string
|
2026-03-29 09:28:49 +08:00
|
|
|
|
for (const char* p = versionHex; p[0] && p[1]; p += 2) {
|
2024-06-22 15:42:56 +08:00
|
|
|
|
unsigned value;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (sscanf(p, "%2x", &value) == 1) {
|
2024-06-22 15:42:56 +08:00
|
|
|
|
ffStrbufAppendC(version, (char) value);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-04-17 20:28:13 +08:00
|
|
|
|
}
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (version->length > 0)
|
|
|
|
|
|
return true;
|
2024-04-17 20:28:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
// kitty 0.21.2 created by Kovid Goyal
|
2024-04-17 20:28:13 +08:00
|
|
|
|
return getExeVersionGeneral(exe, version);
|
|
|
|
|
|
}
|
2024-07-01 23:56:19 +08:00
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionPtyxis([[maybe_unused]] FFstrbuf* exe, FFstrbuf* version) {
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdOut(version, (char* const[]) { "ptyxis", "--version", nullptr }) != nullptr) {
|
2024-07-01 23:56:19 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-07-01 23:56:19 +08:00
|
|
|
|
|
|
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, '\n');
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterFirstC(version, ' '))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
return version->length > 0;
|
2024-07-01 23:56:19 +08:00
|
|
|
|
}
|
2024-11-15 16:18:43 +08:00
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionTilix(FFstrbuf* exe, FFstrbuf* version) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffIsAbsolutePath(exe->chars)) {
|
2025-02-10 09:14:24 +08:00
|
|
|
|
ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0"));
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (version->length) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2025-02-10 09:14:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdOut(version, (char* const[]) { exe->chars, "--version", nullptr }) != nullptr) {
|
2024-11-15 16:18:43 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-11-15 16:18:43 +08:00
|
|
|
|
|
|
|
|
|
|
uint32_t index = ffStrbufFirstIndexS(version, "Tilix version: ");
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (index == version->length) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2024-11-15 16:18:43 +08:00
|
|
|
|
|
|
|
|
|
|
index += (uint32_t) strlen("Tilix version:");
|
|
|
|
|
|
uint32_t end = ffStrbufNextIndexC(version, index, '\n');
|
|
|
|
|
|
|
|
|
|
|
|
ffStrbufSubstrBefore(version, end);
|
|
|
|
|
|
ffStrbufSubstrAfter(version, index);
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return version->length > 0;
|
2024-11-15 16:18:43 +08:00
|
|
|
|
}
|
2024-12-02 19:09:27 +08:00
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionSakura(FFstrbuf* exe, FFstrbuf* version) {
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdErr(version, (char* const[]) { exe->chars, "--version", nullptr }) != nullptr) { // sakura version is 3.8.8
|
2024-12-02 19:09:27 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-12-02 19:09:27 +08:00
|
|
|
|
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterLastC(version, ' '))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
return version->length > 0;
|
2024-12-02 19:09:27 +08:00
|
|
|
|
}
|
2025-02-26 09:28:18 +08:00
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
[[maybe_unused]] static bool getTerminalVersionTermite(FFstrbuf* exe, FFstrbuf* version) {
|
2026-07-10 18:08:04 +08:00
|
|
|
|
if (ffProcessAppendStdOut(version, (char* const[]) { exe->chars, "--version", nullptr }) != nullptr) { // termite v16.9\nvte 0.78.1 +BIDI +GNUTLS +ICU +SYSTEMD
|
2025-02-26 09:28:18 +08:00
|
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2025-02-26 09:28:18 +08:00
|
|
|
|
|
|
|
|
|
|
ffStrbufSubstrBeforeFirstC(version, '\n');
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (!ffStrbufSubstrAfterLastC(version, 'v'))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
return version->length > 0;
|
2025-02-26 09:28:18 +08:00
|
|
|
|
}
|
2024-04-17 23:18:32 +08:00
|
|
|
|
#endif
|
2024-04-17 20:28:13 +08:00
|
|
|
|
|
2023-01-11 22:01:37 +08:00
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getTerminalVersionWindowsTerminal(FFstrbuf* exe, FFstrbuf* version) {
|
2023-01-11 22:01:37 +08:00
|
|
|
|
FF_STRBUF_AUTO_DESTROY buildInfoPath;
|
|
|
|
|
|
ffStrbufInitNS(&buildInfoPath, ffStrbufLastIndexC(exe, '\\') + 1, exe->chars);
|
|
|
|
|
|
ffStrbufAppendS(&buildInfoPath, "BuildInfo.xml");
|
|
|
|
|
|
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (ffParsePropFile(buildInfoPath.chars, "StoreVersion=\"", version) && version->length > 0) {
|
2023-01-11 22:01:37 +08:00
|
|
|
|
ffStrbufTrimRight(version, '"');
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return version->length > 0;
|
2023-01-11 22:01:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return getFileVersion(exe, nullptr, version) && version->length > 0;
|
2023-01-11 22:01:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
static bool getTerminalVersionConEmu(FFstrbuf* exe, FFstrbuf* version) {
|
2023-01-11 22:14:47 +08:00
|
|
|
|
ffStrbufSetS(version, getenv("ConEmuBuild"));
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (version->length) {
|
2023-01-11 22:14:47 +08:00
|
|
|
|
return true;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-01-11 22:14:47 +08:00
|
|
|
|
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return getFileVersion(exe, nullptr, version) && version->length > 0;
|
2023-01-11 22:14:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 22:01:37 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
|
2026-07-10 21:13:03 +08:00
|
|
|
|
bool fftsGetTerminalVersion(FFstrbuf* processName, [[maybe_unused]] FFstrbuf* exe, FFstrbuf* version) {
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#ifdef __ANDROID__
|
2023-01-10 21:11:44 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufEqualS(processName, "com.termux")) {
|
2023-01-11 15:03:16 +08:00
|
|
|
|
return getTerminalVersionTermux(version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-01-10 21:11:44 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#endif
|
2023-01-10 21:11:44 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun) || defined(__NetBSD__) || defined(__HAIKU__) || defined(__GNU__)
|
2023-01-11 15:03:42 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufStartsWithIgnCaseS(processName, "gnome-terminal")) {
|
2024-08-17 14:06:34 +08:00
|
|
|
|
return getTerminalVersionGnome(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-01-11 15:03:42 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "konsole")) {
|
2023-01-11 15:56:24 +08:00
|
|
|
|
return getTerminalVersionKonsole(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-01-11 15:56:24 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "yakuake")) {
|
2024-09-20 16:30:00 +08:00
|
|
|
|
return getTerminalVersionKonsole(exe, version); // yakuake shares code with konsole
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-07-30 11:32:44 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "xfce4-terminal")) {
|
2024-08-29 16:40:33 +08:00
|
|
|
|
return getTerminalVersionXfce4Terminal(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-01-11 16:07:34 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "terminator")) {
|
|
|
|
|
|
return getExeVersionGeneral(exe, version); // terminator 2.1.3
|
|
|
|
|
|
}
|
2023-07-03 18:19:41 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "deepin-terminal")) {
|
|
|
|
|
|
return getExeVersionGeneral(exe, version); // deepin-terminal 5.4.36
|
|
|
|
|
|
}
|
2023-01-11 18:12:36 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "foot")) {
|
2023-03-09 11:08:02 +08:00
|
|
|
|
return getTerminalVersionFoot(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-03-09 11:08:02 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "qterminal")) {
|
|
|
|
|
|
return getExeVersionRaw(exe, version); // 1.2.0
|
|
|
|
|
|
}
|
2023-06-11 12:19:40 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "mate-terminal")) {
|
2023-06-11 13:28:02 +08:00
|
|
|
|
return getTerminalVersionMateTerminal(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-06-11 13:28:02 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "cockpit-bridge")) {
|
2023-07-07 09:40:04 +08:00
|
|
|
|
return getTerminalVersionCockpit(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-07-07 09:40:04 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "xterm")) {
|
2023-07-09 22:15:03 +08:00
|
|
|
|
return getTerminalVersionXterm(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-07-09 22:15:03 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "blackbox")) {
|
2024-02-01 09:02:36 +08:00
|
|
|
|
return getTerminalVersionBlackbox(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-02-01 09:02:36 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "st")) {
|
2023-10-24 15:57:40 +08:00
|
|
|
|
return getTerminalVersionSt(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-10-24 15:57:40 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "lxterminal")) {
|
2024-03-26 08:42:48 +00:00
|
|
|
|
return getTerminalVersionLxterminal(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-03-26 08:42:48 +00:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "weston-terminal")) {
|
2024-03-28 03:12:46 +00:00
|
|
|
|
return getTerminalVersionWeston(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-03-28 03:12:46 +00:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "urxvt") ||
|
2023-10-24 15:47:53 +08:00
|
|
|
|
ffStrbufIgnCaseEqualS(processName, "urxvtd") ||
|
|
|
|
|
|
ffStrbufIgnCaseEqualS(processName, "rxvt") ||
|
2026-03-29 09:28:49 +08:00
|
|
|
|
ffStrbufIgnCaseEqualS(processName, "rxvt-unicode")) {
|
2023-10-24 15:47:53 +08:00
|
|
|
|
return getTerminalVersionUrxvt(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-10-24 15:47:53 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "ptyxis-agent")) {
|
2024-07-01 23:56:19 +08:00
|
|
|
|
return getTerminalVersionPtyxis(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-07-01 23:56:19 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "tilix")) {
|
2024-11-15 16:18:43 +08:00
|
|
|
|
return getTerminalVersionTilix(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-11-15 16:18:43 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "sakura")) {
|
2024-12-02 19:09:27 +08:00
|
|
|
|
return getTerminalVersionSakura(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-12-02 19:09:27 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "termite")) {
|
2025-02-26 09:28:18 +08:00
|
|
|
|
return getTerminalVersionTermite(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2025-02-26 09:28:18 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "cosmic-term")) {
|
2026-01-01 22:32:32 +08:00
|
|
|
|
return getTerminalVersionTmux(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2026-01-01 22:32:32 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#endif
|
2023-01-11 15:03:42 +08:00
|
|
|
|
|
2026-06-11 09:20:52 +08:00
|
|
|
|
#ifdef __linux__
|
|
|
|
|
|
|
|
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "kmscon")) {
|
|
|
|
|
|
return getTerminalVersionKmscon(exe, version);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#ifdef _WIN32
|
2023-01-11 22:01:37 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "WindowsTerminal.exe")) {
|
2023-01-11 22:01:37 +08:00
|
|
|
|
return getTerminalVersionWindowsTerminal(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-01-11 22:01:37 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufStartsWithIgnCaseS(processName, "ConEmu")) {
|
2023-01-11 22:14:47 +08:00
|
|
|
|
return getTerminalVersionConEmu(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-01-11 22:14:47 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "warp.exe")) {
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return getFileVersion(exe, L"ProductVersion", version) && version->length > 0;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2025-03-06 22:11:36 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#endif
|
2023-01-11 22:01:37 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#ifndef _WIN32
|
2023-01-11 15:14:29 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "kitty")) {
|
2024-04-17 20:28:13 +08:00
|
|
|
|
return getTerminalVersionKitty(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-01-11 15:14:29 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "Tabby") && getExeVersionRaw(exe, version)) {
|
2023-05-27 21:25:32 +08:00
|
|
|
|
return true;
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-05-27 21:25:32 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#endif
|
2023-01-11 15:14:29 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufStartsWithIgnCaseS(processName, "alacritty")) {
|
2023-01-11 23:46:15 +08:00
|
|
|
|
return getExeVersionGeneral(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-01-11 23:46:15 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufStartsWithIgnCaseS(processName, "contour")) {
|
2023-09-02 07:40:23 -04:00
|
|
|
|
return getTerminalVersionContour(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2023-09-02 07:40:23 -04:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufStartsWithIgnCaseS(processName, "screen")) {
|
2024-01-22 14:33:55 +08:00
|
|
|
|
return getTerminalVersionScreen(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-01-22 14:33:55 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufStartsWithIgnCaseS(processName, "zellij")) {
|
2024-03-02 18:32:31 +08:00
|
|
|
|
return getTerminalVersionZellij(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-03-02 18:32:31 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufStartsWithIgnCaseS(processName, "zed")) {
|
2024-07-16 14:53:14 +08:00
|
|
|
|
return getTerminalVersionZed(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-07-16 14:53:14 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#if __HAIKU__
|
|
|
|
|
|
if (ffStrbufEqualS(processName, "Terminal")) {
|
2025-02-17 21:22:12 +08:00
|
|
|
|
return ffGetFileVersion(exe->chars, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
#endif
|
2025-02-17 21:22:12 +08:00
|
|
|
|
|
2023-01-10 19:24:59 +08:00
|
|
|
|
const char* termProgramVersion = getenv("TERM_PROGRAM_VERSION");
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (termProgramVersion && *termProgramVersion) {
|
2023-01-10 19:24:59 +08:00
|
|
|
|
const char* termProgram = getenv("TERM_PROGRAM");
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (termProgram) {
|
|
|
|
|
|
if (ffStrbufStartsWithIgnCaseS(processName, termProgram) || // processName ends with `.exe` on Windows
|
2023-06-19 16:07:23 +08:00
|
|
|
|
(ffStrEquals(termProgram, "vscode") && ffStrbufStartsWithIgnCaseS(processName, "code")) ||
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#ifdef __APPLE__
|
2024-02-27 23:11:38 +08:00
|
|
|
|
(ffStrEquals(termProgram, "iTerm.app") && ffStrbufStartsWithIgnCaseS(processName, "iTermServer-")) ||
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#elif defined(__linux__)
|
2024-02-27 23:11:38 +08:00
|
|
|
|
(ffStrEquals(termProgram, "WarpTerminal") && ffStrbufEqualS(processName, "warp")) ||
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
false) {
|
2023-01-10 19:24:59 +08:00
|
|
|
|
ffStrbufSetS(version, termProgramVersion);
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return version->length > 0;
|
2023-01-10 19:24:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-01 08:55:00 +08:00
|
|
|
|
termProgramVersion = getenv("LC_TERMINAL_VERSION");
|
2026-09-25 11:34:13 +08:00
|
|
|
|
if (termProgramVersion && *termProgramVersion) {
|
2023-11-01 08:55:00 +08:00
|
|
|
|
const char* termProgram = getenv("LC_TERMINAL");
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (termProgram) {
|
|
|
|
|
|
if (ffStrbufStartsWithIgnCaseS(processName, termProgram) || // processName ends with `.exe` on Windows
|
2023-11-01 08:55:00 +08:00
|
|
|
|
(ffStrEquals(termProgram, "vscode") && ffStrbufStartsWithIgnCaseS(processName, "code")) ||
|
2026-03-29 09:28:49 +08:00
|
|
|
|
(ffStrStartsWith(termProgram, "iTerm") && ffStrbufStartsWithIgnCaseS(processName, "iTermServer-"))) {
|
2023-11-01 08:55:00 +08:00
|
|
|
|
ffStrbufSetS(version, termProgramVersion);
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return version->length > 0;
|
2023-11-01 08:55:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufStartsWithIgnCaseS(processName, "tmux")) {
|
2024-02-26 23:33:30 +08:00
|
|
|
|
return getTerminalVersionTmux(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2024-02-26 23:33:30 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
if (ffStrbufIgnCaseEqualS(processName, "sshd") || ffStrbufStartsWithIgnCaseS(processName, "sshd-")) {
|
2025-11-25 10:33:57 +08:00
|
|
|
|
return getTerminalVersionSshd(exe, version);
|
2026-03-29 09:28:49 +08:00
|
|
|
|
}
|
2025-11-25 10:33:57 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#ifdef _WIN32
|
2023-01-10 19:24:59 +08:00
|
|
|
|
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return getFileVersion(exe, nullptr, version) && version->length > 0;
|
2023-01-10 19:24:59 +08:00
|
|
|
|
|
2026-05-22 13:37:48 +08:00
|
|
|
|
#elif __APPLE__
|
|
|
|
|
|
|
2026-09-25 11:34:13 +08:00
|
|
|
|
return ffGetAppNameAndVersion(exe->chars, nullptr, version) && version->length > 0;
|
2026-05-22 13:37:48 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#else
|
2023-01-10 19:24:59 +08:00
|
|
|
|
|
|
|
|
|
|
return false;
|
2023-01-10 21:11:44 +08:00
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
|
#endif
|
2023-01-10 19:24:59 +08:00
|
|
|
|
}
|