##Eskil Plugin : Compare comma separated value (CSV) files
## Option -csvignore : A list of columns to ignore
## Option -csvkey : A list of columns to sort on before comparison
## Flag -csvheader : First line is a header line defining names of columns
#
# This plugin compares CSV files with some preprocessing available
# Example usage:
# eskil -plugin csv -csvignore "head3 head5" -csvkey head2 -sep , \
# examples/dir*/csv1.txt
# Example file for a plugin.
# A plugin's first line must start exactly like this one.
# The text after : is the summary you can get at the command line
# A plugin may declare command line options that should be allowed through
# to ::argv
# A plugin must define this procedure to do the job.
# side: left or right
# chi: An input channel for reading the original file.
# cho: An output channel for writing the processed file.
proc PreProcess {side chi cho} {
# Look for parameters in command line
set opts(-sep) ","
set opts(-csvignore) ""
set opts(-csvkey) ""
set opts(-csvheader) 0
foreach opt {-sep -csvignore -csvkey} {
set i [lsearch -exact $::argv $opt]
if {$i >= 0} {
incr i
set opts($opt) [lindex $::argv $i]
}
}
set i [lsearch -exact $::argv "-csvheader"]
if {$i >= 0} {
set opts(-csvheader) 1
}
# Also allow options via info
foreach {opt val} $::Info {
set opts($opt) $val
}
# Allow backslash for easy access to \t
set opts(-sep) [subst -nocommands -novariables $opts(-sep)]
# If any column is given by name, assume the file starts with
# a header line of column names
foreach col [concat $opts(-csvignore) $opts(-csvkey)] {
if { ! [string is integer $col]} {
set opts(-csvheader) 1
}
}
if {$opts(-csvheader)} {
set nameLine [gets $chi]
# Keep it first in file
puts $cho $nameLine
set nameList [split $nameLine $opts(-sep)]
}
set icol {}
foreach col $opts(-csvignore) {
if {[string is integer $col]} {
lappend icol $col
} else {
set i [lsearch $nameList $col]
if {$i < 0} {
return -code error "CSV Plugin Error: No such heading '$col'"
}
lappend icol $i
}
}
set icol [lsort -integer $icol]
set kcol {}
foreach col $opts(-csvkey) {
if {[string is integer $col]} {
lappend kcol $col
} else {
set i [lsearch $nameList $col]
if {$i < 0} {
return -code error "CSV Plugin Error: No such heading '$col'"
}
lappend kcol $i
}
}
set olines {}
while {[gets $chi line] >= 0} {
set items [split $line $opts(-sep)]
foreach i $icol {
lset items $i ""
}
lappend olines $items
}
# Sort on keys
foreach i [lreverse $kcol] {
set olines [lsort -index $i $olines]
}
foreach items $olines {
puts $cho [join $items $opts(-sep)]
}
# Signal that the file after processing should be used both
# for comparison and for displaying.
return 1
}