Share via


Powershell code to implement the uudecode command line utility

Question

Thursday, February 20, 2014 10:44 PM

Unix has had a utility named uudecode.  For example see this webpage:
[http://www.lehman.cuny.edu/cgi-bin/man-cgi?uuencode+1

](http://www.lehman.cuny.edu/cgi-bin/man-cgi?uuencode+1)I have coded some PowerShell scripts that depend on a uudecode.exe utility that I have acquired
from a 3rd party website, but I'd like to have some PowerShell code to do the decoding in a PowerShell 
script that does not have the dependency on uudecode.exe

Has anyone seen any such PowerShell code implementing the uudecode functionality?

All replies (6)

Friday, February 21, 2014 3:07 AM âś…Answered | 1 vote

Here you go. This is very quick and dirty (and has no comments or help whatsoever), but it seems to work.  I used the functions like this, to avoid any extra CRLF characters injected into the file during encoding:

$encodedString = ConvertTo-UuEncoding -Path .\cat.txt

[System.IO.File]::WriteAllText("$pwd\catencoded.txt", $encodedString)

Get-Content -Path .\catencoded.txt |
ConvertFrom-UuEncoding |
Set-Content -Path .\catdecoded.txt -Encoding Byte

Here are the functions.  They can probably use a lot of cleanup work, but it's a starting point:

function ConvertTo-UuEncoding
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Path
    )

    try
    {
        $item = Get-Item -LiteralPath $Path -ErrorAction Stop
        if ($item -isnot [System.IO.FileInfo])
        {
            throw "Path '$Path' does not refer to a valid file."
        }
    }
    catch
    {
        throw
    }

    $fileBuilder = New-Object System.Text.StringBuilder

    $null = $fileBuilder.Append("begin 0644 $($item.Name)`n")

    $lineBuilder = New-Object System.Text.StringBuilder
    $lineBytes = 0

    Get-Content -LiteralPath $Path -Encoding Byte -ReadCount 3 |
    ForEach-Object {
        $bytes = $_

        $lineBytes += $bytes.Count

        $int = [int]0

        for ($i = 0; $i -lt $bytes.Count; $i++)
        {
            $int += (([int]$bytes[$i]) -shl (8 * (2 - $i)))
        }

        $null = $lineBuilder.Append([char](32 + (($int -shr 18) -band 0x3F)))
        $null = $lineBuilder.Append([char](32 + (($int -shr 12) -band 0x3F)))
        $null = $lineBuilder.Append([char](32 + (($int -shr 6) -band 0x3F)))
        $null = $lineBuilder.Append([char](32 + ($int -band 0x3F)))

        if ($lineBytes -ge 45)
        {
            $null = $lineBuilder.Append("`n")
            $null = $lineBuilder.Insert(0, [char](32 + $lineBytes))
            $null = $fileBuilder.Append($lineBuilder.ToString())

            $lineBuilder.Length = 0
            $lineBytes = 0
        }
    }

    if ($lineBuilder.Length -gt 0)
    {
        $null = $lineBuilder.Append("`n")
        $null = $lineBuilder.Insert(0, [char](32 + $lineBytes))
        $null = $fileBuilder.Append($lineBuilder.ToString())
    }

    $null = $fileBuilder.Append("```nend`n")

    $fileBuilder.ToString()
}

function ConvertFrom-UuEncoding
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [AllowEmptyString()]
        [string[]]
        $EncodedText,

        [ref]
        $FileName
    )

    begin
    {
        $state = 'ExpectingHeader'
    }

    process
    {
        foreach ($line in $EncodedText -split '\r?\n')
        {
            switch ($state)
            {
                'ExpectingHeader'
                {
                    if ($line -notmatch '^begin \d+ (.+)')
                    {
                        throw "Invalid header in UUEncoded text."
                    }

                    if ($null -ne $FileName)
                    {
                        $FileName.Value = $matches[1]
                    }

                    $state = 'Data'

                    break
                }

                'Data'
                {
                    if ($line -eq '`')
                    {
                        $state = 'ExpectingFooter'
                    }
                    else
                    {
                        $chars = $line.ToCharArray()

                        if ($chars.Count -eq 0 -or ($chars.Count - 1) % 4 -ne 0 -or
                            ($chars | Where-Object { [int]$_ -lt 32 -or [int]$_ -gt 96 }).Count -gt 0)
                        {
                            throw "Invalid content in UUEncoded text."
                        }

                        $bytes = [int]$chars[0] - 32

                        if ($bytes -gt 45 -or $bytes -lt 0)
                        {
                            throw "Invalid content in UUEncoded text."
                        }

                        $byteCounter = 0

                        for ($i = 1; $i -lt $chars.Count; $i += 4)
                        {
                            $int = ((([int]$chars[$i] - 32) -band 0x3F) -shl 18) -bor
                                      ((([int]$chars[$i+1] - 32) -band 0x3F) -shl 12) -bor
                                      ((([int]$chars[$i+2] - 32) -band 0x3F) -shl 6) -bor
                                      (([int]$chars[$i+3] - 32) -band 0x3F)
                            
                            for ($j = 0; $j -lt 3 -and $byteCounter -lt $bytes; $j++, $byteCounter++)
                            {
                                Write-Output ([byte](($int -shr (8 * (2 - $j))) -band 0xFF))
                            }
                        }
                    }

                    break
                }

                'ExpectingFooter'
                {
                    if ($line -ne 'end')
                    {
                        Write-Warning 'Invalid footer line detected.'
                    }
                    else
                    {
                        $state = 'Done'
                    }

                    break
                }

                'Done'
                {
                    Write-Warning 'Detected content after footer line.'
                }
            }
        }
    }
}

Friday, February 21, 2014 12:31 AM | 1 vote

It looks like that utility, at its core, just base64 encodes a binary file (which is easy to do in PowerShell), but it also sets up a header or some sort that contains the file name and permissions. To write PowerShell code that could read a file created by uuencode, you'd have to know the format and contents of those headers (not to mention figure out what you're supposed to do with unix permissions on NTFS.)

If you just want to implement something that's similar to those utilities (but not actually compatible with them), here's how you can take a file and convert it to a base64 string.  Keep in mind, though, that this is not a streaming approach, so the entire file has to be temporarily held in memory, both in its original binary form and the base64 string version:

$file = '.\someFile.bin'

$bytes = Get-Content -Path $file -Encoding Byte -ReadCount 0

$encoded = [Convert]::ToBase64String($bytes)

$encoded

Friday, February 21, 2014 12:37 AM

I dabbled some today with    [Convert]::ToBase64String
but it turns out that this is a different encoding than uuencode uses.
I'm just hoping that someone knows a link to some PowerShell source that does it the uuencode way.

And, I do need it to be compatible with the standard uuencode format.

See:  https://en.wikipedia.org/wiki/Base64#Design

for a comparison of the design criteria that contrasts Base64 and uuencode format.


Friday, February 21, 2014 1:10 AM | 1 vote

Perhaps you can do something with this:

http://sourceforge.net/projects/smartcoder/

Using C# within PowerShell is beyond my abilities, but I figured I'd at least throw this out there.

Don't retire TechNet! - (Don't give up yet - 12,700+ strong and growing)


Friday, February 21, 2014 1:27 AM | 1 vote

Ah, I see. It uses the same encoding techniques as base64, but different characters to represent the data (https://en.wikipedia.org/wiki/Uuencoding ).  It's certainly possible to implement this in PowerShell, though I'm not familiar with all of the original command's options. I'll write up a quick encoder / decoder that just includes the filename and a generic permissions number (which will be garbage, not actually read from or applied to a file), and you can try testing them against the actual executable.


Friday, February 21, 2014 3:14 PM

Thanks!   I tested the decoder and it produced the same output as the uudecode.txt utility I was using on my test files.