#!/usr/local/bin/tcl

#############################################################################
### Add something
#############################################################################
# Type Name Mode User Group Barf Size Hash

proc CTMadd {t n m u g b s h} {
    global fo_files fo_mkdir changes CTMref

    puts stderr "A $b  $t  $n"
    incr changes

    if {$t == "d"} {
	puts $fo_mkdir "CTMDM $n $u $g $m"
    } elseif {$t == "f"} {
	puts $fo_files "CTMFM $n $u $g $m $h $s"
	flush $fo_files
	exec cat $CTMref/$n >@ $fo_files
	puts $fo_files ""
    } else {
	puts "confused in CTMadd"
	exit 0
    }
}

#############################################################################
### Delete something
#############################################################################
# Type Name Mode User Group Barf Size Hash

proc CTMdel {t n m u g b s h} {
    global fo_del fo_rmdir changes damage max_damage CTMlock

    puts stderr "D $b  $t  $n"
    incr damage 
    incr changes

    if {$damage > $max_damage} {
	exec rm -f $CTMlock
	return
    }
    if {$t == "d"} {
	puts $fo_rmdir "CTMDR $n"
    } elseif {$t == "f"} {
	puts $fo_del "CTMFR $n $h"
    } else {
	puts "confused in CTMdel"
	exit 0
    }
}

#############################################################################
### Change something
#############################################################################
# Type Name Mode User Group Barf Size Hash

proc CTMchg {t1 n1 m1 u1 g1 b1 s1 h1 t2 n2 m2 u2 g2 b2 s2 h2} {
    global fo_files CTMref CTMcopy changes damage CTMscratch

    # Ignore attribute changes for directories
    if {$t1 == "d" && $t2 == "d"} return

    # turn file into dir or vice versa...
    if {$t1 != $t2} {
	CTMdel $t1 $n1 $m1 $u1 $g1 $b1 $s1 $h1
	CTMadd $t2 $n2 $m2 $u2 $g2 $b2 $s2 $h2
	return
    }

    # only files allowed past this poing...
    if {$t1 != "f" && $t2 != "f"} {
        puts "confused in CTMchg"
        exit 0
    }

    # Ignore attribute changes for files
    if {"x$h1" == "x$h2" && $s1 == $s2} return

    if {$s2 == 0} { incr damage }
    incr changes

    # If diff will deal with it...
    if {$b1 == "0" && $b2 == "0"} {
	set i [catch "exec diff -n $CTMcopy/$n1 $CTMref/$n2 > $CTMscratch" j]
	set s [file size $CTMscratch]
	if {$s < $s2} {
	    puts stderr "E $b1$b2 $t1$t2 $n1"
	    puts $fo_files "CTMFN $n1 $u2 $g2 $m2 $h1 $h2 $s"
	    flush $fo_files
	    exec cat $CTMscratch >@ $fo_files
	    puts $fo_files ""
	    return
	}
    } 
    puts stderr "R $b1$b2 $t1$t2 $n1"
    puts $fo_files "CTMFS $n2 $u2 $g2 $m2 $h1 $h2 $s2"
    flush $fo_files
    exec cat $CTMref/$n2 >@ $fo_files
    puts $fo_files ""
}

#############################################################################
### Do we already have this delta ?
#############################################################################

proc find_delta {nbr} {
    global CTMname CTMdest
    if {[file exists [format "%s/$CTMname.%04d" $CTMdest $nbr]]} { return 1 }
    if {[file exists [format "%s/$CTMname.%04d.gz" $CTMdest $nbr]]} { return 1 }
    return 0
}

#############################################################################
### The top level code...
#############################################################################

set CTMSW	/home/ctm/SW

cd $CTMSW

# Defaults...
set CTMapply 1
set CTMdont {^///}
set CTMmail {}
set CTMsuff {}
set CTMdate     [exec date -u +%Y%m%d%H%M%SZ]
set CTMtmp  {}
set CTMcopy  {}
set CTMdest  {}
set CTMprefix  .
set CTMtest 0
set CTMspecial 0
set CTMscan .
set CTMfirst 0
set max_damage 1200

set damage 0
set changes 0

source $argv

if {$CTMtmp == ""} {
    set CTMtmp $CTMSW/../tmp/${CTMname}_${CTMsuff}
}
if {$CTMcopy == ""} {
    set CTMcopy $CTMSW/../$CTMname
}
if {$CTMdest == ""} {
    set CTMdest $CTMSW/../CTM-pub/$CTMname
}

# Make sure we only run one at a time...

set CTMlock Lck.${CTMname}.${CTMdate}.[id process]
exec rm -f ${CTMlock}
exec echo starting > ${CTMlock}
if {[catch "exec ln $CTMlock LCK.$CTMname" a]} {
	puts "Not going, lock exists..."
	exec rm -f $CTMlock
	exit 0
}
exec rm -f $CTMlock
set CTMlock LCK.$CTMname

set CTMscratch ${CTMtmp}.tmp

while 1 {
    if { ! $CTMspecial} {
	if {$CTMfirst} {
		set CTMnbr 0
	} else {
		set CTMnbr [lindex [exec cat $CTMcopy/.ctm_status] 1]
	}

	if {$CTMnbr > 0 && ![find_delta $CTMnbr]} {
	    puts "$CTMname delta $CTMnbr doesn't exist..."
	    exec rm -f $CTMlock
	    exit 0
	}

	incr CTMnbr

	if {[find_delta $CTMnbr]} {
	    puts "$CTMname delta $CTMnbr does already exist..."
	    exec rm -f $CTMlock
	    exit 0
	}

	set fo [open $CTMref/.ctm_status w]
	puts $fo "$CTMname $CTMnbr"
	close $fo
	incr changes -1

    } else {
	set CTMnbr [lindex [exec cat $CTMref/.ctm_status] 1]
    }

    if {"$CTMcopy" == "" } { 
	set f1 [open /dev/null]
    } else {
	set f1 [open "| ./ctm_scan $CTMcopy $CTMscan"]
    }

    puts "Doing CTMname $CTMname CTMnbr $CTMnbr$CTMsuff CTMdate $CTMdate"
    flush stdout
    exec sh -c "rm -f ${CTMtmp}.* ${CTMtmp}:*" >&@ stdout

    set f2 [open "| ./ctm_scan $CTMref $CTMscan"]

    set fo_del   [open $CTMtmp.del w]
    set fo_rmdir [open $CTMtmp.rmdir w]
    set fo_mkdir [open $CTMtmp.mkdir w]
    set fo_files [open $CTMtmp.files w]

    set l1 ""
    set l2 ""

    while 1 {

	if {$l1 == ""} {gets $f1 l1}

	if {$l2 == ""} {gets $f2 l2}
	
	if {$l1 == "" && $l2 == ""} break

	set n1 [lindex $l1 1]
	set n2 [lindex $l2 1]

	if {[regexp $CTMdont /$n1]} { set l1 "" ; continue }
	if {[regexp $CTMdont /$n2]} { set l2 "" ; continue }

	# they're all the same...
	if {$l1 == $l2}  { set l1 "" ; set l2 "" ; continue }

	if {$l1 == "" }   { eval CTMadd $l2 ; set l2 "" ; continue }

	if {$l2 == "" }   { eval CTMdel $l1 ; set l1 "" ; continue }

	# if the name is the same we're safe...
	if {$n1 == $n2}  { 
	    eval CTMchg $l1 $l2
	    set l1 ""
	    set l2 "" 
	    continue 
	}

	# To avoid this anomaly:
	# A -  d  src/gnu/lib/libreadline/readline/Attic
	# A 0  f  src/gnu/lib/libreadline/readline/Attic/readline.h,v
	# A 0  f  src/gnu/lib/libreadline/readline.c,v
	# D 0  f  src/gnu/lib/libreadline/readline/readline.h,v
	# D 0  f  src/gnu/lib/libreadline/readline.c,v
	# we have to make things somewhat complicated...

	# if they have the same number of components...
	set ll1 [llength [split $n1 /]]
	set ll2 [llength [split $n2 /]]
	if {$ll1 == $ll2} {
	    if {$n1 < $n2 } { 
		eval CTMdel $l1 ; set l1 "" ; continue 
	    } else {
		eval CTMadd $l2 ; set l2 "" ; continue 
	    }
	} 
	if {$ll1 < $ll2} {
		eval CTMadd $l2 ; set l2 "" ; continue 
	} else {
		eval CTMdel $l1 ; set l1 "" ; continue 
	}
    }

    close $fo_del
    close $fo_rmdir
    close $fo_mkdir
    close $fo_files

    if {$damage > $max_damage} {
	puts "Too much damage: $damage deletes"
	exec sh -c "rm -f ${CTMtmp}.*"
        exec rm -f $CTMlock
	exit 0
    }

    if {!$changes} {
	puts "no changes"
	exec sh -c "rm -f ${CTMtmp}.*"
        exec rm -f $CTMlock
	exit 0
    }

    exec echo CTM_BEGIN 2.0 $CTMname $CTMnbr $CTMdate $CTMprefix > $CTMtmp.begin

    puts "Assembling delta"
    flush stdout
    set  nm [format "%s.%04d%s" $CTMname $CTMnbr $CTMsuff]

    set fdout [open "| /sbin/md5 -p | gzip -9 > ${CTMtmp}:${nm}.gz" w]

    foreach i {begin del rmdir mkdir files} {
	exec cat $CTMtmp.$i >@$fdout
    }
    puts $fdout "CTM_END " nonewline
    close $fdout ; unset fdout

    exec sh -x -c "rm -f ${CTMtmp}.*" >&@ stdout

    if {$CTMtest} {
	puts "testing, stopping now."
        exec rm -f $CTMlock
	exit 0
    }
    if {$CTMapply} {
	    puts "Applying delta"
	    flush stdout
	    exec echo now applying > $CTMlock
	    exec sh -e -x -c "cd $CTMcopy ; $CTMSW/ctm -v -v -v ${CTMtmp}:${nm}.gz" >&@ stdout
	    exec echo did apply > $CTMlock
    }
    puts "Moving delta"
    flush stdout
    exec mv ${CTMtmp}:${nm}.gz $CTMdest/.CTMtmp_${nm}.gz >&@ stdout
    exec mv $CTMdest/.CTMtmp_${nm}.gz $CTMdest/${nm}.gz >&@ stdout
    exec echo moved > $CTMlock

    if {$CTMmail != ""} {
	puts "Mailing delta"
	flush stdout
	exec $CTMSW/ctm_smail -m 100000 -c 3000000 $CTMdest/${nm}.gz $CTMmail >&@ stdout
    }
    exec echo mailed > $CTMlock

    # If we did an absolute delta: stop.
    if {$CTMsuff != ""} break

    # Make a absolute delta (!) every 100 deltas
    if {$CTMnbr == 0 || ($CTMnbr % 100)} break

    # Make an absolute delta too...
    set CTMref $CTMcopy
    set CTMsuff A
    set CTMcopy ""
    set CTMmail ""
    set CTMapply 0
    set CTMspecial 1
    exec rm -f $CTMlock
}
puts "done."
exec rm -f $CTMlock
