Utils/cat.ps1
|
Register-GnuUtil @{ Name = 'cat' Category = 'Viewing file contents' Synopsis = 'Concatenate files and print on the standard output.' Usage = @('cat [OPTION]... [FILE]...') Description = @('Concatenate FILE(s) to standard output.', '', 'With no FILE, or when FILE is -, read standard input.') FileOperands = $true Options = @( @{ Short = 'A'; Long = 'show-all'; Sets = @{ 'show-nonprinting' = 1; 'show-ends' = 1; 'show-tabs' = 1 }; Help = 'equivalent to -vET' } @{ Short = 'b'; Long = 'number-nonblank'; Help = 'number nonempty output lines, overrides -n' } @{ Short = 'e'; Sets = @{ 'show-nonprinting' = 1; 'show-ends' = 1 }; Help = 'equivalent to -vE' } @{ Short = 'E'; Long = 'show-ends'; Help = 'display $ at end of each line' } @{ Short = 'n'; Long = 'number'; Help = 'number all output lines' } @{ Short = 's'; Long = 'squeeze-blank'; Help = 'suppress repeated empty output lines' } @{ Short = 't'; Sets = @{ 'show-nonprinting' = 1; 'show-tabs' = 1 }; Help = 'equivalent to -vT' } @{ Short = 'T'; Long = 'show-tabs'; Help = 'display TAB characters as ^I' } @{ Short = 'u'; Help = '(ignored)' } @{ Short = 'v'; Long = 'show-nonprinting'; Help = 'use ^ and M- notation, except for LFD and TAB' } ) Notes = @('PowerShell note: pipeline input is read as text lines; files are read as UTF-8.') } function script:New-GnuCatState { <# .SYNOPSIS Formatting state of one cat invocation (line counter and squeeze tracking carry across files). #> param([Parameter(Mandatory)][hashtable]$Context) $state = @{ NumberAll = Test-GnuOption -Context $Context -Key 'number' NumberNonBlank = Test-GnuOption -Context $Context -Key 'number-nonblank' Squeeze = Test-GnuOption -Context $Context -Key 'squeeze-blank' ShowEnds = Test-GnuOption -Context $Context -Key 'show-ends' ShowTabs = Test-GnuOption -Context $Context -Key 'show-tabs' ShowNonprinting = Test-GnuOption -Context $Context -Key 'show-nonprinting' Number = [long]0 PreviousBlank = $false Raw = $false } $state.Raw = $state.ShowEnds -or $state.ShowNonprinting $state } function script:Format-GnuCatLine { <# .SYNOPSIS Formats one cat line, or returns nothing if it is squeezed away. #> param( [Parameter(Mandatory)][hashtable]$State, [Parameter(Mandatory)][AllowEmptyString()][string]$Line, [Parameter(Mandatory)][bool]$Terminated ) $blank = $Line.Length -eq 0 if ($State.Squeeze -and $blank -and $State.PreviousBlank) { return } $State.PreviousBlank = $blank $text = $Line if ($State.ShowNonprinting -or $State.ShowTabs) { $text = ConvertTo-GnuVisibleText -Text $text -Nonprinting:$State.ShowNonprinting -Tabs:$State.ShowTabs } if ($State.ShowEnds -and $Terminated) { $text += '$' } if ($State.NumberNonBlank) { if (-not $blank) { $State.Number++ $text = $State.Number.ToString([System.Globalization.CultureInfo]::InvariantCulture).PadLeft(6) + "`t" + $text } } elseif ($State.NumberAll) { $State.Number++ $text = $State.Number.ToString([System.Globalization.CultureInfo]::InvariantCulture).PadLeft(6) + "`t" + $text } $text } function script:ConvertTo-GnuVisibleText { <# .SYNOPSIS cat -v / -T rendering: ^X for control characters, ^? for DEL, M- notation for bytes >= 128, ^I for TAB (with -T). #> param( [Parameter(Mandatory)][AllowEmptyString()][string]$Text, [switch]$Nonprinting, [switch]$Tabs ) $builder = New-Object System.Text.StringBuilder $i = 0 while ($i -lt $Text.Length) { $char = $Text[$i] $code = [int]$char $i++ if ($code -eq 9) { if ($Tabs) { [void]$builder.Append('^I') } else { [void]$builder.Append($char) } continue } if (-not $Nonprinting) { [void]$builder.Append($char); continue } if ($code -lt 32) { [void]$builder.Append('^').Append([char]($code + 64)); continue } if ($code -eq 127) { [void]$builder.Append('^?'); continue } if ($code -lt 128) { [void]$builder.Append($char); continue } $chunk = [string]$char if ([char]::IsHighSurrogate($char) -and $i -lt $Text.Length -and [char]::IsLowSurrogate($Text[$i])) { $chunk += $Text[$i] $i++ } # UTF-8 bytes of non-ASCII characters are 0x80-0xF4, so 'M-^?' (byte 0xFF) cannot occur here. foreach ($byte in $script:GnuUtf8.GetBytes($chunk)) { $low = $byte - 128 if ($low -lt 32) { [void]$builder.Append('M-^').Append([char]($low + 64)) } else { [void]$builder.Append('M-').Append([char]$low) } } } $builder.ToString() } |