base64.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 package require Tcl 8.2
00024 namespace ::base64 {
00025 namespace export encode decode
00026 }
00027
00028 if {![catch {package require Trf 2.0}]} {
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045 ret ::base64::encode (type args) {
00046 # Set the default wrapchar and maximum line length to match the output
00047 # of GNU uuencode 4.2. Various RFCs allow for different wrapping
00048 # characters and wraplengths, so these may be overridden by command line
00049 # options.
00050 set wrapchar "\n"
00051 set maxlen 60
00052
00053 if { [llength $args] == 0 } {
00054 error "wrong # args: should be \"[lindex [info level 0] 0]\
00055 ?-maxlen maxlen? ?-wrapchar wrapchar? string\""
00056 }
00057
00058 set optionStrings [list "-maxlen" "-wrapchar"]
00059 for {set i 0} {$i < [llength $args] - 1} {incr i} {
00060 set arg [lindex $args $i]
00061 set index [lsearch -glob $optionStrings "${arg}*"]
00062 if { $index == -1 } {
00063 error "unknown option \"$arg\": must be -maxlen or -wrapchar"
00064 }
00065 incr i
00066 if { $i >= [llength $args] - 1 } {
00067 error "value for \"$arg\" missing"
00068 }
00069 set val [lindex $args $i]
00070
00071 # The name of the variable to assign the value to is extracted
00072 # from the list of known options, all of which have an
00073 # associated variable of the same name as the option without
00074 # a leading "-". The [string range] command is used to strip
00075 # of the leading "-" from the name of the option.
00076 #
00077 # FRINK: nocheck
00078 set [string range [lindex $optionStrings $index] 1 end] $val
00079 }
00080
00081 # [string is] requires Tcl8.2; this works with 8.0 too
00082 if {[catch {expr {$maxlen % 2}}]} {
00083 error "expected integer but got \"$maxlen\""
00084 }
00085
00086 set string [lindex $args end]
00087 set result [::base64 -mode encode -- $string]
00088 set result [string map [list \n ""] $result]
00089
00090 if {$maxlen > 0} {
00091 set res ""
00092 set edge [expr {$maxlen - 1}]
00093 while {[string length $result] > $maxlen} {
00094 append res [string range $result 0 $edge]$wrapchar
00095 set result [string range $result $maxlen end]
00096 }
00097 if {[string length $result] > 0} {
00098 append res $result
00099 }
00100 set result $res
00101 }
00102
00103 return $result
00104 }
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117 ret ::base64::decode (type string) {
00118 regsub -all {\s} $string {} string
00119 ::base64 -mode decode -- $string
00120 }
00121
00122 } else {
00123
00124
00125 namespace base64 {
00126 variable base64 {}
00127 variable base64_en {}
00128
00129
00130
00131 i = 0
00132 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 \
00133 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 \
00134 0 1 2 3 4 5 6 7 8 9 + /} {
00135 base64 = _tmp($char) $i
00136 lappend base64_en $char
00137 incr i
00138 }
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148 scan z %c len
00149 for { i = 0} {$i <= $len} {incr i} {
00150 char = [format %c $i]
00151 val = {}
00152 if {[info exists base64_tmp($char)]} {
00153 val = $base64_tmp($char)
00154 } else {
00155 val = {}
00156 }
00157 lappend base64 $val
00158 }
00159
00160
00161 scan = %c i
00162 base64 = [lreplace $base64 $i $i -1]
00163
00164
00165 un base64 = _tmp i char len val
00166
00167 namespace export encode decode
00168 }
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183 ret ::base64::encode (type args) {
00184 set base64_en $::base64::base64_en
00185
00186 # Set the default wrapchar and maximum line length to match the output
00187 # of GNU uuencode 4.2. Various RFCs allow for different wrapping
00188 # characters and wraplengths, so these may be overridden by command line
00189 # options.
00190 set wrapchar "\n"
00191 set maxlen 60
00192
00193 if { [llength $args] == 0 } {
00194 error "wrong # args: should be \"[lindex [info level 0] 0]\
00195 ?-maxlen maxlen? ?-wrapchar wrapchar? string\""
00196 }
00197
00198 set optionStrings [list "-maxlen" "-wrapchar"]
00199 for {set i 0} {$i < [llength $args] - 1} {incr i} {
00200 set arg [lindex $args $i]
00201 set index [lsearch -glob $optionStrings "${arg}*"]
00202 if { $index == -1 } {
00203 error "unknown option \"$arg\": must be -maxlen or -wrapchar"
00204 }
00205 incr i
00206 if { $i >= [llength $args] - 1 } {
00207 error "value for \"$arg\" missing"
00208 }
00209 set val [lindex $args $i]
00210
00211 # The name of the variable to assign the value to is extracted
00212 # from the list of known options, all of which have an
00213 # associated variable of the same name as the option without
00214 # a leading "-". The [string range] command is used to strip
00215 # of the leading "-" from the name of the option.
00216 #
00217 # FRINK: nocheck
00218 set [string range [lindex $optionStrings $index] 1 end] $val
00219 }
00220
00221 # [string is] requires Tcl8.2; this works with 8.0 too
00222 if {[catch {expr {$maxlen % 2}}]} {
00223 error "expected integer but got \"$maxlen\""
00224 }
00225
00226 set string [lindex $args end]
00227
00228 set result {}
00229 set state 0
00230 set length 0
00231
00232
00233 # Process the input bytes 3-by-3
00234
00235 binary scan $string c* X
00236 foreach {x y z} $X {
00237 # Do the line length check before appending so that we don't get an
00238 # extra newline if the output is a multiple of $maxlen chars long.
00239 if {$maxlen && $length >= $maxlen} {
00240 append result $wrapchar
00241 set length 0
00242 }
00243
00244 append result [lindex $base64_en [expr {($x >>2) & 0x3F}]]
00245 if {$y != {}} {
00246 append result [lindex $base64_en [expr {(($x << 4) & 0x30) | (($y >> 4) & 0xF)}]]
00247 if {$z != {}} {
00248 append result \
00249 [lindex $base64_en [expr {(($y << 2) & 0x3C) | (($z >> 6) & 0x3)}]]
00250 append result [lindex $base64_en [expr {($z & 0x3F)}]]
00251 } else {
00252 set state 2
00253 break
00254 }
00255 } else {
00256 set state 1
00257 break
00258 }
00259 incr length 4
00260 }
00261 if {$state == 1} {
00262 append result [lindex $base64_en [expr {(($x << 4) & 0x30)}]]==
00263 } elseif {$state == 2} {
00264 append result [lindex $base64_en [expr {(($y << 2) & 0x3C)}]]=
00265 }
00266 return $result
00267 }
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280 ret ::base64::decode (type string) {
00281 if {[string length $string] == 0} {return ""}
00282
00283 set base64 $::base64::base64
00284 set output "" ; # Fix for [Bug 821126]
00285
00286 binary scan $string c* X
00287 foreach x $X {
00288 set bits [lindex $base64 $x]
00289 if {$bits >= 0} {
00290 if {[llength [lappend nums $bits]] == 4} {
00291 foreach {v w z y} $nums break
00292 set a [expr {($v << 2) | ($w >> 4)}]
00293 set b [expr {(($w & 0xF) << 4) | ($z >> 2)}]
00294 set c [expr {(($z & 0x3) << 6) | $y}]
00295 append output [binary format ccc $a $b $c]
00296 set nums {}
00297 }
00298 } elseif {$bits == -1} {
00299 # = indicates end of data. Output whatever chars are left.
00300 # The encoding algorithm dictates that we can only have 1 or 2
00301 # padding characters. If x=={}, we have 12 bits of input
00302 # (enough for 1 8-bit output). If x!={}, we have 18 bits of
00303 # input (enough for 2 8-bit outputs).
00304
00305 foreach {v w z} $nums break
00306 set a [expr {($v << 2) | (($w & 0x30) >> 4)}]
00307 if {$z == {}} {
00308 append output [binary format c $a ]
00309 } else {
00310 set b [expr {(($w & 0xF) << 4) | (($z & 0x3C) >> 2)}]
00311 append output [binary format cc $a $b]
00312 }
00313 break
00314 } else {
00315 # RFC 2045 says that line breaks and other characters not part
00316 # of the Base64 alphabet must be ignored, and that the decoder
00317 # can optionally emit a warning or reject the message. We opt
00318 # not to do so, but to just ignore the character.
00319 continue
00320 }
00321 }
00322 return $output
00323 }
00324 }
00325
00326 package provide base64 2.3.2
00327