Eskil

Check-in [08f5027954]
Login

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

Overview
Comment:Allow GUI to set privilege for plugins. Better search for pdftotext in pdf plugin.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 08f5027954ed0ed3c3cd3a4bde9122f457b12977
User & Date: peter 2016-04-08 14:23:41.798
Context
2016-04-12
21:50
Rename check-in: 002cf90f63 user: peter tags: trunk
2016-04-08
14:23
Allow GUI to set privilege for plugins. Better search for pdftotext in pdf plugin. check-in: 08f5027954 user: peter tags: trunk
2015-12-10
14:48
Better log of versions check-in: 82700551cc user: peter tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to Changes.




1
2
3
4
5
6
7




2015-11-22
 Bumped revision to 2.7.1
 Allow plugins to define command line options.

2015-11-20
 Rebuilt command line option handling.

>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
2016-04-08
 Allow GUI to set privilege for plugins.
 Better search for pdftotext in pdf plugin.

2015-11-22
 Bumped revision to 2.7.1
 Allow plugins to define command line options.

2015-11-20
 Rebuilt command line option handling.

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
21
22
23
24
25
26
27
28
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 may further define command line options that it accepts.

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

Example plugins are included in the kit.

A plugin may give a result that has a line-by-line correspondence to
the original, in which case the preprocessed data is used for comparing
while the original is used for displaying.  The main plugin procedure
returns 0 to signify this case.








|








|
>
|
|
|







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
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 privilege for a plugin.
A plugin may further define command line options that it accepts.

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
::WhoAmIFull : The full path to the plugin source
::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

Example plugins are included in the kit.

A plugin may give a result that has a line-by-line correspondence to
the original, in which case the preprocessed data is used for comparing
while the original is used for displaying.  The main plugin procedure
returns 0 to signify this case.
Changes to htdocs/plugins.wiki.
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
<h1>Usage</h1>

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 may further define command line options that it accepts.

<h1>General Format</h1>

A plugin is a Tcl script file that must start with the verbatim sequence
"##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

<h2>Additional options</h2>
A plugin can declare command line options that should be accepted by Eskil.
They will be passed on to the plugin through the ::argv list.
If the initial "##Eskil" line is followed by comments formatted as below,
it adds options. Any empty line will end parsing for such lines.








|


















|
>
|
|
|







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
<h1>Usage</h1>

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 privilege for a plugin.

A plugin may further define command line options that it accepts.

<h1>General Format</h1>

A plugin is a Tcl script file that must start with the verbatim sequence
"##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
  *  ::WhoAmIFull : The full path to the plugin source
  *  ::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

<h2>Additional options</h2>
A plugin can declare command line options that should be accepted by Eskil.
They will be passed on to the plugin through the ::argv list.
If the initial "##Eskil" line is followed by comments formatted as below,
it adds options. Any empty line will end parsing for such lines.

Changes to plugins/pdf.tcl.
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

# 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} {
    if {[info commands exec] eq ""} {
        puts $cho "PDF plugin must be run with -pluginallow"

        return 1
    }
    if {[auto_execok pdftotext] eq ""} {

















        puts $cho "PDF plugin needs external tool 'pdftotext' to run"
        return 1
    }
    if {[catch {llength $::Info}]} {
        puts $cho "PDF plugin needs -plugininfo parameter to be a list"
        return 1
    }
    if {[catch {exec pdftotext {*}$::Info - - <@ $chi >&@ $cho}]} {
        puts $cho "**************************************"
        puts $cho "PDF plugin got an error from pdftotext"
    }

    # Signal that the file after processing should be used both
    # for comparison and for displaying.
    return 1







|
>


|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







|







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

# 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} {
    if {[info commands exec] eq ""} {
        puts $cho "PDF plugin must be run with privilege to be able\
                   to execute pdftotext"
        return 1
    }
    set cands [auto_execok pdftotext]
    lappend cands [file join $::WhoAmIFull pdftotext]
    lappend cands [file join $::WhoAmIFull .. pdftotext]
    lappend cands [file join $::WhoAmIFull .. .. pdftotext]
    set found 0
    foreach cand $cands {
        if {[file exists $cand]} {
            set found 1
            break
        }
        if {[file exists $cand.exe]} {
            set cand $cand.exe
            set found 1
            break
        }
    }

    if {!$found} {
        puts $cho "PDF plugin needs external tool 'pdftotext' to run"
        return 1
    }
    if {[catch {llength $::Info}]} {
        puts $cho "PDF plugin needs -plugininfo parameter to be a list"
        return 1
    }
    if {[catch {exec $cand {*}$::Info - - <@ $chi >&@ $cho}]} {
        puts $cho "**************************************"
        puts $cho "PDF plugin got an error from pdftotext"
    }

    # Signal that the file after processing should be used both
    # for comparison and for displaying.
    return 1
Changes to src/eskil.tcl.
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
  -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 -`/'}
}







|







4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
  -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 privilege 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 -`/'}
}
4640
4641
4642
4643
4644
4645
4646

4647
4648
4649
4650
4651
4652
4653
            # 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
        set opts(pluginpinfo) $pinfo
    }

    # Store the command line given opts
    set ::eskil(defaultopts) [array get opts]








>







4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
            # 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(pluginallow) $pluginallow
        set opts(plugininfo) $plugininfo
        set opts(pluginpinfo) $pinfo
    }

    # Store the command line given opts
    set ::eskil(defaultopts) [array get opts]

Changes to src/plugin.tcl.
72
73
74
75
76
77
78

79
80
81
82
83
84
85
    } 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







>







72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
    } 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 ::WhoAmIFull [file normalize $src]]
    $pi eval [list set ::Info $info]
    interp share {} stdout $pi

    # Expose needed commands
    if {!$allow} {
        interp expose $pi fconfigure ;# ??
        interp hide $pi close
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
            if {![file readable $file]} continue

            set done($file) 1
            set ch [open $file r]
            set data [read $ch 200]
            if {[regexp {^\#\#Eskil Plugin :(.*?)(\n|$)} $data -> descr]} {
                set root [file rootname [file tail $file]]
                dict set result $root "descr" $descr 
                dict set result $root "file" 0
                dict set result $root "dir"  0
                # Load it all for inspection
                append data [read $ch]
                if {[string first "proc PreProcess " $data] >= 0} {
                    dict set result $root "file" 1
                }







|







124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
            if {![file readable $file]} continue

            set done($file) 1
            set ch [open $file r]
            set data [read $ch 200]
            if {[regexp {^\#\#Eskil Plugin :(.*?)(\n|$)} $data -> descr]} {
                set root [file rootname [file tail $file]]
                dict set result $root "descr" $descr
                dict set result $root "file" 0
                dict set result $root "dir"  0
                # Load it all for inspection
                append data [read $ch]
                if {[string first "proc PreProcess " $data] >= 0} {
                    dict set result $root "file" 1
                }
276
277
278
279
280
281
282



283
284

285
286
287
288
289

290
291
292
293
294

295
296

297

298
299
300





301
302

303
304

305
306
307
308
309
310
311
312
313
314
315
316
317
318
319

320
321
322

323
324
325
326
327
328
329
    }
    if {![info exists ::eskil($top,pluginname)]} {
        set ::eskil($top,pluginname) ""
    }
    if {![info exists ::eskil($top,plugininfo)]} {
        set ::eskil($top,plugininfo) ""
    }



    set ::eskil($top,edit,pluginname) $::eskil($top,pluginname) 
    set ::eskil($top,edit,plugininfo) $::eskil($top,plugininfo)

    set t 0
    foreach {plugin info} $plugins {
        set descr [dict get $info descr]
        if {$dirdiff && ![dict get $info dir]} continue
        ttk::radiobutton $w.rb$t -variable ::eskil($top,edit,pluginname) -value $plugin -text $plugin

        ttk::label $w.l$t -text $descr -anchor "w"
        grid $w.rb$t $w.l$t -sticky we -padx 3 -pady 3
        incr t
    }
    ttk::radiobutton $w.rb$t -variable ::eskil($top,edit,pluginname) -value "" -text "No Plugin"

    grid $w.rb$t -sticky we -padx 3 -pady 3


    ttk::label $w.li -text "Info" -anchor "w"

    ttk::entry $w.ei -textvariable ::eskil($top,edit,plugininfo)
    grid $w.li $w.ei -sticky we -padx 3 -pady 3






    ttk::frame $w.fb -padding 3
    ttk::button $w.fb.b1 -text "Ok"     -command [list EditPrefPluginsOk $top $w]

    ttk::button $w.fb.b2 -text "Show" \
            -command "ShowPlugin $w \$::eskil($top,edit,pluginname)"

    ttk::button $w.fb.b3 -text "Cancel" -command [list destroy $w]
    set ::widgets($top,prefPluginsOk) $w.fb.b1

    grid $w.fb.b1 x $w.fb.b2 x $w.fb.b3 -sticky we
    grid columnconfigure $w.fb {0 2 4} -uniform a
    grid columnconfigure $w.fb {1 3} -weight 1

    grid $w.fb - -sticky we
    grid columnconfigure $w 1 -weight 1
}

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
}







>
>
>
|

>




|
>




|
>


>

>



>
>
>
>
>

|
>


>













|

>


|
>







277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
    }
    if {![info exists ::eskil($top,pluginname)]} {
        set ::eskil($top,pluginname) ""
    }
    if {![info exists ::eskil($top,plugininfo)]} {
        set ::eskil($top,plugininfo) ""
    }
    if {![info exists ::eskil($top,pluginallow)]} {
        set ::eskil($top,pluginallow) 0
    }
    set ::eskil($top,edit,pluginname) $::eskil($top,pluginname)
    set ::eskil($top,edit,plugininfo) $::eskil($top,plugininfo)
    set ::eskil($top,edit,pluginallow) $::eskil($top,pluginallow)
    set t 0
    foreach {plugin info} $plugins {
        set descr [dict get $info descr]
        if {$dirdiff && ![dict get $info dir]} continue
        ttk::radiobutton $w.rb$t -variable ::eskil($top,edit,pluginname) \
                -value $plugin -text $plugin
        ttk::label $w.l$t -text $descr -anchor "w"
        grid $w.rb$t $w.l$t -sticky we -padx 3 -pady 3
        incr t
    }
    ttk::radiobutton $w.rb$t -variable ::eskil($top,edit,pluginname) \
            -value "" -text "No Plugin"
    grid $w.rb$t -sticky we -padx 3 -pady 3


    ttk::label $w.li -text "Info" -anchor "w"
    addBalloon $w.li "Info passed to plugin. Plugin specific."
    ttk::entry $w.ei -textvariable ::eskil($top,edit,plugininfo)
    grid $w.li $w.ei -sticky we -padx 3 -pady 3

    ttk::checkbutton $w.cb -text "Privilege" \
            -variable ::eskil($top,edit,pluginallow)
    addBalloon $w.cb "Run plugin with raised privileges"
    grid $w.cb - -sticky w -padx 3 -pady 3

    ttk::frame $w.fb -padding 3
    ttk::button $w.fb.b1 -text "Ok"   \
            -command [list EditPrefPluginsOk $top $w]
    ttk::button $w.fb.b2 -text "Show" \
            -command "ShowPlugin $w \$::eskil($top,edit,pluginname)"
    addBalloon $w.fb.b2 "Show plugin source code."
    ttk::button $w.fb.b3 -text "Cancel" -command [list destroy $w]
    set ::widgets($top,prefPluginsOk) $w.fb.b1

    grid $w.fb.b1 x $w.fb.b2 x $w.fb.b3 -sticky we
    grid columnconfigure $w.fb {0 2 4} -uniform a
    grid columnconfigure $w.fb {1 3} -weight 1

    grid $w.fb - -sticky we
    grid columnconfigure $w 1 -weight 1
}

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