Files
fastfetch/src/detection/terminalshell/terminalshell.c
T

735 lines
21 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "fastfetch.h"
#include "common/io/io.h"
#include "common/processing.h"
#include "common/properties.h"
#include "util/stringUtils.h"
#include <ctype.h>
#ifdef __FreeBSD__
#include <paths.h>
#endif
#ifdef _WIN32
#include "util/mallocHelper.h"
#include <winver.h>
static bool getFileVersion(const char* exePath, FFstrbuf* version)
{
DWORD handle;
DWORD size = GetFileVersionInfoSizeA(exePath, &handle);
if(size > 0)
{
FF_AUTO_FREE void* versionData = malloc(size);
if(GetFileVersionInfoA(exePath, handle, size, versionData))
{
VS_FIXEDFILEINFO* verInfo;
UINT len;
if(VerQueryValueW(versionData, L"\\", (void**)&verInfo, &len) && len && verInfo->dwSignature == 0xFEEF04BD)
{
ffStrbufAppendF(version, "%u.%u.%u.%u",
(unsigned)(( verInfo->dwFileVersionMS >> 16 ) & 0xffff),
(unsigned)(( verInfo->dwFileVersionMS >> 0 ) & 0xffff),
(unsigned)(( verInfo->dwFileVersionLS >> 16 ) & 0xffff),
(unsigned)(( verInfo->dwFileVersionLS >> 0 ) & 0xffff)
);
return true;
}
}
}
return false;
}
#endif
static bool getExeVersionRaw(FFstrbuf* exe, FFstrbuf* version)
{
return ffProcessAppendStdOut(version, (char* const[]) {
exe->chars,
"--version",
NULL
}) == NULL;
}
static bool getExeVersionGeneral(FFstrbuf* exe, FFstrbuf* version)
{
if(!getExeVersionRaw(exe, version))
return false;
ffStrbufSubstrAfterFirstC(version, ' ');
ffStrbufSubstrBeforeFirstC(version, ' ');
return true;
}
static bool getShellVersionBash(FFstrbuf* exe, FFstrbuf* version)
{
if(!getExeVersionRaw(exe, version))
return false;
// GNU bash, version 5.1.16(1)-release (x86_64-pc-msys)\nCopyright...
ffStrbufSubstrBeforeFirstC(version, '\n'); // GNU bash, version 5.1.16(1)-release (x86_64-pc-msys)
ffStrbufSubstrBeforeLastC(version, ' '); // GNU bash, version 5.1.16(1)-release
ffStrbufSubstrAfterLastC(version, ' '); // 5.1.16(1)-release
ffStrbufSubstrBeforeFirstC(version, '('); // 5.1.16
return true;
}
static bool getShellVersionFish(FFstrbuf* exe, FFstrbuf* version)
{
if(!getExeVersionRaw(exe, version))
return false;
//fish, version 3.6.0
ffStrbufSubstrAfterLastC(version, ' ');
return true;
}
static bool getShellVersionPwsh(FFstrbuf* exe, FFstrbuf* version)
{
#ifdef _WIN32
if(getFileVersion(exe->chars, version))
{
ffStrbufSubstrBeforeLastC(version, '.');
return true;
}
#endif
if(!getExeVersionRaw(exe, version))
return false;
ffStrbufSubstrAfterLastC(version, ' ');
return true;
}
static bool getShellVersionKsh(FFstrbuf* exe, FFstrbuf* version)
{
if(ffProcessAppendStdErr(version, (char* const[]) {
exe->chars,
"--version",
NULL
}) != NULL)
return false;
// version sh (AT&T Research) 93u+ 2012-08-01
ffStrbufSubstrAfterLastC(version, ')');
ffStrbufTrim(version, ' ');
return true;
}
static bool getShellVersionOksh(FFstrbuf* exe, FFstrbuf* version)
{
if(ffProcessAppendStdOut(version, (char* const[]) {
exe->chars,
"-c",
"echo $OKSH_VERSION",
NULL
}) != NULL)
return false;
//oksh 7.3
ffStrbufSubstrAfterFirstC(version, ' ');
return true;
}
static bool getShellVersionOils(FFstrbuf* exe, FFstrbuf* version)
{
if(ffProcessAppendStdOut(version, (char* const[]) {
exe->chars,
"--version",
NULL
}) != NULL)
return false;
// Oils 0.18.0 https://www.oilshell.org/...
ffStrbufSubstrAfterFirstC(version, ' ');
ffStrbufSubstrBeforeFirstC(version, '\t');
return true;
}
static bool getShellVersionNushell(FFstrbuf* exe, FFstrbuf* version)
{
ffStrbufSetS(version, getenv("NU_VERSION"));
if (version->length) return true;
return getExeVersionRaw(exe, version); //0.73.0
}
static bool getShellVersionAsh(FFstrbuf* exe, FFstrbuf* version)
{
if(ffProcessAppendStdErr(version, (char* const[]) {
exe->chars,
"--help",
NULL
}) != NULL)
return false;
// BusyBox v1.36.1 (2023-11-07 18:53:09 UTC) multi-call binary...
ffStrbufSubstrAfterFirstC(version, ' ');
ffStrbufSubstrBeforeFirstC(version, ' ');
ffStrbufTrimLeft(version, 'v');
return true;
}
static bool getShellVersionXonsh(FFstrbuf* exe, FFstrbuf* version)
{
ffStrbufSetS(version, getenv("XONSH_VERSION"));
if (version->length) return true;
if(ffProcessAppendStdErr(version, (char* const[]) {
exe->chars,
"--version",
NULL
}) != NULL)
return false;
// xonsh/0.14.1
ffStrbufSubstrAfterFirstC(version, '/');
return true;
}
#ifdef _WIN32
static bool getShellVersionWinPowerShell(FFstrbuf* exe, FFstrbuf* version)
{
return ffProcessAppendStdOut(version, (char* const[]) {
exe->chars,
"-NoLogo",
"-NoProfile",
"-Command",
"$PSVersionTable.PSVersion.ToString()",
NULL
}) == NULL;
}
#endif
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version)
{
if (!instance.config.general.detectVersion) return false;
if(ffStrEqualsIgnCase(exeName, "sh")) // #849
return false;
if(ffStrEqualsIgnCase(exeName, "bash"))
return getShellVersionBash(exe, version);
if(ffStrEqualsIgnCase(exeName, "zsh"))
return getExeVersionGeneral(exe, version); //zsh 5.9 (arm-apple-darwin21.3.0)
if(ffStrEqualsIgnCase(exeName, "fish"))
return getShellVersionFish(exe, version);
if(ffStrEqualsIgnCase(exeName, "pwsh"))
return getShellVersionPwsh(exe, version);
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"))
return getShellVersionNushell(exe, version);
if(ffStrEqualsIgnCase(exeName, "ksh"))
return getShellVersionKsh(exe, version);
if(ffStrEqualsIgnCase(exeName, "oksh"))
return getShellVersionOksh(exe, version);
if(ffStrEqualsIgnCase(exeName, "oil.ovm"))
return getShellVersionOils(exe, version);
if(ffStrEqualsIgnCase(exeName, "elvish"))
return getExeVersionRaw(exe, version);
if(ffStrEqualsIgnCase(exeName, "ash"))
return getShellVersionAsh(exe, version);
if(ffStrEqualsIgnCase(exeName, "xonsh"))
return getShellVersionXonsh(exe, version);
#ifdef _WIN32
if(ffStrEqualsIgnCase(exeName, "powershell") || ffStrEqualsIgnCase(exeName, "powershell_ise"))
return getShellVersionWinPowerShell(exe, version);
return getFileVersion(exe->chars, version);
#endif
return false;
}
FF_MAYBE_UNUSED static bool getTerminalVersionTermux(FFstrbuf* version)
{
ffStrbufSetS(version, getenv("TERMUX_VERSION"));
return version->length > 0;
}
FF_MAYBE_UNUSED static bool getTerminalVersionGnome(FFstrbuf* version)
{
if(ffProcessAppendStdOut(version, (char* const[]){
"gnome-terminal",
"--version",
NULL
})) return false;
//# GNOME Terminal 3.46.7 using VTE 0.70.2 +BIDI +GNUTLS +ICU +SYSTEMD
ffStrbufSubstrAfterFirstS(version, "Terminal ");
ffStrbufSubstrBeforeFirstC(version, ' ');
return true;
}
FF_MAYBE_UNUSED static bool getTerminalVersionKgx(FFstrbuf* version)
{
if(ffProcessAppendStdOut(version, (char* const[]){
"kgx",
"--version",
NULL
})) return false;
//# KGX 45.0 using VTE 0.74.0 +BIDI +GNUTLS +ICU +SYSTEMD
ffStrbufSubstrAfterFirstS(version, "KGX ");
ffStrbufSubstrBeforeFirstC(version, ' ');
return true;
}
FF_MAYBE_UNUSED static bool getTerminalVersionKonsole(FFstrbuf* exe, FFstrbuf* version)
{
const char* konsoleVersion = getenv("KONSOLE_VERSION");
if(konsoleVersion)
{
//221201
long major = strtol(konsoleVersion, NULL, 10);
if (major >= 0)
{
long patch = major % 100;
major /= 100;
long minor = major % 100;
major /= 100;
ffStrbufSetF(version, "%ld.%ld.%ld", major, minor, patch);
return true;
}
}
return getExeVersionGeneral(exe, version);
}
FF_MAYBE_UNUSED static bool getTerminalVersionFoot(FFstrbuf* exe, FFstrbuf* version)
{
if(!getExeVersionRaw(exe, version)) return false;
//foot version: 1.13.1 -pgo +ime -graphemes -assertions
ffStrbufSubstrAfterFirstS(version, "version: ");
ffStrbufSubstrBeforeFirstC(version, ' ');
return true;
}
FF_MAYBE_UNUSED static bool getTerminalVersionMateTerminal(FFstrbuf* exe, FFstrbuf* version)
{
if(!getExeVersionRaw(exe, version)) return false;
//MATE Terminal 1.26.1
ffStrbufSubstrAfterLastC(version, ' ');
return version->length > 0;
}
FF_MAYBE_UNUSED static bool getTerminalVersionCockpit(FFstrbuf* exe, FFstrbuf* version)
{
if(!getExeVersionRaw(exe, version)) return false;
//Version: 295\n...
ffStrbufSubstrBeforeFirstC(version, '\n');
ffStrbufSubstrAfterFirstC(version, ' ');
return version->length > 0;
}
FF_MAYBE_UNUSED static bool getTerminalVersionXterm(FFstrbuf* exe, FFstrbuf* version)
{
ffStrbufSetS(version, getenv("XTERM_VERSION"));
if (!version->length)
{
if(ffProcessAppendStdOut(version, (char* const[]){
exe->chars,
"-v",
NULL
})) return false;
}
//xterm(273)
ffStrbufTrimRight(version, ')');
ffStrbufSubstrAfterFirstC(version, '(');
return version->length > 0;
}
FF_MAYBE_UNUSED static bool getTerminalVersionBlackbox(FFstrbuf* exe, FFstrbuf* version)
{
if(ffProcessAppendStdOut(version, (char* const[]){
exe->chars,
"--version",
NULL
})) return false;
//BlackBox version 0.14.0 (flatpak)
ffStrbufSubstrAfterFirstS(version, "version ");
ffStrbufSubstrBeforeFirstC(version, ' ');
return version->length > 0;
}
FF_MAYBE_UNUSED static bool getTerminalVersionUrxvt(FF_MAYBE_UNUSED FFstrbuf* exe, FFstrbuf* version)
{
if(ffProcessAppendStdErr(version, (char* const[]){
"urxvt", // Don't use exe because of urxvtd
"-invalid",
NULL
})) return false;
//urxvt: "invalid": unknown or malformed option.
//rxvt-unicode (urxvt) v9.31 - released: 2023-01-02
ffStrbufSubstrAfterFirstS(version, "(urxvt) v");
ffStrbufSubstrBeforeFirstC(version, ' ');
return version->length > 0;
}
FF_MAYBE_UNUSED static bool getTerminalVersionSt(FF_MAYBE_UNUSED FFstrbuf* exe, FFstrbuf* version)
{
if(ffProcessAppendStdErr(version, (char* const[]){
exe->chars,
"-v",
NULL
})) return false;
//st 0.9
ffStrbufSubstrAfterFirstC(version, ' ');
return version->length > 0;
}
FF_MAYBE_UNUSED static bool getTerminalVersionLxterminal(FFstrbuf* exe, FFstrbuf* version)
{
if(!getExeVersionRaw(exe, version)) return false;
// lxterminal 0.3.2
ffStrbufSubstrAfterFirstC(version, ' ');
return version->length > 0;
}
FF_MAYBE_UNUSED static bool getTerminalVersionWeston(FF_MAYBE_UNUSED FFstrbuf* exe, FFstrbuf* version)
{
// weston-terminal doesn't report a version, use weston version instead
if(ffProcessAppendStdOut(version, (char* const[]){
"weston",
"--version",
NULL
})) return false;
//weston 8.0.0
ffStrbufSubstrAfterFirstC(version, ' ');
return version->length > 0;
}
static bool getTerminalVersionContour(FFstrbuf* exe, FFstrbuf* version)
{
const char* env = getenv("TERMINAL_VERSION_STRING");
if (env)
{
ffStrbufAppendS(version, env);
return true;
}
if(!getExeVersionRaw(exe, version)) return false;
// Contour Terminal Emulator 0.3.12.262
ffStrbufSubstrAfterLastC(version, ' ');
return version->length > 0;
}
static bool getTerminalVersionScreen(FFstrbuf* exe, FFstrbuf* version)
{
if(!getExeVersionRaw(exe, version)) return false;
// Screen version 4.09.01 (GNU) 20-Aug-23
ffStrbufSubstrAfter(version, strlen("Screen version ") - 1);
ffStrbufSubstrBeforeFirstC(version, ' ');
return version->length > 0;
}
static bool getTerminalVersionTmux(FFstrbuf* exe, FFstrbuf* version)
{
if (ffProcessAppendStdOut(version, (char* const[]) {
exe->chars,
"-V",
NULL
}) != NULL)
return false;
// tmux 3.4
ffStrbufSubstrAfterFirstC(version, ' ');
return version->length > 0;
}
static bool getTerminalVersionZellij(FFstrbuf* exe, FFstrbuf* version)
{
if(!getExeVersionRaw(exe, version)) return false;
// zellij 0.39.2
ffStrbufSubstrAfterFirstC(version, ' ');
return version->length > 0;
}
static bool getTerminalVersionZed(FFstrbuf* exe, FFstrbuf* version)
{
FF_STRBUF_AUTO_DESTROY cli = ffStrbufCreateCopy(exe);
ffStrbufSubstrBeforeLastC(&cli, '/');
ffStrbufAppendS(&cli, "/cli"
#ifdef _WIN32
".exe"
#endif
);
if(ffProcessAppendStdOut(version, (char* const[]) {
cli.chars,
"--version",
NULL
}) != NULL)
return false;
// Zed 0.142.6 /Applications/Zed.app
ffStrbufSubstrAfterFirstC(version, ' ');
ffStrbufSubstrBeforeFirstC(version, ' ');
return true;
}
#ifndef _WIN32
static bool getTerminalVersionKitty(FFstrbuf* exe, FFstrbuf* version)
{
#if defined(__linux__) || defined(__FreeBSD__)
char buffer[1024] = {};
if (
#ifdef __linux__
ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib64/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer) ||
ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer)
#else
ffReadFileData(_PATH_LOCALBASE "/share/kitty/kitty/constants.py", sizeof(buffer) - 1, buffer)
#endif
)
{
// Starts from version 0.17.0
// https://github.com/kovidgoyal/kitty/blob/master/kitty/constants.py#L25
const char* p = memmem(buffer, sizeof(buffer) - 1, "version: Version = Version(", strlen("version: Version = Version("));
if (p)
{
p += strlen("version: Version = Version(");
int major, minor, patch;
if (sscanf(p, "%d,%d,%d", &major, &minor, &patch) == 3)
{
ffStrbufSetF(version, "%d.%d.%d", major, minor, patch);
return true;
}
}
}
#endif
char versionHex[64];
// https://github.com/fastfetch-cli/fastfetch/discussions/1030#discussioncomment-9845233
if (ffGetTerminalResponse(
"\eP+q6b697474792d71756572792d76657273696f6e\e\\", // kitty-query-version
"\eP1+r%*[^=]=%63[^\e]\e\\\\", versionHex) == NULL)
{
// decode hex string
for (const char* p = versionHex; p[0] && p[1]; p += 2)
{
unsigned value;
if (sscanf(p, "%2x", &value) == 1)
ffStrbufAppendC(version, (char) value);
}
return true;
}
//kitty 0.21.2 created by Kovid Goyal
return getExeVersionGeneral(exe, version);
}
FF_MAYBE_UNUSED static bool getTerminalVersionPtyxis(FF_MAYBE_UNUSED FFstrbuf* exe, FFstrbuf* version)
{
if(ffProcessAppendStdOut(version, (char* const[]) {
"ptyxis",
"--version",
NULL
}) != NULL)
return false;
ffStrbufSubstrBeforeFirstC(version, '\n');
ffStrbufSubstrAfterFirstC(version, ' ');
return true;
}
#endif
#ifdef _WIN32
static bool getTerminalVersionWindowsTerminal(FFstrbuf* exe, FFstrbuf* version)
{
FF_STRBUF_AUTO_DESTROY buildInfoPath;
ffStrbufInitNS(&buildInfoPath, ffStrbufLastIndexC(exe, '\\') + 1, exe->chars);
ffStrbufAppendS(&buildInfoPath, "BuildInfo.xml");
if(ffParsePropFile(buildInfoPath.chars, "StoreVersion=\"", version))
{
ffStrbufTrimRight(version, '"');
return true;
}
return getFileVersion(exe->chars, version);
}
static bool getTerminalVersionConEmu(FFstrbuf* exe, FFstrbuf* version)
{
ffStrbufSetS(version, getenv("ConEmuBuild"));
if(version->length)
return true;
return getFileVersion(exe->chars, version);
}
#endif
bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe, FFstrbuf* version)
{
if (!instance.config.general.detectVersion) return false;
#ifdef __ANDROID__
if(ffStrbufEqualS(processName, "com.termux"))
return getTerminalVersionTermux(version);
#endif
#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun)
if(ffStrbufStartsWithIgnCaseS(processName, "gnome-terminal"))
return getTerminalVersionGnome(version);
if(ffStrbufIgnCaseEqualS(processName, "konsole"))
return getTerminalVersionKonsole(exe, version);
if(ffStrbufIgnCaseEqualS(processName, "yakuake"))
return getExeVersionGeneral(exe, version);//yakuake 22.12.3
if(ffStrbufIgnCaseEqualS(processName, "xfce4-terminal"))
return getExeVersionGeneral(exe, version);//xfce4-terminal 1.0.4 (Xfce 4.18)...
if(ffStrbufIgnCaseEqualS(processName, "terminator"))
return getExeVersionGeneral(exe, version);//terminator 2.1.3
if(ffStrbufIgnCaseEqualS(processName, "deepin-terminal"))
return getExeVersionGeneral(exe, version);//deepin-terminal 5.4.36
if(ffStrbufIgnCaseEqualS(processName, "foot"))
return getTerminalVersionFoot(exe, version);
if(ffStrbufIgnCaseEqualS(processName, "qterminal"))
return getExeVersionRaw(exe, version); //1.2.0
if(ffStrbufIgnCaseEqualS(processName, "mate-terminal"))
return getTerminalVersionMateTerminal(exe, version);
if(ffStrbufIgnCaseEqualS(processName, "cockpit-bridge"))
return getTerminalVersionCockpit(exe, version);
if(ffStrbufIgnCaseEqualS(processName, "xterm"))
return getTerminalVersionXterm(exe, version);
if(ffStrbufIgnCaseEqualS(processName, "blackbox"))
return getTerminalVersionBlackbox(exe, version);
if(ffStrbufIgnCaseEqualS(processName, "st"))
return getTerminalVersionSt(exe, version);
if(ffStrbufIgnCaseEqualS(processName, "lxterminal"))
return getTerminalVersionLxterminal(exe, version);
if(ffStrbufIgnCaseEqualS(processName, "weston-terminal"))
return getTerminalVersionWeston(exe, version);
if(ffStrbufIgnCaseEqualS(processName, "urxvt") ||
ffStrbufIgnCaseEqualS(processName, "urxvtd") ||
ffStrbufIgnCaseEqualS(processName, "rxvt") ||
ffStrbufIgnCaseEqualS(processName, "rxvt-unicode")
)
return getTerminalVersionUrxvt(exe, version);
if(ffStrbufIgnCaseEqualS(processName, "ptyxis-agent"))
return getTerminalVersionPtyxis(exe, version);
#endif
#ifdef _WIN32
if(ffStrbufIgnCaseEqualS(processName, "WindowsTerminal.exe"))
return getTerminalVersionWindowsTerminal(exe, version);
if(ffStrbufStartsWithIgnCaseS(processName, "ConEmu"))
return getTerminalVersionConEmu(exe, version);
#endif
#ifndef _WIN32
if(ffStrbufIgnCaseEqualS(processName, "kitty"))
return getTerminalVersionKitty(exe, version);
if (ffStrbufIgnCaseEqualS(processName, "Tabby") && getExeVersionRaw(exe, version))
return true;
#endif
if(ffStrbufStartsWithIgnCaseS(processName, "alacritty"))
return getExeVersionGeneral(exe, version);
if(ffStrbufStartsWithIgnCaseS(processName, "contour"))
return getTerminalVersionContour(exe, version);
if(ffStrbufStartsWithIgnCaseS(processName, "screen"))
return getTerminalVersionScreen(exe, version);
if(ffStrbufStartsWithIgnCaseS(processName, "zellij"))
return getTerminalVersionZellij(exe, version);
if(ffStrbufStartsWithIgnCaseS(processName, "zed"))
return getTerminalVersionZed(exe, version);
const char* termProgramVersion = getenv("TERM_PROGRAM_VERSION");
if(termProgramVersion)
{
const char* termProgram = getenv("TERM_PROGRAM");
if(termProgram)
{
if(ffStrbufStartsWithIgnCaseS(processName, termProgram) || // processName ends with `.exe` on Windows
(ffStrEquals(termProgram, "vscode") && ffStrbufStartsWithIgnCaseS(processName, "code")) ||
#ifdef __APPLE__
(ffStrEquals(termProgram, "iTerm.app") && ffStrbufStartsWithIgnCaseS(processName, "iTermServer-")) ||
#elif defined(__linux__)
(ffStrEquals(termProgram, "WarpTerminal") && ffStrbufEqualS(processName, "warp")) ||
#endif
false
) {
ffStrbufSetS(version, termProgramVersion);
return true;
}
}
}
termProgramVersion = getenv("LC_TERMINAL_VERSION");
if(termProgramVersion)
{
const char* termProgram = getenv("LC_TERMINAL");
if(termProgram)
{
if(ffStrbufStartsWithIgnCaseS(processName, termProgram) || // processName ends with `.exe` on Windows
(ffStrEquals(termProgram, "vscode") && ffStrbufStartsWithIgnCaseS(processName, "code")) ||
(ffStrStartsWith(termProgram, "iTerm") && ffStrbufStartsWithIgnCaseS(processName, "iTermServer-"))
) {
ffStrbufSetS(version, termProgramVersion);
return true;
}
}
}
if(ffStrbufStartsWithIgnCaseS(processName, "tmux"))
return getTerminalVersionTmux(exe, version);
#ifdef _WIN32
return getFileVersion(exe->chars, version);
#else
return false;
#endif
}