mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
tk.rb :
* TkToplevel, TkFrame, TkPanedwindow, TkOptionDB : bug fix * TkOptionDB : make it more secure to use procs defined on resourceDB sample/tkoptdb.rb, sample/resource.ja, sample/resource.en : * sample script how to use TkOptionDB. resource.ja and resource.en are samples of resource definition file which are read by tkoptdb.rb. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@3998 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
b60ba59429
commit
17e1936d8b
5 changed files with 203 additions and 21 deletions
|
@ -27,5 +27,8 @@ sample/tkfrom.rb
|
||||||
sample/tkhello.rb
|
sample/tkhello.rb
|
||||||
sample/tkline.rb
|
sample/tkline.rb
|
||||||
sample/tkmenubutton.rb
|
sample/tkmenubutton.rb
|
||||||
|
sample/tkoptdb.rb
|
||||||
|
sample/resource.ja
|
||||||
|
sample/resource.en
|
||||||
sample/tktimer.rb
|
sample/tktimer.rb
|
||||||
sample/tktimer2.rb
|
sample/tktimer2.rb
|
||||||
|
|
146
ext/tk/lib/tk.rb
146
ext/tk/lib/tk.rb
|
@ -2682,20 +2682,37 @@ module TkOptionDB
|
||||||
ADD_METHOD = false
|
ADD_METHOD = false
|
||||||
SAFE_MODE = 4
|
SAFE_MODE = 4
|
||||||
|
|
||||||
|
def __closed_block_check__(str)
|
||||||
|
depth = 0
|
||||||
|
str.scan(/[{}]/){|x|
|
||||||
|
if x == "{"
|
||||||
|
depth += 1
|
||||||
|
elsif x == "}"
|
||||||
|
depth -= 1
|
||||||
|
end
|
||||||
|
if depth <= 0 && !($' =~ /\A\s*\Z/)
|
||||||
|
fail RuntimeError, "bad string for procedure : #{str.inspect}"
|
||||||
|
end
|
||||||
|
}
|
||||||
|
str
|
||||||
|
end
|
||||||
|
|
||||||
def __check_proc_string__(str)
|
def __check_proc_string__(str)
|
||||||
# If you want to check the proc_string, do it in this method.
|
# If you want to check the proc_string, do it in this method.
|
||||||
|
# Please define this in the block given to 'new_proc_class' method.
|
||||||
str
|
str
|
||||||
end
|
end
|
||||||
|
|
||||||
def method_missing(id, *args)
|
def method_missing(id, *args)
|
||||||
res_proc = self::METHOD_TBL[id]
|
res_proc = self::METHOD_TBL[id]
|
||||||
unless res_proc.kind_of? Proc
|
unless res_proc.kind_of? Proc
|
||||||
if id == :new || (!self::METHOD_TBL.has_key?(id) && !self::ADD_METHOD)
|
if id == :new || !(self::METHOD_TBL.has_key?(id) || self::ADD_METHOD)
|
||||||
raise NoMethodError,
|
raise NoMethodError,
|
||||||
"not support resource-proc '#{id.id2name}' for #{self.name}"
|
"not support resource-proc '#{id.id2name}' for #{self.name}"
|
||||||
end
|
end
|
||||||
proc_str = TkOptionDB.get(self::CARRIER, id.id2name, '')
|
proc_str = TkOptionDB.get(self::CARRIER, id.id2name, '').strip
|
||||||
proc_str = '{' + proc_str + '}' unless /\A\{.*\}\Z/ =~ proc_str
|
proc_str = '{' + proc_str + '}' unless /\A\{.*\}\Z/ =~ proc_str
|
||||||
|
proc_str = __closed_block_check__(proc_str)
|
||||||
proc_str = __check_proc_string__(proc_str)
|
proc_str = __check_proc_string__(proc_str)
|
||||||
res_proc = eval 'Proc.new' + proc_str
|
res_proc = eval 'Proc.new' + proc_str
|
||||||
self::METHOD_TBL[id] = res_proc
|
self::METHOD_TBL[id] = res_proc
|
||||||
|
@ -2706,10 +2723,11 @@ module TkOptionDB
|
||||||
}.call
|
}.call
|
||||||
end
|
end
|
||||||
|
|
||||||
private :__check_proc_string__, :method_missing
|
private :__closed_block_check__, :__check_proc_string__, :method_missing
|
||||||
end
|
end
|
||||||
|
@@resource_proc_class.freeze
|
||||||
|
|
||||||
def new_proc_class(klass, func, safe = 4, add = false, parent = nil)
|
def __create_new_class(klass, func, safe = 4, add = false, parent = nil)
|
||||||
klass = klass.to_s if klass.kind_of? Symbol
|
klass = klass.to_s if klass.kind_of? Symbol
|
||||||
unless (?A..?Z) === klass[0]
|
unless (?A..?Z) === klass[0]
|
||||||
fail ArgumentError, "bad string '#{klass}' for class name"
|
fail ArgumentError, "bad string '#{klass}' for class name"
|
||||||
|
@ -2733,21 +2751,86 @@ module TkOptionDB
|
||||||
METHOD_TBL = {}
|
METHOD_TBL = {}
|
||||||
ADD_METHOD = #{add}
|
ADD_METHOD = #{add}
|
||||||
SAFE_MODE = #{safe}
|
SAFE_MODE = #{safe}
|
||||||
%w(#{func_str}).each{|f| METHOD_TBL.delete(f.intern) }
|
%w(#{func_str}).each{|f| METHOD_TBL[f.intern] = nil }
|
||||||
end
|
end
|
||||||
EOD
|
EOD
|
||||||
|
|
||||||
if parent.kind_of?(Class) && parent <= @@resource_proc_class
|
if parent.kind_of?(Class) && parent <= @@resource_proc_class
|
||||||
parent.class_eval body
|
parent.class_eval(body)
|
||||||
eval parent.name + '::' + klass
|
eval(parent.name + '::' + klass)
|
||||||
else
|
else
|
||||||
eval body
|
eval(body)
|
||||||
eval 'TkOptionDB::' + klass
|
eval('TkOptionDB::' + klass)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
module_function :__create_new_class
|
||||||
|
private_class_method :__create_new_class
|
||||||
|
|
||||||
|
def __remove_methods_of_proc_class(klass)
|
||||||
|
# for security, make these methods invalid
|
||||||
|
class << klass
|
||||||
|
attr_reader :class_eval, :name, :superclass,
|
||||||
|
:ancestors, :const_defined?, :const_get, :const_set,
|
||||||
|
:constants, :included_modules, :instance_methods,
|
||||||
|
:method_defined?, :module_eval, :private_instance_methods,
|
||||||
|
:protected_instance_methods, :public_instance_methods,
|
||||||
|
:remove_const, :remove_method, :undef_method,
|
||||||
|
:to_s, :inspect, :display, :method, :methods,
|
||||||
|
:instance_eval, :instance_variables, :kind_of?, :is_a?,
|
||||||
|
:private_methods, :protected_methods, :public_methods
|
||||||
|
end
|
||||||
|
end
|
||||||
|
module_function :__remove_methods_of_proc_class
|
||||||
|
private_class_method :__remove_methods_of_proc_class
|
||||||
|
|
||||||
|
RAND_BASE_CNT = [0]
|
||||||
|
RAND_BASE_HEAD = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
|
RAND_BASE_CHAR = RAND_BASE_HEAD + 'abcdefghijklmnopqrstuvwxyz0123456789_'
|
||||||
|
def __get_random_basename
|
||||||
|
name = '%s%03d' % [RAND_BASE_HEAD[rand(RAND_BASE_HEAD.size),1],
|
||||||
|
RAND_BASE_CNT[0]]
|
||||||
|
len = RAND_BASE_CHAR.size
|
||||||
|
(6+rand(10)).times{
|
||||||
|
name << RAND_BASE_CHAR[rand(len),1]
|
||||||
|
}
|
||||||
|
RAND_BASE_CNT[0] = RAND_BASE_CNT[0] + 1
|
||||||
|
name
|
||||||
|
end
|
||||||
|
module_function :__get_random_basename
|
||||||
|
private_class_method :__get_random_basename
|
||||||
|
|
||||||
|
# define new proc class :
|
||||||
|
# If you want to modify the new class or create a new subclass,
|
||||||
|
# you must do such operation in the block parameter.
|
||||||
|
# Because the created class is flozen after evaluating the block.
|
||||||
|
def new_proc_class(klass, func, safe = 4, add = false, parent = nil, &b)
|
||||||
|
new_klass = __create_new_class(klass, func, safe, add, parent)
|
||||||
|
new_klass.class_eval(&b) if block_given?
|
||||||
|
__remove_methods_of_proc_class(new_klass)
|
||||||
|
new_klass.freeze
|
||||||
|
new_klass
|
||||||
|
end
|
||||||
module_function :new_proc_class
|
module_function :new_proc_class
|
||||||
|
|
||||||
|
def eval_under_random_base(parent = nil, &b)
|
||||||
|
new_klass = __create_new_class(__get_random_basename(),
|
||||||
|
[], 4, false, parent)
|
||||||
|
ret = new_klass.class_eval(&b) if block_given?
|
||||||
|
__remove_methods_of_proc_class(new_klass)
|
||||||
|
new_klass.freeze
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
module_function :eval_under_random_base
|
||||||
|
|
||||||
|
def new_proc_class_random(klass, func, safe = 4, add = false, &b)
|
||||||
|
eval_under_random_base(){
|
||||||
|
TkOption.new_proc_class(klass, func, safe, add, self, &b)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
module_function :new_proc_class_random
|
||||||
end
|
end
|
||||||
TkOption = TkOptionDB
|
TkOption = TkOptionDB
|
||||||
|
TkResourceDB = TkOptionDB
|
||||||
|
|
||||||
module TkTreatFont
|
module TkTreatFont
|
||||||
def font_configinfo
|
def font_configinfo
|
||||||
|
@ -3638,7 +3721,6 @@ class TkToplevel<TkWindow
|
||||||
keys['class'] = keys.delete('classname')
|
keys['class'] = keys.delete('classname')
|
||||||
end
|
end
|
||||||
@classname = keys['class']
|
@classname = keys['class']
|
||||||
@screen = keys['screen']
|
|
||||||
@colormap = keys['colormap']
|
@colormap = keys['colormap']
|
||||||
@container = keys['container']
|
@container = keys['container']
|
||||||
@screen = keys['screen']
|
@screen = keys['screen']
|
||||||
|
@ -3647,8 +3729,11 @@ class TkToplevel<TkWindow
|
||||||
if @classname.kind_of? TkBindTag
|
if @classname.kind_of? TkBindTag
|
||||||
@db_class = @classname
|
@db_class = @classname
|
||||||
@classname = @classname.id
|
@classname = @classname.id
|
||||||
else
|
elsif @classname
|
||||||
@db_class = TkDatabaseClass.new(@classname)
|
@db_class = TkDatabaseClass.new(@classname)
|
||||||
|
else
|
||||||
|
@db_class = self.class
|
||||||
|
@classname = @db_class::WidgetClassName
|
||||||
end
|
end
|
||||||
keys, cmds = _wm_command_option_chk(keys)
|
keys, cmds = _wm_command_option_chk(keys)
|
||||||
super(keys)
|
super(keys)
|
||||||
|
@ -3661,33 +3746,47 @@ class TkToplevel<TkWindow
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if screen.kind_of? Hash
|
if screen.kind_of? Hash
|
||||||
keys = screen
|
keys = screen
|
||||||
else
|
else
|
||||||
@screen = screen
|
@screen = screen
|
||||||
end
|
if classname.kind_of? Hash
|
||||||
|
keys = classname
|
||||||
|
else
|
||||||
@classname = classname
|
@classname = classname
|
||||||
|
end
|
||||||
|
end
|
||||||
if keys.kind_of? Hash
|
if keys.kind_of? Hash
|
||||||
keys = _symbolkey2str(keys)
|
keys = _symbolkey2str(keys)
|
||||||
if keys.key?('classname')
|
if keys.key?('classname')
|
||||||
keys['class'] = keys.delete('classname')
|
keys['class'] = keys.delete('classname')
|
||||||
end
|
end
|
||||||
@classname = keys['class']
|
@classname = keys['class'] unless @classname
|
||||||
@colormap = keys['colormap']
|
@colormap = keys['colormap']
|
||||||
@container = keys['container']
|
@container = keys['container']
|
||||||
@screen = keys['screen']
|
@screen = keys['screen'] unless @screen
|
||||||
@use = keys['use']
|
@use = keys['use']
|
||||||
@visual = keys['visual']
|
@visual = keys['visual']
|
||||||
end
|
end
|
||||||
if @classname.kind_of? TkBindTag
|
if @classname.kind_of? TkBindTag
|
||||||
@db_class = @classname
|
@db_class = @classname
|
||||||
@classname = @classname.id
|
@classname = @classname.id
|
||||||
else
|
elsif @classname
|
||||||
@db_class = TkDatabaseClass.new(@classname)
|
@db_class = TkDatabaseClass.new(@classname)
|
||||||
|
else
|
||||||
|
@db_class = self.class
|
||||||
|
@classname = @db_class::WidgetClassName
|
||||||
end
|
end
|
||||||
keys, cmds = _wm_command_option_chk(keys)
|
keys, cmds = _wm_command_option_chk(keys)
|
||||||
super(parent, keys)
|
super(parent, keys)
|
||||||
cmds.each{|k,v| self.send(k,v)}
|
cmds.each{|k,v|
|
||||||
|
if v.kind_of? Array
|
||||||
|
self.send(k,*v)
|
||||||
|
else
|
||||||
|
self.send(k,v)
|
||||||
|
end
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_self(keys)
|
def create_self(keys)
|
||||||
|
@ -3750,8 +3849,11 @@ class TkFrame<TkWindow
|
||||||
if @classname.kind_of? TkBindTag
|
if @classname.kind_of? TkBindTag
|
||||||
@db_class = @classname
|
@db_class = @classname
|
||||||
@classname = @classname.id
|
@classname = @classname.id
|
||||||
else
|
elsif @classname
|
||||||
@db_class = TkDatabaseClass.new(@classname)
|
@db_class = TkDatabaseClass.new(@classname)
|
||||||
|
else
|
||||||
|
@db_class = self.class
|
||||||
|
@classname = @db_class::WidgetClassName
|
||||||
end
|
end
|
||||||
super(keys)
|
super(keys)
|
||||||
end
|
end
|
||||||
|
@ -3812,7 +3914,7 @@ class TkPanedWindow<TkWindow
|
||||||
alias remove forget
|
alias remove forget
|
||||||
|
|
||||||
def identify(x, y)
|
def identify(x, y)
|
||||||
#########
|
list(tk_send('identify', x, y))
|
||||||
end
|
end
|
||||||
|
|
||||||
def proxy_coord
|
def proxy_coord
|
||||||
|
@ -3830,12 +3932,16 @@ class TkPanedWindow<TkWindow
|
||||||
def sash_coord(index)
|
def sash_coord(index)
|
||||||
list(tk_send('sash', 'coord', index))
|
list(tk_send('sash', 'coord', index))
|
||||||
end
|
end
|
||||||
|
def sash_dragto(index)
|
||||||
|
tk_send('sash', 'dragto', index, x, y)
|
||||||
|
self
|
||||||
|
end
|
||||||
def sash_mark(index, x, y)
|
def sash_mark(index, x, y)
|
||||||
tk_send('sash', 'mark', x, y)
|
tk_send('sash', 'mark', index, x, y)
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
def sash_place(index, x, y)
|
def sash_place(index, x, y)
|
||||||
tk_send('sash', 'place', x, y)
|
tk_send('sash', 'place', index, x, y)
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
12
ext/tk/sample/resource.en
Normal file
12
ext/tk/sample/resource.en
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#
|
||||||
|
# see Tcl/Tk's "options" manual for "Database Name" and "Database Class"
|
||||||
|
#
|
||||||
|
*BtnFrame.borderWidth: 5
|
||||||
|
*BtnFrame.relief: ridge
|
||||||
|
*BtnFrame.Button.background: wheat
|
||||||
|
*BtnFrame.Button.foreground: red
|
||||||
|
*hello.text: HELLO
|
||||||
|
*quit.text: QUIT
|
||||||
|
*BTN_CMD.show_msg: {|arg| print "($SAFE=#{$SAFE}) ";\
|
||||||
|
print "Hello!! This is a sample of #{arg}.\n"}
|
||||||
|
*BTN_CMD.bye_msg: {print "($SAFE=#{$SAFE}) Good-bye¡¥\n"}
|
12
ext/tk/sample/resource.ja
Normal file
12
ext/tk/sample/resource.ja
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#
|
||||||
|
# see Tcl/Tk's "options" manual for "Database Name" and "Database Class"
|
||||||
|
#
|
||||||
|
*BtnFrame.borderWidth: 5
|
||||||
|
*BtnFrame.relief: ridge
|
||||||
|
*BtnFrame.Button.background: wheat
|
||||||
|
*BtnFrame.Button.foreground: red
|
||||||
|
*hello.text: こんにちは
|
||||||
|
*quit.text: 終了
|
||||||
|
*BTN_CMD.show_msg: {|arg| print "($SAFE=#{$SAFE}) ";\
|
||||||
|
print "こんにちは!! #{arg} のサンプルです.\n"}
|
||||||
|
*BTN_CMD.bye_msg: {print "($SAFE=#{$SAFE}) さようなら.\n"}
|
49
ext/tk/sample/tkoptdb.rb
Normal file
49
ext/tk/sample/tkoptdb.rb
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
#
|
||||||
|
# sample script of TkOptionDB
|
||||||
|
#
|
||||||
|
# If 'LANG' environment variable's value is started by 'ja',
|
||||||
|
# then read Japanese resource data and display Japanese button text.
|
||||||
|
# In other case, read English resource data and display English text.
|
||||||
|
#
|
||||||
|
require "tk"
|
||||||
|
|
||||||
|
if ENV['LANG'] =~ /^ja/
|
||||||
|
# read Japanese resource
|
||||||
|
TkOptionDB.readfile(File.expand_path('resource.ja', File.dirname(__FILE__)))
|
||||||
|
else
|
||||||
|
# read English resource
|
||||||
|
TkOptionDB.readfile(File.expand_path('resource.en', File.dirname(__FILE__)))
|
||||||
|
end
|
||||||
|
|
||||||
|
# 'show_msg' and 'bye_msg' procedures can be defined on BTN_CMD resource.
|
||||||
|
# Those procedures are called under $SAFE==2
|
||||||
|
cmd = TkOptionDB.new_proc_class(:BTN_CMD, [:show_msg, :bye_msg], 2) {
|
||||||
|
# If you want to check resource string (str),
|
||||||
|
# please define __check_proc_string__(str) like this.
|
||||||
|
class << self
|
||||||
|
def __check_proc_string__(str)
|
||||||
|
print "($SAFE=#{$SAFE}) check!! str.tainted?::#{str.tainted?}"
|
||||||
|
str.untaint
|
||||||
|
print "==>#{str.tainted?} : "
|
||||||
|
str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
TkFrame.new(:class=>'BtnFrame'){|f|
|
||||||
|
pack(:padx=>5, :pady=>5)
|
||||||
|
TkButton.new(:parent=>f, :widgetname=>'hello'){
|
||||||
|
command proc{
|
||||||
|
print "($SAFE=#{$SAFE}) : "
|
||||||
|
cmd.show_msg(TkOptionDB.inspect)
|
||||||
|
}
|
||||||
|
pack(:fill=>:x, :padx=>10, :pady=>10)
|
||||||
|
}
|
||||||
|
TkButton.new(:command=>proc{print "($SAFE=#{$SAFE}) : "; cmd.bye_msg; exit},
|
||||||
|
:parent=>f, :widgetname=>'quit'){
|
||||||
|
pack(:fill=>:x, :padx=>10, :pady=>10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tk.mainloop
|
Loading…
Add table
Reference in a new issue