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

516 lines
19 KiB
C
Raw Normal View History

2023-02-01 15:56:25 +01:00
#include "terminalshell.h"
2026-09-21 22:46:40 +08:00
#include "common/FFcache.h"
#include "common/debug.h"
2026-01-06 09:48:38 +08:00
#include "common/io.h"
#include "common/parsing.h"
#include "common/processing.h"
#include "common/thread.h"
2026-05-28 15:58:08 +08:00
#include "common/strutil.h"
2021-06-14 23:22:14 +02:00
#ifdef __ANDROID__
#include "common/android/applabel.h"
#endif
2021-06-14 23:22:14 +02:00
#include <string.h>
2022-06-18 14:25:31 +02:00
#include <stdlib.h>
2021-06-14 23:22:14 +02:00
#include <unistd.h>
2026-03-29 09:28:49 +08:00
static void setExeName(FFstrbuf* exe, const char** exeName) {
assert(exe->length > 0);
2021-06-14 23:22:14 +02:00
uint32_t lastSlashIndex = ffStrbufLastIndexC(exe, '/');
2026-03-29 09:28:49 +08:00
if (lastSlashIndex < exe->length) {
2021-06-14 23:22:14 +02:00
*exeName = exe->chars + lastSlashIndex + 1;
2026-03-29 09:28:49 +08:00
}
2021-06-14 23:22:14 +02:00
}
2026-03-29 09:28:49 +08:00
static pid_t getShellInfo(FFShellResult* result, pid_t pid) {
2022-10-11 14:59:09 +08:00
pid_t ppid = 0;
int32_t tty = -1;
2021-06-14 23:22:14 +02:00
2026-07-10 18:08:04 +08:00
const char* userShellName = nullptr;
{
uint32_t index = ffStrbufLastIndexC(&instance.state.platform.userShell, '/');
2026-03-29 09:28:49 +08:00
if (index == instance.state.platform.userShell.length) {
userShellName = instance.state.platform.userShell.chars;
2026-03-29 09:28:49 +08:00
} else {
userShellName = instance.state.platform.userShell.chars + index + 1;
2026-03-29 09:28:49 +08:00
}
}
2026-07-10 18:08:04 +08:00
while (pid > 1 && ffProcessGetBasicInfoLinux(pid, &result->processName, &ppid, &tty) == nullptr) {
2026-03-29 09:28:49 +08:00
if (!ffStrbufEqualS(&result->processName, userShellName)) {
// Common programs that are between terminal and own process, but are not the shell
if (
// tty < 0 || //A shell should connect to a tty
2026-03-29 09:28:49 +08:00
pid == 1 || // init/systemd
ffStrbufEqualS(&result->processName, "sh") || // This prevents us from detecting things like pipes and redirects, i hope nobody uses plain `sh` as shell
ffStrbufEqualS(&result->processName, "sudo") ||
ffStrbufEqualS(&result->processName, "su") ||
ffStrbufEqualS(&result->processName, "strace") ||
ffStrbufEqualS(&result->processName, "gdb") ||
ffStrbufEqualS(&result->processName, "lldb") ||
ffStrbufEqualS(&result->processName, "lldb-mi") ||
ffStrbufEqualS(&result->processName, "login") ||
ffStrbufEqualS(&result->processName, "ltrace") ||
ffStrbufEqualS(&result->processName, "perf") ||
ffStrbufEqualS(&result->processName, "guake-wrapped") ||
ffStrbufEqualS(&result->processName, "time") ||
ffStrbufEqualS(&result->processName, "clifm") || // https://github.com/leo-arch/clifm/issues/289
ffStrbufEqualS(&result->processName, "valgrind") ||
ffStrbufEqualS(&result->processName, "fastfetch") || // #994
ffStrbufEqualS(&result->processName, "flashfetch") ||
2026-11-01 07:17:10 +05:30
ffStrbufEqualS(&result->processName, "mise") ||
ffStrbufEqualS(&result->processName, "mise-shim") ||
2026-11-04 13:36:44 +08:00
ffStrbufEqualS(&result->processName, "atuin") ||
2026-03-29 09:28:49 +08:00
ffStrbufEqualS(&result->processName, "proot") ||
ffStrbufEqualS(&result->processName, "script") ||
#ifdef __linux__
ffStrbufEqualS(&result->processName, "run-parts") ||
#endif
ffStrbufContainS(&result->processName, "debug") ||
ffStrbufContainS(&result->processName, "command-not-") ||
ffStrbufEndsWithS(&result->processName, ".sh")) {
pid = ppid;
2024-05-28 23:46:13 +08:00
ffStrbufClear(&result->processName);
continue;
}
}
result->pid = (uint32_t) pid;
result->ppid = (uint32_t) ppid;
result->tty = tty;
ffProcessGetInfoLinux(pid, &result->processName, &result->exe, &result->exeName, &result->exePath);
break;
}
return pid > 1 ? ppid : 0;
}
2026-03-29 09:28:49 +08:00
static pid_t getTerminalInfo(FFTerminalResult* result, pid_t pid) {
pid_t ppid = 0;
2026-07-10 18:08:04 +08:00
while (pid > 1 && ffProcessGetBasicInfoLinux(pid, &result->processName, &ppid, nullptr) == nullptr) {
2026-03-29 09:28:49 +08:00
// Known shells
if (
2025-08-21 00:12:41 +08:00
pid == 1 || // init/systemd
2026-03-29 09:28:49 +08:00
ffStrbufEqualS(&result->processName, "sudo") ||
ffStrbufEqualS(&result->processName, "su") ||
ffStrbufEqualS(&result->processName, "sh") ||
ffStrbufEqualS(&result->processName, "ash") ||
ffStrbufEqualS(&result->processName, "bash") ||
ffStrbufEqualS(&result->processName, "zsh") ||
ffStrbufEqualS(&result->processName, "ksh") ||
2026-09-25 11:33:46 +08:00
ffStrbufEqualS(&result->processName, "ksh93") ||
2026-03-29 09:28:49 +08:00
ffStrbufEqualS(&result->processName, "mksh") ||
2026-09-25 11:33:46 +08:00
ffStrbufEqualS(&result->processName, "pdksh") ||
2026-03-29 09:28:49 +08:00
ffStrbufEqualS(&result->processName, "oksh") ||
ffStrbufEqualS(&result->processName, "csh") ||
ffStrbufEqualS(&result->processName, "tcsh") ||
ffStrbufEqualS(&result->processName, "fish") ||
ffStrbufEqualS(&result->processName, "dash") ||
ffStrbufEqualS(&result->processName, "pwsh") ||
ffStrbufEqualS(&result->processName, "nu") ||
ffStrbufEqualS(&result->processName, "git-shell") ||
ffStrbufEqualS(&result->processName, "elvish") ||
ffStrbufEqualS(&result->processName, "oil.ovm") ||
ffStrbufEqualS(&result->processName, "xonsh") || // works in Linux but not in macOS because kernel returns `Python` in this case
ffStrbufEqualS(&result->processName, "login") ||
ffStrbufEqualS(&result->processName, "clifm") || // https://github.com/leo-arch/clifm/issues/289
ffStrbufEqualS(&result->processName, "chezmoi") || // #762
ffStrbufEqualS(&result->processName, "proot") ||
ffStrbufEqualS(&result->processName, "script") ||
#ifdef __linux__
ffStrbufStartsWithS(&result->processName, "Relay(") || // Unknown process in WSL2
2024-05-28 23:46:13 +08:00
ffStrbufStartsWithS(&result->processName, "flatpak-") || // #707
2026-03-29 09:28:49 +08:00
ffStrbufEqualS(&result->processName, "run-parts") || // #2048
#endif
ffStrbufEndsWithS(&result->processName, ".sh")) {
pid = ppid;
2024-05-28 23:46:13 +08:00
ffStrbufClear(&result->processName);
continue;
}
2026-03-29 09:28:49 +08:00
#ifdef __APPLE__
// https://github.com/fastfetch-cli/fastfetch/discussions/501
2024-05-28 23:46:13 +08:00
const char* pLeft = strstr(result->processName.chars, " (");
2026-03-29 09:28:49 +08:00
if (pLeft) {
pLeft += 2;
const char* pRight = strstr(pLeft, "term)");
2026-03-29 09:28:49 +08:00
if (pRight && pRight[5] == '\0') {
for (; pLeft < pRight; ++pLeft) {
if (*pLeft < 'a' || *pLeft > 'z') {
break;
2026-03-29 09:28:49 +08:00
}
}
2026-07-10 18:08:04 +08:00
if (pLeft == pRight && ffProcessGetBasicInfoLinux(ppid, &result->processName, &ppid, nullptr) != nullptr) {
return 0;
2026-03-29 09:28:49 +08:00
}
}
}
2026-03-29 09:28:49 +08:00
#endif
result->pid = (uint32_t) pid;
result->ppid = (uint32_t) ppid;
ffProcessGetInfoLinux(pid, &result->processName, &result->exe, &result->exeName, &result->exePath);
break;
}
return pid > 1 ? ppid : 0;
2021-06-14 23:22:14 +02:00
}
2026-03-29 09:28:49 +08:00
static bool getTerminalInfoByPidEnv(FFTerminalResult* result, const char* pidEnv) {
2024-01-30 15:22:26 +08:00
const char* envStr = getenv(pidEnv);
2026-07-10 18:08:04 +08:00
if (envStr == nullptr) {
2024-01-30 15:22:26 +08:00
return false;
2026-03-29 09:28:49 +08:00
}
2024-01-30 15:22:26 +08:00
2026-07-10 18:08:04 +08:00
pid_t pid = (pid_t) strtol(envStr, nullptr, 10);
result->pid = (uint32_t) pid;
2026-07-10 18:08:04 +08:00
if (ffProcessGetBasicInfoLinux(pid, &result->processName, (pid_t*) &result->ppid, nullptr) == nullptr) {
ffProcessGetInfoLinux(pid, &result->processName, &result->exe, &result->exeName, &result->exePath);
return true;
}
return false;
}
2026-03-29 09:28:49 +08:00
static void getTerminalFromEnv(FFTerminalResult* result) {
if (result->processName.length > 0) {
if (!ffStrbufStartsWithS(&result->processName, "login") &&
!ffStrbufEqualS(&result->processName, "(login)") &&
2026-03-29 09:28:49 +08:00
#ifdef __APPLE__
!ffStrbufEqualS(&result->processName, "launchd") &&
2026-03-29 09:28:49 +08:00
#else
!ffStrbufEqualS(&result->processName, "systemd") &&
!ffStrbufEqualS(&result->processName, "init") &&
!ffStrbufEqualS(&result->processName, "(init)") &&
!ffStrbufEqualS(&result->processName, "SessionLeader") && // #750
2026-03-29 09:28:49 +08:00
#endif
2026-03-29 09:28:49 +08:00
!ffStrbufEqualS(&result->processName, "0"))
return;
ffStrbufClear(&result->processName);
ffStrbufClear(&result->exe);
result->exeName = result->exe.chars;
ffStrbufClear(&result->exePath);
result->pid = result->ppid = 0;
}
2021-06-14 23:22:14 +02:00
2026-07-10 18:08:04 +08:00
const char* term = nullptr;
2026-03-29 09:28:49 +08:00
// SSH
if (
2026-07-10 18:08:04 +08:00
getenv("SSH_TTY") != nullptr) {
term = getenv("SSH_TTY");
2026-03-29 09:28:49 +08:00
} else if (
2026-07-10 18:08:04 +08:00
getenv("KITTY_PID") != nullptr ||
getenv("KITTY_INSTALLATION_DIR") != nullptr) {
2026-03-29 09:28:49 +08:00
if (getTerminalInfoByPidEnv(result, "KITTY_PID")) {
return;
2026-03-29 09:28:49 +08:00
}
term = "kitty";
}
2026-03-29 09:28:49 +08:00
#ifdef __linux__ // WSL
// Windows Terminal
else if (
2026-07-10 18:08:04 +08:00
getenv("WT_SESSION") != nullptr ||
getenv("WT_PROFILE_ID") != nullptr)
2026-03-29 09:28:49 +08:00
term = "Windows Terminal";
2026-03-29 09:28:49 +08:00
// ConEmu
else if (
2026-07-10 18:08:04 +08:00
getenv("ConEmuPID") != nullptr) {
2026-03-29 09:28:49 +08:00
term = "ConEmu";
}
#endif
2026-03-29 09:28:49 +08:00
// Alacritty
else if (
2026-07-10 18:08:04 +08:00
getenv("ALACRITTY_SOCKET") != nullptr ||
getenv("ALACRITTY_LOG") != nullptr ||
getenv("ALACRITTY_WINDOW_ID") != nullptr) {
2026-03-29 09:28:49 +08:00
term = "Alacritty";
}
#ifdef __ANDROID__
// Termux
else if (
2026-07-10 18:08:04 +08:00
getenv("TERMUX_VERSION") != nullptr ||
getenv("TERMUX_MAIN_PACKAGE_FORMAT") != nullptr) {
2026-03-29 09:28:49 +08:00
if (getTerminalInfoByPidEnv(result, "TERMUX_APP__PID")) {
return;
2026-03-29 09:28:49 +08:00
}
term = "com.termux";
}
2026-03-29 09:28:49 +08:00
#endif
2022-06-18 00:45:28 +02:00
2026-03-29 09:28:49 +08:00
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__GNU__)
// Konsole
else if (
2026-07-10 18:08:04 +08:00
getenv("KONSOLE_VERSION") != nullptr)
2026-03-29 09:28:49 +08:00
term = "konsole";
2026-03-29 09:28:49 +08:00
else if (
2026-07-10 18:08:04 +08:00
getenv("GNOME_TERMINAL_SCREEN") != nullptr ||
getenv("GNOME_TERMINAL_SERVICE") != nullptr) {
2026-03-29 09:28:49 +08:00
term = "gnome-terminal";
}
#endif
2022-09-20 11:16:08 +02:00
2026-03-29 09:28:49 +08:00
// MacOS, mintty
2026-07-10 18:08:04 +08:00
else if (getenv("TERM_PROGRAM") != nullptr) {
2022-09-04 09:02:11 -07:00
term = getenv("TERM_PROGRAM");
2026-03-29 09:28:49 +08:00
}
2022-09-04 09:02:11 -07:00
2026-07-10 18:08:04 +08:00
else if (getenv("LC_TERMINAL") != nullptr) {
term = getenv("LC_TERMINAL");
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
// Normal Terminal
else {
term = getenv("TERM");
2026-03-29 09:28:49 +08:00
// TTY
if (!ffStrSet(term) || ffStrEquals(term, "linux")) {
term = ttyname(STDIN_FILENO);
2026-03-29 09:28:49 +08:00
}
}
2021-06-14 23:22:14 +02:00
2026-03-29 09:28:49 +08:00
if (ffStrSet(term)) {
ffStrbufSetS(&result->processName, term);
ffStrbufSetS(&result->exe, term);
setExeName(&result->exe, &result->exeName);
}
2021-06-14 23:22:14 +02:00
}
2026-03-29 09:28:49 +08:00
static void getUserShellFromEnv(FFShellResult* result) {
// If shell detection via processes failed
if (result->processName.length == 0 && instance.state.platform.userShell.length > 0) {
ffStrbufSet(&result->exe, &instance.state.platform.userShell);
setExeName(&result->exe, &result->exeName);
ffStrbufAppendS(&result->processName, result->exeName);
2021-06-14 23:22:14 +02:00
}
}
2026-03-29 09:28:49 +08:00
static void setShellInfoDetails(FFShellResult* result) {
if (ffStrbufEqualS(&result->processName, "pwsh")) {
ffStrbufInitStatic(&result->prettyName, "PowerShell");
2026-03-29 09:28:49 +08:00
} else if (ffStrbufEqualS(&result->processName, "nu")) {
ffStrbufInitStatic(&result->prettyName, "nushell");
2026-03-29 09:28:49 +08:00
} else if (ffStrbufEqualS(&result->processName, "oil.ovm")) {
ffStrbufInitStatic(&result->prettyName, "Oils");
2026-06-03 18:29:33 +08:00
} else if (ffStrbufEqualS(&result->processName, "busybox")) {
ffStrbufInitStatic(&result->prettyName, "ash");
2026-03-29 09:28:49 +08:00
} else {
// https://github.com/fastfetch-cli/fastfetch/discussions/280#discussioncomment-3831734
ffStrbufInitS(&result->prettyName, result->exeName);
}
}
2026-03-29 09:28:49 +08:00
static void setTerminalInfoDetails(FFTerminalResult* result) {
// For Nixpkgs. Ref: #510 and https://github.com/NixOS/nixpkgs/pull/249428
// We use processName when detecting version and font, overriding it for simplification
if (ffStrbufStartsWithC(&result->processName, '.') && ffStrbufStartsWithS(&result->exePath,"/nix/store")) {
2024-04-27 23:12:45 +08:00
ffStrbufSubstrAfter(&result->processName, 0);
if (strlen(result->exeName) < 15) {
ffStrbufSubstrBeforeLastC(&result->processName, '-');
}
}
2026-03-29 09:28:49 +08:00
if (ffStrbufEqualS(&result->processName, "wezterm-gui")) {
ffStrbufInitStatic(&result->prettyName, "WezTerm");
2026-03-29 09:28:49 +08:00
} else if (ffStrbufStartsWithS(&result->processName, "tmux:")) {
ffStrbufInitStatic(&result->prettyName, "tmux");
2026-03-29 09:28:49 +08:00
} else if (ffStrbufStartsWithS(&result->processName, "screen-")) {
ffStrbufInitStatic(&result->prettyName, "screen");
2026-03-29 09:28:49 +08:00
} else if (ffStrbufEqualS(&result->processName, "sshd") || ffStrbufStartsWithS(&result->processName, "sshd-")) {
if (result->tty.length) {
ffStrbufInitCopy(&result->prettyName, &result->tty);
2026-03-29 09:28:49 +08:00
} else {
ffStrbufSetStatic(&result->prettyName, "sshd");
2026-03-29 09:28:49 +08:00
}
}
2026-03-29 09:28:49 +08:00
#if defined(__ANDROID__)
else if (ffStrbufEqualS(&result->processName, "com.termux")) {
// The app is installed under a label rather than under its package name, and the label is
// what every screen the user sees calls it -- "Termux:Launcher" on the test device, not
// "Termux". The package name is the fallback for a build whose label cannot be read, which
// includes an app whose label is a literal string in its manifest rather than a resource
// reference; see common/android/applabel.h for what that read costs and when it fails.
ffStrbufInitStatic(&result->prettyName, "Termux");
const char* labelError = ffAndroidGetAppLabel("com.termux", &result->prettyName);
if (labelError != nullptr) {
FF_DEBUG("The label of com.termux could not be resolved: %s", labelError);
}
}
2026-03-29 09:28:49 +08:00
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__GNU__)
2026-03-29 09:28:49 +08:00
else if (ffStrbufStartsWithS(&result->processName, "gnome-terminal"))
ffStrbufInitStatic(&result->prettyName, "GNOME Terminal");
2026-03-29 09:28:49 +08:00
else if (ffStrbufStartsWithS(&result->processName, "kgx")) {
ffStrbufInitStatic(&result->prettyName, "GNOME Console");
2026-03-29 09:28:49 +08:00
} else if (ffStrbufEqualS(&result->processName, "urxvt") ||
ffStrbufEqualS(&result->processName, "urxvtd") ||
2026-03-29 09:28:49 +08:00
ffStrbufEqualS(&result->processName, "rxvt")) {
ffStrbufInitStatic(&result->prettyName, "rxvt-unicode");
2026-03-29 09:28:49 +08:00
} else if (ffStrbufStartsWithS(&result->processName, "ptyxis-agent")) {
ffStrbufInitStatic(&result->prettyName, "Ptyxis");
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
#elif defined(__APPLE__)
2026-03-29 09:28:49 +08:00
else if (ffStrbufEqualS(&result->processName, "iTerm.app") || ffStrbufStartsWithS(&result->processName, "iTermServer-"))
ffStrbufInitStatic(&result->prettyName, "iTerm");
2026-03-29 09:28:49 +08:00
else if (ffStrbufEndsWithS(&result->exePath, "Terminal.app/Contents/MacOS/Terminal")) {
ffStrbufSetStatic(&result->processName, "Apple_Terminal"); // $TERM_PROGRAM, for terminal font detection
ffStrbufInitStatic(&result->prettyName, "Apple Terminal");
2026-03-29 09:28:49 +08:00
} else if (ffStrbufEqualS(&result->processName, "Apple_Terminal")) {
ffStrbufInitStatic(&result->prettyName, "Apple Terminal");
2026-03-29 09:28:49 +08:00
} else if (ffStrbufEndsWithS(&result->exePath, "Warp.app/Contents/MacOS/stable")) {
ffStrbufSetStatic(&result->processName, "WarpTerminal"); // $TERM_PROGRAM, for terminal font detection
ffStrbufInitStatic(&result->prettyName, "Warp");
2026-03-29 09:28:49 +08:00
} else if (ffStrbufEqualS(&result->processName, "WarpTerminal")) {
ffStrbufInitStatic(&result->prettyName, "Warp");
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
#elif defined(__HAIKU__)
2026-03-29 09:28:49 +08:00
else if (ffStrbufEqualS(&result->processName, "Terminal"))
ffStrbufInitStatic(&result->prettyName, "Haiku Terminal");
2026-03-29 09:28:49 +08:00
#endif
else if (strncmp(result->exeName, result->processName.chars, result->processName.length) == 0 || (ffStrbufStartsWithS(&result->exePath,"/nix/store") && strlen(result->exeName) > 15)) { // if exeName starts with processName, print it. Otherwise print processName. For nixpkgs, use exeName if processName can't be unwrapped
ffStrbufInitS(&result->prettyName, result->exeName);
2026-03-29 09:28:49 +08:00
} else {
ffStrbufInitCopy(&result->prettyName, &result->processName);
2026-03-29 09:28:49 +08:00
}
}
2024-01-18 14:07:29 +08:00
#if defined(MAXPATH)
#define FF_EXE_PATH_LEN MAXPATH
#elif defined(PATH_MAX)
#define FF_EXE_PATH_LEN PATH_MAX
#else
#define FF_EXE_PATH_LEN 260
#endif
2026-09-21 22:46:40 +08:00
static FFShellResult shellResult;
static void destroyShellResult(void* storage) {
FFShellResult* result = storage;
ffStrbufDestroy(&result->processName);
ffStrbufDestroy(&result->exe);
result->exeName = result->exe.chars; // `exeName` points into `exe`, which is empty again
ffStrbufDestroy(&result->exePath);
ffStrbufDestroy(&result->prettyName);
ffStrbufDestroy(&result->version);
result->pid = 0;
result->ppid = 0;
result->tty = -1;
}
static FFcacheEntry ffCacheEntryShell = {
.name = "shell",
.storage = &shellResult,
.destroy = destroyShellResult,
};
2026-03-29 09:28:49 +08:00
const FFShellResult* ffDetectShell() {
2026-09-21 22:46:40 +08:00
FFShellResult* result = &shellResult;
if (!ffCacheBeginInit(&ffCacheEntryShell)) {
return result;
2026-03-29 09:28:49 +08:00
}
2021-06-14 23:22:14 +02:00
2026-09-21 22:46:40 +08:00
ffStrbufInit(&result->processName);
ffStrbufInitA(&result->exe, FF_EXE_PATH_LEN);
result->exeName = result->exe.chars;
ffStrbufInit(&result->exePath);
ffStrbufInit(&result->version);
result->pid = 0;
result->ppid = 0;
result->tty = -1;
2021-06-14 23:22:14 +02:00
pid_t ppid = getppid();
const char* ignoreParent = getenv("FFTS_IGNORE_PARENT");
2026-03-29 09:28:49 +08:00
if (ignoreParent && ffStrEquals(ignoreParent, "1")) {
FF_STRBUF_AUTO_DESTROY _ = ffStrbufCreate();
2026-07-10 18:08:04 +08:00
ffProcessGetBasicInfoLinux(ppid, &_, &ppid, nullptr);
}
2026-09-21 22:46:40 +08:00
ppid = getShellInfo(result, ppid);
getUserShellFromEnv(result);
2026-05-28 10:31:35 +08:00
2026-09-21 22:46:40 +08:00
if (result->processName.length > 0) {
setShellInfoDetails(result);
2026-05-28 10:31:35 +08:00
if (instance.config.general.detectVersion) {
if (!fftsGetShellVersion(result->exePath.length > 0 ? &result->exePath : &result->exe, result->exeName, &result->version)) {
ffStrbufClear(&result->version);
}
2026-05-28 10:31:35 +08:00
}
}
2021-06-14 23:22:14 +02:00
2026-09-21 22:46:40 +08:00
return result;
}
2026-09-21 22:46:40 +08:00
static FFTerminalResult terminalResult;
static void destroyTerminalResult(void* storage) {
FFTerminalResult* result = storage;
ffStrbufDestroy(&result->processName);
ffStrbufDestroy(&result->exe);
result->exeName = result->exe.chars; // `exeName` points into `exe`, which is empty again
ffStrbufDestroy(&result->exePath);
ffStrbufDestroy(&result->prettyName);
ffStrbufDestroy(&result->version);
ffStrbufDestroy(&result->tty);
result->pid = 0;
result->ppid = 0;
}
static FFcacheEntry ffCacheEntryTerminal = {
.name = "terminal",
.storage = &terminalResult,
.destroy = destroyTerminalResult,
};
2026-03-29 09:28:49 +08:00
const FFTerminalResult* ffDetectTerminal() {
2026-09-21 22:46:40 +08:00
FFTerminalResult* result = &terminalResult;
if (!ffCacheBeginInit(&ffCacheEntryTerminal)) {
return result;
2026-03-29 09:28:49 +08:00
}
2026-09-21 22:46:40 +08:00
ffStrbufInit(&result->processName);
ffStrbufInitA(&result->exe, FF_EXE_PATH_LEN);
result->exeName = result->exe.chars;
ffStrbufInit(&result->exePath);
ffStrbufInit(&result->version);
ffStrbufInitS(&result->tty, ttyname(STDOUT_FILENO));
result->pid = 0;
result->ppid = 0;
pid_t ppid = (pid_t) ffDetectShell()->ppid;
2026-03-29 09:28:49 +08:00
if (ppid) {
2026-09-21 22:46:40 +08:00
ppid = getTerminalInfo(result, ppid);
2026-03-29 09:28:49 +08:00
}
2026-09-21 22:46:40 +08:00
getTerminalFromEnv(result);
2026-05-28 10:31:35 +08:00
2026-09-21 22:46:40 +08:00
if (result->processName.length > 0) {
setTerminalInfoDetails(result);
2026-05-28 10:31:35 +08:00
if (instance.config.general.detectVersion) {
if (!fftsGetTerminalVersion(&result->processName, result->exePath.length > 0 ? &result->exePath : &result->exe, &result->version)) {
ffStrbufClear(&result->version);
}
2026-05-28 10:31:35 +08:00
}
}
2026-09-21 22:46:40 +08:00
return result;
2021-06-14 23:22:14 +02:00
}