2023-02-01 15:56:25 +01:00
#include "terminalshell.h"
2023-01-28 12:40:12 +01:00
#include "common/io/io.h"
2022-07-25 13:59:19 +02:00
#include "common/parsing.h"
2022-07-25 14:45:41 +02:00
#include "common/processing.h"
2022-10-12 14:13:20 +08:00
#include "common/thread.h"
2023-02-01 15:56:25 +01:00
#include "util/stringUtils.h"
2024-01-18 14:07:29 +08:00
#include "util/mallocHelper.h"
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>
2024-01-18 14:07:29 +08:00
#if defined(__FreeBSD__) || defined(__APPLE__)
2022-10-26 19:49:25 +08:00
#include <sys/types.h>
#include <sys/user.h>
#include <sys/sysctl.h>
2022-10-11 14:59:09 +08:00
#endif
2021-06-14 23:22:14 +02: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 , '/' );
if ( lastSlashIndex < exe -> length )
* exeName = exe -> chars + lastSlashIndex + 1 ;
}
2022-10-11 14:59:09 +08:00
static void getProcessInformation ( pid_t pid , FFstrbuf * processName , FFstrbuf * exe , const char ** exeName )
2021-06-14 23:22:14 +02:00
{
2022-10-08 22:16:45 +08:00
assert ( processName -> length > 0 );
2022-10-11 14:59:09 +08:00
ffStrbufClear ( exe );
2022-10-08 22:16:45 +08:00
2022-10-14 16:39:48 +08:00
#ifdef __linux__
2021-06-14 23:22:14 +02:00
2022-10-11 14:59:09 +08:00
char cmdlineFilePath [ 64 ];
snprintf ( cmdlineFilePath , sizeof ( cmdlineFilePath ), "/proc/%d/cmdline" , ( int ) pid );
if ( ffAppendFileBuffer ( cmdlineFilePath , exe ))
2022-10-08 22:16:45 +08:00
{
2023-12-23 04:55:14 +09:00
ffStrbufTrimRightSpace ( exe );
2024-01-18 14:07:29 +08:00
ffStrbufRecalculateLength ( exe ); //Trim the arguments
ffStrbufTrimLeft ( exe , '-' ); //Login shells start with a dash
2022-10-08 22:16:45 +08:00
}
2021-06-14 23:22:14 +02:00
2022-10-11 14:59:09 +08:00
#elif defined(__APPLE__)
2024-01-18 14:07:29 +08:00
size_t len = 0 ;
int mibs [] = { CTL_KERN , KERN_PROCARGS2 , pid };
if ( sysctl ( mibs , sizeof ( mibs ) / sizeof ( * mibs ), NULL , & len , NULL , 0 ) == 0 )
{
FF_AUTO_FREE char * const procArgs2 = malloc ( len );
if ( sysctl ( mibs , sizeof ( mibs ) / sizeof ( * mibs ), procArgs2 , & len , NULL , 0 ) == 0 )
{
// https://gist.github.com/nonowarn/770696#file-getargv-c-L46
uint32_t argc = * ( uint32_t * ) procArgs2 ;
const char * realExePath = procArgs2 + sizeof ( argc );
const char * arg0 = memchr ( realExePath , '\0' , len - ( size_t ) ( realExePath - procArgs2 ));
while ( * arg0 == '\0' ) arg0 ++ ;
assert ( arg0 < procArgs2 + len );
if ( * arg0 == '-' ) arg0 ++ ; // Login shells
ffStrbufSetS ( exe , arg0 );
}
}
2022-10-11 14:59:09 +08:00
2023-08-24 10:49:59 +08:00
#elif defined(__FreeBSD__)
2022-10-11 14:59:09 +08:00
2024-01-19 10:15:02 +08:00
size_t size = ARG_MAX ;
FF_AUTO_FREE char * args = malloc ( size );
if ( sysctl (
( int []){ CTL_KERN , KERN_PROC , KERN_PROC_ARGS , pid }, 4 ,
args , & size ,
2022-10-26 19:49:25 +08:00
NULL , 0
2024-01-19 10:15:02 +08:00
) == 0 )
{
char * arg0 = args ;
size_t arg0Len = strlen ( args );
if ( size > arg0Len + 1 )
{
char * p = ( char * ) memrchr ( args , '/' , arg0Len );
if ( p )
{
p ++ ;
if ( ffStrStartsWith ( p , "python" )) // /usr/local/bin/python3.9 /home/carter/.local/bin/xonsh
{
arg0 += arg0Len + 1 ;
}
}
}
if ( arg0 [ 0 ] == '-' ) arg0 ++ ;
ffStrbufSetS ( exe , arg0 );
}
2022-10-11 14:59:09 +08:00
#endif
2021-06-14 23:22:14 +02:00
if ( exe -> length == 0 )
ffStrbufSet ( exe , processName );
setExeName ( exe , exeName );
}
2022-10-11 14:59:09 +08:00
static const char * getProcessNameAndPpid ( pid_t pid , char * name , pid_t * ppid )
2021-06-14 23:22:14 +02:00
{
2022-10-14 16:39:48 +08:00
#ifdef __linux__
2021-06-14 23:22:14 +02:00
2022-10-11 14:59:09 +08:00
char statFilePath [ 64 ];
snprintf ( statFilePath , sizeof ( statFilePath ), "/proc/%d/stat" , ( int ) pid );
2023-12-04 17:42:00 +09:00
char buf [ PROC_FILE_BUFFSIZ ];
ssize_t nRead = ffReadFileData ( statFilePath , sizeof ( buf ) - 1 , buf );
if ( nRead < 0 )
2024-01-12 21:13:59 +08:00
return "ffReadFileData(statFilePath, sizeof(buf)-1, buf) failed" ;
2023-12-04 17:42:00 +09:00
buf [ nRead ] = '\0' ;
2022-10-11 14:59:09 +08:00
* ppid = 0 ;
2024-01-17 23:12:55 +08:00
static_assert ( sizeof ( * ppid ) == sizeof ( int ), "" );
2022-10-11 14:59:09 +08:00
if (
2023-12-04 17:42:00 +09:00
sscanf ( buf , "%*s (%255[^)]) %*c %d" , name , ppid ) != 2 || //stat (comm) state ppid
2022-10-11 14:59:09 +08:00
! ffStrSet ( name ) ||
* ppid == 0
)
2023-12-12 09:50:36 +08:00
return "sscanf(stat) failed" ;
2021-06-14 23:22:14 +02:00
2022-10-11 14:59:09 +08:00
#elif defined(__APPLE__)
2024-01-18 14:07:29 +08:00
struct kinfo_proc proc ;
size_t size = sizeof ( proc );
if ( sysctl (
( int []){ CTL_KERN , KERN_PROC , KERN_PROC_PID , pid }, 4 ,
& proc , & size ,
NULL , 0
))
return "sysctl(KERN_PROC_PID) failed" ;
2022-10-11 14:59:09 +08:00
2024-01-18 14:07:29 +08:00
* ppid = ( pid_t ) proc . kp_eproc . e_ppid ;
strncpy ( name , proc . kp_proc . p_comm , MAXCOMLEN ); //trancated to 16 chars
2023-12-12 09:50:36 +08:00
#elif defined(__FreeBSD__)
2022-10-11 14:59:09 +08:00
2022-10-26 19:49:25 +08:00
struct kinfo_proc proc ;
size_t size = sizeof ( proc );
if ( sysctl (
( int []){ CTL_KERN , KERN_PROC , KERN_PROC_PID , pid }, 4 ,
& proc , & size ,
NULL , 0
))
2023-12-12 09:50:36 +08:00
return "sysctl(KERN_PROC_PID) failed" ;
* ppid = ( pid_t ) proc . ki_ppid ;
strncpy ( name , proc . ki_comm , COMMLEN );
#else
return "Unsupported platform" ;
2022-10-11 14:59:09 +08:00
#endif
2023-12-12 09:50:36 +08:00
return NULL ;
2022-10-11 14:59:09 +08:00
}
2024-01-12 20:42:33 +08:00
static pid_t getShellInfo ( FFShellResult * result , pid_t pid )
2022-10-11 14:59:09 +08:00
{
2021-06-14 23:22:14 +02:00
char name [ 256 ];
name [ 0 ] = '\0' ;
2022-10-11 14:59:09 +08:00
pid_t ppid = 0 ;
2021-06-14 23:22:14 +02:00
2024-01-12 23:57:56 +09:00
while ( getProcessNameAndPpid ( pid , name , & ppid ) == NULL )
{
//Common programs that are between terminal and own process, but are not the shell
if (
ffStrEquals ( name , "sh" ) || //This prevents us from detecting things like pipes and redirects, i hope nobody uses plain `sh` as shell
ffStrEquals ( name , "sudo" ) ||
ffStrEquals ( name , "su" ) ||
ffStrEquals ( name , "strace" ) ||
ffStrEquals ( name , "sshd" ) ||
ffStrEquals ( name , "gdb" ) ||
ffStrEquals ( name , "lldb" ) ||
ffStrEquals ( name , "lldb-mi" ) ||
ffStrEquals ( name , "login" ) ||
ffStrEquals ( name , "ltrace" ) ||
ffStrEquals ( name , "perf" ) ||
ffStrEquals ( name , "guake-wrapped" ) ||
ffStrContainsIgnCase ( name , "debug" ) ||
ffStrContainsIgnCase ( name , "not-found" ) ||
ffStrEndsWith ( name , ".sh" )
)
{
pid = ppid ;
continue ;
}
result -> pid = ( uint32_t ) pid ;
result -> ppid = ( uint32_t ) ppid ;
ffStrbufSetS ( & result -> processName , name );
getProcessInformation ( pid , & result -> processName , & result -> exe , & result -> exeName );
break ;
}
2024-01-12 16:40:21 +08:00
return ppid ;
}
2024-01-12 20:42:33 +08:00
static pid_t getTerminalInfo ( FFTerminalResult * result , pid_t pid )
2024-01-12 16:40:21 +08:00
{
char name [ 256 ];
name [ 0 ] = '\0' ;
pid_t ppid = 0 ;
2024-01-12 23:57:56 +09:00
while ( getProcessNameAndPpid ( pid , name , & ppid ) == NULL )
{
//Known shells
if (
ffStrEquals ( name , "ash" ) ||
ffStrEquals ( name , "bash" ) ||
ffStrEquals ( name , "zsh" ) ||
ffStrEquals ( name , "ksh" ) ||
ffStrEquals ( name , "mksh" ) ||
ffStrEquals ( name , "oksh" ) ||
ffStrEquals ( name , "csh" ) ||
ffStrEquals ( name , "tcsh" ) ||
ffStrEquals ( name , "fish" ) ||
ffStrEquals ( name , "dash" ) ||
ffStrEquals ( name , "pwsh" ) ||
ffStrEquals ( name , "nu" ) ||
ffStrEquals ( name , "git-shell" ) ||
ffStrEquals ( name , "elvish" ) ||
ffStrEquals ( name , "oil.ovm" ) ||
2024-01-18 14:07:29 +08:00
ffStrEquals ( name , "xonsh" ) || // works in Linux but not in macOS because kernel returns `Python` in this case
2024-01-17 23:45:39 +08:00
ffStrEndsWith ( name , ".sh" )
2024-01-12 23:57:56 +09:00
)
{
pid = ppid ;
continue ;
}
#ifdef __APPLE__
// https://github.com/fastfetch-cli/fastfetch/discussions/501
if ( ffStrEndsWith ( name , " (figterm)" ) || ffStrEndsWith ( name , " (cwterm)" ))
{
if ( __builtin_expect ( getProcessNameAndPpid ( ppid , name , & ppid ) != NULL , false ))
return 0 ;
}
#endif
result -> pid = ( uint32_t ) pid ;
ffStrbufSetS ( & result -> processName , name );
getProcessInformation ( pid , & result -> processName , & result -> exe , & result -> exeName );
break ;
}
2024-01-12 16:40:21 +08:00
return ppid ;
2021-06-14 23:22:14 +02:00
}
2024-01-12 20:42:33 +08:00
static void getTerminalFromEnv ( FFTerminalResult * result )
2021-06-14 23:22:14 +02:00
{
if (
2024-01-12 20:42:33 +08:00
result -> processName . length > 0 &&
2024-01-12 21:13:59 +08:00
! ffStrbufStartsWithS ( & result -> processName , "login" ) &&
! ffStrbufEqualS ( & result -> processName , "(login)" ) &&
2022-11-17 18:14:14 +08:00
#ifdef __APPLE__
2024-01-12 21:13:59 +08:00
! ffStrbufEqualS ( & result -> processName , "launchd" ) &&
! ffStrbufEqualS ( & result -> processName , "stable" ) && //for WarpTerminal
2022-11-17 18:14:14 +08:00
#else
2024-01-12 21:13:59 +08:00
! ffStrbufEqualS ( & result -> processName , "systemd" ) &&
! ffStrbufEqualS ( & result -> processName , "init" ) &&
! ffStrbufEqualS ( & result -> processName , "(init)" ) &&
2022-11-17 18:14:14 +08:00
#endif
2024-01-13 23:36:58 +08:00
! ffStrbufEqualS ( & result -> processName , "0" )
2021-06-14 23:22:14 +02:00
) return ;
2022-10-14 18:38:18 +08:00
const char * term = NULL ;
2021-10-17 01:05:45 +08:00
//SSH
if ( getenv ( "SSH_CONNECTION" ) != NULL )
term = getenv ( "SSH_TTY" );
2022-11-17 18:14:14 +08:00
#ifdef __linux__
2021-10-17 01:05:45 +08:00
//Windows Terminal
2022-06-18 14:51:10 +02:00
if ( ! ffStrSet ( term ) && (
getenv ( "WT_SESSION" ) != NULL ||
getenv ( "WT_PROFILE_ID" ) != NULL
)) term = "Windows Terminal" ;
2023-07-16 13:12:50 +08:00
//ConEmu
if ( ! ffStrSet ( term ) && (
getenv ( "ConEmuPID" ) != NULL
)) term = "ConEmu" ;
2022-11-17 18:14:14 +08:00
#endif
2021-10-17 01:05:45 +08:00
2022-09-21 15:15:36 +08:00
//Alacritty
if ( ! ffStrSet ( term ) && (
getenv ( "ALACRITTY_SOCKET" ) != NULL ||
getenv ( "ALACRITTY_LOG" ) != NULL ||
getenv ( "ALACRITTY_WINDOW_ID" ) != NULL
)) term = "Alacritty" ;
2022-10-14 18:51:04 +08:00
#ifdef __ANDROID__
2022-06-18 00:45:28 +02:00
//Termux
if ( ! ffStrSet ( term ) && (
getenv ( "TERMUX_VERSION" ) != NULL ||
2023-10-24 10:14:08 +08:00
getenv ( "TERMUX_MAIN_PACKAGE_FORMAT" ) != NULL
2023-09-03 20:49:46 +08:00
)) term = "com.termux" ;
2022-10-14 18:51:04 +08:00
#endif
2022-06-18 00:45:28 +02:00
2022-10-14 18:51:04 +08:00
#ifdef __linux__
2022-09-20 11:16:08 +02:00
//Konsole
if ( ! ffStrSet ( term ) && (
getenv ( "KONSOLE_VERSION" ) != NULL
)) term = "konsole" ;
2022-10-14 18:51:04 +08:00
#endif
2022-09-20 11:16:08 +02:00
2022-10-02 17:52:09 +08:00
//MacOS, mintty
2022-09-04 09:02:11 -07:00
if ( ! ffStrSet ( term ))
term = getenv ( "TERM_PROGRAM" );
2023-11-01 08:55:00 +08:00
if ( ! ffStrSet ( term ))
term = getenv ( "LC_TERMINAL" );
2021-10-17 01:05:45 +08:00
//Normal Terminal
2022-01-07 18:00:38 +01:00
if ( ! ffStrSet ( term ))
2021-10-17 01:05:45 +08:00
term = getenv ( "TERM" );
2021-06-14 23:22:14 +02:00
//TTY
2024-01-12 21:13:59 +08:00
if ( ! ffStrSet ( term ) || ffStrEquals ( term , "linux" ))
2021-06-14 23:22:14 +02:00
term = ttyname ( STDIN_FILENO );
2022-10-08 22:16:45 +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
}
2024-01-12 20:42:33 +08:00
static void getUserShellFromEnv ( FFShellResult * result )
2021-06-14 23:22:14 +02:00
{
//If shell detection via processes failed
2024-01-12 20:42:33 +08:00
if ( result -> processName . length == 0 && instance . state . platform . userShell . length > 0 )
2021-06-14 23:22:14 +02:00
{
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
}
}
2022-10-14 16:28:03 +08:00
bool fftsGetShellVersion ( FFstrbuf * exe , const char * exeName , FFstrbuf * version );
2023-01-10 19:24:59 +08:00
bool fftsGetTerminalVersion ( FFstrbuf * processName , FFstrbuf * exe , FFstrbuf * version );
2024-01-12 20:42:33 +08:00
static void setShellInfoDetails ( FFShellResult * result )
2024-01-12 16:40:21 +08:00
{
2024-01-12 20:42:33 +08:00
ffStrbufClear ( & result -> version );
fftsGetShellVersion ( & result -> exe , result -> exeName , & result -> version );
if ( ffStrbufEqualS ( & result -> processName , "pwsh" ))
ffStrbufInitStatic ( & result -> prettyName , "PowerShell" );
else if ( ffStrbufEqualS ( & result -> processName , "nu" ))
ffStrbufInitStatic ( & result -> prettyName , "nushell" );
2024-01-12 21:13:59 +08:00
else if ( ffStrbufEqualS ( & result -> processName , "oil.ovm" ))
2024-01-12 20:42:33 +08:00
ffStrbufInitStatic ( & result -> prettyName , "Oils" );
2024-01-12 16:40:21 +08:00
else
{
// 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
}
}
2024-01-12 20:42:33 +08:00
static void setTerminalInfoDetails ( FFTerminalResult * result )
2024-01-12 16:40:21 +08:00
{
2024-01-12 20:42:33 +08:00
if ( result -> exeName [ 0 ] == '.' && ffStrEndsWith ( result -> exeName , "-wrapped" ))
2024-01-12 16:40:21 +08:00
{
// For NixOS. Ref: #510 and https://github.com/NixOS/nixpkgs/pull/249428
2024-01-12 20:42:33 +08:00
// We use processName when detecting version and font, overriding it for simplification
2024-01-12 16:40:21 +08:00
ffStrbufSetNS (
2024-01-12 20:42:33 +08:00
& result -> processName ,
( uint32_t ) ( strlen ( result -> exeName ) - strlen ( ".-wrapped" )),
result -> exeName + 1 );
2024-01-12 16:40:21 +08:00
}
2024-01-12 20:42:33 +08:00
if ( ffStrbufEqualS ( & result -> processName , "wezterm-gui" ))
ffStrbufInitStatic ( & result -> prettyName , "WezTerm" );
2024-01-14 17:26:54 +08:00
else if ( ffStrbufStartsWithS ( & result -> processName , "tmux:" ))
2024-01-12 20:42:33 +08:00
ffStrbufInitStatic ( & result -> prettyName , "tmux" );
2024-01-12 16:40:21 +08:00
#if defined(__ANDROID__)
2024-01-12 20:42:33 +08:00
else if ( ffStrbufEqualS ( & result -> processName , "com.termux" ))
ffStrbufInitStatic ( & result -> prettyName , "Termux" );
2024-01-12 16:40:21 +08:00
#elif defined(__linux__) || defined(__FreeBSD__)
2024-01-12 20:42:33 +08:00
else if ( ffStrbufStartsWithS ( & result -> processName , "gnome-terminal-" ))
ffStrbufInitStatic ( & result -> prettyName , "GNOME Terminal" );
else if ( ffStrbufStartsWithS ( & result -> processName , "kgx" ))
ffStrbufInitStatic ( & result -> prettyName , "GNOME Console" );
else if ( ffStrbufEqualS ( & result -> processName , "urxvt" ) ||
ffStrbufEqualS ( & result -> processName , "urxvtd" ) ||
ffStrbufEqualS ( & result -> processName , "rxvt" )
2024-01-12 16:40:21 +08:00
)
2024-01-12 20:42:33 +08:00
ffStrbufInitStatic ( & result -> prettyName , "rxvt-unicode" );
2024-01-12 16:40:21 +08:00
#elif defined(__APPLE__)
2024-01-12 20:42:33 +08:00
else if ( ffStrbufEqualS ( & result -> processName , "iTerm.app" ) || ffStrbufStartsWithS ( & result -> processName , "iTermServer-" ))
ffStrbufInitStatic ( & result -> prettyName , "iTerm" );
else if ( ffStrbufEqualS ( & result -> processName , "Apple_Terminal" ))
ffStrbufInitStatic ( & result -> prettyName , "Apple Terminal" );
else if ( ffStrbufEqualS ( & result -> processName , "WarpTerminal" ))
ffStrbufInitStatic ( & result -> prettyName , "Warp" );
2024-01-12 16:40:21 +08:00
#endif
2024-01-12 20:42:33 +08:00
else if ( strncmp ( result -> exeName , result -> processName . chars , result -> processName . length ) == 0 ) // if exeName starts with processName, print it. Otherwise print processName
ffStrbufInitS ( & result -> prettyName , result -> exeName );
2024-01-12 16:40:21 +08:00
else
2024-01-12 20:42:33 +08:00
ffStrbufInitCopy ( & result -> prettyName , & result -> processName );
2024-01-12 16:40:21 +08:00
2024-01-12 20:42:33 +08:00
ffStrbufInit ( & result -> version );
fftsGetTerminalVersion ( & result -> processName , & result -> exe , & result -> version );
2024-01-12 16:40:21 +08:00
}
2024-01-18 14:07:29 +08:00
#if defined(MAXPATH)
2024-01-12 20:42:33 +08:00
#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
const FFShellResult * ffDetectShell ()
2021-06-14 23:22:14 +02:00
{
2024-01-12 20:42:33 +08:00
static FFShellResult result ;
2021-06-14 23:22:14 +02:00
static bool init = false ;
if ( init )
return & result ;
init = true ;
2024-01-12 20:42:33 +08:00
ffStrbufInit ( & result . processName );
ffStrbufInitA ( & result . exe , FF_EXE_PATH_LEN );
result . exeName = result . exe . chars ;
ffStrbufInit ( & result . version );
result . pid = 0 ;
result . ppid = 0 ;
2021-06-14 23:22:14 +02:00
2024-01-12 16:40:21 +08:00
pid_t ppid = getppid ();
ppid = getShellInfo ( & result , ppid );
2023-06-24 10:59:53 +08:00
getUserShellFromEnv ( & result );
2024-01-12 16:40:21 +08:00
setShellInfoDetails ( & result );
2021-06-14 23:22:14 +02:00
2024-01-12 20:42:33 +08:00
return & result ;
}
const FFTerminalResult * ffDetectTerminal ()
{
static FFTerminalResult result ;
static bool init = false ;
if ( init )
return & result ;
init = true ;
ffStrbufInit ( & result . processName );
ffStrbufInitA ( & result . exe , FF_EXE_PATH_LEN );
result . exeName = result . exe . chars ;
result . pid = 0 ;
result . ppid = 0 ;
pid_t ppid = ( pid_t ) ffDetectShell () -> ppid ;
2024-01-12 16:40:21 +08:00
if ( ppid )
ppid = getTerminalInfo ( & result , ppid );
getTerminalFromEnv ( & result );
setTerminalInfoDetails ( & result );
2023-01-10 19:24:59 +08:00
2021-06-14 23:22:14 +02:00
return & result ;
}