#!/usr/bin/env retro

# uudecode

This is an implementation of `uudecode` in Retro. This takes a
file name and renders the decoded data to standard output.

I'm taking shortcuts in implementing this. Since it only writes
to the standard output, I'm going to ignore the `begin ...`
header as well as the footer. The words I define here will use
`pop drop` to exit the calling function.

~~~
:discard-header dup #0 #5 s:substr 'begin s:eq? 0; pop drop-pair drop ;
:discard-empty  dup fetch $` eq? 0; pop drop-pair drop ;
:discard-end    dup #0 #3 s:substr 'end   s:eq? 0; pop drop-pair drop ;
~~~

Now for decoding. uuencode bundles three values into four six bit
characters. I read in four characters, shifting them into position.
The six bit characters are incremented by 32 to make sure they fall
into the printable range, so I also decrement them while packing.

~~~
:gather
  #0 [ fetch-next #32 - #63 and #-18 shift ] dip +
     [ fetch-next #32 - #63 and #-12 shift ] dip +
     [ fetch-next #32 - #63 and #-6  shift ] dip +
     [ fetch-next #32 - #63 and            ] dip + ;
~~~

The decode process then is to take each bundle and split it back
into 8-bit characters. This is a simple bit of shifting and masking.

~~~
:decode
    [ #16 shift #255 and c:put ]
    [ #8 shift  #255 and c:put ]
    [           #255 and c:put ] tri ;
~~~

Iterating over the input file is fairly straightforward. I'm using
the `file:for-each-line` combinator to take care of splitting the
input into lines.

An input line consists of a character indicating the number of
unencoded characters in the line and then the encoded data. As
with the encoded data, the length is raised by 32 to push it
into the printable character set.

~~~
:length  fetch-next #32 - ;
~~~

I define a word to iterate over the encoded data in a line.
This will divide the actual length by 3, then gather the
data and decode it.

~~~
:full-line    #3 / [ gather decode ] times drop ;
~~~

Finally, load the file and iterate over each line.

~~~
#0 sys:argv
[ s:keep
  discard-header discard-empty discard-end
  fetch-next #32 - dup #45 eq?
  [ full-line ]
  [ #3 / [ gather decode ] times  gather decode drop ] choose ] file:for-each-line
~~~
