Eskil

Check-in [ff7f4e9633]
Login

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

Overview
Comment:Added flag -pluginallow
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: ff7f4e9633e6a8188dbe4563b30aba57e2ebad03
User & Date: peter 2015-03-18 21:01:33.375
Context
2015-03-18
21:27
Added pdf plugin check-in: bcf8c3d856 user: peter tags: trunk
21:01
Added flag -pluginallow check-in: ff7f4e9633 user: peter tags: trunk
2015-03-17
23:39
Use hglist extension for better hg dir diff check-in: 6fef3f1b96 user: peter tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to Changes.




1
2
3
4
5
6
7




2015-03-17
 Added command line flag "-sep" to set a separator for table-like files
 like CSV.

2015-03-16
 Added csv plugin.
 Pass ::argv to plugins.
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
2015-03-18
 Added command line flag "-pluginallow" to allow a plugin to run in a standard
 interpreter instead of a safe one.

2015-03-17
 Added command line flag "-sep" to set a separator for table-like files
 like CSV.

2015-03-16
 Added csv plugin.
 Pass ::argv to plugins.
Changes to doc/cmdline.txt.
18
19
20
21
22
23
24

25
26
27
28
29
30
31
  -clip       : Start in clip diff mode. Ignores other args.
  -patch      : View patch file.
  -           : Read patch file from standard input, to allow pipes.
  -review     : View revision control tree as a patch.
  -context <n>: Show only differences, with <n> lines of context.
  -foreach    : Open one diff window per file listed.
  -close      : Close windows with no changes.


  -noparse    : Eskil can perform analysis of changed blocks to
  -line       : improve display. See online help for details.
  -smallblock : The default. Do block analysis on small blocks.
  -block      : Full block analysis. This can be slow if there
                are large change blocks.








>







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  -clip       : Start in clip diff mode. Ignores other args.
  -patch      : View patch file.
  -           : Read patch file from standard input, to allow pipes.
  -review     : View revision control tree as a patch.
  -context <n>: Show only differences, with <n> lines of context.
  -foreach    : Open one diff window per file listed.
  -close      : Close windows with no changes.
  -sep <c>    : See char <c> as separator between columns in files.

  -noparse    : Eskil can perform analysis of changed blocks to
  -line       : improve display. See online help for details.
  -smallblock : The default. Do block analysis on small blocks.
  -block      : Full block analysis. This can be slow if there
                are large change blocks.

67
68
69
70
71
72
73

74
75
76
77
78
79
  -printColorOld <RGB>    : Color for old text (0.7 1.0 0.7)
  -printColorNew <RGB>    : Color for new text (0.8 0.8 1.0)

  -plugin <name>       : Preprocess files using plugin.
  -plugininfo <info>   : Pass info to plugin (plugin specific)
  -pluginlist          : List known plugins
  -plugindump <plugin> : Dump plugin source to stdout


  -limit <lines> : Do not process more than <lines> lines.

To list all options matching a prefix, run 'eskil --query prefix'.
In tcsh use this line to get option completion:
complete eskil 'C/-/`eskil --query -`/'







>






68
69
70
71
72
73
74
75
76
77
78
79
80
81
  -printColorOld <RGB>    : Color for old text (0.7 1.0 0.7)
  -printColorNew <RGB>    : Color for new text (0.8 0.8 1.0)

  -plugin <name>       : Preprocess files using plugin.
  -plugininfo <info>   : Pass info to plugin (plugin specific)
  -pluginlist          : List known plugins
  -plugindump <plugin> : Dump plugin source to stdout
  -pluginallow         : Allow full access for a plugin.

  -limit <lines> : Do not process more than <lines> lines.

To list all options matching a prefix, run 'eskil --query prefix'.
In tcsh use this line to get option completion:
complete eskil 'C/-/`eskil --query -`/'
Changes to doc/plugins.txt.
1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
17
18
19
20
Eskil provides a plugin system where a plugin can preprocess data
before being compared and displayed.

The command line options for plugins are:
  -plugin plugin     : Use plugin
  -plugininfo info   : Pass info to plugin (plugin specific)
  -plugindump plugin : Dump plugin source to stdout
  -pluginlist        : List known plugins


A plugin is a Tcl script that must follow a specific format.
Dump one of the included plugins to see what it looks like.
The plugin is executed in a safe interpreter and thus cannot do any
damage.

A plugin is set up with these global variables filled in:
::WhoAmI  : The name of the plugin
::Info    : The contents of -plugininfo parameter
::Pref    : A copy if Eskil's internal preferences array.
::argv    : A copy of the command line from Eskil's invocation









>




|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Eskil provides a plugin system where a plugin can preprocess data
before being compared and displayed.

The command line options for plugins are:
  -plugin plugin     : Use plugin
  -plugininfo info   : Pass info to plugin (plugin specific)
  -plugindump plugin : Dump plugin source to stdout
  -pluginlist        : List known plugins
  -pluginallow       : Allow full access for a plugin.

A plugin is a Tcl script that must follow a specific format.
Dump one of the included plugins to see what it looks like.
The plugin is executed in a safe interpreter and thus cannot do any
damage. You can turn this safety off with -pluginallow.

A plugin is set up with these global variables filled in:
::WhoAmI  : The name of the plugin
::Info    : The contents of -plugininfo parameter
::Pref    : A copy if Eskil's internal preferences array.
::argv    : A copy of the command line from Eskil's invocation

Changes to htdocs/changes.wiki.
1
2
3
4
5
6
7




8
9
10
11
12
13
14
<title>Changes</title>

Upcoming changes (not yet released):

  *  Mercurial support for Directory Diff, Commit, Revert and Log.
  *  Plugins can read ::argv to know the given command line.
  *  New plugin for CSV files





Changes in v2.7 (2015-03-09):

  *  Directory Diff support for GIT, Fossil and Subversion.
     Directly browse and compare two revisions.
  *  Plugins in Directory Diff.
  *  Added option -printFont to select font for PDF generation.







>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<title>Changes</title>

Upcoming changes (not yet released):

  *  Mercurial support for Directory Diff, Commit, Revert and Log.
  *  Plugins can read ::argv to know the given command line.
  *  New plugin for CSV files
  *  Added option -sep, to set a separator that makes input be interpreted
     in a table like manner.
  *  Added option -pluginallow to run plugins in a standard interpreter instead
     of a safe one. Thus a plugin could use e.g. exec.

Changes in v2.7 (2015-03-09):

  *  Directory Diff support for GIT, Fossil and Subversion.
     Directly browse and compare two revisions.
  *  Plugins in Directory Diff.
  *  Added option -printFont to select font for PDF generation.
Changes to htdocs/plugins.wiki.
28
29
30
31
32
33
34
35


36
37
38
39
40
41
42
"##Eskil Plugin :". A plugin is sourced and used in its own safe
interpreter and thus have free access to its own global space. Hookup
points are defined by declaring specifically named procedures as specified
below, and apart from those, a plugin can define and do whatever within
the limits of a safe interpreter.

In addition to the standard safe interpreter environment, a plugin has
access to stdout as well.



A plugin is set up with these global variables filled in:
  *  ::WhoAmI : The name of the plugin
  *  ::Info   : The contents of -plugininfo parameter
  *  ::Pref   : A copy if Eskil's internal preferences array.
  *  ::argv   : A copy of the command line from Eskil's invocation








|
>
>







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
"##Eskil Plugin :". A plugin is sourced and used in its own safe
interpreter and thus have free access to its own global space. Hookup
points are defined by declaring specifically named procedures as specified
below, and apart from those, a plugin can define and do whatever within
the limits of a safe interpreter.

In addition to the standard safe interpreter environment, a plugin has
access to stdout as well. By using the command line option -pluginallow,
the plugin is run in a standard interpreter and may e.g. do exec to utilize
external tools.

A plugin is set up with these global variables filled in:
  *  ::WhoAmI : The name of the plugin
  *  ::Info   : The contents of -plugininfo parameter
  *  ::Pref   : A copy if Eskil's internal preferences array.
  *  ::argv   : A copy of the command line from Eskil's invocation

Changes to src/dirdiff.tcl.
115
116
117
118
119
120
121

122
123
124
125
126
127





128
129

130
131
132
133
134
135
136
                fconfigure $ch2 -translation binary
            }
            # Allow a plugin to do its thing
            if {$anyPlugin} {
                #puts "PLUGIN!"
                $::eskil(.dirdiff,plugin) eval \
                        [list array set ::Pref [array get ::Pref]]

                interp share {} $ch1 $::eskil(.dirdiff,plugin)
                interp share {} $ch2 $::eskil(.dirdiff,plugin)
                set info1 [dict create name $file1 size $stat1(size)]
                set info2 [dict create name $file2 size $stat2(size)]
                set eq [$::eskil(.dirdiff,plugin) eval \
                                [list FileCompare $ch1 $ch2 $info1 $info2]]





                $::eskil(.dirdiff,plugin) invokehidden close $ch1
                $::eskil(.dirdiff,plugin) invokehidden close $ch2

            }

            if {$ignorekey} {
                # Assume that all keywords are in the first block
                set f1 [read $ch1 $bufsz]
                set f2 [read $ch2 $bufsz]
                regsub -all {\$\w+:[^\$]*\$} $f1 {} f1







>






>
>
>
>
>
|
|
>







115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
                fconfigure $ch2 -translation binary
            }
            # Allow a plugin to do its thing
            if {$anyPlugin} {
                #puts "PLUGIN!"
                $::eskil(.dirdiff,plugin) eval \
                        [list array set ::Pref [array get ::Pref]]
                $::eskil(.dirdiff,plugin) eval [list set ::argv $::eskil(argv)]
                interp share {} $ch1 $::eskil(.dirdiff,plugin)
                interp share {} $ch2 $::eskil(.dirdiff,plugin)
                set info1 [dict create name $file1 size $stat1(size)]
                set info2 [dict create name $file2 size $stat2(size)]
                set eq [$::eskil(.dirdiff,plugin) eval \
                                [list FileCompare $ch1 $ch2 $info1 $info2]]
                set allow [dict get $::eskil(.dirdiff,pluginpinfo) allow]
                if {$allow} {
                    $::eskil(.dirdiff,plugin) eval close $ch1
                    $::eskil(.dirdiff,plugin) eval close $ch2
                } else {
                    $::eskil(.dirdiff,plugin) invokehidden close $ch1
                    $::eskil(.dirdiff,plugin) invokehidden close $ch2
                }
            }

            if {$ignorekey} {
                # Assume that all keywords are in the first block
                set f1 [read $ch1 $bufsz]
                set f2 [read $ch2 $bufsz]
                regsub -all {\$\w+:[^\$]*\$} $f1 {} f1
Changes to src/eskil.tcl.
3867
3868
3869
3870
3871
3872
3873

3874
3875
3876
3877
3878
3879
3880
  -printColorOld <RGB>    : Color for old text (0.7 1.0 0.7)
  -printColorNew <RGB>    : Color for new text (0.8 0.8 1.0)

  -plugin <name>       : Preprocess files using plugin.
  -plugininfo <info>   : Pass info to plugin (plugin specific)
  -pluginlist          : List known plugins
  -plugindump <plugin> : Dump plugin source to stdout


  -limit <lines> : Do not process more than <lines> lines.

To list all options matching a prefix, run 'eskil --query prefix'.
In tcsh use this line to get option completion:
complete eskil 'C/-/`eskil --query -`/'}
}







>







3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
  -printColorOld <RGB>    : Color for old text (0.7 1.0 0.7)
  -printColorNew <RGB>    : Color for new text (0.8 0.8 1.0)

  -plugin <name>       : Preprocess files using plugin.
  -plugininfo <info>   : Pass info to plugin (plugin specific)
  -pluginlist          : List known plugins
  -plugindump <plugin> : Dump plugin source to stdout
  -pluginallow         : Allow full access for a plugin.

  -limit <lines> : Do not process more than <lines> lines.

To list all options matching a prefix, run 'eskil --query prefix'.
In tcsh use this line to get option completion:
complete eskil 'C/-/`eskil --query -`/'}
}
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
        -noparse -line -smallblock -block -char -word -limit -nodiff -dir
        -clip -patch -browse -conflict -print -noempty -pivot
        -printHeaderSize -printCharsPerLine -printPaper
        -printColorChange -printColorOld -printColorNew
        -printFont -sep
        -server -o -a -fine -r -context -cvs -svn -review
        -foreach -preprocess -preprocessleft -preprocessright
        -close -nonewline -plugin -plugininfo
        -plugindump -pluginlist -nocdiff
    }

    # If the first option is "--query", use it to ask about options.
    if {$::eskil(argc) == 2 && [lindex $::eskil(argv) 0] == "--query"} {
        set arg [lindex $::eskil(argv) 1]
        if {[lsearch -exact $allOpts $arg] < 0} {







|







3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
        -noparse -line -smallblock -block -char -word -limit -nodiff -dir
        -clip -patch -browse -conflict -print -noempty -pivot
        -printHeaderSize -printCharsPerLine -printPaper
        -printColorChange -printColorOld -printColorNew
        -printFont -sep
        -server -o -a -fine -r -context -cvs -svn -review
        -foreach -preprocess -preprocessleft -preprocessright
        -close -nonewline -plugin -plugininfo -pluginallow
        -plugindump -pluginlist -nocdiff
    }

    # If the first option is "--query", use it to ask about options.
    if {$::eskil(argc) == 2 && [lindex $::eskil(argv) 0] == "--query"} {
        set arg [lindex $::eskil(argv) 1]
        if {[lsearch -exact $allOpts $arg] < 0} {
3950
3951
3952
3953
3954
3955
3956

3957
3958
3959
3960
3961
3962
3963
    set doreview 0
    set foreach 0
    set preferedRev "GIT"
    set plugin ""
    set plugininfo ""
    set plugindump ""
    set pluginlist 0

    set nocdiff 0

    foreach arg $::eskil(argv) {
        if {$nextArg != ""} {
            if {$nextArg eq "mergeFile"} {
                set opts(mergeFile) [file join [pwd] $arg]
            } elseif {$nextArg eq "ancestorFile"} {







>







3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
    set doreview 0
    set foreach 0
    set preferedRev "GIT"
    set plugin ""
    set plugininfo ""
    set plugindump ""
    set pluginlist 0
    set pluginallow 0
    set nocdiff 0

    foreach arg $::eskil(argv) {
        if {$nextArg != ""} {
            if {$nextArg eq "mergeFile"} {
                set opts(mergeFile) [file join [pwd] $arg]
            } elseif {$nextArg eq "ancestorFile"} {
4086
4087
4088
4089
4090
4091
4092


4093
4094
4095
4096
4097
4098
4099
            set nextArg "plugin"
        } elseif {$arg eq "-plugininfo"} {
            set nextArg "plugininfo"
        } elseif {$arg eq "-plugindump"} {
            set nextArg "plugindump"
        } elseif {$arg eq "-pluginlist"} {
            set pluginlist 1


        } elseif {$arg eq "-context"} {
            set nextArg context
        } elseif {$arg eq "-noparse"} {
            set ::Pref(parse) 0
        } elseif {$arg eq "-line"} {
            set ::Pref(parse) 1
        } elseif {$arg eq "-smallblock"} {







>
>







4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
            set nextArg "plugin"
        } elseif {$arg eq "-plugininfo"} {
            set nextArg "plugininfo"
        } elseif {$arg eq "-plugindump"} {
            set nextArg "plugindump"
        } elseif {$arg eq "-pluginlist"} {
            set pluginlist 1
        } elseif {$arg eq "-pluginallow"} {
            set pluginallow 1
        } elseif {$arg eq "-context"} {
            set nextArg context
        } elseif {$arg eq "-noparse"} {
            set ::Pref(parse) 0
        } elseif {$arg eq "-line"} {
            set ::Pref(parse) 1
        } elseif {$arg eq "-smallblock"} {
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
        exit
    }
    if {$plugindump ne ""} {
        printPlugin $plugindump
        exit
    }
    if {$plugin ne ""} {
        set pinterp [createPluginInterp $plugin $plugininfo pinfo]
        if {$pinterp eq ""} {
            puts "Bad plugin: $plugin"
            printPlugins
            exit
        }
        set opts(plugin) $pinterp
        set opts(pluginname) $plugin







|







4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
        exit
    }
    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
Changes to src/plugin.tcl.
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

82
83
84
85
86
87
88
            }
        }
        if {$src ne ""} break
    }
    return $src
}

proc createPluginInterp {plugin info pinfoName} {
    upvar 1 $pinfoName pinfo
    set src [LocatePlugin $plugin]

    if {$src eq ""} {
        return ""
    }

    # Create interpreter




    set pi [interp create -safe]


    # Load source
    $pi invokehidden -global source $src
    $pi eval [list set ::WhoAmI [file rootname [file tail $src]]]
    $pi eval [list set ::Info $info]
    interp share {} stdout $pi

    # Expose needed commands

    interp expose $pi fconfigure ;# ??
    interp hide $pi close


    set pinfo {file 0 dir 0}

    if {[$pi eval info proc PreProcess] ne ""} {
        dict set pinfo file 1
    }
    if {[$pi eval info proc FileCompare] ne ""} {
        dict set pinfo dir 1
    }








|







|
>
>
>
>
|
>
|
|
|





>
|
|
|
>

>







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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
            }
        }
        if {$src ne ""} break
    }
    return $src
}

proc createPluginInterp {plugin info allow pinfoName} {
    upvar 1 $pinfoName pinfo
    set src [LocatePlugin $plugin]

    if {$src eq ""} {
        return ""
    }

    # Create interpreter and load source
    if {$allow} {
        set pi [interp create]
        $pi eval source $src
    } else {
        set pi [interp create -safe]
        $pi invokehidden -global source $src
    }

    # Setup info
    $pi eval [list set ::WhoAmI [file rootname [file tail $src]]]
    $pi eval [list set ::Info $info]
    interp share {} stdout $pi

    # Expose needed commands
    if {!$allow} {
        interp expose $pi fconfigure ;# ??
        interp hide $pi close
    }

    set pinfo {file 0 dir 0}
    dict set pinfo allow $allow
    if {[$pi eval info proc PreProcess] ne ""} {
        dict set pinfo file 1
    }
    if {[$pi eval info proc FileCompare] ne ""} {
        dict set pinfo dir 1
    }

152
153
154
155
156
157
158

159
160
161
162
163
164
165
        set descr [dict get $info descr]
        puts "Plugin \"$plugin\" : $descr"
    }
}

proc preparePlugin {top} {
    disallowEdit $top

    # Pass ::argv to plugin
    $::eskil($top,plugin) eval [list set ::argv $::eskil(argv)]
    # Pass ::Pref to plugin
    $::eskil($top,plugin) eval [list array set ::Pref [array get ::Pref]]
    set out1 [tmpFile]
    set out2 [tmpFile]








>







160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
        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
    $::eskil($top,plugin) eval [list set ::argv $::eskil(argv)]
    # Pass ::Pref to plugin
    $::eskil($top,plugin) eval [list array set ::Pref [array get ::Pref]]
    set out1 [tmpFile]
    set out2 [tmpFile]

173
174
175
176
177
178
179






180
181
182
183

184
185
186
187
188
189
190
    interp share {} $cho2 $::eskil($top,plugin)

    # TODO: When allowing 8.6, do this in coroutines allowing each call
    # to yield and to alternate between them until done
    set usenew1 [$::eskil($top,plugin) eval [list PreProcess left $chi $cho]]
    set usenew2 [$::eskil($top,plugin) eval [list PreProcess right $chi2 $cho2]]







    $::eskil($top,plugin) invokehidden close $chi
    $::eskil($top,plugin) invokehidden close $cho
    $::eskil($top,plugin) invokehidden close $chi2
    $::eskil($top,plugin) invokehidden close $cho2

    close $chi
    close $cho
    close $chi2
    close $cho2

    if {$usenew1} {
        # The file after processing should be used both







>
>
>
>
>
>
|
|
|
|
>







182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
    interp share {} $cho2 $::eskil($top,plugin)

    # TODO: When allowing 8.6, do this in coroutines allowing each call
    # to yield and to alternate between them until done
    set usenew1 [$::eskil($top,plugin) eval [list PreProcess left $chi $cho]]
    set usenew2 [$::eskil($top,plugin) eval [list PreProcess right $chi2 $cho2]]

    if {$allow} {
        $::eskil($top,plugin) eval close $chi
        $::eskil($top,plugin) eval close $cho
        $::eskil($top,plugin) eval close $chi2
        $::eskil($top,plugin) eval close $cho2
    } else {
        $::eskil($top,plugin) invokehidden close $chi
        $::eskil($top,plugin) invokehidden close $cho
        $::eskil($top,plugin) invokehidden close $chi2
        $::eskil($top,plugin) invokehidden close $cho2
    }
    close $chi
    close $cho
    close $chi2
    close $cho2

    if {$usenew1} {
        # The file after processing should be used both
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286

proc EditPrefPluginsOk {top w} {
    destroy $w
    set ::eskil($top,pluginname) $::eskil($top,edit,pluginname) 
    set ::eskil($top,plugininfo) $::eskil($top,edit,plugininfo)
    if {$::eskil($top,pluginname) ne ""} {
        set pinterp [createPluginInterp $::eskil($top,pluginname) \
                             $::eskil($top,plugininfo) pinfo]
    } else {
        set pinterp ""
        set pinfo ""
    }
    set ::eskil($top,plugin) $pinterp
    set ::eskil($top,pluginpinfo) $pinfo
}







|







288
289
290
291
292
293
294
295
296
297
298
299
300
301
302

proc EditPrefPluginsOk {top w} {
    destroy $w
    set ::eskil($top,pluginname) $::eskil($top,edit,pluginname) 
    set ::eskil($top,plugininfo) $::eskil($top,edit,plugininfo)
    if {$::eskil($top,pluginname) ne ""} {
        set pinterp [createPluginInterp $::eskil($top,pluginname) \
                             $::eskil($top,plugininfo) 0 pinfo]
    } else {
        set pinterp ""
        set pinfo ""
    }
    set ::eskil($top,plugin) $pinterp
    set ::eskil($top,pluginpinfo) $pinfo
}