source: tol/tolsh/tolsh.vfs/lib/tcllib1.10/pluginmgr/pluginmgr.tcl @ 8597867d

1941-docker-image-to-testdevel
Last change on this file since 8597867d was 8597867d, checked in by Jorge Pérez <josp.jorge@…>, 16 years ago

closes #976, se incluye tcllib1.10, base64 debe eliminarse del raiz de lib ya que esta incluido en tcllib

git-svn-id: ​https://www.tol-project.org/svn/tolp/trunk@2556 be4846ae-f051-483a-831f-b64e7d08527f

  • Property mode set to 100644
File size: 8.4 KB
RevLine 
[8597867d]1# plugin.tcl --
2#
3#       Generic plugin management.
4#
5# Copyright (c) 2005 Andreas Kupries <andreas_kupries@sourceforge.net>
6#
7# See the file "license.terms" for information on usage and redistribution
8# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
9#
10# RCS: @(#) $Id: pluginmgr.tcl,v 1.6 2007/06/23 03:39:34 andreas_kupries Exp $
11
12# ### ### ### ######### ######### #########
13## Description
14
15# Each instance of the plugin manager can be configured with data
16# which specifies where to find plugins, and how to validate
17# them. With that it can then be configured to load and provide access
18# to a specific plugin, doing all required checks and
19# initialization. Users for specific plugin types simply have to
20# encapsulate the generic class, providing all the specifics, leaving
21# their users only the task of naming the requested actual plugin.
22
23# ### ### ### ######### ######### #########
24## Requisites
25
26package require Tcl 8.4
27package require snit
28
29# ### ### ### ######### ######### #########
30## Implementation
31
32snit::type ::pluginmgr {
33
34    # ### ### ### ######### ######### #########
35    ## Public API - Options
36
37    # - Pattern to match package name. Exactly one '*'. No default.
38    # - List of commands the plugin has to provide. Empty list default.
39    # - Callback for additional checking after the API presence has
40    #   been verified. Empty list default.
41    # - Dictionary of commands to put into the plugin interpreter.
42    #   Key: cmds for plugin, value is cmds to invoke for them.
43    # - Interpreter to use for the -cmds (invoked commands). Default
44    #   is current interp.
45
46    option -pattern {}
47    option -api     {}
48    option -check   {}
49    option -cmds    {}
50    option -cmdip   {}
51    option -setup   {}
52
53    # ### ### ### ######### ######### #########
54    ## Public API - Methods
55
56    method do {args} {
57        if {$plugin eq ""} {
58            return -code error "No plugin defined"
59        }
60        return [$sip eval $args]
61    }
62
63    method interpreter {} {
64        return $sip
65    }
66
67    method plugin {} {
68        return $plugin
69    }
70
71    method load {name} {
72        if {$name eq $plugin} return
73
74        if {$options(-pattern) eq ""} {
75            return -code error "Translation pattern is not configured"
76        }
77
78        set save $sip
79
80        $self SetupIp
81        if {![$self LoadPlugin $name]} {
82            set sip $save
83            return -code error "Unable to locate plugin \"$name\""
84        }
85
86        if {![$self CheckAPI missing]} {
87            set sip $save
88            return -code error \
89                    "Cannot use plugin \"$name\", API incomplete: \"$missing\" missing"
90        }
91
92        set savedname $plugin
93        set plugin    $name
94        if {![$self CheckExternal]} {
95            set sip    $save
96            set plugin $savedname
97            return -code error \
98                    "Cannot use plugin \"$name\", API bad"
99        }
100        $self SetupExternalCmds
101
102        if {$save ne ""} {interp delete $save}
103        return
104    }
105
106    method unload {} {
107        if {$sip eq ""} return
108        interp delete $sip
109        set sip    ""
110        set plugin ""
111        return
112    }
113
114    method list {} {
115        if {$options(-pattern) eq ""} {
116            return -code error "Translation pattern is not configured"
117        }
118
119        set save $sip
120        $self SetupIp
121
122        set result {}
123        set pattern [string map [list \
124                +  \\+  ?  \\?    \
125                \[ \\\[ \] \\\]   \
126                (  \\(  )  \\)    \
127                . \\. \ *  {(.*)} \
128                ] $options(-pattern)]
129
130        # @mdgen NODEP: bogus-package
131        $sip eval {catch {package require bogus-package}}
132        foreach p [$sip eval {package names}] {
133            if {![regexp $pattern $p -> plugin]} continue
134            lappend result $plugin
135        }
136
137        interp delete $sip
138        set sip $save
139        return $result
140    }
141
142    method path {path} {
143        set path [file join [pwd] $path]
144        if {[lsearch -exact $paths $path] < 0} {
145            lappend paths $path
146        }
147        return
148    }
149
150    method paths {} {
151        return $paths
152    }
153
154    method clone {} {
155        set o [$type create %AUTO% \
156                -pattern $options(-pattern) \
157                -api     $options(-api)    \
158                -check   $options(-check) \
159                -cmds    $options(-cmds) \
160                -cmdip   $options(-cmdip) \
161                -setup   $options(-setup)]
162
163        $o __clone__ $paths $sip $plugin
164
165        # Clone has become owner of the interp.
166        set sip    {}
167        set plugin {}
168
169        return $o
170    }
171
172    method __clone__ {_paths _sip _plugin} {
173        set paths  $_paths
174        set sip    $_sip
175        set plugin $_plugin
176        return
177    }
178
179    # ### ### ### ######### ######### #########
180    ## Internal - Configuration and state
181
182    variable paths  {} ; # List of paths to provide the sip with.
183    variable sip    {} ; # Safe interp used for plugin execution.
184    variable plugin {} ; # Name of currently loaded plugin.
185
186    # ### ### ### ######### ######### #########
187    ## Internal - Object construction and descruction.
188
189    constructor {args} {
190        $self configurelist $args
191        return
192    }
193
194    destructor {
195        if {$sip ne ""} {interp delete $sip}
196        return
197    }
198
199    # ### ### ### ######### ######### #########
200    ## Internal - Option management
201
202    onconfigure -pattern {newvalue} {
203        set current $options(-pattern)
204        if {$newvalue eq $current} return
205
206        set n [regexp -all "\\*" $newvalue]
207        if {$n < 1} {
208            return -code error "Invalid pattern, * missing"
209        } elseif {$n > 1} {
210            return -code error "Invalid pattern, too many *'s"
211        }
212
213        set options(-pattern) $newvalue
214        return
215    }
216
217    onconfigure -api {newvalue} {
218        set current $options(-api)
219        if {$newvalue eq $current} return
220        set options(-api) $newvalue
221        return
222    }
223
224    onconfigure -cmds {newvalue} {
225        set current $options(-cmds)
226        if {$newvalue eq $current} return
227        set options(-cmds) $newvalue
228        return
229    }
230
231    onconfigure -cmdip {newvalue} {
232        set current $options(-cmdip)
233        if {$newvalue eq $current} return
234        set options(-cmdip) $newvalue
235        return
236    }
237
238
239    # ### ### ### ######### ######### #########
240    ## Internal - Helper commands
241
242    method SetupIp {} {
243        set sip [::safe::interpCreate]
244        foreach p $paths {
245            ::safe::interpAddToAccessPath $sip $p
246        }
247
248        if {![llength $options(-setup)]} return
249        uplevel \#0 [linsert $options(-setup) end $self $sip]
250        return
251    }
252
253    method LoadPlugin {name} {
254        if {[file exists $name]} {
255            # Plugin files are loaded directly.
256
257            $sip invokehidden source $name
258            return 1
259        }
260
261        # Otherwise the name is transformed into a package name
262        # and loaded thorugh the package management.
263
264        set pluginpackage [string map \
265                [list * $name] $options(-pattern)]
266        if {[catch {
267            $sip eval [list package require $pluginpackage]
268        } res]} {
269            return 0
270        }
271        return 1
272    }
273
274    method CheckAPI {mv} {
275        upvar 1 $mv missing
276        if {![llength $options(-api)]} {return 1}
277
278        # Check the plugin for useability.
279
280        foreach p $options(-api) {
281            if {[llength [$sip eval [list info commands $p]]] == 1} continue
282            interp delete $sip
283            set missing $p
284            return 0
285        }
286        return 1
287    }
288
289    method CheckExternal {} {
290        if {![llength $options(-check)]} {return 1}
291        return [uplevel \#0 [linsert $options(-check) end $self]]
292    }
293
294
295    method SetupExternalCmds {} {
296        if {![llength $options(-cmds)]} return
297
298        set cip $options(-cmdip)
299        foreach {pcmd ecmd} $options(-cmds) {
300            eval [linsert $ecmd 0 interp alias $sip $pcmd $cip]
301            #interp alias $sip $pcmd $cip {*}$ecmd
302        }
303        return
304    }
305
306    # ### ### ### ######### ######### #########
307
308    proc paths {pmgr args} {
309        if {[llength $args] == 0} {
310            return -code error "wrong#args: Expect \"[info level 0] object name...\""
311        }
312        foreach name $args {
313            AddPaths $pmgr $name
314        }
315        return
316    }
317
318    proc AddPaths {pmgr name} {
319        global env tcl_platform
320
321        if {$tcl_platform(platform) eq "windows"} {
322            set sep \;
323        } else {
324            set sep :
325        }
326
327        #puts "$pmgr += ($name) $sep"
328
329        regsub -all {::+} $name \000 name
330        set name [split $name \000]
331
332        # Environment variables
333
334        set prefix {}
335        foreach part $name {
336            lappend prefix $part
337            set ev [string toupper [join $prefix _]]_PLUGINS
338
339            #puts "+? env($ev)"
340
341            if {[info exists env($ev)]} {
342                foreach path [split $env($ev) $sep] {
343                    $pmgr path $path
344                }
345            }
346        }
347
348        # Windows registry
349
350        if {
351            ($tcl_platform(platform) eq "windows") &&
352            ![catch {package require registry}]
353        } {
354            foreach root {
355                HKEY_LOCAL_MACHINE
356                HKEY_CURRENT_USER
357            } {
358                set prefix {}
359                foreach part $name {
360                    lappend prefix $part
361                    set rk $root\\SOFTWARE\\[join $prefix \\]PLUGINS
362
363                    #puts "+? registry($rk)"
364
365                    if {![catch {set data [registry get $rk {}]}]} {
366                        foreach path [split $data $sep] {
367                            $pmgr path $path
368                        }
369                    }
370                }
371            }
372        }
373
374        # Home directory dot path
375
376        set prefix {}
377        foreach part $name {
378            lappend prefix $part
379            set pd [file join ~ .[join $prefix /] plugin]
380
381            #puts "+? path($pd)"
382
383            if {[file exists $pd]} {
384                $pmgr path $pd
385            }
386        }
387        return
388    }
389}
390
391# ### ### ### ######### ######### #########
392## Ready
393
394package provide pluginmgr 0.1
Note: See TracBrowser for help on using the repository browser.