add uudecode example
FossilOrigin-Name: 1ae8eea6b2580aa8306cdb1741ccb7be8c824278132555e081c3451b0132c545
This commit is contained in:
parent
a000da2032
commit
9c9afd5aad
2 changed files with 73 additions and 0 deletions
|
@ -73,6 +73,7 @@
|
|||
- add Save_and_Restore_Stack.forth
|
||||
- add share.forth and shared.forth
|
||||
- add uuencode.forth
|
||||
- add uudecode.forth
|
||||
- switch to dvorak key bindings in Roo.forth
|
||||
- remove Hooks.forth (now in core language)
|
||||
|
||||
|
|
72
example/uudecode.forth
Executable file
72
example/uudecode.forth
Executable file
|
@ -0,0 +1,72 @@
|
|||
#!/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
|
||||
~~~
|
Loading…
Reference in a new issue