proc klDiff {l1 l2 {icase {}} {except {}} { descrip {}} } {

#	Diff 2 keyed lists
#	if icase exists it means "ignore case"
#	if except exists, it's a list of keys to ignore
#	if descript exists, it's a list of names of descriptive text fields
#		(i.e. semantics of some kind) which should be specially
#		diffed if found

#	return 0 if equal -- good

	if {$icase != ""} {set icase 1} else {set icase 0}

      	if {[cequal $l1 $l2]} {
              return [list 0 {}]
      	}

#	get lists of keys

	set k1 [keylkeys l1]
	set k2 [keylkeys l2]

#	return -1 if keys do not match -- very bad
	if {![cequal $k1 $k2]} {
		set foo [intersect3 $k1 $k2]
		return [list -1 $foo]
	}

#	get list of diffs
	
	set diff ""
	set dc 0
	set nc 0

#	if there are text fields, we should diff them somehow

	set tpct 0
	set tc 0
	foreach k $k1 {
		if {[lcontain $except $k]} continue
		set v1 [keylget l1 $k]
		set v2 [keylget l2 $k]
		if {[lcontain $descrip $k]} {
			lassign [txtDiff $v1 $v2] pct lens words
			set tpct [expr $tpct + $pct]
			incr tc
		}
		if {$icase} {
			set c1 [string toupper $v1]
			set c2 [string toupper $v2]
		} else {
			set c1 $v1
			set c2 $v2
		}
		if {$c1 != $c2} {
		incr dc
		keylset diff $k [list $v1 $v2]
		if {$v1 == ""} {
			incr nc
		}
		}
	}

	if {$tc} {
	set tpct [expr $tpct / double($tc)]
	} else {
#	no text therefore no tpct data
	set tpct 0
	}

	return [list $dc $diff $nc $tpct]

}
