yencode.tcl

Go to the documentation of this file.
00001 /*  yencode.tcl - Copyright (C) 2002 Pat Thoyts <patthoyts@users.sourceforge.net>*/
00002 /* */
00003 /*  Provide a Tcl only implementation of yEnc encoding algorithm*/
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: yencode.tcl,v 1.11 2005/09/28 04:51:19 andreas_kupries Exp $*/
00010 
00011 package require Tcl 8.2;                /*  tcl minimum version*/
00012 catch {package require crc32};          /*  tcllib 1.1*/
00013 catch {package require tcllibc};        /*  critcl enhancements for tcllib*/
00014 
00015 namespace ::yencode {
00016     variable version 1.1.1
00017     namespace export encode decode yencode ydecode
00018 }
00019 
00020 /*  -------------------------------------------------------------------------*/
00021 
00022 ret  ::yencode::Encode (type s) {
00023     set r {}
00024     binary scan $s c* d
00025     foreach {c} $d {
00026         set v [expr {($c + 42) % 256}]
00027         if {$v == 0x00 || $v == 0x09 || $v == 0x0A 
00028             || $v == 0x0D || $v == 0x3D} {
00029             append r "="
00030             set v [expr {($v + 42) % 256}]
00031         }
00032         append r [format %c $v]
00033     }
00034     return $r
00035 }
00036 
00037 ret  ::yencode::Decode (type s) {
00038     if {[string length $s] == 0} {return ""}
00039     set r {}
00040     set esc 0
00041     binary scan $s c* d
00042     foreach c $d {
00043         if {$c == 61 && $esc == 0} {
00044             set esc 1
00045             continue
00046         }
00047         set v [expr {($c - 42) % 256}]
00048         if {$esc} {
00049             set v [expr {($v - 42) % 256}]
00050             set esc 0
00051         }
00052         append r [format %c $v]
00053     }
00054     return $r
00055 }
00056 
00057 /*  -------------------------------------------------------------------------*/
00058 /*  C coded versions for critcl built base64c package*/
00059 /*  -------------------------------------------------------------------------*/
00060 
00061 if {[package provide critcl] != {}} {
00062     namespace ::yencode {
00063         critcl::ccode {
00064             /* include <string.h>*/
00065         }
00066         critcl::ccommand CEncode {dummy interp objc objv} {
00067             Tcl_Obj *inputPtr, *resultPtr;
00068             int len, rlen, xtra;
00069             unsigned char *input, *p, *r, v;
00070             
00071             if (objc !=  2) {
00072                 Tcl_WrongNumArgs(interp, 1, objv, "data");
00073                 return TCL_ERROR;
00074             }
00075             
00076             /* fetch the input data */
00077             inputPtr = objv[1];
00078             input = Tcl_GetByteArrayFromObj(inputPtr, &len);
00079 
00080             /* calculate the length of the encoded result */
00081             rlen = len;
00082             for (p = input; p < input + len; p++) {
00083                 v = (*p + 42) % 256;
00084                 if (v == 0 || v == 9 || v == 0x0A || v == 0x0D || v == 0x3D)
00085                    rlen++;
00086             }
00087             
00088             /* allocate the output buffer */
00089             resultPtr = Tcl_GetObjResult(interp);
00090             if (Tcl_IsShared(resultPtr)) {
00091                 resultPtr = Tcl_DuplicateObj(resultPtr);
00092                 Tcl_SetObjResult(interp, resultPtr);
00093             }
00094             r = Tcl_SetByteArrayLength(resultPtr, rlen);
00095             
00096             /* encode the input */
00097             for (p = input; p < input + len; p++) {
00098                 v = (*p + 42) % 256;
00099                 if (v == 0 || v == 9 || v == 0x0A || v == 0x0D || v == 0x3D) {
00100                     *r++ = '=';
00101                     v = (v + 42) % 256;
00102                 }
00103                 *r++ = v;
00104             }
00105 
00106             return TCL_OK;
00107         }
00108 
00109         critcl::ccommand CDecode {dummy interp objc objv} {
00110             Tcl_Obj *inputPtr, *resultPtr;
00111             int len, rlen, esc;
00112             unsigned char *input, *p, *r, v;
00113             
00114             if (objc !=  2) {
00115                 Tcl_WrongNumArgs(interp, 1, objv, "data");
00116                 return TCL_ERROR;
00117             }
00118             
00119             /* fetch the input data */
00120             inputPtr = objv[1];
00121             input = Tcl_GetByteArrayFromObj(inputPtr, &len);
00122 
00123             /* allocate the output buffer */
00124             resultPtr = Tcl_GetObjResult(interp);
00125             if (Tcl_IsShared(resultPtr)) {
00126                 resultPtr = Tcl_DuplicateObj(resultPtr);
00127                 Tcl_SetObjResult(interp, resultPtr);
00128             }
00129             r = Tcl_SetByteArrayLength(resultPtr, len);
00130             
00131             /* encode the input */
00132             for (p = input, esc = 0, rlen = 0; p < input + len; p++) {
00133                 if (*p == 61 && esc == 0) {
00134                     esc = 1;
00135                     continue;
00136                 }
00137                 v = (*p - 42) % 256;
00138                 if (esc) {
00139                     v = (v - 42) % 256;
00140                     esc = 0;
00141                 }
00142                 *r++ = v;
00143                 rlen++;
00144             }
00145             Tcl_SetByteArrayLength(resultPtr, rlen);
00146 
00147             return TCL_OK;
00148         }
00149     }
00150 }
00151 
00152 if {[info command ::yencode::CEncode] != {}} {
00153     interp alias {} ::yencode::encode {} ::yencode::CEncode
00154     interp alias {} ::yencode::decode {} ::yencode::CDecode
00155 } else {
00156     interp alias {} ::yencode::encode {} ::yencode::Encode
00157     interp alias {} ::yencode::decode {} ::yencode::Decode
00158 }
00159 
00160 /*  -------------------------------------------------------------------------*/
00161 /*  Description:*/
00162 /*   Pop the nth element off a list. Used in options processing.*/
00163 /* */
00164 ret  ::yencode::Pop (type varname , optional nth =0) {
00165     upvar $varname args
00166     set r [lindex $args $nth]
00167     set args [lreplace $args $nth $nth]
00168     return $r
00169 }
00170 
00171 /*  -------------------------------------------------------------------------*/
00172 
00173 ret  ::yencode::yencode (type args) {
00174     array set opts {mode 0644 filename {} name {} line 128 crc32 1}
00175     while {[string match -* [lindex $args 0]]} {
00176         switch -glob -- [lindex $args 0] {
00177             -f* { set opts(filename) [Pop args 1] }
00178             -m* { set opts(mode) [Pop args 1] }
00179             -n* { set opts(name) [Pop args 1] }
00180             -l* { set opts(line) [Pop args 1] }
00181             -c* { set opts(crc32) [Pop args 1] }
00182             --  { Pop args ; break }
00183             default {
00184                 set options [join [lsort [array names opts]] ", -"]
00185                 return -code error "bad option [lindex $args 0]:\
00186                       must be -$options"
00187             }
00188         }
00189         Pop args
00190     }
00191 
00192     if {$opts(name) == {}} {
00193         set opts(name) $opts(filename)
00194     }
00195     if {$opts(name) == {}} {
00196         set opts(name) "data.dat"
00197     }
00198     if {! [string is boolean $opts(crc32)]} {
00199         return -code error "bad option -crc32: argument must be true or false"
00200     }
00201 
00202     if {$opts(filename) != {}} {
00203         set f [open $opts(filename) r]
00204         fconfigure $f -translation binary
00205         set data [read $f]
00206         close $f
00207     } else {
00208         if {[llength $args] != 1} {
00209             return -code error "wrong \# args: should be\
00210                   \"yencode ?options? -file name | data\""
00211         }
00212         set data [lindex $args 0]
00213     }
00214     
00215     set opts(size) [string length $data]
00216     
00217     set r {}
00218     append r [format "=ybegin line=%d size=%d name=%s" \
00219                   $opts(line) $opts(size) $opts(name)] "\n"
00220 
00221     set ndx 0
00222     while {$ndx < $opts(size)} {
00223         set pln [string range $data $ndx [expr {$ndx + $opts(line) - 1}]]
00224         set enc [encode $pln]
00225         incr ndx [string length $pln]
00226         append r $enc "\r\n"
00227     }
00228 
00229     append r [format "=yend size=%d" $ndx]
00230     if {$opts(crc32)} {
00231         append r " crc32=" [crc::crc32 -format %x $data]
00232     }
00233     return $r
00234 }
00235 
00236 /*  -------------------------------------------------------------------------*/
00237 /*  Description:*/
00238 /*   Perform ydecoding of a file or data. A file may contain more than one*/
00239 /*   encoded data section so the result is a list where each element is a */
00240 /*   three element list of the provided filename, the file size and the */
00241 /*   data itself.*/
00242 /* */
00243 ret  ::yencode::ydecode (type args) {
00244     array set opts {mode 0644 filename {} name default.bin}
00245     while {[string match -* [lindex $args 0]]} {
00246         switch -glob -- [lindex $args 0] {
00247             -f* { set opts(filename) [Pop args 1] }
00248             -- { Pop args ; break; }
00249             default {
00250                 set options [join [lsort [array names opts]] ", -"]
00251                 return -code error "bad option [lindex $args 0]:\
00252                       must be -$opts"
00253             }
00254         }
00255         Pop args
00256     }
00257 
00258     if {$opts(filename) != {}} {
00259         set f [open $opts(filename) r]
00260         set data [read $f]
00261         close $f
00262     } else {
00263         if {[llength $args] != 1} {
00264             return -code error "wrong \# args: should be\
00265                   \"ydecode ?options? -file name | data\""
00266         }
00267         set data [lindex $args 0]
00268     }
00269 
00270     set state false
00271     set result {}
00272 
00273     foreach {line} [split $data "\n"] {
00274         set line [string trimright $line "\r\n"]
00275         switch -exact -- $state {
00276             false {
00277                 if {[string match "=ybegin*" $line]} {
00278                     regexp {line=(\d+)} $line -> opts(line)
00279                     regexp {size=(\d+)} $line -> opts(size)
00280                     regexp {name=(\d+)} $line -> opts(name)
00281 
00282                     if {$opts(name) == {}} {
00283                         set opts(name) default.bin
00284                     }
00285 
00286                     set state true
00287                     set r {}
00288                 }
00289             }
00290 
00291             true {
00292                 if {[string match "=yend*" $line]} {
00293                     set state false
00294                     lappend result [list $opts(name) $opts(size) $r]
00295                 } else {
00296                     append r [decode $line]
00297                 }
00298             }
00299         }
00300     }
00301 
00302     return $result
00303 }
00304 
00305 /*  -------------------------------------------------------------------------*/
00306 
00307 package provide yencode $::yencode::version
00308 
00309 /*  -------------------------------------------------------------------------*/
00310 /* */
00311 /*  Local variables:*/
00312 /*    mode: tcl*/
00313 /*    indent-tabs-mode: nil*/
00314 /*  End:*/
00315 
00316 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1