uuencode.tcl

Go to the documentation of this file.
00001 /*  uuencode - Copyright (C) 2002 Pat Thoyts <patthoyts@users.sourceforge.net>*/
00002 /* */
00003 /*  Provide a Tcl only implementation of uuencode and uudecode.*/
00004 /* */
00005 /*  -------------------------------------------------------------------------*/
00006 /*  See the file "license.terms" for information on usage and redistribution*/
00007 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00008 /*  -------------------------------------------------------------------------*/
00009 /*  @(#)$Id: uuencode.tcl,v 1.21 2006/10/14 06:30:55 andreas_kupries Exp $*/
00010 
00011 package require Tcl 8.2;                /*  tcl minimum version*/
00012 
00013 /*  Try and get some compiled helper package.*/
00014 if {[catch {package require tcllibc}]} {
00015     catch {package require Trf}
00016 }
00017 
00018 namespace ::uuencode {
00019     variable version 1.1.4
00020 
00021     namespace export encode decode uuencode uudecode
00022 }
00023 
00024 ret  ::uuencode::Enc (type c) {
00025     return [format %c [expr {($c != 0) ? (($c & 0x3f) + 0x20) : 0x60}]]
00026 }
00027 
00028 ret  ::uuencode::Encode (type s) {
00029     set r {}
00030     binary scan $s c* d
00031     foreach {c1 c2 c3} $d {
00032         if {$c1 == {}} {set c1 0}
00033         if {$c2 == {}} {set c2 0}
00034         if {$c3 == {}} {set c3 0}
00035         append r [Enc [expr {$c1 >> 2}]]
00036         append r [Enc [expr {(($c1 << 4) & 060) | (($c2 >> 4) & 017)}]]
00037         append r [Enc [expr {(($c2 << 2) & 074) | (($c3 >> 6) & 003)}]]
00038         append r [Enc [expr {($c3 & 077)}]]
00039     }
00040     return $r
00041 }
00042 
00043 
00044 ret  ::uuencode::Decode (type s) {
00045     if {[string length $s] == 0} {return ""}
00046     set r {}
00047     binary scan [pad $s] c* d
00048         
00049     foreach {c0 c1 c2 c3} $d {
00050         append r [format %c [expr {((($c0-0x20)&0x3F) << 2) & 0xFF
00051                                    | ((($c1-0x20)&0x3F) >> 4) & 0xFF}]]
00052         append r [format %c [expr {((($c1-0x20)&0x3F) << 4) & 0xFF
00053                                    | ((($c2-0x20)&0x3F) >> 2) & 0xFF}]]
00054         append r [format %c [expr {((($c2-0x20)&0x3F) << 6) & 0xFF
00055                                    | (($c3-0x20)&0x3F) & 0xFF}]]
00056     }
00057     return $r
00058 }
00059 
00060 /*  -------------------------------------------------------------------------*/
00061 /*  C coded version of the Encode/Decode functions for base64c package.*/
00062 /*  -------------------------------------------------------------------------*/
00063 if {[package provide critcl] != {}} {
00064     namespace ::uuencode {
00065         critcl::ccode {
00066             /* include <string.h>*/
00067             static unsigned char Enc(unsigned char c) {
00068                 return (c != 0) ? ((c & 0x3f) + 0x20) : 0x60;
00069             }
00070         }
00071         critcl::ccommand CEncode {dummy interp objc objv} {
00072             Tcl_Obj *inputPtr, *resultPtr;
00073             int len, rlen, xtra;
00074             unsigned char *input, *p, *r;
00075             
00076             if (objc !=  2) {
00077                 Tcl_WrongNumArgs(interp, 1, objv, "data");
00078                 return TCL_ERROR;
00079             }
00080             
00081             inputPtr = objv[1];
00082             input = Tcl_GetByteArrayFromObj(inputPtr, &len);
00083             if ((xtra = (3 - (len % 3))) != 3) {
00084                 if (Tcl_IsShared(inputPtr))
00085                     inputPtr = Tcl_DuplicateObj(inputPtr);
00086                 input = Tcl_SetByteArrayLength(inputPtr, len + xtra);
00087                 mem(input =  + len, 0, xtra);
00088                 len += xtra;
00089             }
00090 
00091             rlen = (len / 3) * 4;
00092             resultPtr = Tcl_GetObjResult(interp);
00093             if (Tcl_IsShared(resultPtr)) {
00094                 resultPtr = Tcl_DuplicateObj(resultPtr);
00095                 Tcl_SetObjResult(interp, resultPtr);
00096             }
00097             r = Tcl_SetByteArrayLength(resultPtr, rlen);
00098             mem(r = , 0, rlen);
00099             
00100             for (p = input; p < input + len; p += 3) {
00101                 char a, b, c;
00102                 a = *p; b = *(p+1), c = *(p+2);
00103                 *r++ = Enc(a >> 2);
00104                 *r++ = Enc(((a << 4) & 060) | ((b >> 4) & 017));
00105                 *r++ = Enc(((b << 2) & 074) | ((c >> 6) & 003));
00106                 *r++ = Enc(c & 077);
00107             }
00108             
00109             return TCL_OK;
00110         }
00111 
00112         critcl::ccommand CDecode {dummy interp objc objv} {
00113             Tcl_Obj *inputPtr, *resultPtr;
00114             int len, rlen, xtra;
00115             unsigned char *input, *p, *r;
00116             
00117             if (objc !=  2) {
00118                 Tcl_WrongNumArgs(interp, 1, objv, "data");
00119                 return TCL_ERROR;
00120             }
00121             
00122             /* if input is not mod 4, extend it with nuls */
00123             inputPtr = objv[1];
00124             input = Tcl_GetByteArrayFromObj(inputPtr, &len);
00125             if ((xtra = (4 - (len % 4))) != 4) {
00126                 if (Tcl_IsShared(inputPtr))
00127                     inputPtr = Tcl_DuplicateObj(inputPtr);
00128                 input = Tcl_SetByteArrayLength(inputPtr, len + xtra);
00129                 mem(input =  + len, 0, xtra);
00130                 len += xtra;
00131             }
00132 
00133             /* output will be 1/3 smaller than input and a multiple of 3 */
00134             rlen = (len / 4) * 3;
00135             resultPtr = Tcl_GetObjResult(interp);
00136             if (Tcl_IsShared(resultPtr)) {
00137                 resultPtr = Tcl_DuplicateObj(resultPtr);
00138                 Tcl_SetObjResult(interp, resultPtr);
00139             }
00140             r = Tcl_SetByteArrayLength(resultPtr, rlen);
00141             mem(r = , 0, rlen);
00142             
00143             for (p = input; p < input + len; p += 4) {
00144                 char a, b, c, d;
00145                 a = *p; b = *(p+1), c = *(p+2), d = *(p+3);
00146                 *r++ = (((a - 0x20) & 0x3f) << 2) | (((b - 0x20) & 0x3f) >> 4);
00147                 *r++ = (((b - 0x20) & 0x3f) << 4) | (((c - 0x20) & 0x3f) >> 2);
00148                 *r++ = (((c - 0x20) & 0x3f) << 6) | (((d - 0x20) & 0x3f) );
00149             }
00150             
00151             return TCL_OK;
00152         }
00153     }
00154 }
00155 
00156 /*  -------------------------------------------------------------------------*/
00157 
00158 /*  Description:*/
00159 /*   Permit more tolerant decoding of invalid input strings by padding to*/
00160 /*   a multiple of 4 bytes with nulls.*/
00161 /*  Result:*/
00162 /*   Returns the input string - possibly padded with uuencoded null chars.*/
00163 /* */
00164 ret  ::uuencode::pad (type s) {
00165     if {[set mod [expr {[string length $s] % 4}]] != 0} {
00166         append s [string repeat "`" [expr {4 - $mod}]]
00167     }
00168     return $s
00169 }
00170 
00171 /*  -------------------------------------------------------------------------*/
00172 
00173 /*  If the Trf package is available then we shall use this by default but the*/
00174 /*  Tcllib implementations are always visible if needed (ie: for testing)*/
00175 if {[info command ::uuencode::CDecode] != {}} {    
00176     /*  tcllib critcl package*/
00177     interp alias {} ::uuencode::encode {} ::uuencode::CEncode
00178     interp alias {} ::uuencode::decode {} ::uuencode::CDecode
00179 } elseif {[package provide Trf] != {}} {
00180     ret  ::uuencode::encode (type s) {
00181         return [::uuencode -mode encode -- $s]
00182     }
00183     ret  ::uuencode::decode (type s) {
00184         return [::uuencode -mode decode -- [pad $s]]
00185     }
00186 } else {
00187     /*  pure-tcl then*/
00188     interp alias {} ::uuencode::encode {} ::uuencode::Encode
00189     interp alias {} ::uuencode::decode {} ::uuencode::Decode
00190 }
00191 
00192 /*  -------------------------------------------------------------------------*/
00193 
00194 ret  ::uuencode::uuencode (type args) {
00195     array set opts {mode 0644 filename {} name {}}
00196     set wrongargs "wrong \# args: should be\
00197             \"uuencode ?-name string? ?-mode octal?\
00198             (-file filename | ?--? string)\""
00199     while {[string match -* [lindex $args 0]]} {
00200         switch -glob -- [lindex $args 0] {
00201             -f* {
00202                 if {[llength $args] < 2} {
00203                     return -code error $wrongargs
00204                 }
00205                 set opts(filename) [lindex $args 1]
00206                 set args [lreplace $args 0 0]
00207             }
00208             -m* {
00209                 if {[llength $args] < 2} {
00210                     return -code error $wrongargs
00211                 }
00212                 set opts(mode) [lindex $args 1]
00213                 set args [lreplace $args 0 0]
00214             }
00215             -n* {
00216                 if {[llength $args] < 2} {
00217                     return -code error $wrongargs
00218                 }
00219                 set opts(name) [lindex $args 1]
00220                 set args [lreplace $args 0 0]
00221             }
00222             -- {
00223                 set args [lreplace $args 0 0]
00224                 break
00225             }
00226             default {
00227                 return -code error "bad option [lindex $args 0]:\
00228                       must be -file, -mode, or -name"
00229             }
00230         }
00231         set args [lreplace $args 0 0]
00232     }
00233 
00234     if {$opts(name) == {}} {
00235         set opts(name) $opts(filename)
00236     }
00237     if {$opts(name) == {}} {
00238         set opts(name) "data.dat"
00239     }
00240 
00241     if {$opts(filename) != {}} {
00242         set f [open $opts(filename) r]
00243         fconfigure $f -translation binary
00244         set data [read $f]
00245         close $f
00246     } else {
00247         if {[llength $args] != 1} {
00248             return -code error $wrongargs
00249         }
00250         set data [lindex $args 0]
00251     }
00252 
00253     set r {}
00254     append r [format "begin %o %s" $opts(mode) $opts(name)] "\n"
00255     for {set n 0} {$n < [string length $data]} {incr n 45} {
00256         set s [string range $data $n [expr {$n + 44}]]
00257         append r [Enc [string length $s]]
00258         append r [encode $s] "\n"
00259     }
00260     append r "`\nend"
00261     return $r
00262 }
00263 
00264 /*  -------------------------------------------------------------------------*/
00265 /*  Description:*/
00266 /*   Perform uudecoding of a file or data. A file may contain more than one*/
00267 /*   encoded data section so the result is a list where each element is a */
00268 /*   three element list of the provided filename, the suggested mode and the */
00269 /*   data itself.*/
00270 /* */
00271 ret  ::uuencode::uudecode (type args) {
00272     array set opts {mode 0644 filename {}}
00273     set wrongargs "wrong \# args: should be \"uudecode (-file filename | ?--? string)\""
00274     while {[string match -* [lindex $args 0]]} {
00275         switch -glob -- [lindex $args 0] {
00276             -f* {
00277                 if {[llength $args] < 2} {
00278                     return -code error $wrongargs
00279                 }
00280                 set opts(filename) [lindex $args 1]
00281                 set args [lreplace $args 0 0]
00282             }
00283             -- {
00284                 set args [lreplace $args 0 0]
00285                 break
00286             }
00287             default {
00288                 return -code error "bad option [lindex $args 0]:\
00289                       must be -file"
00290             }
00291         }
00292         set args [lreplace $args 0 0]
00293     }
00294 
00295     if {$opts(filename) != {}} {
00296         set f [open $opts(filename) r]
00297         set data [read $f]
00298         close $f
00299     } else {
00300         if {[llength $args] != 1} {
00301             return -code error $wrongargs
00302         }
00303         set data [lindex $args 0]
00304     }
00305 
00306     set state false
00307     set result {}
00308 
00309     foreach {line} [split $data "\n"] {
00310         switch -exact -- $state {
00311             false {
00312                 if {[regexp {^begin ([0-7]+) ([^\s]*)} $line \
00313                          -> opts(mode) opts(name)]} {
00314                     set state true
00315                     set r {}
00316                 }
00317             }
00318 
00319             true {
00320                 if {[string match "end" $line]} {
00321                     set state false
00322                     lappend result [list $opts(name) $opts(mode) $r]
00323                 } else {
00324                     scan $line %c c
00325                     set n [expr {($c - 0x21)}]
00326                     append r [string range \
00327                                   [decode [string range $line 1 end]] 0 $n]
00328                 }
00329             }
00330         }
00331     }
00332 
00333     return $result
00334 }
00335 
00336 /*  -------------------------------------------------------------------------*/
00337 
00338 package provide uuencode $::uuencode::version
00339 
00340 /*  -------------------------------------------------------------------------*/
00341 /* */
00342 /*  Local variables:*/
00343 /*    mode: tcl*/
00344 /*    indent-tabs-mode: nil*/
00345 /*  End:*/
00346 
00347 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1