2023-02-01 15:56:25 +01:00
#include "terminalshell.h"
2026-09-21 22:46:40 +08:00
#include "common/FFcache.h"
2026-10-11 10:42:30 +08:00
#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
2026-10-11 10:42:30 +08: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 ) {
2022-10-08 22:16:45 +08:00
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 ;
2024-01-23 16:38:43 +08:00
int32_t tty = - 1 ;
2021-06-14 23:22:14 +02:00
2026-07-10 18:08:04 +08:00
const char * userShellName = nullptr ;
2024-04-18 09:22:08 +08:00
{
uint32_t index = ffStrbufLastIndexC ( & instance . state . platform . userShell , '/' );
2026-03-29 09:28:49 +08:00
if ( index == instance . state . platform . userShell . length ) {
2024-04-18 09:22:08 +08:00
userShellName = instance . state . platform . userShell . chars ;
2026-03-29 09:28:49 +08:00
} else {
2024-04-18 09:22:08 +08:00
userShellName = instance . state . platform . userShell . chars + index + 1 ;
2026-03-29 09:28:49 +08:00
}
2024-04-18 09:22:08 +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 (
2024-04-18 09:22:08 +08:00
// 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" )) {
2024-04-18 09:22:08 +08:00
pid = ppid ;
2024-05-28 23:46:13 +08:00
ffStrbufClear ( & result -> processName );
2024-04-18 09:22:08 +08:00
continue ;
}
2024-01-12 23:57:56 +09:00
}
result -> pid = ( uint32_t ) pid ;
result -> ppid = ( uint32_t ) ppid ;
2024-01-23 16:38:43 +08:00
result -> tty = tty ;
2024-05-28 22:34:41 +08:00
ffProcessGetInfoLinux ( pid , & result -> processName , & result -> exe , & result -> exeName , & result -> exePath );
2024-01-12 23:57:56 +09:00
break ;
}
2025-08-21 00:12:41 +08:00
return pid > 1 ? ppid : 0 ;
2024-01-12 16:40:21 +08:00
}
2026-03-29 09:28:49 +08:00
static pid_t getTerminalInfo ( FFTerminalResult * result , pid_t pid ) {
2024-01-12 16:40:21 +08:00
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
2024-01-12 23:57:56 +09:00
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" )) {
2024-01-12 23:57:56 +09:00
pid = ppid ;
2024-05-28 23:46:13 +08:00
ffStrbufClear ( & result -> processName );
2024-01-12 23:57:56 +09:00
continue ;
}
2026-03-29 09:28:49 +08:00
#ifdef __APPLE__
2024-01-12 23:57:56 +09:00
// 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 ) {
2024-05-09 20:39:17 +09:00
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' ) {
2024-05-09 20:39:17 +09:00
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 ) {
2024-05-09 20:39:17 +09:00
return 0 ;
2026-03-29 09:28:49 +08:00
}
2024-05-09 20:39:17 +09:00
}
2024-01-12 23:57:56 +09:00
}
2026-03-29 09:28:49 +08:00
#endif
2024-01-12 23:57:56 +09:00
result -> pid = ( uint32_t ) pid ;
2024-01-19 10:46:19 +08:00
result -> ppid = ( uint32_t ) ppid ;
2024-05-28 22:34:41 +08:00
ffProcessGetInfoLinux ( pid , & result -> processName , & result -> exe , & result -> exeName , & result -> exePath );
2024-01-12 23:57:56 +09:00
break ;
}
2025-08-21 00:12:41 +08:00
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 );
2024-01-22 10:20:50 +08:00
result -> pid = ( uint32_t ) pid ;
2026-07-10 18:08:04 +08:00
if ( ffProcessGetBasicInfoLinux ( pid , & result -> processName , ( pid_t * ) & result -> ppid , nullptr ) == nullptr ) {
2024-05-28 22:34:41 +08:00
ffProcessGetInfoLinux ( pid , & result -> processName , & result -> exe , & result -> exeName , & result -> exePath );
2024-01-22 10:20:50 +08:00
return true ;
}
return false ;
}
2026-03-29 09:28:49 +08:00
static void getTerminalFromEnv ( FFTerminalResult * result ) {
if ( result -> processName . length > 0 ) {
2024-10-02 20:13:12 +08:00
if ( ! ffStrbufStartsWithS ( & result -> processName , "login" ) &&
! ffStrbufEqualS ( & result -> processName , "(login)" ) &&
2026-03-29 09:28:49 +08:00
#ifdef __APPLE__
2024-10-02 20:13:12 +08:00
! ffStrbufEqualS ( & result -> processName , "launchd" ) &&
2026-03-29 09:28:49 +08:00
#else
2024-10-02 20:13:12 +08:00
! 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
2022-11-17 18:14:14 +08:00
2026-03-29 09:28:49 +08:00
! ffStrbufEqualS ( & result -> processName , "0" ))
return ;
2022-11-17 18:14:14 +08:00
2024-10-02 20:13:12 +08:00
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 ;
2021-10-17 01:05:45 +08:00
2026-03-29 09:28:49 +08:00
// SSH
if (
2026-07-10 18:08:04 +08:00
getenv ( "SSH_TTY" ) != nullptr ) {
2021-10-17 01:05:45 +08:00
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" )) {
2024-01-22 10:20:50 +08:00
return ;
2026-03-29 09:28:49 +08:00
}
2024-01-22 10:20:50 +08:00
term = "kitty" ;
}
2021-10-17 01:05:45 +08:00
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" ;
2023-07-16 13:12:50 +08:00
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
2021-10-17 01:05:45 +08:00
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" )) {
2024-01-22 10:20:50 +08:00
return ;
2026-03-29 09:28:49 +08:00
}
2024-01-22 10:20:50 +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" ;
2024-01-22 10:20:50 +08:00
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 ) {
2023-11-01 08:55:00 +08:00
term = getenv ( "LC_TERMINAL" );
2026-03-29 09:28:49 +08:00
}
2023-11-01 08:55:00 +08:00
2026-03-29 09:28:49 +08:00
// Normal Terminal
else {
2021-10-17 01:05:45 +08:00
term = getenv ( "TERM" );
2026-03-29 09:28:49 +08:00
// TTY
if ( ! ffStrSet ( term ) || ffStrEquals ( term , "linux" )) {
2024-01-22 10:20:50 +08:00
term = ttyname ( STDIN_FILENO );
2026-03-29 09:28:49 +08:00
}
2024-01-22 10:20:50 +08:00
}
2021-06-14 23:22:14 +02:00
2026-03-29 09:28:49 +08:00
if ( ffStrSet ( term )) {
2024-01-12 20:42:33 +08:00
ffStrbufSetS ( & result -> processName , term );
ffStrbufSetS ( & result -> exe , term );
setExeName ( & result -> exe , & result -> exeName );
2022-10-08 22:16:45 +08:00
}
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 ) {
2024-01-12 20:42:33 +08:00
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" )) {
2024-01-12 20:42:33 +08:00
ffStrbufInitStatic ( & result -> prettyName , "PowerShell" );
2026-03-29 09:28:49 +08:00
} else if ( ffStrbufEqualS ( & result -> processName , "nu" )) {
2024-01-12 20:42:33 +08:00
ffStrbufInitStatic ( & result -> prettyName , "nushell" );
2026-03-29 09:28:49 +08:00
} else if ( ffStrbufEqualS ( & result -> processName , "oil.ovm" )) {
2024-01-12 20:42:33 +08:00
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 {
2024-01-12 16:40:21 +08:00
// https://github.com/fastfetch-cli/fastfetch/discussions/280#discussioncomment-3831734
2024-01-12 20:42:33 +08:00
ffStrbufInitS ( & result -> prettyName , result -> exeName );
2024-01-12 16:40:21 +08:00
}
}
2026-03-29 09:28:49 +08:00
static void setTerminalInfoDetails ( FFTerminalResult * result ) {
2026-05-30 02:16:12 +00:00
// 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 );
2026-05-30 02:16:12 +00:00
if ( strlen ( result -> exeName ) < 15 ) {
ffStrbufSubstrBeforeLastC ( & result -> processName , '-' );
}
2024-01-12 16:40:21 +08:00
}
2026-03-29 09:28:49 +08:00
if ( ffStrbufEqualS ( & result -> processName , "wezterm-gui" )) {
2024-01-12 20:42:33 +08:00
ffStrbufInitStatic ( & result -> prettyName , "WezTerm" );
2026-03-29 09:28:49 +08:00
} else if ( ffStrbufStartsWithS ( & result -> processName , "tmux:" )) {
2024-01-12 20:42:33 +08:00
ffStrbufInitStatic ( & result -> prettyName , "tmux" );
2026-03-29 09:28:49 +08:00
} else if ( ffStrbufStartsWithS ( & result -> processName , "screen-" )) {
2024-01-22 14:33:55 +08:00
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 ) {
2025-11-25 10:33:57 +08:00
ffStrbufInitCopy ( & result -> prettyName , & result -> tty );
2026-03-29 09:28:49 +08:00
} else {
2025-11-25 10:33:57 +08:00
ffStrbufSetStatic ( & result -> prettyName , "sshd" );
2026-03-29 09:28:49 +08:00
}
2025-11-25 10:33:57 +08:00
}
2024-01-12 16:40:21 +08:00
2026-03-29 09:28:49 +08:00
#if defined(__ANDROID__)
2024-01-12 16:40:21 +08:00
2026-10-11 10:42:30 +08:00
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.
2024-01-12 20:42:33 +08:00
ffStrbufInitStatic ( & result -> prettyName , "Termux" );
2026-10-11 10:42:30 +08:00
const char * labelError = ffAndroidGetAppLabel ( "com.termux" , & result -> prettyName );
if ( labelError != nullptr ) {
FF_DEBUG ( "The label of com.termux could not be resolved: %s" , labelError );
}
}
2024-01-12 16:40:21 +08:00
2026-03-29 09:28:49 +08:00
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__GNU__)
2024-01-12 16:40:21 +08:00
2026-03-29 09:28:49 +08:00
else if ( ffStrbufStartsWithS ( & result -> processName , "gnome-terminal" ))
2024-01-12 20:42:33 +08:00
ffStrbufInitStatic ( & result -> prettyName , "GNOME Terminal" );
2026-03-29 09:28:49 +08:00
else if ( ffStrbufStartsWithS ( & result -> processName , "kgx" )) {
2024-01-12 20:42:33 +08:00
ffStrbufInitStatic ( & result -> prettyName , "GNOME Console" );
2026-03-29 09:28:49 +08:00
} else if ( ffStrbufEqualS ( & result -> processName , "urxvt" ) ||
2024-01-12 20:42:33 +08:00
ffStrbufEqualS ( & result -> processName , "urxvtd" ) ||
2026-03-29 09:28:49 +08:00
ffStrbufEqualS ( & result -> processName , "rxvt" )) {
2024-01-12 20:42:33 +08:00
ffStrbufInitStatic ( & result -> prettyName , "rxvt-unicode" );
2026-03-29 09:28:49 +08:00
} else if ( ffStrbufStartsWithS ( & result -> processName , "ptyxis-agent" )) {
2024-07-01 23:56:19 +08:00
ffStrbufInitStatic ( & result -> prettyName , "Ptyxis" );
2026-03-29 09:28:49 +08:00
}
2024-01-12 16:40:21 +08:00
2026-03-29 09:28:49 +08:00
#elif defined(__APPLE__)
2024-01-12 16:40:21 +08:00
2026-03-29 09:28:49 +08:00
else if ( ffStrbufEqualS ( & result -> processName , "iTerm.app" ) || ffStrbufStartsWithS ( & result -> processName , "iTermServer-" ))
2024-01-12 20:42:33 +08:00
ffStrbufInitStatic ( & result -> prettyName , "iTerm" );
2026-03-29 09:28:49 +08:00
else if ( ffStrbufEndsWithS ( & result -> exePath , "Terminal.app/Contents/MacOS/Terminal" )) {
2025-03-21 10:08:26 +08:00
ffStrbufSetStatic ( & result -> processName , "Apple_Terminal" ); // $TERM_PROGRAM, for terminal font detection
2024-05-05 13:35:39 +08:00
ffStrbufInitStatic ( & result -> prettyName , "Apple Terminal" );
2026-03-29 09:28:49 +08:00
} else if ( ffStrbufEqualS ( & result -> processName , "Apple_Terminal" )) {
2024-01-12 20:42:33 +08:00
ffStrbufInitStatic ( & result -> prettyName , "Apple Terminal" );
2026-03-29 09:28:49 +08:00
} else if ( ffStrbufEndsWithS ( & result -> exePath , "Warp.app/Contents/MacOS/stable" )) {
2025-03-21 10:08:26 +08:00
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" )) {
2024-01-12 20:42:33 +08:00
ffStrbufInitStatic ( & result -> prettyName , "Warp" );
2026-03-29 09:28:49 +08:00
}
2024-01-12 16:40:21 +08:00
2026-03-29 09:28:49 +08:00
#elif defined(__HAIKU__)
2025-02-12 22:47:37 +00:00
2026-03-29 09:28:49 +08:00
else if ( ffStrbufEqualS ( & result -> processName , "Terminal" ))
2025-02-12 22:47:37 +00:00
ffStrbufInitStatic ( & result -> prettyName , "Haiku Terminal" );
2026-03-29 09:28:49 +08:00
#endif
2024-01-12 16:40:21 +08:00
2026-05-30 02:16:12 +00:00
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
2024-01-12 20:42:33 +08:00
ffStrbufInitS ( & result -> prettyName , result -> exeName );
2026-03-29 09:28:49 +08:00
} else {
2024-01-12 20:42:33 +08:00
ffStrbufInitCopy ( & result -> prettyName , & result -> processName );
2026-03-29 09:28:49 +08:00
}
2024-01-12 16:40:21 +08:00
}
2024-01-18 14:07:29 +08:00
#if defined(MAXPATH)
2026-04-15 16:16:46 +08:00
#define FF_EXE_PATH_LEN MAXPATH
2024-01-12 20:42:33 +08:00
#elif defined(PATH_MAX)
2026-04-15 16:16:46 +08:00
#define FF_EXE_PATH_LEN PATH_MAX
2024-01-12 20:42:33 +08:00
#else
2026-04-15 16:16:46 +08:00
#define FF_EXE_PATH_LEN 260
2024-01-12 20:42:33 +08:00
#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
2024-01-12 16:40:21 +08:00
pid_t ppid = getppid ();
2024-08-23 15:52:14 +08:00
const char * ignoreParent = getenv ( "FFTS_IGNORE_PARENT" );
2026-03-29 09:28:49 +08:00
if ( ignoreParent && ffStrEquals ( ignoreParent , "1" )) {
2024-08-23 15:52:14 +08:00
FF_STRBUF_AUTO_DESTROY _ = ffStrbufCreate ();
2026-07-10 18:08:04 +08:00
ffProcessGetBasicInfoLinux ( ppid , & _ , & ppid , nullptr );
2024-08-23 15:52:14 +08:00
}
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 ) {
2026-09-25 10:58:27 +08:00
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 ;
2024-01-12 20:42:33 +08:00
}
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
}
2024-01-12 20:42:33 +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 ;
2024-01-12 20:42:33 +08:00
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 ) {
2026-09-25 10:58:27 +08:00
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
}
}
2023-01-10 19:24:59 +08:00
2026-09-21 22:46:40 +08:00
return result ;
2021-06-14 23:22:14 +02:00
}