mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
41d41fbdb9
routine creates a Tcl/Tk interpreter and deletes it. However, init cost of Tk's MainWindow is not so small. And that makes it impossible to use libraries written with Tcl functions only on an environment without a graphical display. This changes support delaying initalization of Tk_Stubs until the script needs Tk. * ext/tk/stubs.h: New file. Define prototypes and return codes of functions on stubs.c. * ext/tk/tcltklib.c: Support delaying initalization of Tk_Stubs until the script needs Tk. * ext/tk/tcltklib.c: Show friendly error messages for errors on initialization. * ext/tk/tcltklib.c: Avoid SEGV on ip_finalize() when ruby is exiting and $DEBUG is true. (Not fix. If you know the reason of why, please fix it.) * ext/tk/tkutil/tkutil.c (ary2list, ary2list2): bug fix on handling of encoding. * ext/tk/lib/multi-tk.rb: MultiTkIp#eval_string and bg_eval_string don't work propery. * ext/tk/lib/tk.rb: Forget extending Tk::Encoding module to Tk. * ext/tk/lib/tk/variable.rb: TkVarAccess fails to initialize the object for an element of a Tcl's array variable. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@8860 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2657 lines
65 KiB
Ruby
2657 lines
65 KiB
Ruby
#
|
|
# multi-tk.rb - supports multi Tk interpreters
|
|
# by Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
|
|
|
|
require 'tcltklib'
|
|
require 'tkutil'
|
|
require 'thread'
|
|
|
|
if defined? Tk
|
|
fail RuntimeError,"'multi-tk' library must be required before requiring 'tk'"
|
|
end
|
|
|
|
################################################
|
|
# ignore exception on the mainloop?
|
|
|
|
TclTkLib.mainloop_abort_on_exception = true
|
|
# TclTkLib.mainloop_abort_on_exception = false
|
|
# TclTkLib.mainloop_abort_on_exception = nil
|
|
|
|
|
|
################################################
|
|
# add ThreadGroup check to TclTkIp.new
|
|
class << TclTkIp
|
|
alias __new__ new
|
|
private :__new__
|
|
|
|
def new(*args)
|
|
if Thread.current.group != ThreadGroup::Default
|
|
raise SecurityError, 'only ThreadGroup::Default can call TclTkIp.new'
|
|
end
|
|
__new__(*args)
|
|
end
|
|
end
|
|
|
|
|
|
################################################
|
|
# exceptiopn to treat the return value from IP
|
|
class MultiTkIp_OK < Exception
|
|
def self.send(thread, ret=nil)
|
|
thread.raise self.new(ret)
|
|
end
|
|
|
|
def initialize(ret=nil)
|
|
super('succeed')
|
|
@return_value = ret
|
|
end
|
|
|
|
attr_reader :return_value
|
|
alias value return_value
|
|
end
|
|
MultiTkIp_OK.freeze
|
|
|
|
|
|
################################################
|
|
# methods for construction
|
|
class MultiTkIp
|
|
@@SLAVE_IP_ID = ['slave'.freeze, '0'.taint].freeze
|
|
|
|
@@IP_TABLE = {}.taint unless defined?(@@IP_TABLE)
|
|
|
|
@@INIT_IP_ENV = [].taint unless defined?(@@INIT_IP_ENV) # table of Procs
|
|
@@ADD_TK_PROCS = [].taint unless defined?(@@ADD_TK_PROCS) # table of [name, args, body]
|
|
|
|
@@TK_TABLE_LIST = [].taint unless defined?(@@TK_TABLE_LIST)
|
|
|
|
@@TK_CMD_TBL = {}.taint unless defined?(@@TK_CMD_TBL)
|
|
|
|
######################################
|
|
|
|
@@CB_ENTRY_CLASS = Class.new(TkCallbackEntry){|c|
|
|
def initialize(ip, cmd)
|
|
@ip = ip
|
|
@cmd = cmd
|
|
freeze
|
|
end
|
|
attr_reader :ip, :cmd
|
|
def inspect
|
|
cmd.inspect
|
|
end
|
|
def call(*args)
|
|
unless @ip.deleted?
|
|
current = Thread.current
|
|
backup_ip = current['callback_ip']
|
|
current['callback_ip'] = @ip
|
|
begin
|
|
@ip.cb_eval(@cmd, *args)
|
|
rescue TkCallbackBreak, TkCallbackContinue => e
|
|
fail e
|
|
rescue SecurityError => e
|
|
# in 'exit', 'exit!', and 'abort' : security error --> delete IP
|
|
if e.backtrace[0] =~ /^(.+?):(\d+):in `(exit|exit!|abort)'/
|
|
@ip.delete
|
|
elsif @ip.safe?
|
|
if @ip.respond_to?(:cb_error)
|
|
@ip.cb_error(e)
|
|
else
|
|
nil # ignore
|
|
end
|
|
else
|
|
fail e
|
|
end
|
|
rescue Exception => e
|
|
if @ip.safe?
|
|
if @ip.respond_to?(:cb_error)
|
|
@ip.cb_error(e)
|
|
else
|
|
nil # ignore
|
|
end
|
|
else
|
|
fail e
|
|
end
|
|
ensure
|
|
current['callback_ip'] = backup_ip
|
|
end
|
|
end
|
|
end
|
|
}.freeze
|
|
|
|
######################################
|
|
|
|
def _keys2opts(src_keys)
|
|
return nil if src_keys == nil
|
|
keys = {}; src_keys.each{|k, v| keys[k.to_s] = v}
|
|
#keys.collect{|k,v| "-#{k} #{v}"}.join(' ')
|
|
keys.collect{|k,v| "-#{k} #{TclTkLib._conv_listelement(TkComm::_get_eval_string(v))}"}.join(' ')
|
|
end
|
|
private :_keys2opts
|
|
|
|
def _check_and_return(thread, exception, wait=0)
|
|
unless thread
|
|
unless exception.kind_of?(MultiTkIp_OK)
|
|
msg = "#{exception.class}: #{exception.message}"
|
|
|
|
if @interp.deleted?
|
|
warn("Warning (#{self}): " + msg)
|
|
return nil
|
|
end
|
|
|
|
if safe?
|
|
warn("Warning (#{self}): " + msg) if $DEBUG
|
|
return nil
|
|
end
|
|
|
|
begin
|
|
@interp._eval_without_enc(@interp._merge_tklist('bgerror', msg))
|
|
rescue Exception => e
|
|
warn("Warning (#{self}): " + msg)
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
if wait == 0
|
|
# no wait
|
|
Thread.pass
|
|
if thread.stop?
|
|
thread.raise exception
|
|
end
|
|
return thread
|
|
end
|
|
|
|
# wait to stop the caller thread
|
|
wait.times{
|
|
if thread.stop?
|
|
# ready to send exception
|
|
thread.raise exception
|
|
return thread
|
|
end
|
|
|
|
# wait
|
|
Thread.pass
|
|
}
|
|
|
|
# unexpected error
|
|
thread.raise RuntimeError, "the thread may not wait for the return value"
|
|
return thread
|
|
end
|
|
|
|
######################################
|
|
|
|
def set_cb_error(cmd = Proc.new)
|
|
@cb_error_proc[0] = cmd
|
|
end
|
|
|
|
def cb_error(e)
|
|
if @cb_error_proc[0].respond_to?(:call)
|
|
@cb_error_proc[0].call(e)
|
|
end
|
|
end
|
|
|
|
######################################
|
|
|
|
def set_safe_level(safe)
|
|
if safe > @safe_level[0]
|
|
@safe_level[0] = safe
|
|
@cmd_queue.enq([@system, 'set_safe_level', safe])
|
|
end
|
|
@safe_level[0]
|
|
end
|
|
def safe_level=(safe)
|
|
set_safe_level(safe)
|
|
end
|
|
def self.set_safe_level(safe)
|
|
__getip.set_safe_level(safe)
|
|
end
|
|
def self.safe_level=(safe)
|
|
self.set_safe_level(safe)
|
|
end
|
|
def safe_level
|
|
@safe_level[0]
|
|
end
|
|
def self.safe_level
|
|
__getip.safe_level
|
|
end
|
|
|
|
def wait_on_mainloop?
|
|
@wait_on_mainloop[0]
|
|
end
|
|
def wait_on_mainloop=(bool)
|
|
@wait_on_mainloop[0] = bool
|
|
end
|
|
|
|
def running_mainloop?
|
|
@wait_on_mainloop[1] > 0
|
|
end
|
|
|
|
def _destroy_slaves_of_slaveIP(ip)
|
|
unless ip.deleted?
|
|
# ip._split_tklist(ip._invoke('interp', 'slaves')).each{|name|
|
|
ip._split_tklist(ip._invoke_without_enc('interp', 'slaves')).each{|name|
|
|
name = _fromUTF8(name)
|
|
begin
|
|
# ip._eval_without_enc("#{name} eval {foreach i [after info] {after cancel $i}}")
|
|
after_ids = ip._eval_without_enc("#{name} eval {after info}")
|
|
ip._eval_without_enc("#{name} eval {foreach i {#{after_ids}} {after cancel $i}}")
|
|
rescue Exception
|
|
end
|
|
begin
|
|
# ip._invoke('interp', 'eval', name, 'destroy', '.')
|
|
ip._invoke(name, 'eval', 'destroy', '.')
|
|
rescue Exception
|
|
end
|
|
|
|
# safe_base?
|
|
if ip._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0'
|
|
begin
|
|
ip._eval_without_enc("::safe::interpDelete #{name}")
|
|
rescue Exception
|
|
end
|
|
end
|
|
=begin
|
|
if ip._invoke('interp', 'exists', name) == '1'
|
|
begin
|
|
ip._invoke(name, 'eval', 'exit')
|
|
rescue Exception
|
|
end
|
|
end
|
|
=end
|
|
unless ip.deleted?
|
|
if ip._invoke('interp', 'exists', name) == '1'
|
|
begin
|
|
ip._invoke('interp', 'delete', name)
|
|
rescue Exception
|
|
end
|
|
end
|
|
end
|
|
}
|
|
end
|
|
end
|
|
|
|
def _receiver_eval_proc_core(safe_level, thread, cmd, *args)
|
|
begin
|
|
#ret = proc{$SAFE = safe_level; cmd.call(*args)}.call
|
|
#ret = cmd.call(safe_level, *args)
|
|
normal_ret = false
|
|
ret = catch(:IRB_EXIT) do # IRB hack
|
|
retval = cmd.call(safe_level, *args)
|
|
normal_ret = true
|
|
retval
|
|
end
|
|
unless normal_ret
|
|
# catch IRB_EXIT
|
|
exit(ret)
|
|
end
|
|
ret
|
|
rescue SystemExit => e
|
|
# delete IP
|
|
unless @interp.deleted?
|
|
@slave_ip_tbl.each{|name, subip|
|
|
_destroy_slaves_of_slaveIP(subip)
|
|
begin
|
|
# subip._eval_without_enc("foreach i [after info] {after cancel $i}")
|
|
after_ids = subip._eval_without_enc("after info")
|
|
subip._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}")
|
|
rescue Exception
|
|
end
|
|
=begin
|
|
begin
|
|
subip._invoke('destroy', '.') unless subip.deleted?
|
|
rescue Exception
|
|
end
|
|
=end
|
|
# safe_base?
|
|
if @interp._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0'
|
|
begin
|
|
@interp._eval_without_enc("::safe::interpDelete #{name}")
|
|
rescue Exception
|
|
else
|
|
next if subip.deleted?
|
|
end
|
|
end
|
|
if subip.respond_to?(:safe_base?) && subip.safe_base? &&
|
|
!subip.deleted?
|
|
# do 'exit' to call the delete_hook procedure
|
|
begin
|
|
subip._eval_without_enc('exit')
|
|
rescue Exception
|
|
end
|
|
else
|
|
begin
|
|
subip.delete unless subip.deleted?
|
|
rescue Exception
|
|
end
|
|
end
|
|
}
|
|
|
|
begin
|
|
# @interp._eval_without_enc("foreach i [after info] {after cancel $i}")
|
|
after_ids = @interp._eval_without_enc("after info")
|
|
@interp._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}")
|
|
rescue Exception
|
|
end
|
|
begin
|
|
@interp._invoke('destroy', '.') unless @interp.deleted?
|
|
rescue Exception
|
|
end
|
|
if @safe_base && !@interp.deleted?
|
|
# do 'exit' to call the delete_hook procedure
|
|
@interp._eval_without_enc('exit')
|
|
else
|
|
@interp.delete unless @interp.deleted?
|
|
end
|
|
end
|
|
|
|
if e.backtrace[0] =~ /^(.+?):(\d+):in `(exit|exit!|abort)'/
|
|
_check_and_return(thread, MultiTkIp_OK.new($3 == 'exit'))
|
|
else
|
|
_check_and_return(thread, MultiTkIp_OK.new(nil))
|
|
end
|
|
|
|
# if master? && !safe? && allow_ruby_exit?
|
|
if !@interp.deleted? && master? && !safe? && allow_ruby_exit?
|
|
=begin
|
|
ObjectSpace.each_object(TclTkIp){|obj|
|
|
obj.delete unless obj.deleted?
|
|
}
|
|
=end
|
|
#exit(e.status)
|
|
fail e
|
|
end
|
|
# break
|
|
|
|
rescue SecurityError => e
|
|
# in 'exit', 'exit!', and 'abort' : security error --> delete IP
|
|
if e.backtrace[0] =~ /^(.+?):(\d+):in `(exit|exit!|abort)'/
|
|
ret = ($3 == 'exit')
|
|
unless @interp.deleted?
|
|
@slave_ip_tbl.each{|name, subip|
|
|
_destroy_slaves_of_slaveIP(subip)
|
|
begin
|
|
# subip._eval_without_enc("foreach i [after info] {after cancel $i}")
|
|
after_ids = subip._eval_without_enc("after info")
|
|
subip._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}")
|
|
rescue Exception
|
|
end
|
|
=begin
|
|
begin
|
|
subip._invoke('destroy', '.') unless subip.deleted?
|
|
rescue Exception
|
|
end
|
|
=end
|
|
# safe_base?
|
|
if @interp._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0'
|
|
begin
|
|
@interp._eval_without_enc("::safe::interpDelete #{name}")
|
|
rescue Exception
|
|
else
|
|
next if subip.deleted?
|
|
end
|
|
end
|
|
if subip.respond_to?(:safe_base?) && subip.safe_base? &&
|
|
!subip.deleted?
|
|
# do 'exit' to call the delete_hook procedure
|
|
begin
|
|
subip._eval_without_enc('exit')
|
|
rescue Exception
|
|
end
|
|
else
|
|
begin
|
|
subip.delete unless subip.deleted?
|
|
rescue Exception
|
|
end
|
|
end
|
|
}
|
|
|
|
begin
|
|
# @interp._eval_without_enc("foreach i [after info] {after cancel $i}")
|
|
after_ids = @interp._eval_without_enc("after info")
|
|
@interp._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}")
|
|
rescue Exception
|
|
end
|
|
=begin
|
|
begin
|
|
@interp._invoke('destroy', '.') unless @interp.deleted?
|
|
rescue Exception
|
|
end
|
|
=end
|
|
if @safe_base && !@interp.deleted?
|
|
# do 'exit' to call the delete_hook procedure
|
|
@interp._eval_without_enc('exit')
|
|
else
|
|
@interp.delete unless @interp.deleted?
|
|
end
|
|
end
|
|
_check_and_return(thread, MultiTkIp_OK.new(ret))
|
|
# break
|
|
|
|
else
|
|
# raise security error
|
|
_check_and_return(thread, e)
|
|
end
|
|
|
|
rescue Exception => e
|
|
# raise exception
|
|
begin
|
|
bt = _toUTF8(e.backtrace.join("\n"))
|
|
bt.instance_variable_set(:@encoding, 'utf-8')
|
|
rescue Exception
|
|
bt = e.backtrace.join("\n")
|
|
end
|
|
begin
|
|
@interp._set_global_var('errorInfo', bt)
|
|
rescue Exception
|
|
end
|
|
_check_and_return(thread, e)
|
|
|
|
else
|
|
# no exception
|
|
_check_and_return(thread, MultiTkIp_OK.new(ret))
|
|
end
|
|
end
|
|
|
|
def _receiver_eval_proc(last_thread, safe_level, thread, cmd, *args)
|
|
if thread
|
|
Thread.new{
|
|
last_thread.join if last_thread
|
|
unless @interp.deleted?
|
|
_receiver_eval_proc_core(safe_level, thread, cmd, *args)
|
|
end
|
|
}
|
|
else
|
|
Thread.new{
|
|
unless @interp.deleted?
|
|
_receiver_eval_proc_core(safe_level, thread, cmd, *args)
|
|
end
|
|
}
|
|
last_thread
|
|
end
|
|
end
|
|
|
|
private :_receiver_eval_proc, :_receiver_eval_proc_core
|
|
|
|
def _receiver_mainloop(check_root)
|
|
if @evloop_thread[0] && @evloop_thread[0].alive?
|
|
@evloop_thread[0]
|
|
else
|
|
@evloop_thread[0] = Thread.new{
|
|
while !@interp.deleted?
|
|
#if check_root
|
|
# inf = @interp._invoke_without_enc('info', 'command', '.')
|
|
# break if !inf.kind_of?(String) || inf != '.'
|
|
#end
|
|
break if check_root && !@interp.has_mainwindow?
|
|
sleep 0.5
|
|
end
|
|
}
|
|
@evloop_thread[0]
|
|
end
|
|
end
|
|
|
|
def _create_receiver_and_watchdog(lvl = $SAFE)
|
|
lvl = $SAFE if lvl < $SAFE
|
|
|
|
# command-procedures receiver
|
|
receiver = Thread.new(lvl){|safe_level|
|
|
last_thread = {}
|
|
|
|
loop do
|
|
break if @interp.deleted?
|
|
thread, cmd, *args = @cmd_queue.deq
|
|
if thread == @system
|
|
# control command
|
|
case cmd
|
|
when 'set_safe_level'
|
|
begin
|
|
safe_level = args[0] if safe_level < args[0]
|
|
rescue Exception
|
|
end
|
|
when 'call_mainloop'
|
|
thread = args.shift
|
|
_check_and_return(thread,
|
|
MultiTkIp_OK.new(_receiver_mainloop(*args)))
|
|
else
|
|
# ignore
|
|
end
|
|
|
|
else
|
|
# procedure
|
|
last_thread[thread] = _receiver_eval_proc(last_thread[thread],
|
|
safe_level, thread,
|
|
cmd, *args)
|
|
end
|
|
end
|
|
}
|
|
|
|
# watchdog of receiver
|
|
watchdog = Thread.new{
|
|
begin
|
|
loop do
|
|
sleep 1
|
|
break unless receiver.alive?
|
|
end
|
|
rescue Exception
|
|
# ignore all kind of Exception
|
|
end
|
|
# receiver is dead
|
|
loop do
|
|
thread, cmd, *args = @cmd_queue.deq
|
|
next unless thread
|
|
if thread.alive?
|
|
if @interp.deleted?
|
|
thread.raise RuntimeError, 'the interpreter is already deleted'
|
|
else
|
|
thread.raise RuntimeError,
|
|
'the interpreter no longer receives command procedures'
|
|
end
|
|
end
|
|
end
|
|
}
|
|
|
|
# return threads
|
|
[receiver, watchdog]
|
|
end
|
|
private :_check_and_return, :_create_receiver_and_watchdog
|
|
|
|
######################################
|
|
|
|
if self.const_defined? :DEFAULT_MASTER_NAME
|
|
name = DEFAULT_MASTER_NAME.to_s
|
|
else
|
|
name = nil
|
|
end
|
|
if self.const_defined?(:DEFAULT_MASTER_OPTS) &&
|
|
DEFAULT_MASTER_OPTS.kind_of?(Hash)
|
|
keys = DEFAULT_MASTER_OPTS
|
|
else
|
|
keys = {}
|
|
end
|
|
|
|
@@DEFAULT_MASTER = self.allocate
|
|
@@DEFAULT_MASTER.instance_eval{
|
|
@tk_windows = {}.taint
|
|
|
|
@tk_table_list = [].taint
|
|
|
|
@slave_ip_tbl = {}.taint
|
|
|
|
@slave_ip_top = {}.taint
|
|
|
|
@evloop_thread = [].taint
|
|
|
|
unless keys.kind_of? Hash
|
|
fail ArgumentError, "expecting a Hash object for the 2nd argument"
|
|
end
|
|
|
|
@interp = TclTkIp.new(name, _keys2opts(keys))
|
|
@ip_name = nil
|
|
|
|
@system = Object.new
|
|
|
|
@wait_on_mainloop = [true, 0].taint
|
|
|
|
@threadgroup = Thread.current.group
|
|
|
|
@safe_base = false
|
|
|
|
@safe_level = [$SAFE]
|
|
|
|
@cmd_queue = Queue.new
|
|
|
|
@cmd_receiver, @receiver_watchdog = _create_receiver_and_watchdog(@safe_level[0])
|
|
|
|
@threadgroup.add @cmd_receiver
|
|
@threadgroup.add @receiver_watchdog
|
|
|
|
# NOT enclose @threadgroup for @@DEFAULT_MASTER
|
|
|
|
@@IP_TABLE[ThreadGroup::Default] = self
|
|
@@IP_TABLE[@threadgroup] = self
|
|
|
|
#################################
|
|
|
|
@assign_request = Class.new(Exception){
|
|
def self.new(target, ret)
|
|
obj = super()
|
|
obj.target = target
|
|
obj.ret = ret
|
|
obj
|
|
end
|
|
attr_accessor :target, :ret
|
|
}
|
|
|
|
@assign_thread = Thread.new{
|
|
loop do
|
|
begin
|
|
Thread.stop
|
|
rescue @assign_request=>req
|
|
begin
|
|
req.ret[0] = req.target.instance_eval{
|
|
@cmd_receiver, @receiver_watchdog =
|
|
_create_receiver_and_watchdog(@safe_level[0])
|
|
@threadgroup.add @cmd_receiver
|
|
@threadgroup.add @receiver_watchdog
|
|
@threadgroup.enclose
|
|
true
|
|
}
|
|
rescue Exception=>e
|
|
begin
|
|
req.ret[0] = e
|
|
rescue Exception
|
|
# ignore
|
|
end
|
|
end
|
|
rescue Exception
|
|
# ignore
|
|
end
|
|
end
|
|
}
|
|
|
|
def self.assign_receiver_and_watchdog(target)
|
|
ret = [nil]
|
|
@assign_thread.raise(@assign_request.new(target, ret))
|
|
while ret[0] == nil
|
|
unless @assign_thread.alive?
|
|
raise RuntimeError, 'lost the thread to assign a receiver and a watchdog thread'
|
|
end
|
|
end
|
|
if ret[0].kind_of?(Exception)
|
|
raise ret[0]
|
|
else
|
|
ret[0]
|
|
end
|
|
end
|
|
|
|
#################################
|
|
|
|
class << self
|
|
undef :instance_eval
|
|
end
|
|
}
|
|
@@DEFAULT_MASTER.freeze # defend against modification
|
|
|
|
######################################
|
|
|
|
def self.inherited(subclass)
|
|
# trust if on ThreadGroup::Default or @@DEFAULT_MASTER's ThreadGroup
|
|
if @@IP_TABLE[Thread.current.group] == @@DEFAULT_MASTER
|
|
begin
|
|
class << subclass
|
|
self.methods.each{|m|
|
|
begin
|
|
unless m == '__id__' || m == '__send__' || m == 'freeze'
|
|
undef_method(m)
|
|
end
|
|
rescue Exception
|
|
# ignore all exceptions
|
|
end
|
|
}
|
|
end
|
|
ensure
|
|
subclass.freeze
|
|
fail SecurityError,
|
|
"cannot create subclass of MultiTkIp on a untrusted ThreadGroup"
|
|
end
|
|
end
|
|
end
|
|
|
|
######################################
|
|
|
|
@@SAFE_OPT_LIST = [
|
|
'accessPath'.freeze,
|
|
'statics'.freeze,
|
|
'nested'.freeze,
|
|
'deleteHook'.freeze
|
|
].freeze
|
|
|
|
def _parse_slaveopts(keys)
|
|
name = nil
|
|
safe = false
|
|
safe_opts = {}
|
|
tk_opts = {}
|
|
|
|
keys.each{|k,v|
|
|
k_str = k.to_s
|
|
if k_str == 'name'
|
|
name = v
|
|
elsif k_str == 'safe'
|
|
safe = v
|
|
elsif @@SAFE_OPT_LIST.member?(k_str)
|
|
safe_opts[k_str] = v
|
|
else
|
|
tk_opts[k_str] = v
|
|
end
|
|
}
|
|
|
|
if keys['without_tk'] || keys[:without_tk]
|
|
[name, safe, safe_opts, nil]
|
|
else
|
|
[name, safe, safe_opts, tk_opts]
|
|
end
|
|
end
|
|
private :_parse_slaveopts
|
|
|
|
def _create_slave_ip_name
|
|
name = @@SLAVE_IP_ID.join('')
|
|
@@SLAVE_IP_ID[1].succ!
|
|
name
|
|
end
|
|
private :_create_slave_ip_name
|
|
|
|
######################################
|
|
|
|
def __check_safetk_optkeys(optkeys)
|
|
# based on 'safetk.tcl'
|
|
new_keys = {}
|
|
optkeys.each{|k,v| new_keys[k.to_s] = v}
|
|
|
|
# check 'display'
|
|
if !new_keys.key?('display')
|
|
begin
|
|
#new_keys['display'] = @interp._invoke('winfo screen .')
|
|
new_keys['display'] = @interp._invoke('winfo', 'screen', '.')
|
|
rescue
|
|
if ENV[DISPLAY]
|
|
new_keys['display'] = ENV[DISPLAY]
|
|
elsif !new_keys.key?('use')
|
|
warn "Warning: no screen info or ENV[DISPLAY], so use ':0.0'"
|
|
new_keys['display'] = ':0.0'
|
|
end
|
|
end
|
|
end
|
|
|
|
# check 'use'
|
|
if new_keys.key?('use')
|
|
# given 'use'
|
|
case new_keys['use']
|
|
when TkWindow
|
|
new_keys['use'] = TkWinfo.id(new_keys['use'])
|
|
#assoc_display = @interp._eval('winfo screen .')
|
|
assoc_display = @interp._invoke('winfo', 'screen', '.')
|
|
when /^\..*/
|
|
new_keys['use'] = @interp._invoke('winfo', 'id', new_keys['use'])
|
|
assoc_display = @interp._invoke('winfo', 'screen', new_keys['use'])
|
|
else
|
|
begin
|
|
pathname = @interp._invoke('winfo', 'pathname', new_keys['use'])
|
|
assoc_display = @interp._invoke('winfo', 'screen', pathname)
|
|
rescue
|
|
assoc_display = new_keys['display']
|
|
end
|
|
end
|
|
|
|
# match display?
|
|
if assoc_display != new_keys['display']
|
|
if optkeys.key?(:display) || optkeys.key?('display')
|
|
fail RuntimeError,
|
|
"conflicting 'display'=>#{new_keys['display']} " +
|
|
"and display '#{assoc_display}' on 'use'=>#{new_keys['use']}"
|
|
else
|
|
new_keys['display'] = assoc_display
|
|
end
|
|
end
|
|
end
|
|
|
|
# return
|
|
new_keys
|
|
end
|
|
private :__check_safetk_optkeys
|
|
|
|
def __create_safetk_frame(slave_ip, slave_name, app_name, keys)
|
|
# display option is used by ::safe::loadTk
|
|
loadTk_keys = {}
|
|
loadTk_keys['display'] = keys['display']
|
|
dup_keys = keys.dup
|
|
|
|
# keys for toplevel : allow followings
|
|
toplevel_keys = {}
|
|
['height', 'width', 'background', 'menu'].each{|k|
|
|
toplevel_keys[k] = dup_keys.delete(k) if dup_keys.key?(k)
|
|
}
|
|
toplevel_keys['classname'] = 'SafeTk'
|
|
toplevel_keys['screen'] = dup_keys.delete('display')
|
|
|
|
# other keys used by pack option of container frame
|
|
|
|
# create toplevel widget
|
|
begin
|
|
top = TkToplevel.new(toplevel_keys)
|
|
rescue NameError => e
|
|
fail e unless @interp.safe?
|
|
fail SecurityError, "unable create toplevel on the safe interpreter"
|
|
end
|
|
msg = "Untrusted Ruby/Tk applet (#{slave_name})"
|
|
if app_name.kind_of?(String)
|
|
top.title "#{app_name} (#{slave_name})"
|
|
else
|
|
top.title msg
|
|
end
|
|
|
|
# procedure to delete slave interpreter
|
|
slave_delete_proc = proc{
|
|
unless slave_ip.deleted?
|
|
#if slave_ip._invoke('info', 'command', '.') != ""
|
|
# slave_ip._invoke('destroy', '.')
|
|
#end
|
|
#slave_ip.delete
|
|
slave_ip._eval_without_enc('exit')
|
|
end
|
|
begin
|
|
top.destroy if top.winfo_exist?
|
|
rescue
|
|
# ignore
|
|
end
|
|
}
|
|
tag = TkBindTag.new.bind('Destroy', slave_delete_proc)
|
|
|
|
top.bindtags = top.bindtags.unshift(tag)
|
|
|
|
# create control frame
|
|
TkFrame.new(top, :bg=>'red', :borderwidth=>3, :relief=>'ridge') {|fc|
|
|
fc.bindtags = fc.bindtags.unshift(tag)
|
|
|
|
TkFrame.new(fc, :bd=>0){|f|
|
|
TkButton.new(f,
|
|
:text=>'Delete', :bd=>1, :padx=>2, :pady=>0,
|
|
:highlightthickness=>0, :command=>slave_delete_proc
|
|
).pack(:side=>:right, :fill=>:both)
|
|
f.pack(:side=>:right, :fill=>:both, :expand=>true)
|
|
}
|
|
|
|
TkLabel.new(fc, :text=>msg, :padx=>2, :pady=>0,
|
|
:anchor=>:w).pack(:side=>:left, :fill=>:both, :expand=>true)
|
|
|
|
fc.pack(:side=>:bottom, :fill=>:x)
|
|
}
|
|
|
|
# container frame for slave interpreter
|
|
dup_keys['fill'] = :both unless dup_keys.key?('fill')
|
|
dup_keys['expand'] = true unless dup_keys.key?('expand')
|
|
c = TkFrame.new(top, :container=>true).pack(dup_keys)
|
|
|
|
# return keys
|
|
loadTk_keys['use'] = TkWinfo.id(c)
|
|
[loadTk_keys, top.path]
|
|
end
|
|
private :__create_safetk_frame
|
|
|
|
def __create_safe_slave_obj(safe_opts, app_name, tk_opts)
|
|
# safe interpreter
|
|
ip_name = _create_slave_ip_name
|
|
slave_ip = @interp.create_slave(ip_name, true)
|
|
@slave_ip_tbl[ip_name] = slave_ip
|
|
def slave_ip.safe_base?
|
|
true
|
|
end
|
|
|
|
@interp._eval("::safe::interpInit #{ip_name}")
|
|
|
|
slave_ip._invoke('set', 'argv0', app_name) if app_name.kind_of?(String)
|
|
|
|
if tk_opts
|
|
tk_opts = __check_safetk_optkeys(tk_opts)
|
|
if tk_opts.key?('use')
|
|
@slave_ip_top[ip_name] = ''
|
|
else
|
|
tk_opts, top_path = __create_safetk_frame(slave_ip, ip_name, app_name,
|
|
tk_opts)
|
|
@slave_ip_top[ip_name] = top_path
|
|
end
|
|
@interp._eval("::safe::loadTk #{ip_name} #{_keys2opts(tk_opts)}")
|
|
else
|
|
@slave_ip_top[ip_name] = nil
|
|
end
|
|
|
|
if safe_opts.key?('deleteHook') || safe_opts.key?(:deleteHook)
|
|
@interp._eval("::safe::interpConfigure #{ip_name} " +
|
|
_keys2opts(safe_opts))
|
|
else
|
|
@interp._eval("::safe::interpConfigure #{ip_name} " +
|
|
_keys2opts(safe_opts) + '-deleteHook {' +
|
|
TkComm._get_eval_string(proc{|slave|
|
|
self._default_delete_hook(slave)
|
|
}) + '}')
|
|
end
|
|
|
|
[slave_ip, ip_name]
|
|
end
|
|
|
|
def __create_trusted_slave_obj(name, keys)
|
|
ip_name = _create_slave_ip_name
|
|
slave_ip = @interp.create_slave(ip_name, false)
|
|
slave_ip._invoke('set', 'argv0', name) if name.kind_of?(String)
|
|
slave_ip._invoke('set', 'argv', _keys2opts(keys))
|
|
@interp._invoke('load', '', 'Tk', ip_name)
|
|
@slave_ip_tbl[ip_name] = slave_ip
|
|
[slave_ip, ip_name]
|
|
end
|
|
|
|
######################################
|
|
|
|
def _create_slave_object(keys={})
|
|
ip = MultiTkIp.new_slave(self, keys={})
|
|
@slave_ip_tbl[ip.name] = ip
|
|
end
|
|
|
|
######################################
|
|
|
|
def initialize(master, safeip=true, keys={})
|
|
if $SAFE >= 4
|
|
fail SecurityError, "cannot create a new interpreter at level #{$SAFE}"
|
|
end
|
|
|
|
if safeip == nil && $SAFE >= 2
|
|
fail SecurityError, "cannot create a master-ip at level #{$SAFE}"
|
|
end
|
|
|
|
if master.deleted? && safeip == nil
|
|
fail RuntimeError, "cannot create a slave of a deleted interpreter"
|
|
end
|
|
|
|
if !master.deleted? && !master.master? && master.safe?
|
|
fail SecurityError, "safe-slave-ip cannot create a new interpreter"
|
|
end
|
|
|
|
if safeip == nil && !master.master?
|
|
fail SecurityError, "slave-ip cannot create a master-ip"
|
|
end
|
|
|
|
unless keys.kind_of? Hash
|
|
fail ArgumentError, "expecting a Hash object for the 2nd argument"
|
|
end
|
|
|
|
@tk_windows = {}
|
|
@tk_table_list = []
|
|
@slave_ip_tbl = {}
|
|
@slave_ip_top = {}
|
|
@cb_error_proc = []
|
|
@evloop_thread = []
|
|
|
|
@tk_windows.taint unless @tk_windows.tainted?
|
|
@tk_table_list.taint unless @tk_table_list.tainted?
|
|
@slave_ip_tbl.taint unless @slave_ip_tbl.tainted?
|
|
@slave_ip_top.taint unless @slave_ip_top.tainted?
|
|
@cb_error_proc.taint unless @cb_error_proc.tainted?
|
|
@evloop_thread.taint unless @evloop_thread.tainted?
|
|
|
|
name, safe, safe_opts, tk_opts = _parse_slaveopts(keys)
|
|
|
|
safe = 4 if safe && !safe.kind_of?(Fixnum)
|
|
|
|
@safe_base = false
|
|
|
|
if safeip == nil
|
|
# create master-ip
|
|
@interp = TclTkIp.new(name, _keys2opts(tk_opts))
|
|
|
|
@ip_name = nil
|
|
if safe
|
|
safe = $SAFE if safe < $SAFE
|
|
@safe_level = [safe]
|
|
else
|
|
@safe_level = [$SAFE]
|
|
end
|
|
else
|
|
# create slave-ip
|
|
if safeip || master.safe?
|
|
@safe_base = true
|
|
@interp, @ip_name = master.__create_safe_slave_obj(safe_opts,
|
|
name, tk_opts)
|
|
if safe
|
|
safe = master.safe_level if safe < master.safe_level
|
|
@safe_level = [safe]
|
|
else
|
|
@safe_level = [4]
|
|
end
|
|
else
|
|
@interp, @ip_name = master.__create_trusted_slave_obj(name, tk_opts)
|
|
if safe
|
|
safe = master.safe_level if safe < master.safe_level
|
|
@safe_level = [safe]
|
|
else
|
|
@safe_level = [master.safe_level]
|
|
end
|
|
end
|
|
@set_alias_proc = proc{|name|
|
|
master._invoke('interp', 'alias', @ip_name, name, '', name)
|
|
}.freeze
|
|
end
|
|
|
|
@system = Object.new
|
|
|
|
@wait_on_mainloop = [true, 0].taint
|
|
# @wait_on_mainloop = [false, 0].taint
|
|
|
|
@threadgroup = ThreadGroup.new
|
|
|
|
@cmd_queue = Queue.new
|
|
|
|
=begin
|
|
@cmd_receiver, @receiver_watchdog = _create_receiver_and_watchdog(@safe_level[0])
|
|
|
|
@threadgroup.add @cmd_receiver
|
|
@threadgroup.add @receiver_watchdog
|
|
|
|
@threadgroup.enclose
|
|
=end
|
|
@@DEFAULT_MASTER.assign_receiver_and_watchdog(self)
|
|
|
|
@@IP_TABLE[@threadgroup] = self
|
|
_init_ip_internal(@@INIT_IP_ENV, @@ADD_TK_PROCS)
|
|
@@TK_TABLE_LIST.size.times{
|
|
(tbl = {}).tainted? || tbl.taint
|
|
@tk_table_list << tbl
|
|
}
|
|
|
|
class << self
|
|
undef :instance_eval
|
|
end
|
|
|
|
# dummy call for initialization
|
|
self.eval_proc{ Tk.tk_call('set', 'tcl_patchLevel') }
|
|
|
|
self.freeze # defend against modification
|
|
end
|
|
|
|
######################################
|
|
|
|
def _default_delete_hook(slave)
|
|
@slave_ip_tbl.delete(slave)
|
|
top = @slave_ip_top.delete(slave)
|
|
if top.kind_of?(String)
|
|
# call default hook of safetk.tcl (ignore exceptions)
|
|
if top == ''
|
|
begin
|
|
@interp._eval("::safe::disallowTk #{slave}")
|
|
rescue
|
|
warn("Waring: fail to call '::safe::disallowTk'") if $DEBUG
|
|
end
|
|
else # toplevel path
|
|
begin
|
|
@interp._eval("::safe::tkDelete {} #{top} #{slave}")
|
|
rescue
|
|
warn("Waring: fail to call '::safe::tkDelete'") if $DEBUG
|
|
begin
|
|
@interp._eval("destroy #{top}")
|
|
rescue
|
|
warn("Waring: fail to destroy toplevel") if $DEBUG
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
|
|
# get target IP
|
|
class MultiTkIp
|
|
def self._ip_id_
|
|
__getip._ip_id_
|
|
end
|
|
def _ip_id_
|
|
''
|
|
end
|
|
|
|
def self.__getip
|
|
current = Thread.current
|
|
if TclTkLib.mainloop_thread? != false && current['callback_ip']
|
|
return current['callback_ip']
|
|
end
|
|
if current.group == ThreadGroup::Default
|
|
@@DEFAULT_MASTER
|
|
else
|
|
ip = @@IP_TABLE[current.group]
|
|
unless ip
|
|
fail SecurityError,
|
|
"cannot call Tk methods on #{Thread.current.inspect}"
|
|
end
|
|
ip
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
# aliases of constructor
|
|
class << MultiTkIp
|
|
alias __new new
|
|
private :__new
|
|
|
|
|
|
def new_master(safe=nil, keys={})
|
|
if safe.kind_of?(Hash)
|
|
keys = safe
|
|
elsif safe.kind_of?(Integer)
|
|
raise ArgumentError, "unexpected argument(s)" unless keys.kind_of?(Hash)
|
|
if !keys.key?(:safe) && !keys.key?('safe')
|
|
keys[:safe] = safe
|
|
end
|
|
elsif safe == nil
|
|
# do nothing
|
|
else
|
|
raise ArgumentError, "unexpected argument(s)"
|
|
end
|
|
|
|
ip = __new(__getip, nil, keys)
|
|
#ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given?
|
|
if block_given?
|
|
Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call)}
|
|
end
|
|
ip
|
|
end
|
|
|
|
alias new new_master
|
|
|
|
def new_slave(safe=nil, keys={})
|
|
if safe.kind_of?(Hash)
|
|
keys = safe
|
|
elsif safe.kind_of?(Integer)
|
|
raise ArgumentError, "unexpected argument(s)" unless keys.kind_of?(Hash)
|
|
if !keys.key?(:safe) && !keys.key?('safe')
|
|
keys[:safe] = safe
|
|
end
|
|
elsif safe == nil
|
|
# do nothing
|
|
else
|
|
raise ArgumentError, "unexpected argument(s)"
|
|
end
|
|
|
|
ip = __new(__getip, false, keys)
|
|
# ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given?
|
|
if block_given?
|
|
Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call)}
|
|
end
|
|
ip
|
|
end
|
|
alias new_trusted_slave new_slave
|
|
|
|
def new_safe_slave(safe=4, keys={})
|
|
if safe.kind_of?(Hash)
|
|
keys = safe
|
|
elsif safe.kind_of?(Integer)
|
|
raise ArgumentError, "unexpected argument(s)" unless keys.kind_of?(Hash)
|
|
if !keys.key?(:safe) && !keys.key?('safe')
|
|
keys[:safe] = safe
|
|
end
|
|
else
|
|
raise ArgumentError, "unexpected argument(s)"
|
|
end
|
|
|
|
ip = __new(__getip, true, keys)
|
|
# ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given?
|
|
if block_given?
|
|
Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call)}
|
|
end
|
|
ip
|
|
end
|
|
alias new_safeTk new_safe_slave
|
|
end
|
|
|
|
|
|
# get info
|
|
class MultiTkIp
|
|
def inspect
|
|
s = self.to_s.chop!
|
|
if master?
|
|
if @interp.deleted?
|
|
s << ':deleted-master'
|
|
else
|
|
s << ':master'
|
|
end
|
|
else
|
|
if @interp.deleted?
|
|
s << ':deleted-slave'
|
|
elsif @interp.safe?
|
|
s << ':safe-slave'
|
|
else
|
|
s << ':trusted-slave'
|
|
end
|
|
end
|
|
s << '>'
|
|
end
|
|
|
|
def master?
|
|
if @ip_name
|
|
false
|
|
else
|
|
true
|
|
end
|
|
end
|
|
def self.master?
|
|
__getip.master?
|
|
end
|
|
|
|
def slave?
|
|
not master?
|
|
end
|
|
def self.slave?
|
|
not self.master?
|
|
end
|
|
|
|
def alive?
|
|
begin
|
|
return false unless @cmd_receiver.alive?
|
|
return false if @interp.deleted?
|
|
return false if @interp._invoke('interp', 'exists', '') == '0'
|
|
rescue Exception
|
|
return false
|
|
end
|
|
true
|
|
end
|
|
def self.alive?
|
|
__getip.alive?
|
|
end
|
|
|
|
def path
|
|
@ip_name || ''
|
|
end
|
|
def self.path
|
|
__getip.path
|
|
end
|
|
def ip_name
|
|
@ip_name || ''
|
|
end
|
|
def self.ip_name
|
|
__getip.ip_name
|
|
end
|
|
def to_eval
|
|
@ip_name || ''
|
|
end
|
|
def self.to_eval
|
|
__getip.to_eval
|
|
end
|
|
|
|
def slaves(all = false)
|
|
@interp._invoke('interp','slaves').split.map!{|name|
|
|
if @slave_ip_tbl.key?(name)
|
|
@slave_ip_tbl[name]
|
|
elsif all
|
|
name
|
|
else
|
|
nil
|
|
end
|
|
}.compact!
|
|
end
|
|
def self.slaves(all = false)
|
|
__getip.slaves(all)
|
|
end
|
|
end
|
|
|
|
|
|
# instance methods to treat tables
|
|
class MultiTkIp
|
|
def _tk_cmd_tbl
|
|
tbl = {}
|
|
MultiTkIp.tk_cmd_tbl.each{|id, ent| tbl[id] = ent if ent.ip == self }
|
|
tbl
|
|
end
|
|
|
|
def _tk_windows
|
|
@tk_windows
|
|
end
|
|
|
|
def _tk_table_list
|
|
@tk_table_list
|
|
end
|
|
|
|
def _add_new_tables
|
|
(@@TK_TABLE_LIST.size - @tk_table_list.size).times{
|
|
(tbl = {}).tainted? || tbl.taint
|
|
@tk_table_list << tbl
|
|
}
|
|
end
|
|
|
|
def _init_ip_env(script)
|
|
script.call(self)
|
|
end
|
|
|
|
def _add_tk_procs(name, args, body)
|
|
return if slave?
|
|
@interp._invoke('proc', name, args, body) if args && body
|
|
@interp._invoke('interp', 'slaves').split.each{|slave|
|
|
@interp._invoke('interp', 'alias', slave, name, '', name)
|
|
}
|
|
end
|
|
|
|
def _init_ip_internal(init_ip_env, add_tk_procs)
|
|
init_ip_env.each{|script| script.call(self)}
|
|
add_tk_procs.each{|name, args, body|
|
|
if master?
|
|
@interp._invoke('proc', name, args, body) if args && body
|
|
else
|
|
@set_alias_proc.call(name)
|
|
end
|
|
}
|
|
end
|
|
end
|
|
|
|
|
|
# class methods to treat tables
|
|
class MultiTkIp
|
|
def self.tk_cmd_tbl
|
|
@@TK_CMD_TBL
|
|
end
|
|
def self.tk_windows
|
|
__getip._tk_windows
|
|
end
|
|
def self.tk_object_table(id)
|
|
__getip._tk_table_list[id]
|
|
end
|
|
def self.create_table
|
|
#if __getip.slave?
|
|
# raise SecurityError, "slave-IP has no permission creating a new table"
|
|
#end
|
|
id = @@TK_TABLE_LIST.size
|
|
obj = Object.new
|
|
@@TK_TABLE_LIST << obj
|
|
obj.instance_eval <<-EOD
|
|
def self.method_missing(m, *args)
|
|
MultiTkIp.tk_object_table(#{id}).__send__(m, *args)
|
|
end
|
|
EOD
|
|
obj.freeze
|
|
@@IP_TABLE.each{|tg, ip| ip._add_new_tables }
|
|
return obj
|
|
end
|
|
|
|
def self.init_ip_env(script = Proc.new)
|
|
@@INIT_IP_ENV << script
|
|
@@IP_TABLE.each{|tg, ip|
|
|
ip._init_ip_env(script)
|
|
}
|
|
end
|
|
|
|
def self.add_tk_procs(name, args=nil, body=nil)
|
|
@@ADD_TK_PROCS << [name, args, body]
|
|
@@IP_TABLE.each{|tg, ip|
|
|
ip._add_tk_procs(name, args, body)
|
|
}
|
|
end
|
|
|
|
def self.init_ip_internal
|
|
__getip._init_ip_internal(@@INIT_IP_ENV, @@ADD_TK_PROCS)
|
|
end
|
|
end
|
|
|
|
|
|
# for callback operation
|
|
class MultiTkIp
|
|
def self.cb_entry_class
|
|
@@CB_ENTRY_CLASS
|
|
end
|
|
def self.get_cb_entry(cmd)
|
|
@@CB_ENTRY_CLASS.new(__getip, cmd).freeze
|
|
end
|
|
|
|
def cb_eval(cmd, *args)
|
|
#self.eval_callback{ TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *args)) }
|
|
#ret = self.eval_callback{ TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *args)) }
|
|
ret = self.eval_callback(*args){|safe, *params|
|
|
$SAFE=safe if $SAFE < safe
|
|
TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *params))
|
|
}
|
|
if ret.kind_of?(Exception)
|
|
raise ret
|
|
end
|
|
ret
|
|
end
|
|
end
|
|
|
|
|
|
# evaluate a procedure on the proper interpreter
|
|
class MultiTkIp
|
|
# instance method
|
|
def eval_proc_core(req_val, cmd, *args)
|
|
# check
|
|
unless cmd.kind_of?(Proc) || cmd.kind_of?(Method)
|
|
raise RuntimeError, "A Proc/Method object is expected for the 'cmd' argument"
|
|
end
|
|
|
|
# on IP thread
|
|
if @cmd_receiver == Thread.current ||
|
|
(!req_val && TclTkLib.mainloop_thread? != false) # callback
|
|
begin
|
|
ret = cmd.call(safe_level, *args)
|
|
rescue SystemExit => e
|
|
# exit IP
|
|
warn("Warning: "+ $! + " on " + self.inspect) if $DEBUG
|
|
begin
|
|
self._eval_without_enc('exit')
|
|
rescue Exception
|
|
end
|
|
self.delete
|
|
ret = nil
|
|
rescue Exception => e
|
|
if $DEBUG
|
|
warn("Warning: " + e.class.inspect +
|
|
((e.message.length > 0)? ' "' + e.message + '"': '') +
|
|
" on " + self.inspect)
|
|
end
|
|
=begin
|
|
begin
|
|
bt = _toUTF8(e.backtrace.join("\n"))
|
|
bt.instance_variable_set(:@encoding, 'utf-8')
|
|
rescue Exception
|
|
bt = e.backtrace.join("\n")
|
|
end
|
|
begin
|
|
@interp._set_global_var('errorInfo', bt)
|
|
rescue Exception
|
|
end
|
|
=end
|
|
ret = e
|
|
end
|
|
return ret
|
|
end
|
|
|
|
# send cmd to the proc-queue
|
|
unless req_val
|
|
begin
|
|
@cmd_queue.enq([nil, cmd, *args])
|
|
rescue Exception => e
|
|
# ignore
|
|
if $DEBUG
|
|
warn("Warning: " + e.class.inspect +
|
|
((e.message.length > 0)? ' "' + e.message + '"': '') +
|
|
" on " + self.inspect)
|
|
end
|
|
return e
|
|
end
|
|
return nil
|
|
end
|
|
|
|
# send and get return value by exception
|
|
begin
|
|
@cmd_queue.enq([Thread.current, cmd, *args])
|
|
Thread.stop
|
|
rescue MultiTkIp_OK => ret
|
|
# return value
|
|
return ret.value
|
|
rescue SystemExit => e
|
|
# exit IP
|
|
warn("Warning: " + $! + " on " + self.inspect) if $DEBUG
|
|
begin
|
|
self._eval_without_enc('exit')
|
|
rescue Exception
|
|
end
|
|
if !self.deleted? && !safe? && allow_ruby_exit?
|
|
self.delete
|
|
fail e
|
|
else
|
|
self.delete
|
|
end
|
|
rescue Exception => e
|
|
if $DEBUG
|
|
warn("Warning: " + e.class.inspect +
|
|
((e.message.length > 0)? ' "' + e.message + '"': '') +
|
|
" on " + self.inspect)
|
|
end
|
|
return e
|
|
end
|
|
return nil
|
|
end
|
|
private :eval_proc_core
|
|
|
|
def eval_callback(*args)
|
|
if block_given?
|
|
cmd = Proc.new
|
|
else
|
|
cmd = args.shift
|
|
end
|
|
current = Thread.current
|
|
backup_ip = current['callback_ip']
|
|
current['callback_ip'] = self
|
|
begin
|
|
eval_proc_core(false, cmd, *args)
|
|
ensure
|
|
current['callback_ip'] = backup_ip
|
|
end
|
|
end
|
|
|
|
def eval_proc(*args)
|
|
# The scope of the eval-block of 'eval_proc' method is different from
|
|
# the external. If you want to pass local values to the eval-block,
|
|
# use arguments of eval_proc method. They are passed to block-arguments.
|
|
if block_given?
|
|
cmd = Proc.new
|
|
else
|
|
unless (cmd = args.shift)
|
|
fail ArgumentError, "A Proc or Method object is expected for 1st argument"
|
|
end
|
|
end
|
|
if TclTkLib.mainloop_thread? == true
|
|
# call from eventloop
|
|
current = Thread.current
|
|
backup_ip = current['callback_ip']
|
|
current['callback_ip'] = self
|
|
begin
|
|
eval_proc_core(false,
|
|
proc{|safe, *params|
|
|
$SAFE=safe if $SAFE < safe
|
|
cmd.call(*params)
|
|
}, *args)
|
|
ensure
|
|
current['callback_ip'] = backup_ip
|
|
end
|
|
else
|
|
eval_proc_core(true,
|
|
proc{|safe, *params|
|
|
$SAFE=safe if $SAFE < safe
|
|
Thread.new(*params, &cmd).value
|
|
},
|
|
*args)
|
|
end
|
|
end
|
|
alias call eval_proc
|
|
|
|
def bg_eval_proc(*args)
|
|
if block_given?
|
|
cmd = Proc.new
|
|
else
|
|
unless (cmd = args.shift)
|
|
fail ArgumentError, "A Proc or Method object is expected for 1st argument"
|
|
end
|
|
end
|
|
Thread.new{
|
|
eval_proc(cmd, *args)
|
|
=begin
|
|
eval_proc_core(false,
|
|
proc{|safe, *params|
|
|
$SAFE=safe if $SAFE < safe
|
|
Thread.new(*params, &cmd).value
|
|
},
|
|
safe_level, *args)
|
|
=end
|
|
}
|
|
end
|
|
alias background_eval_proc bg_eval_proc
|
|
alias thread_eval_proc bg_eval_proc
|
|
alias bg_call bg_eval_proc
|
|
alias background_call bg_eval_proc
|
|
|
|
def eval_string(cmd, *eval_args)
|
|
# cmd string ==> proc
|
|
unless cmd.kind_of?(String)
|
|
raise RuntimeError, "A String object is expected for the 'cmd' argument"
|
|
end
|
|
|
|
eval_proc_core(true,
|
|
proc{|safe|
|
|
$SAFE=safe if $SAFE < safe
|
|
Kernel.eval(cmd, *eval_args)
|
|
})
|
|
end
|
|
alias eval_str eval_string
|
|
|
|
def bg_eval_string(*args)
|
|
# cmd string ==> proc
|
|
unless cmd.kind_of?(String)
|
|
raise RuntimeError, "A String object is expected for the 'cmd' argument"
|
|
end
|
|
Thread.new{
|
|
eval_proc_core(true,
|
|
proc{|safe|
|
|
$SAFE=safe if $SAFE < safe
|
|
Kernel.eval(cmd, *eval_args)
|
|
})
|
|
}
|
|
end
|
|
alias background_eval_string bg_eval_string
|
|
alias bg_eval_str bg_eval_string
|
|
alias background_eval_str bg_eval_string
|
|
end
|
|
|
|
class << MultiTkIp
|
|
# class method
|
|
def eval_proc(*args, &blk)
|
|
# class ==> interp object
|
|
__getip.eval_proc(*args, &blk)
|
|
end
|
|
alias call eval_proc
|
|
alias eval_string eval_proc
|
|
end
|
|
|
|
|
|
# event loop
|
|
# all master/slave IPs are controled by only one event-loop
|
|
class << MultiTkIp
|
|
def mainloop(check_root = true)
|
|
__getip.mainloop(check_root)
|
|
end
|
|
def mainloop_watchdog(check_root = true)
|
|
__getip.mainloop_watchdog(check_root)
|
|
end
|
|
def do_one_event(flag = TclTkLib::EventFlag::ALL)
|
|
__getip.do_one_event(flag)
|
|
end
|
|
def mainloop_abort_on_exception
|
|
# __getip.mainloop_abort_on_exception
|
|
TclTkLib.mainloop_abort_on_exception
|
|
end
|
|
def mainloop_abort_on_exception=(mode)
|
|
# __getip.mainloop_abort_on_exception=(mode)
|
|
TclTkLib.mainloop_abort_on_exception=(mode)
|
|
end
|
|
def set_eventloop_tick(tick)
|
|
__getip.set_eventloop_tick(tick)
|
|
end
|
|
def get_eventloop_tick
|
|
__getip.get_eventloop_tick
|
|
end
|
|
def set_no_event_wait(tick)
|
|
__getip.set_no_event_wait(tick)
|
|
end
|
|
def get_no_event_wait
|
|
__getip.get_no_event_wait
|
|
end
|
|
def set_eventloop_weight(loop_max, no_event_tick)
|
|
__getip.set_eventloop_weight(loop_max, no_event_tick)
|
|
end
|
|
def get_eventloop_weight
|
|
__getip.get_eventloop_weight
|
|
end
|
|
end
|
|
|
|
# class methods to delegate to TclTkIp
|
|
class << MultiTkIp
|
|
def method_missing(id, *args)
|
|
__getip.__send__(id, *args)
|
|
end
|
|
|
|
def make_safe
|
|
__getip.make_safe
|
|
end
|
|
|
|
def safe?
|
|
__getip.safe?
|
|
end
|
|
|
|
def safe_base?
|
|
begin
|
|
__getip.safe_base?
|
|
rescue
|
|
false
|
|
end
|
|
end
|
|
|
|
def allow_ruby_exit?
|
|
__getip.allow_ruby_exit?
|
|
end
|
|
|
|
def allow_ruby_exit= (mode)
|
|
__getip.allow_ruby_exit = mode
|
|
end
|
|
|
|
def delete
|
|
__getip.delete
|
|
end
|
|
|
|
def deleted?
|
|
__getip.deleted?
|
|
end
|
|
|
|
def has_mainwindow?
|
|
__getip.has_mainwindow?
|
|
end
|
|
|
|
def invalid_namespace?
|
|
__getip.invalid_namespace?
|
|
end
|
|
|
|
def abort(msg = nil)
|
|
__getip.abort(msg)
|
|
end
|
|
|
|
def exit(st = true)
|
|
__getip.exit(st)
|
|
end
|
|
|
|
def exit!(st = false)
|
|
__getip.exit!(st)
|
|
end
|
|
|
|
def restart(app_name = nil, keys = {})
|
|
init_ip_internal
|
|
|
|
__getip._invoke('set', 'argv0', app_name) if app_name
|
|
if keys.kind_of?(Hash)
|
|
__getip._invoke('set', 'argv', _keys2opts(keys))
|
|
end
|
|
|
|
__getip.restart
|
|
end
|
|
|
|
def _eval(str)
|
|
__getip._eval(str)
|
|
end
|
|
|
|
def _invoke(*args)
|
|
__getip._invoke(*args)
|
|
end
|
|
|
|
def _eval_without_enc(str)
|
|
__getip._eval_without_enc(str)
|
|
end
|
|
|
|
def _invoke_without_enc(*args)
|
|
__getip._invoke_without_enc(*args)
|
|
end
|
|
|
|
def _eval_with_enc(str)
|
|
__getip._eval_with_enc(str)
|
|
end
|
|
|
|
def _invoke_with_enc(*args)
|
|
__getip._invoke_with_enc(*args)
|
|
end
|
|
|
|
def _toUTF8(str, encoding=nil)
|
|
__getip._toUTF8(str, encoding)
|
|
end
|
|
|
|
def _fromUTF8(str, encoding=nil)
|
|
__getip._fromUTF8(str, encoding)
|
|
end
|
|
|
|
def _thread_vwait(var)
|
|
__getip._thread_vwait(var)
|
|
end
|
|
|
|
def _thread_tkwait(mode, target)
|
|
__getip._thread_tkwait(mode, target)
|
|
end
|
|
|
|
def _return_value
|
|
__getip._return_value
|
|
end
|
|
|
|
def _get_variable(var, flag)
|
|
__getip._get_variable(var, flag)
|
|
end
|
|
def _get_variable2(var, idx, flag)
|
|
__getip._get_variable2(var, idx, flag)
|
|
end
|
|
def _set_variable(var, value, flag)
|
|
__getip._set_variable(var, value, flag)
|
|
end
|
|
def _set_variable2(var, idx, value, flag)
|
|
__getip._set_variable2(var, idx, value, flag)
|
|
end
|
|
def _unset_variable(var, flag)
|
|
__getip._unset_variable(var, flag)
|
|
end
|
|
def _unset_variable2(var, idx, flag)
|
|
__getip._unset_variable2(var, idx, flag)
|
|
end
|
|
|
|
def _get_global_var(var)
|
|
__getip._get_global_var(var)
|
|
end
|
|
def _get_global_var2(var, idx)
|
|
__getip._get_global_var2(var, idx)
|
|
end
|
|
def _set_global_var(var, value)
|
|
__getip._set_global_var(var, value)
|
|
end
|
|
def _set_global_var2(var, idx, value)
|
|
__getip._set_global_var2(var, idx, value)
|
|
end
|
|
def _unset_global_var(var)
|
|
__getip._unset_global_var(var)
|
|
end
|
|
def _unset_global_var2(var, idx)
|
|
__getip._unset_global_var2(var, idx)
|
|
end
|
|
|
|
def _split_tklist(str)
|
|
__getip._split_tklist(str)
|
|
end
|
|
def _merge_tklist(*args)
|
|
__getip._merge_tklist(*args)
|
|
end
|
|
def _conv_listelement(arg)
|
|
__getip._conv_listelement(arg)
|
|
end
|
|
|
|
def _create_console
|
|
__getip._create_console
|
|
end
|
|
end
|
|
|
|
|
|
# wrap methods on TclTkLib : not permit calling TclTkLib module methods
|
|
class << TclTkLib
|
|
def mainloop(check_root = true)
|
|
MultiTkIp.mainloop(check_root)
|
|
end
|
|
def mainloop_watchdog(check_root = true)
|
|
MultiTkIp.mainloop_watchdog(check_root)
|
|
end
|
|
def do_one_event(flag = TclTkLib::EventFlag::ALL)
|
|
MultiTkIp.do_one_event(flag)
|
|
end
|
|
#def mainloop_abort_on_exception
|
|
# MultiTkIp.mainloop_abort_on_exception
|
|
#end
|
|
#def mainloop_abort_on_exception=(mode)
|
|
# MultiTkIp.mainloop_abort_on_exception=(mode)
|
|
#end
|
|
def set_eventloop_tick(tick)
|
|
MultiTkIp.set_eventloop_tick(tick)
|
|
end
|
|
def get_eventloop_tick
|
|
MultiTkIp.get_eventloop_tick
|
|
end
|
|
def set_no_event_wait(tick)
|
|
MultiTkIp.set_no_event_wait(tick)
|
|
end
|
|
def get_no_event_wait
|
|
MultiTkIp.get_no_event_wait
|
|
end
|
|
def set_eventloop_weight(loop_max, no_event_tick)
|
|
MultiTkIp.set_eventloop_weight(loop_max, no_event_tick)
|
|
end
|
|
def get_eventloop_weight
|
|
MultiTkIp.get_eventloop_weight
|
|
end
|
|
def restart(*args)
|
|
MultiTkIp.restart(*args)
|
|
end
|
|
|
|
def _merge_tklist(*args)
|
|
MultiTkIp._merge_tklist(*args)
|
|
end
|
|
def _conv_listelement(arg)
|
|
MultiTkIp._conv_listelement(arg)
|
|
end
|
|
end
|
|
|
|
|
|
# depend on TclTkIp
|
|
class MultiTkIp
|
|
def mainloop(check_root = true, restart_on_dead = true)
|
|
#return self if self.slave?
|
|
#return self if self != @@DEFAULT_MASTER
|
|
if self != @@DEFAULT_MASTER
|
|
if @wait_on_mainloop[0]
|
|
begin
|
|
@wait_on_mainloop[1] += 1
|
|
if $SAFE >= 4
|
|
_receiver_mainloop(check_root).join
|
|
else
|
|
@cmd_queue.enq([@system, 'call_mainloop',
|
|
Thread.current, check_root])
|
|
Thread.stop
|
|
end
|
|
rescue MultiTkIp_OK => ret
|
|
# return value
|
|
if ret.value.kind_of?(Thread)
|
|
return ret.value.value
|
|
else
|
|
return ret.value
|
|
end
|
|
rescue SystemExit => e
|
|
# exit IP
|
|
warn("Warning: " + $! + " on " + self.inspect) if $DEBUG
|
|
begin
|
|
self._eval_without_enc('exit')
|
|
rescue Exception
|
|
end
|
|
self.delete
|
|
rescue StandardError => e
|
|
if $DEBUG
|
|
warn("Warning: " + e.class.inspect +
|
|
((e.message.length > 0)? ' "' + e.message + '"': '') +
|
|
" on " + self.inspect)
|
|
end
|
|
return e
|
|
rescue Exception => e
|
|
return e
|
|
ensure
|
|
@wait_on_mainloop[1] -= 1
|
|
end
|
|
end
|
|
return
|
|
end
|
|
|
|
unless restart_on_dead
|
|
@wait_on_mainloop[1] += 1
|
|
=begin
|
|
begin
|
|
@interp.mainloop(check_root)
|
|
rescue StandardError => e
|
|
if $DEBUG
|
|
warn("Warning: " + e.class.inspect +
|
|
((e.message.length > 0)? ' "' + e.message + '"': '') +
|
|
" on " + self.inspect)
|
|
end
|
|
end
|
|
=end
|
|
begin
|
|
@interp.mainloop(check_root)
|
|
ensure
|
|
@wait_on_mainloop[1] -= 1
|
|
end
|
|
else
|
|
loop do
|
|
break unless self.alive?
|
|
if check_root
|
|
begin
|
|
break if TclTkLib.num_of_mainwindows == 0
|
|
rescue StandardError
|
|
break
|
|
end
|
|
end
|
|
break if @interp.deleted?
|
|
begin
|
|
@wait_on_mainloop[1] += 1
|
|
@interp.mainloop(check_root)
|
|
rescue StandardError => e
|
|
if TclTkLib.mainloop_abort_on_exception != nil
|
|
#STDERR.print("Warning: Tk mainloop receives ", $!.class.inspect,
|
|
# " exception (ignore) : ", $!.message, "\n");
|
|
if $DEBUG
|
|
warn("Warning: Tk mainloop receives " << e.class.inspect <<
|
|
" exception (ignore) : " << e.message);
|
|
end
|
|
end
|
|
#raise e
|
|
rescue Exception => e
|
|
=begin
|
|
if TclTkLib.mainloop_abort_on_exception != nil
|
|
#STDERR.print("Warning: Tk mainloop receives ", $!.class.inspect,
|
|
# " exception (ignore) : ", $!.message, "\n");
|
|
if $DEBUG
|
|
warn("Warning: Tk mainloop receives " << e.class.inspect <<
|
|
" exception (ignore) : " << e.message);
|
|
end
|
|
end
|
|
=end
|
|
raise e
|
|
ensure
|
|
@wait_on_mainloop[1] -= 1
|
|
Thread.pass # avoid eventloop conflict
|
|
end
|
|
end
|
|
end
|
|
self
|
|
end
|
|
|
|
def make_safe
|
|
@interp.make_safe
|
|
end
|
|
|
|
def safe?
|
|
@interp.safe?
|
|
end
|
|
|
|
def safe_base?
|
|
@safe_base
|
|
end
|
|
|
|
def allow_ruby_exit?
|
|
@interp.allow_ruby_exit?
|
|
end
|
|
|
|
def allow_ruby_exit= (mode)
|
|
@interp.allow_ruby_exit = mode
|
|
end
|
|
|
|
def delete
|
|
@slave_ip_tbl.each{|name, subip|
|
|
_destroy_slaves_of_slaveIP(subip)
|
|
=begin
|
|
begin
|
|
subip._invoke('destroy', '.') unless subip.deleted?
|
|
rescue Exception
|
|
end
|
|
=end
|
|
begin
|
|
# subip._eval_without_enc("foreach i [after info] {after cancel $i}")
|
|
unless subip.deleted?
|
|
after_ids = subip._eval_without_enc("after info")
|
|
subip._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}")
|
|
end
|
|
rescue Exception
|
|
end
|
|
|
|
# safe_base?
|
|
if @interp._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0'
|
|
begin
|
|
@interp._eval_without_enc("::safe::interpDelete #{name}")
|
|
rescue Exception
|
|
else
|
|
next if subip.deleted?
|
|
end
|
|
end
|
|
if subip.respond_to?(:safe_base?) && subip.safe_base? &&
|
|
!subip.deleted?
|
|
# do 'exit' to call the delete_hook procedure
|
|
begin
|
|
subip._eval_without_enc('exit')
|
|
rescue Exception
|
|
end
|
|
else
|
|
begin
|
|
subip.delete unless subip.deleted?
|
|
rescue Exception
|
|
end
|
|
end
|
|
}
|
|
|
|
begin
|
|
# @interp._eval_without_enc("foreach i [after info] {after cancel $i}")
|
|
after_ids = @interp._eval_without_enc("after info")
|
|
@interp._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}")
|
|
rescue Exception
|
|
end
|
|
|
|
begin
|
|
@interp._invoke('destroy', '.') unless @interp.deleted?
|
|
rescue Exception
|
|
end
|
|
|
|
if @safe_base && !@interp.deleted?
|
|
# do 'exit' to call the delete_hook procedure
|
|
@interp._eval_without_enc('exit')
|
|
end
|
|
@interp.delete
|
|
self
|
|
end
|
|
|
|
def deleted?
|
|
@interp.deleted?
|
|
end
|
|
|
|
def has_mainwindow?
|
|
@interp.has_mainwindow?
|
|
end
|
|
|
|
def invalid_namespace?
|
|
@interp.invalid_namespace?
|
|
end
|
|
|
|
def abort(msg = nil)
|
|
if master?
|
|
if msg
|
|
Kernel.abort(msg)
|
|
else
|
|
Kernel.abort
|
|
end
|
|
else
|
|
# ignore msg
|
|
delete
|
|
1
|
|
end
|
|
end
|
|
|
|
def exit(st = true)
|
|
if master?
|
|
Kernel.exit(st)
|
|
else
|
|
delete
|
|
st
|
|
end
|
|
end
|
|
|
|
def exit!(st = false)
|
|
if master? && !safe? && allow_ruby_exit?
|
|
Kernel.exit!(st)
|
|
else
|
|
delete
|
|
st
|
|
end
|
|
end
|
|
|
|
def restart(app_name = nil, keys = {})
|
|
_init_ip_internal(@@INIT_IP_ENV, @@ADD_TK_PROCS)
|
|
|
|
@interp._invoke('set', 'argv0', app_name) if app_name
|
|
if keys.kind_of?(Hash)
|
|
@interp._invoke('set', 'argv', _keys2opts(keys))
|
|
end
|
|
|
|
@interp.restart
|
|
end
|
|
|
|
def __eval(str)
|
|
@interp.__eval(str)
|
|
end
|
|
|
|
def __invoke(*args)
|
|
@interp.__invoke(*args)
|
|
end
|
|
|
|
def _eval(str)
|
|
@interp._eval(str)
|
|
end
|
|
|
|
def _invoke(*args)
|
|
@interp._invoke(*args)
|
|
end
|
|
|
|
def _eval_without_enc(str)
|
|
@interp._eval_without_enc(str)
|
|
end
|
|
|
|
def _invoke_without_enc(*args)
|
|
@interp._invoke_without_enc(*args)
|
|
end
|
|
|
|
def _eval_with_enc(str)
|
|
@interp._eval_with_enc(str)
|
|
end
|
|
|
|
def _invoke_with_enc(*args)
|
|
@interp._invoke_with_enc(*args)
|
|
end
|
|
|
|
def _toUTF8(str, encoding=nil)
|
|
@interp._toUTF8(str, encoding)
|
|
end
|
|
|
|
def _fromUTF8(str, encoding=nil)
|
|
@interp._fromUTF8(str, encoding)
|
|
end
|
|
|
|
def _thread_vwait(var)
|
|
@interp._thread_vwait(var)
|
|
end
|
|
|
|
def _thread_tkwait(mode, target)
|
|
@interp._thread_tkwait(mode, target)
|
|
end
|
|
|
|
def _return_value
|
|
@interp._return_value
|
|
end
|
|
|
|
def _get_variable(var, flag)
|
|
@interp._get_variable(var, flag)
|
|
end
|
|
def _get_variable2(var, idx, flag)
|
|
@interp._get_variable2(var, idx, flag)
|
|
end
|
|
def _set_variable(var, value, flag)
|
|
@interp._set_variable(var, value, flag)
|
|
end
|
|
def _set_variable2(var, idx, value, flag)
|
|
@interp._set_variable2(var, idx, value, flag)
|
|
end
|
|
def _unset_variable(var, flag)
|
|
@interp._unset_variable(var, flag)
|
|
end
|
|
def _unset_variable2(var, idx, flag)
|
|
@interp._unset_variable2(var, idx, flag)
|
|
end
|
|
|
|
def _get_global_var(var)
|
|
@interp._get_global_var(var)
|
|
end
|
|
def _get_global_var2(var, idx)
|
|
@interp._get_global_var2(var, idx)
|
|
end
|
|
def _set_global_var(var, value)
|
|
@interp._set_global_var(var, value)
|
|
end
|
|
def _set_global_var2(var, idx, value)
|
|
@interp._set_global_var2(var, idx, value)
|
|
end
|
|
def _unset_global_var(var)
|
|
@interp._unset_global_var(var)
|
|
end
|
|
def _unset_global_var2(var, idx)
|
|
@interp._unset_global_var2(var, idx)
|
|
end
|
|
|
|
def _split_tklist(str)
|
|
@interp._split_tklist(str)
|
|
end
|
|
def _merge_tklist(*args)
|
|
@interp._merge_tklist(*args)
|
|
end
|
|
def _conv_listelement(arg)
|
|
@interp._conv_listelement(arg)
|
|
end
|
|
end
|
|
|
|
|
|
# interp command support
|
|
class MultiTkIp
|
|
def _lst2ary(str)
|
|
return [] if str == ""
|
|
idx = str.index('{')
|
|
while idx and idx > 0 and str[idx-1] == ?\\
|
|
idx = str.index('{', idx+1)
|
|
end
|
|
return str.split unless idx
|
|
|
|
list = str[0,idx].split
|
|
str = str[idx+1..-1]
|
|
i = -1
|
|
brace = 1
|
|
str.each_byte {|c|
|
|
i += 1
|
|
brace += 1 if c == ?{
|
|
brace -= 1 if c == ?}
|
|
break if brace == 0
|
|
}
|
|
if i == 0
|
|
list.push ''
|
|
elsif str[0, i] == ' '
|
|
list.push ' '
|
|
else
|
|
list.push str[0..i-1]
|
|
end
|
|
#list += _lst2ary(str[i+1..-1])
|
|
list.concat(_lst2ary(str[i+1..-1]))
|
|
list
|
|
end
|
|
private :_lst2ary
|
|
|
|
def _slavearg(slave)
|
|
if slave.kind_of?(MultiTkIp)
|
|
slave.path
|
|
elsif slave.kind_of?(String)
|
|
slave
|
|
else
|
|
slave.to_s
|
|
end
|
|
end
|
|
private :_slavearg
|
|
|
|
def alias_info(slave, cmd_name)
|
|
_lst2ary(@interp._invoke('interp', 'alias', _slavearg(slave), cmd_name))
|
|
end
|
|
def self.alias_info(slave, cmd_name)
|
|
__getip.alias_info(slave, cmd_name)
|
|
end
|
|
|
|
def alias_delete(slave, cmd_name)
|
|
@interp._invoke('interp', 'alias', _slavearg(slave), cmd_name, '')
|
|
self
|
|
end
|
|
def self.alias_delete(slave, cmd_name)
|
|
__getip.alias_delete(slave, cmd_name)
|
|
self
|
|
end
|
|
|
|
def def_alias(slave, new_cmd, org_cmd, *args)
|
|
ret = @interp._invoke('interp', 'alias', _slavearg(slave), new_cmd,
|
|
'', org_cmd, *args)
|
|
(ret == new_cmd)? self: nil
|
|
end
|
|
def self.def_alias(slave, new_cmd, org_cmd, *args)
|
|
ret = __getip.def_alias(slave, new_cmd, org_cmd, *args)
|
|
(ret == new_cmd)? self: nil
|
|
end
|
|
|
|
def aliases(slave = '')
|
|
_lst2ary(@interp._invoke('interp', 'aliases', _slavearg(slave)))
|
|
end
|
|
def self.aliases(slave = '')
|
|
__getip.aliases(slave)
|
|
end
|
|
|
|
def delete_slaves(*args)
|
|
slaves = args.collect{|s| _slavearg(s)}
|
|
@interp._invoke('interp', 'delete', *slaves) if slaves.size > 0
|
|
self
|
|
end
|
|
def self.delete_slaves(*args)
|
|
__getip.delete_slaves(*args)
|
|
self
|
|
end
|
|
|
|
def exist?(slave = '')
|
|
ret = @interp._invoke('interp', 'exists', _slavearg(slave))
|
|
(ret == '1')? true: false
|
|
end
|
|
def self.exist?(slave = '')
|
|
__getip.exist?(slave)
|
|
end
|
|
|
|
def delete_cmd(slave, cmd)
|
|
slave_invoke = @interp._invoke('list', 'rename', cmd, '')
|
|
@interp._invoke('interp', 'eval', _slavearg(slave), slave_invoke)
|
|
self
|
|
end
|
|
def self.delete_cmd(slave, cmd)
|
|
__getip.delete_cmd(slave, cmd)
|
|
self
|
|
end
|
|
|
|
def expose_cmd(slave, cmd, aliasname = nil)
|
|
if aliasname
|
|
@interp._invoke('interp', 'expose', _slavearg(slave), cmd, aliasname)
|
|
else
|
|
@interp._invoke('interp', 'expose', _slavearg(slave), cmd)
|
|
end
|
|
self
|
|
end
|
|
def self.expose_cmd(slave, cmd, aliasname = nil)
|
|
__getip.expose_cmd(slave, cmd, aliasname)
|
|
self
|
|
end
|
|
|
|
def hide_cmd(slave, cmd, aliasname = nil)
|
|
if aliasname
|
|
@interp._invoke('interp', 'hide', _slavearg(slave), cmd, aliasname)
|
|
else
|
|
@interp._invoke('interp', 'hide', _slavearg(slave), cmd)
|
|
end
|
|
self
|
|
end
|
|
def self.hide_cmd(slave, cmd, aliasname = nil)
|
|
__getip.hide_cmd(slave, cmd, aliasname)
|
|
self
|
|
end
|
|
|
|
def hidden_cmds(slave = '')
|
|
_lst2ary(@interp._invoke('interp', 'hidden', _slavearg(slave)))
|
|
end
|
|
def self.hidden_cmds(slave = '')
|
|
__getip.hidden_cmds(slave)
|
|
end
|
|
|
|
def invoke_hidden(slave, cmd, *args)
|
|
if args[-1].kind_of?(Hash)
|
|
keys = _symbolkey2str(args.pop)
|
|
else
|
|
keys = []
|
|
end
|
|
if Tk::TCL_MAJOR_VERSION > 8 ||
|
|
(Tk::TCL_MAJOR_VERSION == 8 && Tk::TCL_MINOR_VERSION >= 5)
|
|
keys << '--'
|
|
end
|
|
keys << _slavearg(slave) << cmd
|
|
keys.concat(args)
|
|
@interp._invoke('interp', 'invokehidden', *keys)
|
|
end
|
|
def self.invoke_hidden(slave, cmd, *args)
|
|
__getip.invoke_hidden(slave, cmd, *args)
|
|
end
|
|
|
|
def invoke_hidden_on_global(slave, cmd, *args)
|
|
if args[-1].kind_of?(Hash)
|
|
keys = _symbolkey2str(args.pop)
|
|
else
|
|
keys = []
|
|
end
|
|
keys << '-global'
|
|
if Tk::TCL_MAJOR_VERSION > 8 ||
|
|
(Tk::TCL_MAJOR_VERSION == 8 && Tk::TCL_MINOR_VERSION >= 5)
|
|
keys << '--'
|
|
end
|
|
keys << _slavearg(slave) << cmd
|
|
keys.concat(args)
|
|
@interp._invoke('interp', 'invokehidden', *keys)
|
|
end
|
|
def self.invoke_hidden_on_global(slave, cmd, *args)
|
|
__getip.invoke_hidden_on_global(slave, cmd, *args)
|
|
end
|
|
|
|
def mark_trusted(slave = '')
|
|
@interp._invoke('interp', 'marktrusted', _slavearg(slave))
|
|
self
|
|
end
|
|
def self.mark_trusted(slave = '')
|
|
__getip.mark_trusted(slave)
|
|
self
|
|
end
|
|
|
|
def set_bgerror_handler(cmd = Proc.new, slave = nil, &b)
|
|
unless TkComm._callback_entry?(cmd)
|
|
if !slave && b
|
|
slave = cmd
|
|
cmd = Proc.new(&b)
|
|
end
|
|
end
|
|
slave = '' unless slave
|
|
|
|
@interp._invoke('interp', 'bgerror', _slavearg(slave), cmd)
|
|
end
|
|
def self.bgerror(cmd = Proc.new, slave = nil, &b)
|
|
__getip.bgerror(cmd, slave, &b)
|
|
end
|
|
|
|
def get_bgerror_handler(slave = '')
|
|
procedure(@interp._invoke('interp', 'bgerror', _slavearg(slave)))
|
|
end
|
|
def self.bgerror(slave = '')
|
|
__getip.bgerror(slave)
|
|
end
|
|
|
|
def set_limit(limit_type, slave = '', opts = {})
|
|
@interp._invoke('interp', 'limit', _slavearg(slave), limit_type, opts)
|
|
end
|
|
def self.set_limit(limit_type, slave = '', opts = {})
|
|
__getip.set_limit(limit_type, slave, opts)
|
|
end
|
|
|
|
def get_limit(limit_type, slave = '', slot = nil)
|
|
if slot
|
|
num_or_str(@interp._invoke('interp', 'limit', _slavearg(slave),
|
|
limit_type, slot))
|
|
else
|
|
l = @interp._split_tklist(@interp._invoke_without_enc('interp', 'limit',
|
|
_slavearg(slave),
|
|
limit_type))
|
|
l.map!{|s| _fromUTF8(s)}
|
|
r = {}
|
|
until l.empty?
|
|
key = l.shift[1..-1]
|
|
val = l.shift
|
|
val = num_or_str(val) if val
|
|
r[key] = val
|
|
end
|
|
r
|
|
end
|
|
end
|
|
def self.get_limit(limit_type, slave = '', slot = nil)
|
|
__getip.get_limit(limit_type, slave, slot)
|
|
end
|
|
|
|
def recursion_limit(slave = '', limit = None)
|
|
number(@interp._invoke('interp', 'recursionlimit',
|
|
_slavearg(slave), limit))
|
|
end
|
|
def self.recursion_limit(slave = '', limit = None)
|
|
__getip.recursion_limit(slave)
|
|
end
|
|
|
|
def alias_target(aliascmd, slave = '')
|
|
@interp._invoke('interp', 'target', _slavearg(slave), aliascmd)
|
|
end
|
|
def self.alias_target(aliascmd, slave = '')
|
|
__getip.alias_target(aliascmd, slave)
|
|
end
|
|
|
|
def share_stdin(dist, src = '')
|
|
@interp._invoke('interp', 'share', src, 'stdin', dist)
|
|
self
|
|
end
|
|
def self.share_stdin(dist, src = '')
|
|
__getip.share_stdin(dist, src)
|
|
self
|
|
end
|
|
|
|
def share_stdout(dist, src = '')
|
|
@interp._invoke('interp', 'share', src, 'stdout', dist)
|
|
self
|
|
end
|
|
def self.share_stdout(dist, src = '')
|
|
__getip.share_stdout(dist, src)
|
|
self
|
|
end
|
|
|
|
def share_stderr(dist, src = '')
|
|
@interp._invoke('interp', 'share', src, 'stderr', dist)
|
|
self
|
|
end
|
|
def self.share_stderr(dist, src = '')
|
|
__getip.share_stderr(dist, src)
|
|
self
|
|
end
|
|
|
|
def transfer_stdin(dist, src = '')
|
|
@interp._invoke('interp', 'transfer', src, 'stdin', dist)
|
|
self
|
|
end
|
|
def self.transfer_stdin(dist, src = '')
|
|
__getip.transfer_stdin(dist, src)
|
|
self
|
|
end
|
|
|
|
def transfer_stdout(dist, src = '')
|
|
@interp._invoke('interp', 'transfer', src, 'stdout', dist)
|
|
self
|
|
end
|
|
def self.transfer_stdout(dist, src = '')
|
|
__getip.transfer_stdout(dist, src)
|
|
self
|
|
end
|
|
|
|
def transfer_stderr(dist, src = '')
|
|
@interp._invoke('interp', 'transfer', src, 'stderr', dist)
|
|
self
|
|
end
|
|
def self.transfer_stderr(dist, src = '')
|
|
__getip.transfer_stderr(dist, src)
|
|
self
|
|
end
|
|
|
|
def share_stdio(dist, src = '')
|
|
@interp._invoke('interp', 'share', src, 'stdin', dist)
|
|
@interp._invoke('interp', 'share', src, 'stdout', dist)
|
|
@interp._invoke('interp', 'share', src, 'stderr', dist)
|
|
self
|
|
end
|
|
def self.share_stdio(dist, src = '')
|
|
__getip.share_stdio(dist, src)
|
|
self
|
|
end
|
|
|
|
def transfer_stdio(dist, src = '')
|
|
@interp._invoke('interp', 'transfer', src, 'stdin', dist)
|
|
@interp._invoke('interp', 'transfer', src, 'stdout', dist)
|
|
@interp._invoke('interp', 'transfer', src, 'stderr', dist)
|
|
self
|
|
end
|
|
def self.transfer_stdio(dist, src = '')
|
|
__getip.transfer_stdio(dist, src)
|
|
self
|
|
end
|
|
end
|
|
|
|
|
|
# Safe Base :: manipulating safe interpreter
|
|
class MultiTkIp
|
|
def safeip_configure(slot, value=None)
|
|
# use for '-noStatics' option ==> {statics=>false}
|
|
# for '-nestedLoadOk' option ==> {nested=>true}
|
|
if slot.kind_of?(Hash)
|
|
ip = MultiTkIp.__getip
|
|
ip._eval('::safe::interpConfigure ' + @ip_name + ' ' + _keys2opts(slot))
|
|
else
|
|
ip._eval('::safe::interpConfigure ' + @ip_name + ' ' +
|
|
"-#{slot} #{_get_eval_string(value)}")
|
|
end
|
|
self
|
|
end
|
|
|
|
def safeip_configinfo(slot = nil)
|
|
ip = MultiTkIp.__getip
|
|
ret = {}
|
|
if slot
|
|
conf = _lst2ary(ip._eval("::safe::interpConfigure " +
|
|
@ip_name + " -#{slot}"))
|
|
if conf[0] == '-deleteHook'
|
|
if conf[1] =~ /^rb_out\S* (c(_\d+_)?\d+)/
|
|
ret[conf[0][1..-1]] = MultiTkIp._tk_cmd_tbl[$1]
|
|
else
|
|
ret[conf[0][1..-1]] = conf[1]
|
|
end
|
|
else
|
|
ret[conf[0][1..-1]] = conf[1]
|
|
end
|
|
else
|
|
Hash[*_lst2ary(ip._eval("::safe::interpConfigure " +
|
|
@ip_name))].each{|k, v|
|
|
if k == '-deleteHook'
|
|
if v =~ /^rb_out\S* (c(_\d+_)?\d+)/
|
|
ret[k[1..-1]] = MultiTkIp._tk_cmd_tbl[$1]
|
|
else
|
|
ret[k[1..-1]] = v
|
|
end
|
|
else
|
|
ret[k[1..-1]] = v
|
|
end
|
|
}
|
|
end
|
|
ret
|
|
end
|
|
|
|
def safeip_delete
|
|
ip = MultiTkIp.__getip
|
|
ip._eval("::safe::interpDelete " + @ip_name)
|
|
end
|
|
|
|
def safeip_add_to_access_path(dir)
|
|
ip = MultiTkIp.__getip
|
|
ip._eval("::safe::interpAddToAccessPath #{@ip_name} #{dir}")
|
|
end
|
|
|
|
def safeip_find_in_access_path(dir)
|
|
ip = MultiTkIp.__getip
|
|
ip._eval("::safe::interpFindInAccessPath #{@ip_name} #{dir}")
|
|
end
|
|
|
|
def safeip_set_log_cmd(cmd = Proc.new)
|
|
ip = MultiTkIp.__getip
|
|
ip._eval("::safe::setLogCmd #{@ip_name} #{_get_eval_string(cmd)}")
|
|
end
|
|
end
|
|
|
|
|
|
# encoding convert
|
|
class MultiTkIp
|
|
def encoding
|
|
@interp.encoding
|
|
end
|
|
def encoding=(enc)
|
|
@interp.encoding = enc
|
|
end
|
|
|
|
def encoding_convertfrom(str, enc=None)
|
|
@interp.encoding_convertfrom(str, enc)
|
|
end
|
|
alias encoding_convert_from encoding_convertfrom
|
|
|
|
def encoding_convertto(str, enc=None)
|
|
@interp.encoding_convertto(str, enc)
|
|
end
|
|
alias encoding_convert_to encoding_convertto
|
|
end
|
|
|
|
|
|
# remove methods for security
|
|
class MultiTkIp
|
|
# undef_method :instance_eval
|
|
undef_method :instance_variable_get
|
|
undef_method :instance_variable_set
|
|
end
|
|
|
|
|
|
# end of MultiTkIp definition
|
|
|
|
# defend against modification
|
|
#MultiTkIp.freeze
|
|
#TclTkLib.freeze
|
|
|
|
########################################
|
|
# start Tk which depends on MultiTkIp
|
|
module TkCore
|
|
INTERP = MultiTkIp
|
|
end
|
|
require 'tk'
|