1 ! Copyright (C) 2013-2014 Björn Lindqvist
2 ! See http://factorcode.org/license.txt for BSD license
3 USING: accessors ascii base64 fry grouping.extras io
4 io.encodings io.encodings.string io.encodings.utf16 kernel math
5 math.functions sequences splitting strings ;
8 TUPLE: utf7codec dialect buffer ;
10 ! These words encodes the difference between standard utf7 and the
11 ! dialect used by IMAP which wants slashes replaced with commas when
12 ! encoding and uses '&' instead of '+' as the escaping character.
13 : utf7 ( -- utf7codec )
16 { { CHAR: + } { CHAR: - } }
17 } V{ } utf7codec boa ;
19 : utf7imap4 ( -- utf7codec )
21 { { CHAR: / } { CHAR: , } }
22 { { CHAR: & } { CHAR: - } }
23 } V{ } utf7codec boa ;
25 : >raw-base64 ( bytes -- bytes' )
26 >string utf16be encode >base64 [ CHAR: = = ] trim-tail ;
28 : raw-base64> ( str -- str' )
29 dup length 4 / ceiling 4 * CHAR: = pad-tail base64> utf16be decode ;
31 : encode-chunk ( repl-pair surround-pair chunk ascii? -- bytes )
32 [ swap [ first ] [ concat ] bi replace nip ]
33 [ >raw-base64 -rot [ first2 replace ] [ first2 surround ] bi* ] if ;
35 : encode-utf7-string ( str codec -- bytes )
36 [ [ printable? ] group-by ] dip
37 dialect>> first2 '[ _ _ rot first2 swap encode-chunk ] map
40 M: utf7codec encode-string ( str stream codec -- )
41 swapd encode-utf7-string swap stream-write ;
45 : decode-chunk ( dialect -- ch buffer )
46 dup first2 swap [ second read-until drop ] [ first2 swap replace ] bi*
47 [ second first first { } ] [ raw-base64> emit-char ] if-empty ;
49 : fill-buffer ( dialect -- ch buffer )
50 dup second first first read1 dup swapd = [
54 : emit-char ( dialect buffer -- ch buffer' )
55 [ fill-buffer ] [ nip unclip swap ] if-empty ;
57 : replace-all! ( src dst -- )
58 [ delete-all ] keep push-all ;
60 M: utf7codec decode-char ( stream codec -- char/f )
62 [ dialect>> ] [ buffer>> ] bi [ emit-char ] keep replace-all!