package Terminal::Identify;
# ------------------------------------------------------------------------------
# This package is inspired by a method using a shell like BASH to identify the
# terminal emulator which is in use by the logged-in user.
#
# The Linux command ps can be used to do this: 
# ps -o 'cmd=' -p $(ps -o 'ppid=' -p $$)
# ------------------------------------------------------------------------------

# Load the basic Perl pragmas.
use 5.030000;
use strict;
use warnings;

# Load the Perl pragma Exporter.
use vars qw(@ISA @EXPORT @EXPORT_OK);
use Exporter 'import';

# Export the implemented subroutines and the global variable.
our @EXPORT = qw(
    whichterminalami
    whichtermami
    which_terminal
    identify_terminal
    $OutputFormat
);

# Base class of this module.
our @ISA = qw(Exporter);

# Set the package version. 
our $VERSION = '0.14';

# Load the Perl modules.
use POSIX qw(ttyname);

# Define the directory for Inline. 
use Inline Config => DIRECTORY => '/tmp/';

# Use inline C code.
use Inline 'C';

# Define the Perl BEGIN block.
BEGIN {
    # Set the subroutine aliases.
    *whichtermami = \&whichterminalami;
    *which_terminal = \&whichterminalami;
    *identify_terminal = \&whichterminalami;
};

# Set the global output format.
our $OutputFormat = ""; 

# Assign terminal process name to known terminal name.
my %termhash = (
    "alacritty"              => "Alacritty",               # successful tested 
    "altyo"                  => "AltYo",                   # known, but not tested 
    "aterm"                  => "aterm",                   # successful tested
    "black-screen"           => "Black Screen",            # know, but not tested
    "cool-retro-term"        => "Cool Retro Term",         # successful tested
    "deepin-terminal"        => "Deepin Terminal",         # successful tested
    "domterm"                => "DomTerm",                 # know, but not tested
    "Eterm"                  => "Eterm",                   # successful tested
    "evilvte"                => "EvilVTE",                 # know, but not tested
    "extraterm"              => "Extraterm",               # know, but not tested 
    "fbpad"                  => "Fbpad",                   # know, but not tested
    "fbterm"                 => "Fbterm",                  # know, but not tested
    "finalterm"              => "Final Term",              # know, but not tested  
    "fish"                   => "FISH",                    # know, but not tested
    "flexterm"               => "FlexTerm",                # know, but not tested
    "foot"                   => "foot",                    # know, but not tested
    "gnome-terminal-server"  => "Gnome Terminal",          # successful tested
    "guake"                  => "Guake Terminal",          # successful tested
    "havoc"                  => "Havoc",                   # know, but not tested
    "hyper"                  => "Hyper",                   # successful tested
    "kitty"                  => "kitty",                   # successful tested
    "konsole"                => "Konsole",                 # successful tested
    "lilyterm"               => "LilyTerm",                # successful tested
    "lxterminal"             => "LXTerminal",              # successful tested
    "mate-terminal"          => "MATE-Terminal",           # successful tested
    "mlterm"                 => "mlterm",                  # successful tested
    "mlterm-tiny"            => "mlterm-tiny",             # successful tested
    "pantheon-terminal"      => "Pantheon Terminal",       # know, but not tested 
    "pterm"                  => "pterm",                   # successful tested
    "qterminal"              => "QTerminal",               # successful tested
    "roxterm"                => "ROXTerm",                 # successful tested
    "rxvt"                   => "rxvt",                    # successful tested
    "rxvt-unicode"           => "rxvt-unicode",            # successful tested
    "rxvt-unicode-truecolor" => "rxvt-unicode-truecolor",  # know, but not tested
    "sakura"                 => "Sakura",                  # successful tested
    "screen"                 => "SCREEN",                  # successful tested
    "stterm"                 => "stterm",                  # successful tested
    "tabby"                  => "Tabby",                   # successful tested
    "terminal"               => "Terminal",                # know, but not tested
    "terminator"             => "Terminator",              # successful tested
    "terminology"            => "Terminology",             # successful tested
    "terminix"               => "Terminix",                # know, but not tested
    "termit"                 => "Termit",                  # successful tested
    "termite"                => "Termite",                 # know, but not tested
    "tilda"                  => "Tilda",                   # successful tested
    "tilix"                  => "Tilix",                   # successful tested
    "tinyterm"               => "TinyTERM",                # know, but not tested
    "tmux"                   => "tmux",                    # successful tested
    "tym"                    => "tym",                     # know, but not tested 
    "urxvt"                  => "urxvt",                   # successful tested
    "uxterm"                 => "UXTerm",                  # successful tested
    "wazaterm"               => "Wazaterm",                # know, but not tested
    "wterm"                  => "Wterm",                   # know, but not tested
    "wezterm"                => "WezTerm",                 # Test failed
    "xfce4-terminal"         => "Xfce4-Terminal-Emulator", # successful tested
    "xiki"                   => "Xiki",                    # know, but not tested
    "xiterm+thai"            => "xiterm+thai",             # successful tested
    "xterm"                  => "XTerm",                   # successful tested
    "xvt"                    => "Xvt",                     # know, but not tested
    "yakuake"                => "Yakuake"                  # successful tested
);

# Set some variables.
our $FN_PASSWD = "/etc/passwd";
our $FN_SHELLS = "/etc/shells";

#------------------------------------------------------------------------------#
# Subroutine trim                                                              #
#                                                                              #
# Description:                                                                 #
# The subroutine removes white spaces from both ends of a string. This is done #
# by a logical or operation and using \s from regular expressions. Anchors are #
# begin ^ of string and end $ of string.                                       #
#                                                                              #
# @argument: $_[0] => $string  String to trim  (scalar)                        #
# @return:   $string           Trimmed string  (scalar)                        #
#------------------------------------------------------------------------------# 
sub trim {
    # Assign the function argument to the local string variable $str.  
    my $string = ((defined $_[0] && $_[0] ne "") ? $_[0] : "");
    # Trim the string from the left side and the right side.
    $string =~ s/^\s+|\s+$//g;
    # Return the trimmed string.
    return $string;    
};

#------------------------------------------------------------------------------# 
# Subroutine search_brackets                                                   #
#                                                                              #
# Description:                                                                 #
# The subroutine adds the square brackets [ and ] to the searchstring for      #
# use with grep. E.g. shell becomes [s]hell. Therefor the string must have     #
# a minumum length of 1.                                                       # 
#                                                                              #
# @argument: $_[0] => $str  Searchstring                       (scalar)        #
# @return:   $str           Searchstring with square brackets  (scalar)        #
#------------------------------------------------------------------------------# 
sub search_brackets {
    # Initialise the local variable $searchstring.
    my $searchstring = "";
    # Check if $searchstring is defined and is not empty.
    if ((defined $_[0]) && (length($_[0]) > 0)) {
        # Assign the subroutine argument to the local variable.  
        $searchstring = $_[0];
    } else {
        # Return string of length 0. 
        return "";
    }
    # Add square brackets [ and ] to the given string.
    substr($searchstring, 0, 0) = '[';
    substr($searchstring, 2, 0) = ']';
    # Return the modified string.
    return $searchstring;
};

#------------------------------------------------------------------------------# 
# Subroutine read_file                                                         #
#                                                                              #
# Description:                                                                 #
# Read a file in one chunk. The retrieved content is stored in a string        #
# variable.                                                                    #
#                                                                              #
# @argument: $_[0] => $file  Filename      (scalar)                            #
# @return:   $content        File content  (scalar)                            #
#------------------------------------------------------------------------------# 
sub read_file {
    # Assign the function argument to the local variable.  
    my $file = $_[0];
    # Open a file handler for reading.
    open(my $fh, "<", $file);
    # Read the complete content from the file.
    my $content = do {local $/; <$fh>};
    # Close the file handler.
    close($fh);
    # Return the file content.
    return $content;
};

#------------------------------------------------------------------------------# 
# Subroutine login_users                                                       #
#                                                                              #
# Description:                                                                 #
# Split the logged-in users by sperator space and store them to an array.      #
#                                                                              #
# @argument: None                                                              #
# @return:   $user_arr  Array with the logged-in users  (array)                #
#------------------------------------------------------------------------------# 
sub login_users {
    # Get the logged-in users from Linux command users.   
    my $user = `users`;
    # Split the logged-in users and store them to the array.
    my @user_array = split ' ', $user;
    # Return the array with the logged-in users.
    return @user_array;
};

#------------------------------------------------------------------------------# 
# Subroutine login_shells                                                      #
#                                                                              #
# Description:                                                                 #
# Read the content of the file /etc/shells and store the content in a string   #
# variable. Then the content is splited up into lines. In a loop the lines     #
# are used where a valid login shell is given. Then the path is removed from   #
# the line with the login shell. The login shell is added to an array. In a    #
# last step douple entries are removed from the array.                         #
#                                                                              #
# @argument: None                                                              #
# @return:   @login_shells  Array with valid login shells  (array)             #
#------------------------------------------------------------------------------# 
sub login_shells {
    # Declare the login shells array.
    my @login_shells;
    # Set the file for reading.
    my $file = "/etc/shells";
    # Read the content from the file.
    my $content = read_file($file);
    # Loop over the lines of the file content.
    foreach (split '\n', $content) {
        # Trim the new line.
        my $line = trim($_);
        # Use only a line with a valid login shell. 
        if ($line =~ /^\/.*\/(.*)$/) {
            # Remove the path from the line with the login shell.
            my ($shell) = $line =~ /^\/.*\/(.*)$/;
            # Add the login shell to the array.
            push(@login_shells, $shell);
        };
    };
    # Remove the double entries from the array.
    my %login_hash = map({$_, 1} @login_shells);
    @login_shells = keys %login_hash;
    # Return the array with the unique login shells.
    return @login_shells;
};

#------------------------------------------------------------------------------# 
# Subroutine get_ppid                                                          #
#                                                                              #
# Description:                                                                 #
# Determine the PPID of the calling Perl script using the Linux command ps.    # 
#                                                                              #
# @argument: $_[0] => $pid  PID   (scalar)                                     # 
# @return:   $ppid          PPID  (scalar)                                     # 
#------------------------------------------------------------------------------# 
sub get_ppid {
    # Assign the subroutine argument to the local variable $pid.
    my $pid = $_[0]; 
    # Store the output of the Linux command ps in the local variable $ppid.
    my $ppid = `ps --no-headers -o ppid:1 -p $pid --sort lstart 2>/dev/null`;
    # Split a multiline Perl scalar with the PPID's in parts.
    my @parts = split /\s+/, $ppid;
    # If there is more than one PPID, use the first PPID.
    $ppid = $parts[0];
    # Trim the Perl scalar with the PPID.  
    $ppid = trim($ppid);
    # Return the PPID.
    return $ppid;
};

#------------------------------------------------------------------------------# 
# Subroutine term_user_shell                                                   #
#                                                                              #
# Description:                                                                 #
# Create a multi dimensional array with term, user and shell.                  #
#                                                                              #
# @arguments: $_[0] => $passwd_content  Content of passwd      (scalar)        # 
#             @{$_[1]} => @user_array   User array             (array)         # 
#             @{$_[2]} => @shell_array  Shell array            (array)         # 
# @return:    @result_array             Array with the result  (array)         #
#------------------------------------------------------------------------------# 
sub term_user_shell {
    # Assign the subroutine arguments to the local variables.
    my $passwd_content = $_[0];
    my @user_array = @{$_[1]};
    my @shell_array = @{$_[2]};
    # Get the username related to the terminal.
    my $username = (getlogin() || (getpwuid($<))[0] ||
                    $ENV{LOGNAME} || $ENV{USER});
    # Declare the return array.
    my @result_array = ();
    # Get the terminal path.
    #my $term_path = TermPath();
    my $fileno = fileno(STDIN);
    my $term_path = ttyname($fileno);
    # If $term_path is not defined leave the subroutine.
    if (!defined $term_path) {
        # Return an empty array. 
        return ();
    };
    # Extract the pseudo terminal slave. 
    my ($term) = $term_path =~ /^.*\/dev\/(pts\/\d+)$/;
    # If $term is not defined check if it is a ? or a tty.
    if (!defined $term) {
        ($term) = $term_path =~ /^.*\/dev\/(tty\d+)$/;
    };
    # Loop over the array with the lines of $passwd_content.
    foreach (split '\n', $passwd_content) {
        # Get user and shell from each line of the content.
        my ($user) = $_ =~ /^(.*?):.*/;
        my ($shell) = $_ =~ /^.*\/(.*)$/;
        if ($shell ne "nologin" && $shell ne "sync" && $shell ne "false") {
            # Check user and shell against the given arrays.
            if (grep(/^$user$/, @user_array) &&
                grep(/^$shell$/, @shell_array)) {
                # Assemble a new list.  
                my @tmp = ($term, $user, $shell); 
                # Add data to array.
                push(@result_array, \@tmp);
            } elsif ($user eq $username) {
                # Assemble a new list.  
                my @tmp = ($term, $user, $shell); 
                # Add data to array.
                push(@result_array, \@tmp);
            }; 
        };
    };
    # Return the array.
    return @result_array;
};

#------------------------------------------------------------------------------# 
# Subroutine terminal_process                                                  #
#                                                                              #
# Description:                                                                 # 
# Get the process related to term, user and shell.                             #
#                                                                              #
# @argument: @{$_[0]} => @data  Array with term, user and shell  (array)       # 
# @return:   @result            Array with matching processes    (array)       #
#------------------------------------------------------------------------------# 
sub terminal_process {
    # Assign the subroutine argument to the local variable.
    my @data = @{$_[0]};
    # Initialise the return array.
    my @match = ();
    # Loop over the elements of the array.
    foreach my $item (@data) {
        # Get term, user and shell from the array.
        my $term = $item->[0];
        my $user = $item->[1];
        my $shell = $item->[2];
        $term = search_brackets($term);
        $user = search_brackets($user);
        $shell = search_brackets($shell);
        # Search for processes which is matching shell, user and terminal.
        my $process = `ps aux | grep $shell | grep $user | grep $term`;
        # Check if the result is not empty.
        if ($process ne "") {
            # Add the process to the return array.
            push(@match, $process);
        };
    };
    # If array @match is empty, it is not a local process.
    if (@match == 0) {
        # Loop over the elements of the array.
        foreach my $item (@data) {
            # Get term and user from the array.
            my $term = $item->[0];
            my $user = $item->[1];
            # Add square brackets to searchstrings.
            $term = search_brackets($term);
            $user = search_brackets($user);
            # Search for processes which is matching shell and terminal.
            my $process = `ps aux | grep $user | grep $term | grep "?" | grep "sshd"`;
            # Check if the result is not empty.
            if ($process ne "") {
                # Add the process to the return array.
                push(@match, $process);
            };
        };
    };
    # Return the array.
    return @match;
};

#------------------------------------------------------------------------------# 
# Subroutine terminal_command_line                                             #
#                                                                              #
# Description:                                                                 #
# Use the Linux command ps to get the command line of the process which is     #
# related to the terminal in use.                                              #
#                                                                              #
# @argument  $_[0] => $ppid  PPID                   (scalar)                   #
# @return    $termproc       Terminal command line  (scalar)                   #
#------------------------------------------------------------------------------# 
sub terminal_command_line {
    # Assign the subroutine argument to the local variable $ppid.
    my $ppid = $_[0];
    # Get the command column from the ps output.
    my $termproc = `ps --no-headers -o cmd:1 -p $ppid 2>/dev/null`;
    # Trim the command line output string.  
    $termproc = trim($termproc);
    # Return the process related to the terminal in use.
    return $termproc
};

#------------------------------------------------------------------------------# 
# Subroutine terminal_identifier                                               #
#                                                                              #
# Description:                                                                 #
# Identify the process command line of the terminal in use.                    #
#                                                                              #
# @argument: None                                                              # 
# @return:   $terminal  Process command line  (scalar)                         #
#------------------------------------------------------------------------------# 
sub terminal_identifier {
    # Declare the return variable $terminal.
    my $terminal_command;
    # Set the filename.
    my $filename = "/etc/passwd";
    # Get the logged-in users.
    my @user_arr = login_users();
    # Get the available login shells.
    my @shell_arr = login_shells();
    # Read the file /etc/passwd in and store it in the variable $content.
    my $content = read_file($filename);
    # Create the array with user and shell.
    my @result = term_user_shell($content, \@user_arr, \@shell_arr);
    # Create the array with user and term.
    my @match = terminal_process(\@result);
    # Check if array is empty.
    if (@match > 0) {
        # Split up the process by lines and white spaces.
        my @columns = split /\s+/, $match[0];
        # Extract the PID from the array.
        my $pid = $columns[1];
        # Get the PPID from the command ps.
        my $ppid = get_ppid($pid);
        # Get the terminal in use from the command ps.
        $terminal_command = terminal_command_line($ppid);
    } else {
        # Set terminal command.
        $terminal_command = "";
    };
    # Return the terminal process command or NONE.
    return $terminal_command;
};

#------------------------------------------------------------------------------# 
# Subroutine check_prereq                                                      # 
#                                                                              #
# Check package prerequisites.                                                 #
#------------------------------------------------------------------------------# 
sub check_prereq {
    # Check if Linux command which exists.
    if (! -x "/bin/which" || ! -x "/usr/bin/which") {
        die "Linux command 'which' does not exist.\n";
    };
    # Check if grep, users and ps exists.
    die unless `which grep`;
    die unless `which users`;
    die unless `which ps`;
    # Check if passwd exists.
    if (! -e $FN_PASSWD) {
        die "$!, $FN_PASSWD not exists";
    };
    # Check if shells exists.
    if (! -e $FN_SHELLS) {
        die "$!, $FN_SHELLS not exists";
    };
};

#------------------------------------------------------------------------------# 
# Subroutine whichterminalami                                                  # 
#                                                                              #
# Description:                                                                 #
# Identify the terminal name.                                                  #
#                                                                              #
# @argument: $_[0] => $flag  Output format flag  (array)                       # 
# @return:   $terminal       Terminal name       (scalar)                      #
#------------------------------------------------------------------------------# 
sub whichterminalami {
    # Get the output flag from the subroutine argument.
    my $flag = (defined $_[0] ? $_[0] : '');
    # Overwriting subroutine argument if existing.
    $flag = ($OutputFormat ne "" ? $OutputFormat : $flag);
    # Initialise the terminal variables.
    my $terminal = "";
    my $terminal_ftn = "";
    my $terminal_name = "";
    my $terminal_path = "";
    # Check prerequisites.
    check_prereq();
    # Identify the terminal process command.
    my $terminal_command = terminal_identifier();
    # If terminal command is empty it is a unknown terminal.
    if ($terminal_command eq "") {
        # Return unknown terminal.
        return "Unknown Terminal";
    };
    # Check if there are spaces in the terminal command string.
    if ($terminal_command =~ /\s+/) {
        my $pparts = $terminal_command;
        # Remove command line arguments.
        $pparts =~ s/(?<= )(-.*?)(?= |$)//g;
        # Split the terminal command line by white spaces.
        my @parts = split /\s+/, $pparts;
        # Check number of valid program parts of the terminal command line.
        if (@parts == 2) {
            # Check first and second element of array @parts.
            if (-f $parts[0] && -f $parts[1]) {
                # If both are files, second part is the terminal. 
                $terminal_path = $parts[1];
            } else {
                # Set variable $terminal_path.
                $terminal_path = $parts[0];
            };
        } else {
            # Set variable $terminal_path.
            $terminal_path = $parts[0];
        };
    } else {
        # Preset variable $terminal_path.
        $terminal_path = $terminal_command;
    };
    # Check if in variable $terminal_path is a path.
    if ($terminal_path =~ /^.*\//) {
        # Remove the path from the Terminal name.
        $terminal_name = $terminal_path =~ s/^.*\///r;
    } else {
        # Set terminal name. 
        $terminal_name = $terminal_path;
    };
    # Lookup terminal name in the terminal hash.
    if (defined $termhash{$terminal_name}) {
        # Get terminal ftn. 
        $terminal_ftn = $termhash{$terminal_name};
    } else {
        # Set terminal ftn. 
        $terminal_ftn = $terminal_name;
    };
    # Return remote console or system console based on sshd or login.
    if (($terminal_name) =~ /^sshd.*/) {
        return "Remote Console";
    } elsif (($terminal_name) =~ /^login.*/) {
        return "System Console";
    };
    # Return the terminal string based on the flag setting.
    if ($flag eq "") {
        $terminal = $terminal_name;
    } elsif ($flag eq "FTN") {
        $terminal = $terminal_ftn;
    } elsif ($flag eq "PATH") {
        $terminal = $terminal_path;
    } elsif ($flag eq "PROC") {
        $terminal = $terminal_command;
    };
    # Return the found terminal string.
    return $terminal;
};

1;

__DATA__
__C__

#include <stdio.h>
#include <unistd.h>

/*
 * -------------------
 * Function TermPath()
 * -------------------
 */
char* TermPath() {
  char* tty_name = ttyname(STDIN_FILENO);
  char* term_path = (tty_name != NULL) ? tty_name : NULL;
  return term_path;
};

__END__

# ---------------------------------------------------------------------------- #
# The package documentation in POD format starts here.                         #
# ---------------------------------------------------------------------------- #

=head1 NAME

Terminal::Identify - Perl extension for identifying the terminal emulator

=head1 SYNOPSIS

Use the package like this

  use Terminal::Identify;                      # Imports all methods
  use Terminal::Identify qw(whichterminalami); # Imports method whichterminalami()

  # Identify the terminal emulator in use.
  whichterminalami(["PROC"|"PATH"|"FTN"]);                     # Standard methods invocation
  Terminal::Identify::whichterminalami(["PROC"|"PATH"|"FTN"]); # Alternate methods invocation

or like this

  use Terminal::Identify;                      # Exports all methods
  use Terminal::Identify qw(whichterminalami); # Exports method whichterminalami()

  # Set the global output format flag of the package.
  $OutputFormat = ["PROC"|"PATH"|"FTN"];
  $Terminal::Identify::OutputFormat = ["PROC"|"PATH"|"FTN"];

  # Identify the terminal emulator in use.
  whichterminalami();                     # Standard methods invocation
  Terminal::Identify::whichterminalami(); # Alternate methods invocation

=head1 OUTPUT CONTROL

The former introduced string arguments C<PROC>, C<PATH> and C<FTN> in square
brackets in the method call are optional.

  ["PROC"|"PATH"|"FTN"]

Valid method arguments are separated by a logical or. Without such a given
subroutine argument, the process name of the terminal is printed to the screen.
These arguments are controlling the format of the output of the identified
terminal.

In addition to the method arguments introduced previously, there is a global
package variable which can be used to control the format of the output of the
identified terminal.

  $OutputFormat

If the global package variable is set, the method arguments are ignored if
existing in the method call.

=head1 OUTPUT FORMAT

The output format of the identified terminal can be influenced by the subroutine
arguments C<"PROC">, C<"PATH"> and C<"FTN">.

=over 4 

  PROC => Full process of the terminal emulator which is used. 

  PATH => Path to the location of the terminal emulator which is used.

  FTN => Friendly terminal name of the terminal emulator which is used.

=back

=head1 DESCRIPTION

The main objective of this package is to provide a method which is capable of
identifying the terminal emulator a logged-in user is actual using. 

In addition to the terminal emulator, the system console and a remote console
are also recognised.

The logged-in user is related to a valid login shell directly. The login shell
of the logged-in user as well as the logged-in user is determined. Next the
terminal path to the pseudo terminal slave (pts) is identified.

Based on the previously informations the related process of the logged-in user,
the login shell and the terminal path is determined. The evaluation of the PID
of the process of the current running Perl script results in the PPID. The
command related to this PPID is the name of the terminal emulator in use.

The package works together with different terminal emulators. When terminal
emulators are spawned from an initial invoked terminal emulator, each terminal
emulator is correctly recognised.

If the logged-in user changes during the session, this is recognised. Also 
using the sudo command does not affect the recognition of the terminal
emulator. 

The terminal emulator in use by the logged-in user can be identified by the
main command C<whichterminalami()> or the other defined aliases.

=head1 EXAMPLES

=head2 Example 1
  
  # Load the Perl module.
  use Terminal::Identify;

  # Declare the terminal variable.
  my $terminal;

  # Method call without an argument.
  $terminal = whichterminalami();
  print $terminal . "\n";

  # Method call with argument "PROC".
  $terminal = whichterminalami("PROC");
  print $terminal . "\n";

  # Method call with argument "PATH".
  $terminal = whichterminalami("PATH"); 
  print $terminal . "\n";

  # Method call with argument "FTN".
  $terminal = whichterminalami("FTN");
  print $terminal . "\n";

=head2 Example 2
  
  # Load the Perl module.
  use Terminal::Identify;

  # Declare the terminal variable.
  my $terminal;

  # Reset the global output format flag.
  $OutputFormat = "";

  # Method call without an argument.
  $terminal = whichterminalami();
  print $terminal . "\n";

  # Set the global output format flag.
  $OutputFormat = "PROC";

  # Method call without an argument.
  $terminal = whichterminalami();
  print $terminal . "\n";

  # Set the global output format flag.
  $OutputFormat = "PATH";

  # Method call without an argument.
  $terminal = whichterminalami();
  print $terminal . "\n";

  # Set the global output format flag.
  $OutputFormat = "FTN";

  # Method call without an argument.
  $terminal = whichterminalami();
  print $terminal . "\n";

=head1 SYSTEM COMPATIBILITY

The module will work on B<Linux> and on B<Unix> or B<Unix-like>
operating systems in general until something else was shown.

=head1 FUNCTIONALITY REQUIREMENT

The following Linux commands must be available for package functionality:

=over 4 

=item * ps

=item * users

=item * which

=item * grep

=back

The subsequent system files must be exist for package functionality:

=over 4 

=item * /etc/shells

=item * /etc/passwd

=back

=head1 METHOD ALIASES

Aliases for C<whichterminalami>, which can be used are:

  whichtermami       <=  whichterminalami
  which_terminal     <=  whichterminalami
  identify_terminal  <=  whichterminalami

=head1 TERMINALS TESTED

Terminal emulators tested so far with the package:

=over 4

=item * Alacritty

=item * Aterm

=item * Cool Retro Term

=item * Deepin Terminal

=item * Eterm

=item * Gnome Terminal

=item * Guake Terminal

=item * Hyper

=item * kitty

=item * Konsole

=item * LilyTerm

=item * LXTerminal

=item * MATE-Terminal

=item * mlterm

=item * mlterm-tiny

=item * pterm

=item * QTerminal

=item * ROXTerm

=item * Sakura

=item * SCREEN

=item * Tabby

=item * Terminator

=item * Terminology

=item * Termit

=item * Tilda

=item * Tilix

=item * tmux

=item * UXTerm

=item * Xfce4-Terminal-Emulator

=item * xiterm+thai

=item * Xterm

=item * Yakuake 

=back

=head1 LIMITATIONS

The limitations of the package are given by the Linux commands and the Linux
system files which are used by the package. The Linux command C<ps>, the Linux
command C<users>, the Linux command C<which> and the Linux command C<grep> must
be available. The Linux system files C</etc/shells> and C</etc/passwd> must be
exist.

When the Linux command su is used, then the detection results in su as terminal 
emulator, which is wrong. This has to be checked and changed.

=head1 OPEN ISSUES

Subroutines must be implemented to check whether the necessary system programmes
and the necessary system files are available. 

=head1 KNOWN BUGS

The terminal emulator WezTerm can not be identified. Reason is a missing pseudo
terminal slave while analysing the coupled terminal window.

=head1 ERROR CODES

The package returns no error codes at the moment.

=head1 NOTES

Problems were found with the use of the Inline C module. The problem is caused
by user and root rights. Until this issue is resolved, the POSIX module is used
instead of the C code.

=head1 PROGRAM-TECHNICAL BACKGROUND

The Linux command ps can be used e.g. in BASH to find out, which terminal emulator 
is in use by the current user. The command line to do this is quite easy: 

  ps -o 'cmd=' -p $(ps -o 'ppid=' -p $$)

If this is done from within a script it fails. There are a few hurdles to overcome
in order to carry out this procedure from a script.

A distinction must be made between user and superuser. Using su or sudo makes a
difference for running a script.

=head1 ABSTRACT

The module identifies the terminal emulator which the logged-in user is using
currently. For this purpose, the login shells and the logged-in users are
determined. The Perl script from which we identify the terminal emulator itself
runs in a pseudo terminal slave (pts) with its own identification number. This
pseudo terminal slave (pts) is identified, too. Based on all the former
informations, the terminal emulator in use can be determined. If the Perl script
runs from within the system console, the output returns the system console. 

=head1 SEE ALSO

ps(1) - Linux manual page

users(1) - Linux manual page

which(1) - Linux manual page

grep(1) - Linux manual page

shells(5) - Linux manual page

passwd(5) - Linux manual page

=head1 AUTHOR

Dr. Peter Netz, E<lt>ztenretep@cpan.orgE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2022 by Dr. Peter Netz

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.30.0 or,
at your option, any later version of Perl 5 you may have available.

=cut
