SetClipboard.psm1

#region BACKEND/SUPPORTING FUNCTIONS

Function Copy-FilesToClipboard()
{
    [CmdletBinding(PositionalBinding=$false)]
    param
    (
        [System.Collections.Generic.List[string]]$fileList,
        [bool]$append,
        [bool]$isLiteralPath
    )
    [int]$count = 0
    $source = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
    if ($append)
    {
        if (-not [System.Windows.Forms.Clipboard]::ContainsFileDropList())
        {
            Write-Verbose "No appendable content was found."
            $append = $false
        }
        else
        {
            [System.Collections.Generic.List[string]]$fileDropList = [System.Windows.Forms.Clipboard]::GetFileDropList()
            $source = [System.Collections.Generic.HashSet[string]]::new($fileDropList, [System.StringComparer]::OrdinalIgnoreCase)
            $count = $fileDropList.Count
        }
    }

    New-Variable -Name "providerInfo" -Option AllScope -Value $null
    for ($i = 0; $i -lt $fileList.Count; $i++)
    {
        $resolvedProviderPathFromPSPath = New-Object -TypeName 'System.Collections.ObjectModel.Collection[string]'
        try
        {
            if ($isLiteralPath)
            {
                [void]$resolvedProviderPathFromPSPath.Add($PSCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($fileList[$i]))
            }
            else
            {

                [void]$resolvedProviderPathFromPSPath.Add($PSCmdlet.SessionState.Path.GetResolvedProviderPathFromPSPath($fileList[$i], [ref] $providerInfo))
            }
        }
        catch [System.Management.Automation.ItemNotFoundException]
        {
            Write-Error -ErrorRecord $([System.Management.Automation.ErrorRecord]::new($_.Exception, "FailedToSetClipboard", [System.Management.Automation.ErrorCategory]::InvalidOperation, "Clipboard"))
        }
        foreach ($str2 in $resolvedProviderPathFromPSPath)
        {
            if (-not $source.Contains($str2))
            {
                [void]$source.Add($str2)
            }
        }
    }

    if ($source.Count -ne 0)
    {
        if (($source.Count - $count) -eq 1)
        {
            if ($append)
            {
                $msg = "Append single file '{0}' to the clipboard." -f $($source[$source.Count - 1])
            }
            else
            {
                $msg = "Set single file '{0}' to the clipboard." -f $source[0]
            }
        }
        elseif ($append)
        {
            $msg = "Append {0} files to the clipboard." -f ($source.Count - $count)
        }
        else
        {
            $msg = "Set {0} files to the clipboard." -f $source.Count
        }

        if ($PSCmdlet.ShouldProcess($msg, "Set-Clipboard"))
        {
            [void][System.Windows.Forms.Clipboard]::Clear()
            $filePaths = [System.Collections.Specialized.StringCollection]::new()
            [string[]]$strs = $resolvedProviderPathFromPSPath
            $filePaths.AddRange($strs)
            Write-Debug -Message $("FilePaths:`n{0}" -f $strs -join "`n")
            [System.Windows.Forms.Clipboard]::SetFileDropList($filePaths)
        }
    }
}

Function Get-ByteCount([System.Text.StringBuilder]$sb, [int]$start = 0, [int]$end = -1)
{
    $chars = New-Object char[] 1
    [int]$num = 0
    $end = ($end -gt -1) ? $end : $sb.Length
    for ($i = $start; $i -lt $end; $i++)
    {
        $chars[0] = $sb[$i]
        $num += [System.Text.Encoding]::UTF8.GetByteCount($chars)
    }
    $num
}

Function Get-HtmlDataString([string]$html)
{
    $bsString = "Version:0.9\r\nStartHTML:<<<<<<<<1\r\nEndHTML:<<<<<<<<2\r\nStartFragment:<<<<<<<<3\r\nEndFragment:<<<<<<<<4\r\nStartSelection:<<<<<<<<3\r\nEndSelection:<<<<<<<<4"

    [int]$num = 0
    [int]$num2 = 0
    [int]$num8 = -1
    [int]$count = -1
    $sb = New-Object System.Text.StringBuilder
    [void]$sb.AppendLine($bsString)
    [void]$sb.AppendLine('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">')
    [int]$num3 = $html.LastIndexOf('<!--EndFragment-->', [System.StringComparison]::OrdinalIgnoreCase)
    [int]$index = [Regex]::Match($html, '<\s*h\s*t\s*m\s*|', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase).Index
    if ($index -gt 0)
    {
        $count = $html.IndexOf('>', $index) + 1
    }
    [int]$num6 = [Regex]::Match($html, '<\s*/\s*h\s*t\s*m\s*|', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase).Index
    if (($html.IndexOf('<!--StartFragment-->', [System.StringComparison]::OrdinalIgnoreCase) -ge 0) -or ($num3 -ge 0))
    {
        $html
        return
    }
    [int]$startIndex = [Regex]::Match($html, '<\s*b\s*o\s*d\s*y', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase).Index
    if ($startIndex -gt 0)
    {
        $html.IndexOf('>', $startIndex) + 1
    }
    if (($count -lt 0) -and ($num8 -lt 0))
    {
        [void]$sb.Append('<html><body>')
        [void]$sb.Append('<!--StartFragment-->')
        $num = Get-ByteCount -sb $sb -start 0 -end -1
        [void]$sb.Append('<!--EndFragment-->')
        [void]$sb.Append('</body></html>')
    }
    else
    {
        [int]$num9 = [Regex]::Match($html, '<\s*/\s*b\s*o\s*d\s*y', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase).Index
        if ($count -lt 0)
        {
            [void]$sb.Append('<html>')
        }
        else
        {
            [void]$sb.Append($html, 0, $count)
        }

        if ($num8 -gt -1)
        {
            $appendThis = $count -gt -1 ? $coutn : 0
            [void]$sb.Append($html, $appendThis, ($num8 - $appendThis))
        }
        [void]$sb.Append('<!--StartFragment-->')
        $num = Get-ByteCount -sb $sb -start 0 -end -1
        if ($num8 -gt -1)
        {
            [int]$num10 = $num8
        }
        elseif ($count -gt -1)
        {
            [int]$num10 = $count
        }
        else
        {
            [int]$num10 = 0
        }

        if ($num9 -gt 0)
        {
            [int]$num11 = $num9
        }
        elseif ($num6 -gt 0)
        {
            [int]$num11 = $num6
        }
        else
        {
            [int]$num11 = $html.Length
        }

        [void]$sb.Append('<!--EndFragment-->')
        if ($num11 -lt $html.Length)
        {
            [void]$sb.Append($html, $num11, ($html.Length - $num11))
        }
        if ($num6 -le 0)
        {
            [void]$sb.Append('</html>')
        }
    }
    $sb.Replace('<<<<<<<<1', $bsString.Length.ToString("D9", [cultureinfo]::CreateSpecificCulture("en-US")), 0, $bsString.Length)
    $sb.Replace('<<<<<<<<2', ((Get-ByteCount -sb $sb -start 0 -end -1).ToString("D9", [cultureinfo]::CreateSpecificCulture("en-US"))), 0, $bsString.Length)
    $sb.Replace('<<<<<<<<3', $num.ToString("D9", [cultureinfo]::CreateSpecificCulture("en-US")), 0, $bsString.Length)
    $sb.Replace('<<<<<<<<4', $num2.ToString("D9", [cultureinfo]::CreateSpecificCulture("en-US")), 0, $bsString.Length)
    $sb.ToString()
}

Function Set-ClipboardContent([System.Collections.Generic.List[string]]$contentList, [bool]$append, [bool]$asHtml)
{
    if (($null -eq $contentList -or $contentList.Count -eq 0) -and -not $append)
    {
        if ($PSCmdlet.ShouldProcess("Clear clipboard", "Set-Clipboard"))
        {
            [System.Windows.Forms.Clipboard]::Clear()
        }
    }
    else
    {
        $builder = New-Object System.Text.StringBuilder
        if ($append)
        {
            if ([System.Windows.Forms.Clipboard]::ContainsText())
            {
                [void]$builder.AppendLine([System.Windows.Forms.Clipboard]::GetText())
            }
            else
            {
                Write-Verbose "No appendable clipboard found."
                $append = $false
            }
        }

        if ($null -ne $contentList)
        {
            [void]$builder.Append([string]::Join("`n", $contentList.ToArray(), 0, $contentList.Count))
        }

        if ($null -ne $contentList)
        {
            $str2 = $contentList[0]
            if ($str2.Length -ge 20)
            {
                $str2 = $str2.Substring(0, 20) + "..."
            }
        }

        if ($append)
        {
            $msg = "Append string '{0}' to the clipboard." -f $str2
        }
        else
        {
            $msg = "Set string '{0}' to the clipboard." -f $str2
        }

        if ($PSCmdlet.ShouldProcess($msg, "Set-Clipboard"))
        {
            [System.Windows.Forms.Clipboard]::Clear()
            if ($asHtml)
            {
                [System.Windows.Forms.Clipboard]::SetText((Get-HtmlDataString -html $builder.ToString()), [System.Windows.Forms.TextDataFormat]::Html)
            }
            else
            {
                [System.Windows.Forms.Clipboard]::SetText($builder.ToString())
            }
        }
    }
}

#endregion

#region 'THE' FUNCTION
Function Set-Clipboard()
{
    [CmdletBinding(SupportsShouldProcess=$true, DefaultParameterSetName="String")]
    [Alias("scb")]
    param
    (
        [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = "Value")]
        [AllowNull()]
        [AllowEmptyCollection()]
        [AllowEmptyString()]
        [string[]] $Value,

        [Parameter(Mandatory=$false)]
        [switch] $Append,

        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName, ParameterSetName = "Path")]
        [ValidateNotNullOrEmpty()]
        [string[]] $Path,

        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName, ParameterSetName = "LiteralPath")]
        [Alias("PSPath")]
        [ValidateNotNullOrEmpty()]
        [string[]] $LiteralPath,

        [Parameter(Mandatory=$false)]
        [switch] $AsHtml
    )
    Begin
    {
        $contentList = New-Object -TypeName 'System.Collections.Generic.List[string]'
    }
    Process
    {
        if ($null -eq $Value -and $PSBoundParameters.ContainsKey("AsHtml"))
        {
            $PSCmdlet.ThrowTerminatingError([System.Management.Automation.ErrorRecord]::new(
                [System.InvalidOperationException]::new("Html cannot be combined with the Value parameter."),
                "FailedToSetClipboard",
                [System.Management.Automation.ErrorCategory]::InvalidOperation,
                "Clipboard"
            ))
        }

        if ($PSBoundParameters.ContainsKey("Value"))
        {
            $contentList.AddRange($Value)
        }
        elseif ($PSBoundParameters.ContainsKey("Path"))
        {
            $contentList.AddRange($Path)
        }
        elseif ($PSBoundParameters.ContainsKey("LiteralPath"))
        {
            $contentList.AddRange($LiteralPath)
        }
    }
    End
    {
        if ($null -ne $LiteralPath)
        {
            Copy-FilesToClipboard -fileList $contentList -append $Append.ToBool() -isLiteralPath $true
        }
        elseif ($null -ne $Path)
        {
            Copy-FilesToClipboard -fileList $contentList -append $Append.ToBool() -isLiteralPath $false
        }
        else
        {
            Set-ClipboardContent -contentList $contentList -append $Append.ToBool() -asHtml $AsHtml.ToBool()
        }
    }
}

#endregion