660e52bdcb
FossilOrigin-Name: 1a43743f43076eb087ee4dd7fbfa96b8dfda2aa4ddcff41e6a5a9634ba6e239c
72 lines
2.2 KiB
Forth
Executable file
72 lines
2.2 KiB
Forth
Executable file
#!/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 script:get-argument
|
|
[ 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
|
|
~~~
|