Utils/cksum.ps1

Register-GnuUtil @{
    Name         = 'cksum'
    Category     = 'Checksums'
    Synopsis     = 'Compute and verify file checksums.'
    Usage        = @('cksum [OPTION]... [FILE]...')
    Description  = @('Print or verify checksums.', 'By default use the 32 bit CRC algorithm.', '', 'With no FILE, or when FILE is -, read standard input.')
    FileOperands = $true
    Options      = @(
        @{ Short = 'a'; Long = 'algorithm'; Arg = 'TYPE'; Help = 'select the digest type to use. See DIGEST below.' }
        @{ Short = 'c'; Long = 'check'; Help = 'read checksums from the FILEs and check them' }
        @{ Long = 'tag'; Help = 'create a BSD-style checksum (the default)' }
        @{ Long = 'untagged'; Help = 'create a reversed style checksum, without digest type' }
        @{ Short = 'z'; Long = 'zero'; Help = 'end each output line with NUL, not newline, and disable file name escaping' }
        @{ Long = 'ignore-missing'; Help = 'don''t fail or report status for missing files' }
        @{ Long = 'quiet'; Help = 'don''t print OK for each successfully verified file' }
        @{ Long = 'status'; Help = 'don''t output anything, status code shows success' }
        @{ Long = 'strict'; Help = 'exit non-zero for improperly formatted checksum lines' }
        @{ Short = 'w'; Long = 'warn'; Help = 'warn about improperly formatted checksum lines' }
    )
    Notes        = @(
        'DIGEST determines the digest algorithm and default output format:',
        ' sysv (equivalent to sum -s)',
        ' bsd (equivalent to sum -r)',
        ' crc (equivalent to cksum)',
        ' md5 (equivalent to md5sum)',
        ' sha1 (equivalent to sha1sum)',
        ' sha224 (equivalent to sha224sum)',
        ' sha256 (equivalent to sha256sum)',
        ' sha384 (equivalent to sha384sum)',
        ' sha512 (equivalent to sha512sum)',
        '',
        'The check options (--ignore-missing, --quiet, --status, --strict, -w) apply to -c.',
        'PowerShell note: blake2b, sm3, --base64, --raw and --length are not supported.'
    )
}



$script:GnuCrcTable = $null

function script:Get-GnuPosixCrc {
    <#
    .SYNOPSIS
    The POSIX cksum CRC (polynomial 0x04C11DB7, MSB first, length appended, complemented).
    #>

    param([Parameter(Mandatory)][AllowEmptyCollection()][byte[]]$Bytes)

    if ($null -eq $script:GnuCrcTable) {
        $table = New-Object long[] 256
        for ($i = 0; $i -lt 256; $i++) {
            $crc = [long]$i -shl 24
            for ($bit = 0; $bit -lt 8; $bit++) {
                if ($crc -band 0x80000000L) { $crc = (($crc -shl 1) -bxor 0x04C11DB7L) -band 0xFFFFFFFFL }
                else { $crc = ($crc -shl 1) -band 0xFFFFFFFFL }
            }
            $table[$i] = $crc
        }
        $script:GnuCrcTable = $table
    }
    $table = $script:GnuCrcTable
    $crc = [long]0
    foreach ($byte in $Bytes) { $crc = (($crc -shl 8) -band 0xFFFFFFFFL) -bxor $table[(($crc -shr 24) -bxor $byte) -band 0xFF] }
    $length = [long]$Bytes.Length
    while ($length -gt 0) {
        $crc = (($crc -shl 8) -band 0xFFFFFFFFL) -bxor $table[(($crc -shr 24) -bxor ($length -band 0xFF)) -band 0xFF]
        $length = $length -shr 8
    }
    (-bnot $crc) -band 0xFFFFFFFFL
}

function script:Invoke-GnuCksumAlgorithm {
    <#
    .SYNOPSIS
    Implementation of cksum.
    #>

    param([Parameter(Mandatory)][hashtable]$Context)

    $algorithm = [string](Get-GnuOptionValue -Context $Context -Key 'algorithm' -Default 'crc')
    $digests = @{ md5 = 'md5sum'; sha1 = 'sha1sum'; sha224 = 'sha224sum'; sha256 = 'sha256sum'; sha384 = 'sha384sum'; sha512 = 'sha512sum' }
    if (@('crc', 'sysv', 'bsd') -notcontains $algorithm -and -not $digests.ContainsKey($algorithm)) {
        Write-GnuUsageError -Context $Context -Message "invalid argument '$algorithm' for '--algorithm'`nValid arguments are: 'sysv', 'bsd', 'crc', 'md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512'"
        return
    }
    $operands = @($Context.Operands)
    $given = $operands.Count
    if ($given -eq 0) { $operands = @('-') }

    if ($digests.ContainsKey($algorithm)) {
        $definition = $script:GnuChecksumAlgorithms[$digests[$algorithm]]
        if (Test-GnuOption -Context $Context -Key 'check') {
            Test-GnuChecksumList -Context $Context -Algorithm $definition -Operands $operands
            return
        }
        $zero = Test-GnuOption -Context $Context -Key 'zero'
        $records = foreach ($operand in $operands) {
            $hash = Get-GnuChecksumHex -Context $Context -Operand $operand -Algorithm $definition
            if ($null -eq $hash) { continue }
            if (Test-GnuOption -Context $Context -Key 'untagged') { "$hash $operand" }
            else { "$($definition.Tag) ($operand) = $hash" }
        }
        ConvertTo-GnuRecordOutput -Record @($records) -Zero:$zero
        return
    }
    if (Test-GnuOption -Context $Context -Key 'check') {
        Write-GnuError -Context $Context -Message '--check is not supported with --algorithm={bsd,sysv,crc}'
        return
    }

    foreach ($operand in $operands) {
        $bytes = Get-GnuInputBytes -Context $Context -Operand $operand
        if ($null -eq $bytes) { continue }
        if ($algorithm -eq 'crc') { $line = "$(Get-GnuPosixCrc -Bytes $bytes) $($bytes.Length)" }
        else { $line = Get-GnuSumLine -Bytes $bytes -SysV:($algorithm -eq 'sysv') }
        if ($given -gt 0) { $line += " $operand" }
        $line
    }
}