sum.tcl

Go to the documentation of this file.
00001 /*  sum.tcl - Copyright (C) 2002 Pat Thoyts <patthoyts@users.sourceforge.net>*/
00002 /* */
00003 /*  Provides a Tcl only implementation of the unix sum(1) command. There are*/
00004 /*  a number of these and they use differing algorithms to get a checksum of*/
00005 /*  the input data. We provide two: one using the BSD algorithm and the other*/
00006 /*  using the SysV algorithm. More consistent results across multiple*/
00007 /*  implementations can be obtained by using cksum(1).*/
00008 /* */
00009 /*  These commands have been checked against the GNU sum program from the GNU*/
00010 /*  textutils package version 2.0 to ensure the same results.*/
00011 /* */
00012 /*  -------------------------------------------------------------------------*/
00013 /*  See the file "license.terms" for information on usage and redistribution*/
00014 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00015 /*  -------------------------------------------------------------------------*/
00016 /*  $Id: sum.tcl,v 1.7 2004/01/15 06:36:12 andreas_kupries Exp $*/
00017 
00018 package require Tcl 8.2;                /*  tcl minimum version*/
00019 
00020 catch {package require tcllibc};        /*  critcl enhancements to tcllib*/
00021 /* catch {package require crcc};           # critcl enhanced crc module*/
00022 
00023 namespace ::crc {
00024     variable sum_version 1.1.0
00025     namespace export sum
00026 
00027     variable uid
00028     if {![info exists uid]} {
00029          uid =  0
00030     }
00031 }
00032 
00033 /*  -------------------------------------------------------------------------*/
00034 /*  Description:*/
00035 /*   The SysV algorithm is fairly naive. The byte values are summed and any*/
00036 /*   overflow is discarded. The lowest 16 bits are returned as the checksum.*/
00037 /*  Notes:*/
00038 /*   Input with the same content but different ordering will give the same */
00039 /*   result.*/
00040 /* */
00041 ret  ::crc::SumSysV (type s , optional seed =0) {
00042     set t $seed
00043     binary scan $s c* r
00044     foreach n $r {
00045         incr t [expr {$n & 0xFF}]
00046     }
00047     return [expr {$t % 0xFFFF}]
00048 }
00049 
00050 /*  -------------------------------------------------------------------------*/
00051 /*  Description:*/
00052 /*   This algorithm is similar to the SysV version but includes a bit rotation*/
00053 /*   step which provides a dependency on the order of the data values.*/
00054 /* */
00055 ret  ::crc::SumBsd (type s , optional seed =0) {
00056     set t $seed
00057     binary scan $s c* r
00058     foreach n $r {
00059         set t [expr {($t & 1) ? (($t >> 1) + 0x8000) : ($t >> 1)}]
00060         set t [expr {($t + ($n & 0xFF)) & 0xFFFF}]
00061     }
00062     return $t
00063 }
00064 
00065 /*  -------------------------------------------------------------------------*/
00066 
00067 if {[package provide critcl] != {}} {
00068     namespace ::crc {
00069         critcl::ccommand SumSysV_c {dummy interp objc objv} {
00070             int r = TCL_OK;
00071             unsigned int t = 0;
00072 
00073             if (objc < 2 || objc > 3) {
00074                 Tcl_WrongNumArgs(interp, 1, objv, "data ?seed?");
00075                 return TCL_ERROR;
00076             }
00077             
00078             if (objc == 3)
00079                 r = Tcl_GetIntFromObj(interp, objv[2], (int *)&t);
00080 
00081             if (r == TCL_OK) {
00082                 int cn, size;
00083                 unsigned char *data;
00084 
00085                 data = Tcl_GetByteArrayFromObj(objv[1], &size);
00086                 for (cn = 0; cn < size; cn++)
00087                     t += data[cn];
00088             }
00089 
00090             Tcl_SetIntObj(Tcl_GetObjResult(interp), t & 0xFFFF);
00091             return r;
00092         }
00093 
00094         critcl::ccommand SumBsd_c {dummy interp objc objv} {
00095             int r = TCL_OK;
00096             unsigned int t = 0;
00097 
00098             if (objc < 2 || objc > 3) {
00099                 Tcl_WrongNumArgs(interp, 1, objv, "data ?seed?");
00100                 return TCL_ERROR;
00101             }
00102             
00103             if (objc == 3)
00104                 r = Tcl_GetIntFromObj(interp, objv[2], (int *)&t);
00105 
00106             if (r == TCL_OK) {
00107                 int cn, size;
00108                 unsigned char *data;
00109 
00110                 data = Tcl_GetByteArrayFromObj(objv[1], &size);
00111                 for (cn = 0; cn < size; cn++) {
00112                     t = (t & 1) ? ((t >> 1) + 0x8000) : (t >> 1);
00113                     t = (t + data[cn]) & 0xFFFF;
00114                 }
00115             }
00116 
00117             Tcl_SetIntObj(Tcl_GetObjResult(interp), t & 0xFFFF);
00118             return r;
00119         }
00120     }
00121 }
00122 
00123 /*  -------------------------------------------------------------------------*/
00124 /*  Switch from pure tcl to compiled if available.*/
00125 /* */
00126 if {[info command ::crc::SumBsd_c] == {}} {
00127     interp alias {} ::crc::sum-bsd  {} ::crc::SumBsd
00128 } else {
00129     interp alias {} ::crc::sum-bsd  {} ::crc::SumBsd_c
00130 }
00131 
00132 if {[info command ::crc::SumSysV_c] == {}} {
00133     interp alias {} ::crc::sum-sysv {} ::crc::SumSysV
00134 } else {
00135     interp alias {} ::crc::sum-sysv {} ::crc::SumSysV_c
00136 }
00137 
00138 /*  -------------------------------------------------------------------------*/
00139 /*  Description:*/
00140 /*   Pop the nth element off a list. Used in options processing.*/
00141 /* */
00142 ret  ::crc::Pop (type varname , optional nth =0) {
00143     upvar $varname args
00144     set r [lindex $args $nth]
00145     set args [lreplace $args $nth $nth]
00146     return $r
00147 }
00148 
00149 /*  -------------------------------------------------------------------------*/
00150 /*  timeout handler for the chunked file handling*/
00151 /*  This avoids us waiting for ever*/
00152 /* */
00153 ret  ::crc::SumTimeout (type token) {
00154     # FRINK: nocheck
00155     variable $token
00156     upvar 0 $token state
00157     set state(error) "operation timed out"
00158     set state(reading) 0
00159 }
00160 
00161 /*  -------------------------------------------------------------------------*/
00162 /*  fileevent handler for chunked file handling.*/
00163 /* */
00164 ret  ::crc::SumChunk (type token , type channel) {
00165     # FRINK: nocheck
00166     variable $token
00167     upvar 0 $token state
00168     
00169     if {[eof $channel]} {
00170         fileevent $channel readable {}
00171         set state(reading) 0
00172     }
00173     
00174     after cancel $state(after)
00175     set state(after) [after $state(timeout) \
00176                           [list [namespace origin SumTimeout] $token]]
00177     set state(result) [$state(algorithm) \
00178                            [read $channel $state(chunksize)] \
00179                            $state(result)]
00180 }
00181 
00182 /*  -------------------------------------------------------------------------*/
00183 /*  Description:*/
00184 /*   Provide a Tcl equivalent of the unix sum(1) command. We default to the*/
00185 /*   BSD algorithm and return a checkum for the input string unless a filename*/
00186 /*   has been provided. Using sum on a file should give the same results as*/
00187 /*   the unix sum command with equivalent algorithm.*/
00188 /*  Options:*/
00189 /*   -bsd           - use the BSD algorithm to calculate the checksum (default)*/
00190 /*   -sysv          - use the SysV algorithm to calculate the checksum*/
00191 /*   -filename name - return a checksum for the specified file*/
00192 /*   -format string - return the checksum using this format string*/
00193 /* */
00194 ret  ::crc::sum (type args) {
00195     array set opts [list -filename {} -channel {} -chunksize 4096 \
00196                         -timeout 30000 -bsd 1 -sysv 0 -format %u \
00197                         algorithm [namespace origin sum-bsd]]
00198     while {[string match -* [set option [lindex $args 0]]]} {
00199         switch -glob -- $option {
00200             -bsd    { set opts(-bsd) 1 ; set opts(-sysv) 0 }
00201             -sysv   { set opts(-bsd) 0 ; set opts(-sysv) 1 }
00202             -file*  { set opts(-filename) [Pop args 1] }
00203             -for*   { set opts(-format) [Pop args 1] }
00204             -chan*  { set opts(-channel) [Pop args 1] }
00205             -chunk* { set opts(-chunksize) [Pop args 1] }
00206             -time*  { set opts(-timeout) [Pop args 1] }
00207             --      { Pop args ; break }
00208             default {
00209                 set err [join [lsort [array names opts -*]] ", "]
00210                 return -code error "bad option $option:\
00211                     must be one of $err"
00212             }
00213         }
00214         Pop args
00215     }
00216 
00217     # Set the correct sum algorithm
00218     if {$opts(-sysv)} {
00219         set opts(algorithm) [namespace origin sum-sysv]
00220     }
00221 
00222     # If a file was given - open it for binary reading.
00223     if {$opts(-filename) != {}} {
00224         set opts(-channel) [open $opts(-filename) r]
00225         fconfigure $opts(-channel) -translation binary
00226     }
00227 
00228     if {$opts(-channel) == {}} {
00229 
00230         if {[llength $args] != 1} {
00231             return -code error "wrong # args: should be \
00232                  \"sum ?-bsd|-sysv? ?-format string? ?-chunksize size? \
00233                  ?-timeout ms? -file name | -channel chan | data\""
00234         }
00235         set r [$opts(algorithm) [lindex $args 0]]
00236 
00237     } else {
00238 
00239         # Create a unique token for the event handling
00240         variable uid
00241         set token [namespace current]::[incr uid]
00242         upvar #0 $token tok
00243         array set tok [list reading 1 result 0 timeout $opts(-timeout) \
00244                            chunksize $opts(-chunksize) \
00245                            algorithm $opts(algorithm)]
00246         set tok(after) [after $tok(timeout) \
00247                             [list [namespace origin SumTimeout] $token]]
00248 
00249         fileevent $opts(-channel) readable \
00250             [list [namespace origin SumChunk] $token $opts(-channel)]
00251         vwait [subst $token](reading)
00252 
00253         # If we opened the channel we must close it too.
00254         if {$opts(-filename) != {}} {
00255             close $opts(-channel)
00256         }
00257 
00258         # Extract the result or error message if there was a problem.
00259         set r $tok(result)
00260         if {[info exists tok(error)]} {
00261             return -code error $tok(error)
00262         }
00263 
00264         unset tok
00265     }
00266 
00267     return [format $opts(-format) $r]
00268 }
00269 
00270 /*  -------------------------------------------------------------------------*/
00271 
00272 package provide sum $::crc::sum_version
00273 
00274 /*  -------------------------------------------------------------------------    */
00275 /*  Local Variables:*/
00276 /*    mode: tcl*/
00277 /*    indent-tabs-mode: nil*/
00278 /*  End:*/
00279 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1