Eskil

Check-in [bb6d838de8]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Allow plugins to define command line options.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: bb6d838de865c53c1b4555a0c97c2e856bae3158
User & Date: peter 2015-11-22 20:44:41.150
Context
2015-11-22
21:24
Documented new plugin features. Adapted plugins. check-in: 51257da3b3 user: peter tags: trunk
20:44
Allow plugins to define command line options. check-in: bb6d838de8 user: peter tags: trunk
19:50
Put opts option handling into new option handling framework check-in: 0e1d677660 user: peter tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to Changes.



1
2
3
4
5
6
7



2015-11-20
 Rebuilt command line option handling.

2015-11-19
 Added binary plugin.

2015-11-17
>
>
>







1
2
3
4
5
6
7
8
9
10
2015-11-22
 Allow plugins to define command line options.

2015-11-20
 Rebuilt command line option handling.

2015-11-19
 Added binary plugin.

2015-11-17
Changes to plugins/csv.tcl.
1



2
3
4
5



6
7
8
9
10
11
12
13
14
15
16
17
18




19
20
21
22
23

24
25
26


27

28

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
##Eskil Plugin : Compare comma separated value (CSV) files




# Example file for a plugin.
# A plugin must start exactly like this one.
# The text after : is the summary you can get at the command line




# This plugin compares CSV files with some preprocessing available
# Example usage:
# eskil -plugin csv -plugininfo "-ignore {head3 head5} -key head2" -sep , \
#       examples/dir*/csv1.txt

# 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} {
    # Pick default -sep from command line
    set opts(-sep) ","




    set i [lsearch -exact $::argv "-sep"]
    if {$i >= 0} {
        incr i
        set opts(-sep) [subst -nocommands -novariables [lindex $::argv $i]]
    }


    # Look for parameters in info string
    set opts(-ignore) ""


    set opts(-key) ""

    set opts(-header) 0

    foreach {opt val} $::Info {
        set opts($opt) $val
    }
    # If any column is given by name, assume the file starts with
    # a header line of column names
    foreach col [concat $opts(-ignore) $opts(-key)] {
        if {![string is integer $col]} {
            set opts(-header) 1
        }
    }
    if {$opts(-header)} {
        set nameLine [gets $chi]
        # Keep it first in file
        puts $cho $nameLine
        set nameList [split $nameLine $opts(-sep)]
    }

    set icol {}
    foreach col $opts(-ignore) {
        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(-key) {
        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'"
            }

>
>
>


|

>
>
>



|







|

>
>
>
>
|
|
|
<
|
>
|
<
|
>
>
|
>
|
>





|

|


|







|













|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

32
33
34

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
##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

# 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

# This plugin compares CSV files with some preprocessing available
# Example usage:
# eskil -plugin csv -csvignore "head3 head5" -csvkey head2 -sep , \
#       examples/dir*/csv1.txt

# 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 opts(-sep) [subst -nocommands -novariables $opts(-sep)]
    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
    }
    # 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'"
            }
Changes to src/eskil.tcl.
44
45
46
47
48
49
50




51
52
53
54
55
56
57
namespace import tcl::mathop::-
namespace import tcl::mathop::*
namespace import tcl::mathop::/

# Do initalisations for needed packages and globals.
# This is not run until needed to speed up command line error reporting.
proc Init {} {




    package require Tk 8.4
    catch {package require textSearch}
    package require wcb
    package require snit
    package require tablelist_tile

    if {[catch {package require psballoon}]} {







>
>
>
>







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
namespace import tcl::mathop::-
namespace import tcl::mathop::*
namespace import tcl::mathop::/

# Do initalisations for needed packages and globals.
# This is not run until needed to speed up command line error reporting.
proc Init {} {
    if {[info exists ::eskil(initHasRun)]} {
        return
    }
    set ::eskil(initHasRun) 1
    package require Tk 8.4
    catch {package require textSearch}
    package require wcb
    package require snit
    package require tablelist_tile

    if {[catch {package require psballoon}]} {
4198
4199
4200
4201
4202
4203
4204























4205
4206
4207
4208
4209
4210
4211
    package require pdf4tcl
    if {[llength [pdf4tcl::getPaperSize $arg]] != 2} {
        puts "Argument $opt must be a valid paper size"
        puts "Valid paper sizes:"
        puts [join [lsort -dictionary [pdf4tcl::getPaperSizeList]] \n]
        exit
    }























}
# Option database setup
proc initOpts {} {
    set ::eskil(opts) {}
    set ::eskil(defoptinfo) {
        flag 0
        given 0







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
    package require pdf4tcl
    if {[llength [pdf4tcl::getPaperSize $arg]] != 2} {
        puts "Argument $opt must be a valid paper size"
        puts "Valid paper sizes:"
        puts [join [lsort -dictionary [pdf4tcl::getPaperSizeList]] \n]
        exit
    }
}
proc optValidatePlugin {opt arg} {
    # We must start up completely to check a plugin
    Init
    set src [LocatePlugin $arg]
    if {$src eq ""} {
        puts "Bad plugin: $arg"
        printPlugins
        exit
    }
    # Look for declarations of command line options
    set ch [open $src r]
    while {[gets $ch line] >= 0} {
        # Only look until empty line
        if {[string trim $line] eq ""} break
        if {[regexp {^\#\# Option\s+(\S+)} $line -> name]} {
            addOpt $name
        }
        if {[regexp {^\#\# Flag\s+(\S+)} $line -> name]} {
            addFlags $name
        }
    }
    close $ch
}
# Option database setup
proc initOpts {} {
    set ::eskil(opts) {}
    set ::eskil(defoptinfo) {
        flag 0
        given 0
4251
4252
4253
4254
4255
4256
4257




4258
4259
4260
4261
4262
4263
4264
proc addPrefOpt {name elem {validator ""}} {
    dict set ::eskil(opts) $name ""
    dict set ::eskil(opts,info) $name $::eskil(defoptinfo)
    dict set ::eskil(opts,info) $name type Pref
    dict set ::eskil(opts,info) $name "elem" $elem
    dict set ::eskil(opts,info) $name "validator" $validator
}




# Add a filter command prefix to an Opt
proc addFilter {name cmd} {
    dict set ::eskil(opts,info) $name filter $cmd
}
# Add a sideeffect to an Opt
##nagelfar syntax addSideEffect x c
proc addSideEffect {name script} {







>
>
>
>







4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
proc addPrefOpt {name elem {validator ""}} {
    dict set ::eskil(opts) $name ""
    dict set ::eskil(opts,info) $name $::eskil(defoptinfo)
    dict set ::eskil(opts,info) $name type Pref
    dict set ::eskil(opts,info) $name "elem" $elem
    dict set ::eskil(opts,info) $name "validator" $validator
}
# Add a vaildator command to an Opt
proc addValidator {name cmd} {
    dict set ::eskil(opts,info) $name validator $cmd
}
# Add a filter command prefix to an Opt
proc addFilter {name cmd} {
    dict set ::eskil(opts,info) $name filter $cmd
}
# Add a sideeffect to an Opt
##nagelfar syntax addSideEffect x c
proc addSideEffect {name script} {
4390
4391
4392
4393
4394
4395
4396

4397
4398
4399
4400
4401
4402
4403
    addFlags -browse -nodiff
    addFlags -server -cvs -svn -debug
    addFlags -foreach -close
    addFlags -nonewline -nonewline+ -nocdiff
    addFlags -pluginlist -pluginallow
    # Options that take values
    addOpt   -plugin

    addOpt   -plugininfo
    addOpt   -plugindump
    # These options affect Pref
    addPrefOpt -pivot             pivot             optValidatePositive
    addPrefOpt -context           context           optValidateNatural
    addPrefOpt -printHeaderSize   printHeaderSize   optValidatePositive
    addPrefOpt -printCharsPerLine printCharsPerLine optValidatePositive







>







4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
    addFlags -browse -nodiff
    addFlags -server -cvs -svn -debug
    addFlags -foreach -close
    addFlags -nonewline -nonewline+ -nocdiff
    addFlags -pluginlist -pluginallow
    # Options that take values
    addOpt   -plugin
    addValidator -plugin optValidatePlugin
    addOpt   -plugininfo
    addOpt   -plugindump
    # These options affect Pref
    addPrefOpt -pivot             pivot             optValidatePositive
    addPrefOpt -context           context           optValidateNatural
    addPrefOpt -printHeaderSize   printHeaderSize   optValidatePositive
    addPrefOpt -printCharsPerLine printCharsPerLine optValidatePositive
4595
4596
4597
4598
4599
4600
4601

4602
4603
4604
4605
4606
4607
4608
    if {$plugindump ne ""} {
        printPlugin $plugindump
        exit
    }
    if {$plugin ne ""} {
        set pinterp [createPluginInterp $plugin $plugininfo $pluginallow pinfo]
        if {$pinterp eq ""} {

            puts "Bad plugin: $plugin"
            printPlugins
            exit
        }
        set opts(plugin) $pinterp
        set opts(pluginname) $plugin
        set opts(plugininfo) $plugininfo







>







4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
    if {$plugindump ne ""} {
        printPlugin $plugindump
        exit
    }
    if {$plugin ne ""} {
        set pinterp [createPluginInterp $plugin $plugininfo $pluginallow pinfo]
        if {$pinterp eq ""} {
            # This should not happen since the validator should handle it
            puts "Bad plugin: $plugin"
            printPlugins
            exit
        }
        set opts(plugin) $pinterp
        set opts(pluginname) $plugin
        set opts(plugininfo) $plugininfo
Changes to src/plugin.tcl.
151
152
153
154
155
156
157







158
159
160
161
162
163
164
165
166
167
168

proc printPlugins {} {
    set plugins [listPlugins]
    if {[llength $plugins] == 0} {
        puts "No plugins found."
        return
    }







    puts "Available plugins:"
    foreach {plugin info} $plugins {
        set descr [dict get $info descr]
        puts "Plugin \"$plugin\" : $descr"
    }
}

proc preparePlugin {top} {
    disallowEdit $top
    set allow [dict get $::eskil($top,pluginpinfo) allow]
    # Pass ::argv to plugin







>
>
>
>
>
>
>



|







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175

proc printPlugins {} {
    set plugins [listPlugins]
    if {[llength $plugins] == 0} {
        puts "No plugins found."
        return
    }
    set w 0
    foreach {plugin info} $plugins {
        if {[string length $plugin] > $w} {
            set w [string length $plugin]
        }
    }
    incr w 2
    puts "Available plugins:"
    foreach {plugin info} $plugins {
        set descr [dict get $info descr]
        puts "Plugin [format %-*s $w \"$plugin\"] : $descr"
    }
}

proc preparePlugin {top} {
    disallowEdit $top
    set allow [dict get $::eskil($top,pluginpinfo) allow]
    # Pass ::argv to plugin