#!/usr/bin/env retro # uuencode This is an implementation of `uuencode` in Retro. This takes a file name and renders the uuencoded data to standard output. ## Prep Work First, determine the size of the file. ~~~ #0 script:get-argument file:R file:open &file:size &file:close bi 'SIZE const ~~~ With this, I can create a buffer to hold the file and read it into memory. ~~~ 'Data d:create SIZE allot &Data #0 script:get-argument file:slurp ~~~ ## The Header A uuencoded file starts with a header line of the form: begin I am hard coding the mode as 0644. ~~~ :header (-) #0 script:get-argument 'begin_0644_%s1\n s:format s:put ; ~~~ ## Data Encoding Here's how uuencoding works: * Three characters are packed into a cell. Cat = (C) 01000011 (a) 01100001 (t) 01110100 * This is divided into a quartet of six bit values. Cat = 010000 110110 000101 110100 * 32 is added to each, yielding a printable character. * At this point, "Cat" has become 0V%T. So the basic encoding words will need to pack a cell and then unpack it into characters. ~~~ :mask #63 and ; :to-char #32 + ; :decode (n-) dup #18 shift mask to-char c:put dup #12 shift mask to-char c:put dup #6 shift mask to-char c:put mask to-char c:put ; :pack #0 [ fetch-next ] dip + #-8 shift [ fetch-next ] dip + #-8 shift [ fetch-next ] dip + ; ~~~ The actual conversion is pretty easy. Output is given as lines, starting with an encoded length followed by the encoded data. The encoded length is just the actual value with 32 added to it and displayed as an ASCII character. Lines are 45 unencoded characters in length, so will start with an `M`, except for the last one, which may be shorter. So:a ~~~ :full-line $M c:put #15 [ pack decode ] times nl ; :remaining-size SIZE #45 mod #32 + c:put ; :partial-line SIZE #45 mod #3 / [ pack decode ] times ; :final-bundle SIZE #45 mod #3 mod #2 [ fetch-next #-16 shift swap fetch #-8 shift + decode ] case #1 [ fetch #-16 shift decode ] case drop ; ~~~ And then using the factors to do the conversion: ~~~ :convert &Data SIZE #45 / &full-line times remaining-size partial-line final-bundle nl ; ~~~ ## The Footer A uuencoded file ends with two lines: ` end The backtick is used to denote a line with a length of zero characters. ~~~ :footer $` c:put nl 'end s:put nl ; ~~~ ## Run Everything ~~~ :uuencode header convert footer ; uuencode ~~~