nxtools.psm1

#Region './prefix.ps1' 0
Import-Module -Name $PSScriptRoot\Modules\PSNativeCmdDevKit -ErrorAction Stop
#EndRegion './prefix.ps1' 2
#Region './Enum/nxFileSystemAccessRight.ps1' 0

[Flags()]
enum nxFileSystemAccessRight
{
    Read    = 4
    Write   = 2
    Execute = 1
    None    = 0
}
#EndRegion './Enum/nxFileSystemAccessRight.ps1' 10
#Region './Enum/nxFileSystemItemType.ps1' 0
enum nxFileSystemItemType
{
    File
    Directory
    Link
}
#EndRegion './Enum/nxFileSystemItemType.ps1' 7
#Region './Enum/nxFileSystemSpecialMode.ps1' 0

[Flags()]
enum nxFileSystemSpecialMode
{
    SetUserId  = 4 # S_ISUID: Set user ID on execution
    SetGroupId = 2 # S_ISVTX: Set group ID on execution
    StickyBit  = 1 # S_ISVTX: Sticky bit
    None       = 0
}
#EndRegion './Enum/nxFileSystemSpecialMode.ps1' 10
#Region './Enum/nxFileSystemUserClass.ps1' 0
[Flags()]
enum nxFileSystemUserClass
{
    User   = 4 # u
    Group  = 2 # g
    Others = 1 # o
}
#EndRegion './Enum/nxFileSystemUserClass.ps1' 8
#Region './Classes/nxFileSystemInfo.ps1' 0

class nxFileSystemInfo : System.IO.FileSystemInfo
{
    [nxFileSystemPermissions] $nxFileSystemAccessRight
    [nxFileSystemItemType] $nxFileSystemItemType
    [int] $nxLinkCount
    [System.String] $nxOwner
    [System.String] $nxGroup
    [long] $Length

    [string] $Name
    [datetime] $LastWriteTime

    nxFileSystemInfo ([System.Collections.IDictionary]$properties)
    {
        Write-Verbose -Message "Creating [nxFileSystemInfo] with path '$($properties.FullPath)'."
        $this.OriginalPath = $properties.FullPath
        $this.FullPath = $properties.FullPath
        $this.SetPropertiesFromIDictionary($properties)
        $this.Name = [System.Io.Path]::GetFileName($this.FullPath)
    }

    hidden [void] SetPropertiesFromIDictionary ([System.Collections.IDictionary]$properties)
    {
        Write-Verbose -Message "Setting Propeties from Dictionary."

        $properties.keys.Foreach{
            if ($this.psobject.Properties.name -contains $_)
            {
                try
                {
                    Write-Debug -Message "`tAdding '$_' with value '$($properties[$_])'."
                    $this.($_) = $properties[$_]
                }
                catch
                {
                    Write-Warning -Message $_.Exception.Message
                }
            }
            else
            {
                Write-Verbose -Message "The key '$_' is not a property."
            }
        }
    }

    nxFileSystemInfo([string]$Path)
    {
        # ctor
        $this.OriginalPath = $Path
        $this.FullPath = [System.IO.Path]::GetFullPath($Path)
        $this.Name = [System.Io.Path]::GetFileName($this.FullPath)
    }

    nxFileSystemInfo([string]$Path, $permission, $linkCount, $owner, $group, $Length, $lastModifyDate, $lastModifyTime, $fileName)
    {
        # ctor
        $this.OriginalPath = $Path
        $this.FullPath = [System.IO.Path]::GetFullPath($Path)
        $this.Name = [System.Io.Path]::GetFileName($this.FullPath)
    }

    [void] Delete()
    {
        Remove-Item -Path $this.FullName -ErrorAction Stop
        $this.Dispose()
    }
}
#EndRegion './Classes/nxFileSystemInfo.ps1' 69
#Region './Classes/nxFileSystemPermissions.ps1' 0
class nxFileSystemPermissions
{
    hidden static [string] $SymbolicTriadParser = '^[-dl]?(?<User>[-wrxsStT]{3})(?<Group>[-wrxsStT]{3})(?<Others>[-wrxsStT]{3})$'
    hidden static [string] $SymbolicOperationParser = '^(?<userClass>[ugoa]{1,3})(?<operator>[\-\+\=]{1})(?<permissions>[wrxTtSs-]{1,3})$'
    [nxFileSystemSpecialMode]  $SpecialModeFlags
    [nxFileSystemAccessRight]  $OwnerPermission
    [nxFileSystemAccessRight]  $GroupPermission
    [nxFileSystemAccessRight]  $OthersPermission

    nxFileSystemPermissions()
    {
        # default ctor, can be used like this:
        <#
            [nxFileSystemPermissions]@{
                SpecialModeFlags = 'None'
                OwnerPermission = 'Read, Write, Execute'
                GroupPermission = 'Read, Execute'
                OthersPermission = 7
            }
        #>

    }

    nxFileSystemPermissions([String]$Permissions)
    {
        if ($Permissions -match '^\d{3,4}$')
        {
            # Convert from Int to nxFileSystemAccessRight
            $this.setNxFileSystemPermissionFromInt([int]::Parse($Permissions))
        }
        elseif ($Permissions -cmatch [nxFileSystemPermissions]::SymbolicTriadParser)
        {
            $this.setNxFileSystemPermissionFromSymbolicTriadNotation($Permissions)
        }
        elseif (-not ($Permissions -split '\s+').Where{$_ -cnotmatch [nxFileSystemPermissions]::SymbolicOperationParser})
        {
            # All items of the space delimited Symbolic operations have been checked.
            $this.DoSymbolicChmodOperation($Permissions)
        }
        else
        {
            throw "The symbolic string '$Permissions' is invalid."
        }
    }

    nxFileSystemPermissions([int]$Permissions)
    {
        $this.setNxFileSystemPermissionFromInt($Permissions)
    }

    hidden [void] setNxFileSystemPermissionFromSymbolicTriadNotation([string]$SymbolicTriad)
    {
        $null = $SymbolicTriad -cmatch [nxFileSystemPermissions]::SymbolicTriadParser

        $this.DoSymbolicChmodOperation(@(
            ('u=' + $Matches['User'])
            ('g=' + $Matches['Group'])
            ('o=' + $Matches['Others'])
        ) -join ' ')
    }

    hidden [void] setNxFileSystemPermissionFromInt([Int]$Permissions)
    {
        # Adding leading 0s to ensure we have a 0 for the special flags i.e. 777 -> 0777
        $StringPermission = "{0:0000}" -f $Permissions
        Write-Debug -Message "Trying to parse the permission set expressed by '$($Permissions)'."

        if ($StringPermission.Length -gt 4)
        {
            throw "Permission set should be expressed with 4 or 3 digits (you can omit the one on the left): setuid(4)/setgid(2)/sticky bit(1)|Owner|Group|Others). '$($StringPermission)'"
        }

        Write-Debug -Message "Parsing Special Mode Flags: $([int]::Parse($StringPermission[0]))"
        $this.SpecialModeFlags = [int]::Parse($StringPermission[0])
        $this.OwnerPermission  = [int]::Parse($StringPermission[1])
        $this.GroupPermission  = [int]::Parse($StringPermission[2])
        $this.OthersPermission = [int]::Parse($StringPermission[3])
    }

    [void] DoChmodOperation ([nxFileSystemUserClass]$UserClass, [char]$Operator, [nxFileSystemAccessRight]$AccessRights, [nxFileSystemSpecialMode]$SpecialMode)
    {
        switch ($operator)
        {
            '=' {
                $this.SetMode($userClass, $accessRights, $specialMode)
            }

            '+'
            {
                $this.AddMode($userClass, $accessRights, $specialMode)
            }

            '-'
            {
                $this.RemoveMode($userClass, $accessRights, $specialMode)
            }

            default
            {
                throw "Operator not recognised '$operator'."
            }
        }
    }

    [void] DoSymbolicChmodOperation ([string]$SymbolicChmodString)
    {
        $symbolicChmodList = $SymbolicChmodString -split '\s+'

        foreach ($symbolicChmodStringItem in $symbolicChmodList)
        {
            Write-Debug -Message "Doing Symbolic Operation '$symbolicChmodStringItem'."
            if ($symbolicChmodStringItem -match [nxFileSystemPermissions]::SymbolicOperationParser)
            {
                $userClassChars = $Matches['userClass']
                $operator       = $Matches['operator']
                $permissions    = $Matches['permissions']
                $userClass      = [nxFileSystemUserClass](Convert-CharToNxFileSystemUserClass -Char $userClassChars)
                Write-Debug -Message "Parsing $permissions"
                $specialMode    = [nxFileSystemSpecialMode](Convert-CharToNxFileSystemSpecialMode -SpecialModeSymbol $permissions -UserClass $UserClass)
                $accessRights   = [nxFileSystemAccessRight](Convert-CharToNxFileSystemAccessRight -AccessRightSymbol $permissions)

                $this.DoChmodOperation($userClass, $operator, $accessRights, $specialMode)
            }
        }
    }

    [void] SetMode ([nxFileSystemUserClass]$UserClass, [nxFileSystemAccessRight]$AccessRights, [nxFileSystemSpecialMode]$SpecialMode)
    {
        Write-Debug -Message "Setting rights '$($AccessRights)' and special flag '$($SpecialMode)' to '$($UserClass)'"
        switch ($UserClass)
        {
            { $_ -band [nxFileSystemUserClass]::User } {
                $this.OwnerPermission = $AccessRights
            }

            { $_ -band [nxFileSystemUserClass]::Group } {
                $this.GroupPermission = $AccessRights
            }

            { $_ -band [nxFileSystemUserClass]::Others } {
                $this.OthersPermission = $AccessRights
            }

            default {
                throw "Error with unrecognized User Class '$UserClass'."
            }
        }

        $this.SpecialModeFlags = $SpecialMode
    }

    [void] AddMode ([nxFileSystemUserClass]$UserClass, [nxFileSystemAccessRight]$AccessRights, [nxFileSystemSpecialMode]$SpecialMode)
    {
        Write-Debug -Message "Adding rights '$($AccessRights)' and special flag '$($SpecialMode)' to '$($UserClass)'"
        switch ($UserClass)
        {
            { $_ -band [nxFileSystemUserClass]::User } {
                $this.OwnerPermission = $this.OwnerPermission -bor $AccessRights
            }

            { $_ -band [nxFileSystemUserClass]::Group } {
                $this.GroupPermission = $this.GroupPermission -bor $AccessRights
            }

            { $_ -band [nxFileSystemUserClass]::Others } {
                $this.OthersPermission = $this.OthersPermission -bor $AccessRights
            }

            default {
                throw "Error with unrecognized User Class '$UserClass'."
            }
        }

        $this.SpecialModeFlags = $this.SpecialModeFlags -bor $SpecialMode
    }

    [void] RemoveMode ([nxFileSystemUserClass]$UserClass, [nxFileSystemAccessRight]$AccessRights, [nxFileSystemSpecialMode]$SpecialMode)
    {
        Write-Debug -Message "Removing rights '$($AccessRights)' and special flag '$($SpecialMode)' to '$($UserClass)'"
        switch ($UserClass)
        {
            { $_ -band [nxFileSystemUserClass]::User } {
                $this.OwnerPermission = $this.OwnerPermission -band -bnot $AccessRights
            }

            { $_ -band [nxFileSystemUserClass]::Group } {
                $this.GroupPermission = $this.GroupPermission -band -bnot $AccessRights
            }

            { $_ -band [nxFileSystemUserClass]::Others } {
                $this.OthersPermission = $this.OthersPermission -band -bnot $AccessRights
            }

            default {
                throw "Error with unrecognized User Class '$UserClass'."
            }
        }

        $this.SpecialModeFlags = $this.SpecialModeFlags -band -bnot $SpecialMode
    }

    [string] ToString()
    {
        Write-Verbose -Message "$($this.OwnerPermission)"
        Write-Verbose -Message "$(@($this.OthersPermission, $this.SpecialModeFlags) -join '|')"

        $SymbolNotation = [PSCustomObject]@{
            UserClass         = [nxFileSystemUserClass]::User
            AccessRight       = $this.OwnerPermission
            UseDashWhenAbsent = $true
        },
        [PSCustomObject]@{
            UserClass         = [nxFileSystemUserClass]::Group
            AccessRight       = $this.GroupPermission
            UseDashWhenAbsent = $true
        },
        [PSCustomObject]@{
            UserClass         = [nxFileSystemUserClass]::User
            AccessRight       = $this.OthersPermission
            UseDashWhenAbsent = $true
        } | Convert-FileSystemAccessRightToSymbol

        Write-Verbose -Message "SymbolNotation: $SymbolNotation"
        return ($SymbolNotation -join '')
    }
}
#EndRegion './Classes/nxFileSystemPermissions.ps1' 226
#Region './Private/nxFileSystem/Convert-CharToNxFileSystemAccessRight.ps1' 0
function Convert-CharToNxFileSystemAccessRight
{
    [CmdletBinding()]
    [OutputType([nxFileSystemAccessRight])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Char[]]
        [Alias('Char')]
        $AccessRightSymbol
    )

    process {
        foreach ($charItem in $AccessRightSymbol)
        {
            switch -CaseSensitive ($charItem)
            {
                'w'
                {
                    [nxFileSystemAccessRight]::Write
                }

                'r'
                {
                    [nxFileSystemAccessRight]::Read
                }

                'x'
                {
                    [nxFileSystemAccessRight]::Execute
                }

                '-'
                {
                    [nxFileSystemAccessRight]::None
                }

                'T'
                {
                    Write-Debug -Message "The UpperCase 'T' means there's no Execute right."
                    [nxFileSystemAccessRight]::None
                }

                't'
                {
                    [nxFileSystemAccessRight]::Execute
                }

                'S'
                {
                    Write-Debug -Message "The UpperCase 'S' means there's no Execute right."
                    [nxFileSystemAccessRight]::None
                }

                's'
                {
                    [nxFileSystemAccessRight]::Execute
                }
            }
        }
    }
}
#EndRegion './Private/nxFileSystem/Convert-CharToNxFileSystemAccessRight.ps1' 63
#Region './Private/nxFileSystem/Convert-CharToNxFileSystemSpecialMode.ps1' 0
function Convert-CharToNxFileSystemSpecialMode
{
    [CmdletBinding()]
    [OutputType([nxFileSystemSpecialMode])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Char[]]
        [Alias('Char')]
        # the possible char are [sStT], but if other values sucha as [rwx-] are passed, we should just ignore them (no special permission).
        $SpecialModeSymbol,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [nxFileSystemUserClass]
        $UserClass
    )

    process {
        foreach ($charItem in $SpecialModeSymbol)
        {
            Write-Debug -Message "Converting '$charItem' to [nxFileSystemSpecialMode]."
            switch ($charItem)
            {
                't'
                {
                    Write-Debug -Message "Adding StickyBit."
                    [nxFileSystemSpecialMode]::StickyBit
                }

                's'
                {
                    if ($UserClass -eq [nxFileSystemUserClass]::User)
                    {
                        Write-Debug -Message "Adding SetUserId."
                        [nxFileSystemSpecialMode]::SetUserId
                    }

                    if ($UserClass -band [nxFileSystemUserClass]::Group)
                    {
                        Write-Debug -Message "Adding SetGroupId."
                        [nxFileSystemSpecialMode]::SetGroupId
                    }

                    if ((-not $UserClass -band [nxFileSystemUserClass]::Group) -and (-not $UserClass -eq [nxFileSystemUserClass]::User))
                    {
                        Write-Warning -Message "Cannot determine whether to set the SUID or SGID because the User class is invalid: '$UserClass'"
                    }
                }

                default {
                    Write-Debug -Message "Nothing to return for char '$charItem'."
                }
            }
        }
    }
}
#EndRegion './Private/nxFileSystem/Convert-CharToNxFileSystemSpecialMode.ps1' 57
#Region './Private/nxFileSystem/Convert-CharToNxFileSystemUserClass.ps1' 0
function Convert-CharTonxFileSystemUserClass
{
    [CmdletBinding()]
    [OutputType([nxFileSystemUserClass])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [char[]]
        $Char
    )

    process {
        foreach ($charItem in $char)
        {
            switch ($charItem)
            {
                'u' { [nxFileSystemUserClass]'User'   }
                'g' { [nxFileSystemUserClass]'Group'  }
                'o' { [nxFileSystemUserClass]'Others' }
                'a' { [nxFileSystemUserClass]'User, Group, Others' }
                default { throw "Unexpected char '$CharItem'" }
            }
        }
    }
}
#EndRegion './Private/nxFileSystem/Convert-CharToNxFileSystemUserClass.ps1' 26
#Region './Private/nxFileSystem/Convert-FileSystemAccessRightToSymbol.ps1' 0
function Convert-FileSystemAccessRightToSymbol
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [System.String[]]
        [ValidateScript({$_ -as [nxFileSystemAccessRight] -or $_ -as [nxFileSystemSpecialMode]})]
        $AccessRight,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [nxFileSystemUserClass]
        [Alias('Class')]
        $UserClass,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [System.Management.Automation.SwitchParameter]
        $UseDashWhenAbsent
    )

    process {

        Write-Verbose "Access Right: '$($AccessRight -join "', '")'"

        [nxFileSystemAccessRight]$AccessRightEntry = 'none'
        [nxFileSystemSpecialMode]$SpecialModeEntry = 'none'

        $AccessRight.ForEach({
            if ($_ -as [nxFileSystemAccessRight])
            {
                $AccessRightEntry = $AccessRightEntry -bor [nxFileSystemAccessRight]$_
            }
            elseif ($_ -as [nxFileSystemSpecialMode])
            {
                $SpecialModeEntry = $SpecialModeEntry -bor [nxFileSystemSpecialMode]$_
            }
        })

        Write-Debug -Message "AccessRight: '$AccessRightEntry', SpecialMode: $SpecialModeEntry"

        $Symbols = @(
            $AccessRightEntry -band [nxFileSystemAccessRight]::Read ? 'r' : ($UseDashWhenAbsent ? '-':'')
            $AccessRightEntry -band [nxFileSystemAccessRight]::Write ? 'w' : ($UseDashWhenAbsent ? '-':'')

            if (
                $UserClass -band [nxFileSystemUserClass]::Group -and
                $SpecialModeEntry -band [nxFileSystemSpecialMode]::SetGroupId -and
                $AccessRightEntry -band [nxFileSystemAccessRight]::Execute
            )
            {
                's'
            }
            elseif (
                $UserClass -band [nxFileSystemUserClass]::Group -and
                $SpecialModeEntry -band [nxFileSystemSpecialMode]::SetGroupId
            )
            {
                'S'
            }
            elseif (
                $UserClass -band [nxFileSystemUserClass]::User -and
                $SpecialModeEntry -band [nxFileSystemSpecialMode]::SetUserId -and
                $AccessRightEntry -band [nxFileSystemAccessRight]::Execute
            )
            {
                's'
            }
            elseif (
                $UserClass -band [nxFileSystemUserClass]::User -and
                $SpecialModeEntry -band [nxFileSystemSpecialMode]::SetUserId
            )
            {
                'S'
            }
            elseif (
                $UserClass -band [nxFileSystemUserClass]::Others -and
                $SpecialModeEntry -band [nxFileSystemSpecialMode]::StickyBit -and
                $AccessRightEntry -band [nxFileSystemAccessRight]::Execute
            )
            {
                't'
            }
            elseif (
                $UserClass -band [nxFileSystemUserClass]::Others -and
                $SpecialModeEntry -band [nxFileSystemSpecialMode]::StickyBit
            )
            {
                'T'
            }
            elseif ($AccessRightEntry -band [nxFileSystemAccessRight]::Execute)
            {
                'x'
            }
            elseif ($UseDashWhenAbsent)
            {
                '-'
            }
        )

        Write-Verbose -Message "Symbols: '$($Symbols -join '')'."
        ($Symbols -join '')
    }
}
#EndRegion './Private/nxFileSystem/Convert-FileSystemAccessRightToSymbol.ps1' 104
#Region './Private/nxFileSystem/Convert-FileSystemPermissionComparisonToSymbolicOperation.ps1' 0
function Convert-FileSystemPermissionComparisonToSymbolicOperation
{
    [CmdletBinding()]
    [OutputType([string])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('Class')]
        [nxFileSystemUserClass]
        $UserClass,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [System.String]
        [ValidateScript({$_ -as [nxFileSystemAccessRight] -or $_ -as [nxFileSystemSpecialMode]})]
        [Alias('InputObject')]
        $EnumValue,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [string]
        $SideIndicator
    )

    process {
        # FTR the side indicator points where the EnumValue is found: REFERENCE <=> DIFFERENCE
        # The SympolicOperation generated aims to make the DifferencePermission compliante with the reference.

        Write-Debug "[$UserClass] [$EnumValue] [$SideIndicator]"

        if ($SideIndicator -eq '<=')
        {
            # Need to add something that is not in the reference
            $operator = '+'
        }
        else
        {
            # Need to remove something that is not in the reference
            $operator = '-'
        }

        $UserClassSymbol = Convert-FileSystemUserClassToSymbol -UserClass $UserClass
        $PermissionSymbol = Convert-FileSystemAccessRightToSymbol -AccessRight $EnumValue -UserClass $UserClass

        return ('{0}{1}{2}' -f $UserClassSymbol, $operator, $PermissionSymbol)
    }
}
#EndRegion './Private/nxFileSystem/Convert-FileSystemPermissionComparisonToSymbolicOperation.ps1' 46
#Region './Private/nxFileSystem/Convert-FileSystemUserClassToSymbol.ps1' 0
function Convert-FileSystemUserClassToSymbol
{
    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [nxFileSystemUserClass]
        [Alias('Class')]
        $UserClass
    )

    $symbols = switch ($UserClass)
    {
        ([nxFileSystemUserClass]::User)   { 'u' }
        ([nxFileSystemUserClass]::Group)  { 'g' }
        ([nxFileSystemUserClass]::Others) { 'o' }
    }

    return ($symbols -join '')
}
#EndRegion './Private/nxFileSystem/Convert-FileSystemUserClassToSymbol.ps1' 22
#Region './Private/nxFileSystem/Convert-LsEntryToFileSystemInfo.ps1' 0
function Convert-LsEntryToFileSystemInfo
{
    param
    (
        [Parameter(ValueFromPipeline = $true)]
        [System.String]
        $lsLine,

        [Parameter()]
        [System.String]
        $InitialPath = '.',

        [Parameter()]
        [scriptblock]
        $ErrorHandler = {
            switch -Regex ($_)
            {
                default { Write-Error "$_" }
            }
        }
    )

    begin {
        $lastParent = $null
    }

    process {
        foreach ($lineToParse in $lsLine.Where{$_})
        {
            Write-Verbose -Message "Parsing ls line output: '$lineToParse'."

            if ($lineToParse -is [System.Management.Automation.ErrorRecord])
            {
                $lineToParse | &$ErrorHandler
            }
            elseif ($lineToParse -match 'Permission denied|No such file or directory')
            {
                Write-Error -Message $lineToParse
            }
            elseif ($lineToParse -match '^\s*total')
            {
                Write-Verbose -Message $lineToParse
            }
            elseif ($lineToParse -match '^(?<parent>/.*):$')
            {
                $lastParent = $Matches.parent
            }
            else
            {
                $nxFileSystemAccessRight, $nxLinkCount, $nxOwner, $nxGroup, $Length, $lastModifyDate, $lastModifyTime, $lastModifyTimezone, $fileName = $lineToParse -split '\s+',9
                $nxFileSystemItemType = switch ($nxFileSystemAccessRight[0])
                {
                    '-' { 'File' }
                    'd' { 'Directory' }
                    'l' { 'Link' }
                }

                $lastWriteTime = Get-Date -Date ($lastModifyDate + " " + $lastModifyTime + $lastModifyTimezone)

                # Maybe there's no $lastParent yet (top folder from search Path)
                if ($null -eq $lastParent)
                {
                    if ($InitialPath -eq [io.Path]::GetFullPath($fileName, $PWD.Path))
                    {
                        Write-Debug -Message "No `$lastParent and Initial path is '$InitialPath' same as file name is '$fileName'."
                        # no Last parent and the InitialPath is the same as the file Name. (i.e. ./CHANGELOG.md or CHANGELOG.md)
                        $lastParent = [io.path]::GetFullPath("$InitialPath/..")
                    }
                    else
                    {
                        $lastParent = [io.Path]::GetFullPath($InitialPath)
                    }

                    $fullPath = [io.Path]::GetFullPath($fileName, $lastParent)
                }
                else
                {
                    Write-Debug -Message "`$lastParent is '$lastParent', Initial Path is '$InitialPath' and file name is '$fileName'."
                    $fullPath = [io.path]::GetFullPath($fileName, $lastParent)
                }

                [nxFileSystemInfo]::new(
                    @{
                        FullPath                = $fullPath
                        LastWriteTime           = $lastWriteTime
                        nxFileSystemItemType    = $nxFileSystemItemType
                        nxOwner                 = $nxOwner
                        nxGroup                 = $nxGroup
                        Length                  = [long]::Parse($Length)
                        nxLinkCount             = $nxLinkCount
                        nxFileSystemAccessRight = $nxFileSystemAccessRight
                    }
                )
            }
        }
    }
}
#EndRegion './Private/nxFileSystem/Convert-LsEntryToFileSystemInfo.ps1' 98
#Region './Public/Compare-FileSystemPermission.ps1' 0
function Compare-FileSystemPermission
{
    [CmdletBinding()]
    [OutputType([hashtable])]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [nxFileSystemPermissions]
        $ReferencePermission,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [nxFileSystemPermissions[]]
        [Alias('nxFileSystemAccessRight')]
        $DifferencePermission,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]
        [Alias('FullName', 'Path')]
        $DifferencePath,

        [Parameter()]
        [Switch]
        $IncludeEqual
    )

    process {
        foreach ($PermissionItem in $DifferencePermission)
        {
            Write-Verbose -Message "Comparing '$ReferencePermission' with '$PermissionItem'"

            $diffOwner = $ReferencePermission.OwnerPermission -bxor $PermissionItem.OwnerPermission
            $diffGroup = $ReferencePermission.GroupPermission -bxor $PermissionItem.GroupPermission
            $diffOthers = $ReferencePermission.OthersPermission -bxor $PermissionItem.OthersPermission
            $diffSpecialModeFlags = $ReferencePermission.SpecialModeFlags -bxor $PermissionItem.SpecialModeFlags

            foreach ($enumValue in ([Enum]::GetValues([nxFileSystemAccessRight]).Where({$_ -ne [nxFileSystemAccessRight]::None})))
            {
                if ($diffOwner -band $enumValue)
                {
                    $sideIndicator = $ReferencePermission.OwnerPermission -band $enumValue ? '<=' : '=>'
                    Write-Verbose -Message "[$([nxFileSystemUserClass]::User)]'$enumValue' is only on this side [REF '$sideIndicator' DIFF]."
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::User
                        InputObject          = $enumValue
                        SideIndicator        = $sideIndicator
                        DifferencePath       = $DifferencePath
                    } | Add-Member -PassThru -Name RemediationOperation -MemberType ScriptProperty -Value {$this | Convert-FileSystemPermissionComparisonToSymbolicOperation}
                }
                elseif ($IncludeEqual)
                {
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::User
                        InputObject          = $enumValue
                        SideIndicator        = '='
                        RemediationOperation = ''
                        DifferencePath       = $DifferencePath
                    }
                }

                if ($diffGroup -band $enumValue)
                {
                    $sideIndicator = $ReferencePermission.GroupPermission -band $enumValue ? '<=' : '=>'
                    Write-Verbose -Message "[$([nxFileSystemUserClass]::Group)]'$enumValue' is only on this side [REF '$sideIndicator' DIFF]."
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::Group
                        InputObject          = $enumValue
                        SideIndicator        = $sideIndicator
                        DifferencePath       = $DifferencePath
                    } | Add-Member -PassThru -Name RemediationOperation -MemberType ScriptProperty -Value {$this | Convert-FileSystemPermissionComparisonToSymbolicOperation}
                }
                elseif ($IncludeEqual)
                {
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::Group
                        InputObject          = $enumValue
                        SideIndicator        = '='
                        RemediationOperation = ''
                        DifferencePath       = $DifferencePath
                    }
                }

                if ($diffOthers -band $enumValue)
                {
                    $sideIndicator = $ReferencePermission.OthersPermission -band $enumValue ? '<=' : '=>'
                    Write-Verbose -Message "[$([nxFileSystemUserClass]::Others)]'$enumValue' is only on this side [REF '$sideIndicator' DIFF]."
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::Others
                        InputObject          = $enumValue
                        SideIndicator        = $sideIndicator
                        DifferencePath       = $DifferencePath
                    } | Add-Member -PassThru -Name RemediationOperation -MemberType ScriptProperty -Value {$this | Convert-FileSystemPermissionComparisonToSymbolicOperation}
                }
                elseif ($IncludeEqual)
                {
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::Others
                        InputObject          = $enumValue
                        SideIndicator        = '='
                        RemediationOperation = ''
                        DifferencePath       = $DifferencePath
                    }
                }
            }

            foreach ($enumValue in ([Enum]::GetValues([nxFileSystemSpecialMode])))
            {
                if ($diffSpecialModeFlags -band $enumValue)
                {
                    $sideIndicator = $ReferencePermission.SpecialModeFlags -band $enumValue ? '<=' : '=>'
                    Write-Verbose -Message "[$([nxFileSystemUserClass]::None)]'$enumValue' is only on this side [REF '$sideIndicator' DIFF]."
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::None
                        InputObject          = $enumValue
                        SideIndicator        = $sideIndicator
                        DifferencePath       = $DifferencePath
                    } | Add-Member -PassThru -Name RemediationOperation -MemberType ScriptProperty -Value {$this | Convert-FileSystemPermissionComparisonToSymbolicOperation}
                }
                elseif ($IncludeEqual)
                {
                    [PSCustomObject]@{
                        Class                = [nxFileSystemUserClass]::None
                        InputObject          = $enumValue
                        SideIndicator        = '='
                        RemediationOperation = ''
                        DifferencePath       = $DifferencePath
                    }
                }
            }
        }
    }
}
#EndRegion './Public/Compare-FileSystemPermission.ps1' 132
#Region './Public/Get-FileSystemChildItem.ps1' 0
function Get-FileSystemChildItem
{
    param
    (
        [Parameter()]
        [System.String[]]
        $Path = '.',

        [Parameter()]
        [Switch]
        $Recurse,

        [Parameter()]
        [Switch]
        $Directory
    )

    begin
    {
        $lsParams  = @('-Al','--full-time','--group-directories-first')

        switch ($PSboundParameters.keys)
        {
            'Recurse'   { $lsParams += '--recursive' }
            'Directory' { $lsParams += '-d' }
            default     { Write-Debug -Message "Parameter '$_' not added automatically." }
        }
    }

    process
    {
        foreach ($pathItem in $Path.Where{$_})
        {
            $pathItem = [System.IO.Path]::GetFullPath($pathItem, $PWD.Path)
            Invoke-NativeCommand -Executable 'ls' -Parameters ($lsParams + @($pathItem)) | Convert-lsEntryToFileSystemInfo -InitialPath $pathItem
        }
    }
}
#EndRegion './Public/Get-FileSystemChildItem.ps1' 39
#Region './Public/Get-KernelInfo.ps1' 0
function Get-KernelInfo
{
    [CmdletBinding()]
    param
    (

    )

    $kernelName, $ComputerName, $kernelRelease, $machineHardware, $processor, $hardwarePlatform, $OS = (Invoke-NativeCommand -Executable 'uname' -Parameters @(
        '--kernel-name',
        '--nodename',
        '--kernel-release',
        '--machine',
        '--processor',
        '--hardware-platform',
        '--operating-system'
        )
    ) -split '\s'

    $kernelVersion = Invoke-NativeCommand -Executable 'uname' -Parameters '--kernel-version'

    [PSCustomObject]@{
        kernelName       = $kernelName
        ComputerName     = $ComputerName
        KernelRelease    = $kernelRelease
        KernelVersion    = $kernelVersion
        MachineHardware  = $machineHardware
        processor        = $processor
        hardwarePlatform = $hardwarePlatform
        OS               = $OS
    } | Add-Member -TypeName 'OS.KernelInfo' -PassThru
}
#EndRegion './Public/Get-KernelInfo.ps1' 33
#Region './Public/Get-LinuxStandardBaseRelease.ps1' 0
# By default on Debian 10, lsb-release package is not installed, so lsb_release
# gives a command not found.
function Get-LinuxStandardBaseRelease
{
    [OutputType([PSCustomObject])]
    [CmdletBinding()]
    param
    (
    )

    if ($PSBoundParameters.Verbose -or $VerbosePreference -ne 'SilentlyContinue')
    {
        $Verbose = $true
    }
    else
    {
        $Verbose = $false
    }

    $properties = Invoke-NativeCommand -Executable 'lsb_release' -Parameters '--all' -Verbose:$Verbose |
        Get-PropertyHashFromListOutput -ErrorHandling {
            switch -Regex ($_)
            {
                ''                 { }
                'No\sLSB\smodules' { Write-Verbose $_ }
                default            { Write-Error "$_" }
            }
        }

    [PSCustomObject]$properties | Add-Member -TypeName 'Package.LsbRelease' -PassThru
}

Set-Alias -Name Get-LsbRelease -Value Get-LinuxStandardBaseRelease
#EndRegion './Public/Get-LinuxStandardBaseRelease.ps1' 34
#Region './Public/Get-OSDistributionInfo.ps1' 0
function Get-OSDistributionInfo
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [System.String[]]
        $InfoFilePath = '/etc/*-release'
    )

    $InfoFilePath = [string[]](Get-Item $InfoFilePath -ErrorAction Stop)
    Write-Verbose -Message "Extracting distro info from '$($InfoFilePath -join "', '")'"

    $properties = Get-Content -Path $InfoFilePath |
        Get-PropertyHashFromListOutput -Regex '^\s*(?<property>[\w-\s]*)=\s*"?(?<val>.*)\b'

    [PSCustomObject]$properties | Add-Member -TypeName 'OSDistribution.Info' -PassThru
}
#EndRegion './Public/Get-OSDistributionInfo.ps1' 19