3 # Encode/Decode base64 for a string
4 # Stephen Uhler / Brent Welch (c) 1997 Sun Microsystems
5 # The decoder was done for exmh by Chris Garrigues
7 # Copyright (c) 1998-2000 by Ajuba Solutions.
8 # See the file "license.terms" for information on usage and redistribution
9 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 # RCS: @(#) $Id: base64.tcl,v 1.1 2012-04-04 10:50:38 igus Exp $
13 # Version 1.0 implemented Base64_Encode, Base64_Decode
14 # Version 2.0 uses the base64 namespace
15 # Version 2.1 fixes various decode bugs and adds options to encode
16 # Version 2.2 is much faster, Tcl8.0 compatible
17 # Version 2.2.1 bugfixes
18 # Version 2.2.2 bugfixes
19 # Version 2.3 bugfixes and extended to support Trf
21 # @mdgen EXCLUDE: base64c.tcl
23 package require Tcl 8.2
24 namespace eval ::base64 {
25 namespace export encode decode
28 if {![catch {package require Trf 2.0}]} {
29 # Trf is available, so implement the functionality provided here
30 # in terms of calls to Trf for speed.
34 # Base64 encode a given string.
37 # args ?-maxlen maxlen? ?-wrapchar wrapchar? string
39 # If maxlen is 0, the output is not wrapped.
42 # A Base64 encoded version of $string, wrapped at $maxlen characters
45 proc ::base64::encode {args} {
46 # Set the default wrapchar and maximum line length to match the output
47 # of GNU uuencode 4.2. Various RFCs allow for different wrapping
48 # characters and wraplengths, so these may be overridden by command line
53 if { [llength $args] == 0 } {
54 error "wrong # args: should be \"[lindex [info level 0] 0]\
55 ?-maxlen maxlen? ?-wrapchar wrapchar? string\""
58 set optionStrings [list "-maxlen" "-wrapchar"]
59 for {set i 0} {$i < [llength $args] - 1} {incr i} {
60 set arg [lindex $args $i]
61 set index [lsearch -glob $optionStrings "${arg}*"]
63 error "unknown option \"$arg\": must be -maxlen or -wrapchar"
66 if { $i >= [llength $args] - 1 } {
67 error "value for \"$arg\" missing"
69 set val [lindex $args $i]
71 # The name of the variable to assign the value to is extracted
72 # from the list of known options, all of which have an
73 # associated variable of the same name as the option without
74 # a leading "-". The [string range] command is used to strip
75 # of the leading "-" from the name of the option.
78 set [string range [lindex $optionStrings $index] 1 end] $val
81 # [string is] requires Tcl8.2; this works with 8.0 too
82 if {[catch {expr {$maxlen % 2}}]} {
83 error "expected integer but got \"$maxlen\""
86 set string [lindex $args end]
87 set result [::base64 -mode encode -- $string]
88 set result [string map [list \n ""] $result]
92 set edge [expr {$maxlen - 1}]
93 while {[string length $result] > $maxlen} {
94 append res [string range $result 0 $edge]$wrapchar
95 set result [string range $result $maxlen end]
97 if {[string length $result] > 0} {
106 # ::base64::decode --
108 # Base64 decode a given string.
111 # string The string to decode. Characters not in the base64
112 # alphabet are ignored (e.g., newlines)
117 proc ::base64::decode {string} {
118 regsub -all {\s} $string {} string
119 ::base64 -mode decode -- $string
123 # Without Trf use a pure tcl implementation
125 namespace eval base64 {
127 variable base64_en {}
129 # We create the auxiliary array base64_tmp, it will be unset later.
132 foreach char {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
133 a b c d e f g h i j k l m n o p q r s t u v w x y z \
134 0 1 2 3 4 5 6 7 8 9 + /} {
135 set base64_tmp($char) $i
136 lappend base64_en $char
141 # Create base64 as list: to code for instance C<->3, specify
142 # that [lindex $base64 67] be 3 (C is 67 in ascii); non-coded
143 # ascii chars get a {}. we later use the fact that lindex on a
144 # non-existing index returns {}, and that [expr {} < 0] is true
147 # the last ascii char is 'z'
149 for {set i 0} {$i <= $len} {incr i} {
150 set char [format %c $i]
152 if {[info exists base64_tmp($char)]} {
153 set val $base64_tmp($char)
160 # code the character "=" as -1; used to signal end of message
162 set base64 [lreplace $base64 $i $i -1]
164 # remove unneeded variables
165 unset base64_tmp i char len val
167 namespace export encode decode
170 # ::base64::encode --
172 # Base64 encode a given string.
175 # args ?-maxlen maxlen? ?-wrapchar wrapchar? string
177 # If maxlen is 0, the output is not wrapped.
180 # A Base64 encoded version of $string, wrapped at $maxlen characters
183 proc ::base64::encode {args} {
184 set base64_en $::base64::base64_en
186 # Set the default wrapchar and maximum line length to match the output
187 # of GNU uuencode 4.2. Various RFCs allow for different wrapping
188 # characters and wraplengths, so these may be overridden by command line
193 if { [llength $args] == 0 } {
194 error "wrong # args: should be \"[lindex [info level 0] 0]\
195 ?-maxlen maxlen? ?-wrapchar wrapchar? string\""
198 set optionStrings [list "-maxlen" "-wrapchar"]
199 for {set i 0} {$i < [llength $args] - 1} {incr i} {
200 set arg [lindex $args $i]
201 set index [lsearch -glob $optionStrings "${arg}*"]
202 if { $index == -1 } {
203 error "unknown option \"$arg\": must be -maxlen or -wrapchar"
206 if { $i >= [llength $args] - 1 } {
207 error "value for \"$arg\" missing"
209 set val [lindex $args $i]
211 # The name of the variable to assign the value to is extracted
212 # from the list of known options, all of which have an
213 # associated variable of the same name as the option without
214 # a leading "-". The [string range] command is used to strip
215 # of the leading "-" from the name of the option.
218 set [string range [lindex $optionStrings $index] 1 end] $val
221 # [string is] requires Tcl8.2; this works with 8.0 too
222 if {[catch {expr {$maxlen % 2}}]} {
223 error "expected integer but got \"$maxlen\""
226 set string [lindex $args end]
233 # Process the input bytes 3-by-3
235 binary scan $string c* X
237 # Do the line length check before appending so that we don't get an
238 # extra newline if the output is a multiple of $maxlen chars long.
239 if {$maxlen && $length >= $maxlen} {
240 append result $wrapchar
244 append result [lindex $base64_en [expr {($x >>2) & 0x3F}]]
246 append result [lindex $base64_en [expr {(($x << 4) & 0x30) | (($y >> 4) & 0xF)}]]
249 [lindex $base64_en [expr {(($y << 2) & 0x3C) | (($z >> 6) & 0x3)}]]
250 append result [lindex $base64_en [expr {($z & 0x3F)}]]
262 append result [lindex $base64_en [expr {(($x << 4) & 0x30)}]]==
263 } elseif {$state == 2} {
264 append result [lindex $base64_en [expr {(($y << 2) & 0x3C)}]]=
269 # ::base64::decode --
271 # Base64 decode a given string.
274 # string The string to decode. Characters not in the base64
275 # alphabet are ignored (e.g., newlines)
280 proc ::base64::decode {string} {
281 if {[string length $string] == 0} {return ""}
283 set base64 $::base64::base64
284 set output "" ; # Fix for [Bug 821126]
286 binary scan $string c* X
288 set bits [lindex $base64 $x]
290 if {[llength [lappend nums $bits]] == 4} {
291 foreach {v w z y} $nums break
292 set a [expr {($v << 2) | ($w >> 4)}]
293 set b [expr {(($w & 0xF) << 4) | ($z >> 2)}]
294 set c [expr {(($z & 0x3) << 6) | $y}]
295 append output [binary format ccc $a $b $c]
298 } elseif {$bits == -1} {
299 # = indicates end of data. Output whatever chars are left.
300 # The encoding algorithm dictates that we can only have 1 or 2
301 # padding characters. If x=={}, we have 12 bits of input
302 # (enough for 1 8-bit output). If x!={}, we have 18 bits of
303 # input (enough for 2 8-bit outputs).
305 foreach {v w z} $nums break
306 set a [expr {($v << 2) | (($w & 0x30) >> 4)}]
308 append output [binary format c $a ]
310 set b [expr {(($w & 0xF) << 4) | (($z & 0x3C) >> 2)}]
311 append output [binary format cc $a $b]
315 # RFC 2045 says that line breaks and other characters not part
316 # of the Base64 alphabet must be ignored, and that the decoder
317 # can optionally emit a warning or reject the message. We opt
318 # not to do so, but to just ignore the character.
326 package provide base64 2.3.2