Contents of file "btoa.pf"

#! /usr/local/postforth/pf
: BTOA
 ' ARGC , ' 2 , ' > ,
 IF ( infile specified)
 ' 0 , ' CLOSEFILE , ' 2 , ' ARGV , ' 0 , ' 0 , ' OPEN ,
  IF ( nonzero handle returned)
  " Cannot redirect STDIN" ' 2 , ' TYPE , ' 2 , ' CR , ' 1 , ' EXIT ,
  ENDIF
 ENDIF
 ' ARGC , ' LIT , 16# 3 , ' > ,
 IF ( outfile specified)
 ' 1 , ' CLOSEFILE , ' LIT , 16# 3 , ' ARGV ,
 ' LIT , 16# 1FF , ' UMASK , ' ~ , ' & , ' FCREATE ,
 ' 1 , ' = , ' ~ ,
  IF ( handle not 1)
  " Cannot redirect STDOUT" ' 2 , ' TYPE , ' 2 , ' CR , ' 1 , ' EXIT ,
  ENDIF
 ENDIF
 ' CONVERT_BTOA , " ~>" ' 1 , ' TYPE , ' 1 , ' CR , ' 0 , ' EXIT , ' ;S ,

( Ascii85 conversion is the process of changing a 32-bit word [4 bytes] into
a 5-digit base-85 character, where "!" is a zero and "u" is the highest
possible value. In both cases, the leftmost byte [lowest addressed] is taken
as the most significant bits. That is, if the bytes at offset 0 through 3
are, in order, 00 00 00 01, then btoa translation will make it !!!!" where
'!' is zero and '"' is 1. 00 00 00 55 will translate to !!!"! because 55
[85 decimal] is a "ten" in this base, so the '"' [one] is in the second
position from the right. A special case is when the 32-bit number is 0,
in which case it translates to a single 'z'. Another special case is when
less than 4 characters are being converted, as can happen at the end of a
file. In this case, N input characters are padded with 4-N null bytes and
converted into N+1 Ascii85 characters. Padding is done at the end, that is,
the LSBs, since the first characters received are more significant. Also,
if a group of chars is all null bytes and is padded, it does not become 'z'
because 'z' stands for 4 nulls. N+1 '!' characters must be output instead.

Some filters, such as postscript's, expect the Ascii85 stream to end in ~>
or it will not process it correctly. Strictly speaking it should also begin
with <~ but for some reason the postscript interpreters I've used don't
enforce this.)

VARIABLE ASCII85 16# 55 DUP DUP DUP DUP * * * ( 85 ^ 4) ,
 DUP DUP DUP * * ( 85 ^ 3) ,
 DUP DUP * ( 85 ^ 2) ,
 ( 85 ^ 1) ,
 1 ( 85 ^ 0) ,

: SWAPS ( n -n2) ( swap 16-bit words in a 32-bit word)
 ' DUP , ' LIT , 16# 10 , ' >> , ( make a copy, and shift it down)
 ' SWAP , ' LIT , 16# 10 , ' << , ( shift original up)
 ' + , ' ;S , ( merge them with + or OR)

: NEXT4 ( - d count) ' 0 , ' SP@ , ' >R , ( buffer on stack)
 ' 0 , ' LIT , 16# 4 , ' R> , ' READ , ' DUP , ' 0< , IF
 " Read error" ' 2 , ' TYPE , ' 2 , ' CR , ' 1 , ' EXIT , ENDIF
 ' >R , ' SWAB , ' SWAPS , ' SWAB , ' R> , ( reorder)
 ' ;S ,

: 85DIGIT ( n a - n' a') ( output next ascii85 digit, advance
 pointer into table and return remainder for next digit conversion)
 ' SWAP , ' OVER , ' @ , ' 0 , ' SWAP , ' U/M , ( divide n by *a)
 ' LIT , ASCII ! , ' + , ' 1 , ' EMIT , ( convert to ASCII85 and output)
 ' SWAP , ' LIT , 16# 4 , ' + , ' ;S , ( replace n with remainder, update a)

: 85WORD ( n1 n2 -) ( output n1, count n2 as ASCII85 equivalent)
 ' >R , ' R , ' LIT , 16# 4 , ' = , ( count=4 and n=0, output z)
 ' OVER , ' 0= , ' & ,
 IF ' LIT , ASCII z , ' 1 , ' EMIT ,
 ELSE ( any count, any other value)
 ' ASCII85 ,
  BEGIN ' 85DIGIT , ' R> , ' 1- , ' >R , ' R , ' 0< ,
  UNTIL
 ' DROP , ( table addr)
 ENDIF ' R> , ' DROP , ( count)
 ' DROP , ' ;S ,

: CONVERT_BTOA
 BEGIN ' NEXT4 , ' DUP , ' >R , ' R ,
  IF ' 85WORD ,
  ENDIF ' R> , ' 0= ,
 UNTIL ' DROP , ' DROP , ' ;S ,

BTOA ( run BTOA - comment out until debugged)