Version 4 of Using Snit to glue diff, patch, and md5sum

Updated 2003-06-06 15:34:13

Created on 2003-06-06

Howdy y'all, its Rohan Pall here, and here is some code I use to glue diff, patch, and md5sum with Snit.

  package require Tk 8.4
  package require snit 0.81

  puts "loading gunkata..."

  if {[namespace exists gunkata]} {namespace delete gunkata}
  namespace eval gunkata {

    namespace export *

    proc readb {fn} {
      set f [open $fn]
      fconfigure $f -translation binary
      set content [read $f]
      close $f
      return $content
    }

    proc writeb {fn c} {
      set f [open $fn w]
      fconfigure $f -translation binary
      puts -nonewline $f $c
      close $f
    }

  }

  namespace import gunkata::*


  puts "loading cleric..."

  if {[namespace exists cleric]} {cleric destroy}
  snit::type cleric {

    variable tmpdir

    constructor {args} {

      #puts "  $self breathes (type $type)"

      set tmpdir [file join \
                   [file dirname [info script]] \
                   cleric_tmp_[pid]_[clock seconds]_[clock clicks] ]
      file mkdir $tmpdir
      #puts "@> created tmpdir $tmpdir"

      $self configurelist $args
    }

    destructor {

      #puts "  $self dies (type $type)"

      file delete -force $tmpdir
      #puts "@> deleted tmpdir $tmpdir"

    }

    method new_sub_tmpdir {} {
      set sub_tmpdir [file join $tmpdir "tmp_[clock seconds]_[clock clicks]"]
      file mkdir $sub_tmpdir ; return $sub_tmpdir
    }

    method md5_file {fn} {return [lindex [exec md5sum $fn] 0]}

    method md5_data {data} {
      set sub_tmpdir [$self new_sub_tmpdir]
      set data_file [file join $sub_tmpdir data]
      writeb $data_file $data
      set md5 [$self md5_file $data_file]
      file delete -force $sub_tmpdir
      return $md5
    }

    method patch_data {old_data diff_data} {
      set sub_tmpdir [$self new_sub_tmpdir]
      writeb [file join $sub_tmpdir old] $old_data
      writeb [file join $sub_tmpdir diff] $diff_data
      set working_dir [pwd]
      cd $sub_tmpdir ; catch {exec patch -o new old diff} ; cd $working_dir
      set new_data [readb [file join $sub_tmpdir new]]
      file delete -force $sub_tmpdir
      return $new_data
    }

    # You get unified diff_data back, but it is not known if it will help
    # make correct new_data again using patch.
    # This proc is useless if you care about actually using the diff to
    # recreate the original data.
    method diff_data_without_check {old_data new_data} {
      set sub_tmpdir [$self new_sub_tmpdir]
      writeb [file join $sub_tmpdir old] $old_data
      writeb [file join $sub_tmpdir new] $new_data
      set working_dir [pwd]
      cd $sub_tmpdir ; catch {exec diff -u old new > diff} ; cd $working_dir
      set diff_data [readb [file join $sub_tmpdir diff]]
      file delete -force $sub_tmpdir
      return $diff_data
    }

    # You get unified diff_data that is known to generate the correct data.
    method diff_data {old_data new_data} {

      set new_md5 [$self md5_data $new_data]
      set new_size [string length $new_data]
      #puts "$new_md5 -- $new_size"

      set diff_data [$self diff_data_without_check $old_data $new_data]
      set gen_data [$self patch_data $old_data $diff_data]

      set gen_md5 [$self md5_data $gen_data]
      set gen_size [string length $gen_data]
      #puts "$gen_md5 -- $gen_size"

      if {($new_md5 eq $gen_md5) && ($new_size eq $gen_size)} {
        return $diff_data
      } else {
        return -code error "could not recreate correct data"
      }    
    }

  }