File: tk/optiondb.rb

Overview
Module Structure
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: TkOptionDB#6
extends
  Tk   
includes
  Tk   
has properties
constant: TkCommandNames #10
constant: CmdClassID #11
method: mutex #13
function: add / 3 #24
function: clear #30
function: get / 3 #36
function: readfile / 2 #39
alias: read_file readfile #42
function: read_entries / 2 #45
function: read_with_encoding / 3 #90
function: __create_new_class / 5 #254
function: __remove_methods_of_proc_class / 1 #309
constant: RAND_BASE_CNT #330
constant: RAND_BASE_HEAD #331
constant: RAND_BASE_CHAR #332
function: __get_random_basename #333
function: new_proc_class / 6 #350
function: eval_under_random_base / 2 #359
function: new_proc_class_random / 5 #369
  module: Priority#17
has properties
constant: WidgetDefault #18
constant: StartupFile #19
constant: UserDefault #20
constant: Interactive #21

Code

   1  #
   2  # tk/optiondb.rb : treat option database
   3  #
   4  require 'tk'
   5 
   6  module TkOptionDB
   7    include Tk
   8    extend Tk
   9 
  10    TkCommandNames = ['option'.freeze].freeze
  11    (CmdClassID = ['CMD_CLASS'.freeze, '00000'.taint]).instance_eval{
  12      @mutex = Mutex.new
  13      def mutex; @mutex; end
  14      freeze
  15    }
  16 
  17    module Priority
  18      WidgetDefault = 20
  19      StartupFile   = 40
  20      UserDefault   = 60
  21      Interactive   = 80
  22    end
  23 
  24    def add(pat, value, pri=None)
  25      # if $SAFE >= 4
  26      #   fail SecurityError, "can't call 'TkOptionDB.add' at $SAFE >= 4"
  27      # end
  28      tk_call('option', 'add', pat, value, pri)
  29    end
  30    def clear
  31      # if $SAFE >= 4
  32      #   fail SecurityError, "can't call 'TkOptionDB.crear' at $SAFE >= 4"
  33      # end
  34      tk_call_without_enc('option', 'clear')
  35    end
  36    def get(win, name, klass)
  37      tk_call('option', 'get', win ,name, klass)
  38    end
  39    def readfile(file, pri=None)
  40      tk_call('option', 'readfile', file, pri)
  41    end
  42    alias read_file readfile
  43    module_function :add, :clear, :get, :readfile, :read_file
  44 
  45    def read_entries(file, f_enc=nil)
  46      if TkCore::INTERP.safe?
  47        fail SecurityError, 
  48          "can't call 'TkOptionDB.read_entries' on a safe interpreter"
  49      end
  50 
  51      i_enc = ((Tk.encoding)? Tk.encoding : Tk.encoding_system)
  52 
  53      unless f_enc
  54        f_enc = i_enc
  55      end
  56 
  57      ent = []
  58      cline = ''
  59      open(file, 'r') {|f|
  60        while line = f.gets
  61          #cline += line.chomp!
  62          cline.concat(line.chomp!)
  63          case cline
  64          when /\\$/    # continue
  65            cline.chop!
  66            next
  67          when /^\s*(!|#)/     # coment
  68            cline = ''
  69            next
  70          when /^([^:]+):(.*)$/
  71            pat = $1.strip
  72            val = $2.lstrip
  73            p "ResourceDB: #{[pat, val].inspect}" if $DEBUG
  74            pat = TkCore::INTERP._toUTF8(pat, f_enc)
  75            pat = TkCore::INTERP._fromUTF8(pat, i_enc)
  76            val = TkCore::INTERP._toUTF8(val, f_enc)
  77            val = TkCore::INTERP._fromUTF8(val, i_enc)
  78            ent << [pat, val]
  79            cline = ''
  80          else          # unknown --> ignore
  81            cline = ''
  82            next
  83          end
  84        end
  85      }
  86      ent
  87    end
  88    module_function :read_entries
  89        
  90    def read_with_encoding(file, f_enc=nil, pri=None)
  91      # try to read the file as an OptionDB file
  92      read_entries(file, f_enc).each{|pat, val|
  93        add(pat, val, pri)
  94      }
  95 
  96  =begin
  97      i_enc = Tk.encoding()
  98 
  99      unless f_enc
 100        f_enc = i_enc
 101      end
 102 
 103      cline = ''
 104      open(file, 'r') {|f|
 105        while line = f.gets
 106          cline += line.chomp!
 107          case cline
 108          when /\\$/    # continue
 109            cline.chop!
 110            next
 111          when /^\s*!/     # coment
 112            cline = ''
 113            next
 114          when /^([^:]+):\s(.*)$/
 115            pat = $1
 116            val = $2
 117            p "ResourceDB: #{[pat, val].inspect}" if $DEBUG
 118            pat = TkCore::INTERP._toUTF8(pat, f_enc)
 119            pat = TkCore::INTERP._fromUTF8(pat, i_enc)
 120            val = TkCore::INTERP._toUTF8(val, f_enc)
 121            val = TkCore::INTERP._fromUTF8(val, i_enc)
 122            add(pat, val, pri)
 123            cline = ''
 124          else          # unknown --> ignore
 125            cline = ''
 126            next
 127          end
 128        end
 129      }
 130  =end
 131    end
 132    module_function :read_with_encoding
 133 
 134    # support procs on the resource database
 135    @@resource_proc_class = Class.new
 136 
 137    @@resource_proc_class.const_set(:CARRIER, '.'.freeze)
 138 
 139    @@resource_proc_class.instance_variable_set('@method_tbl', 
 140                                                TkCore::INTERP.create_table)
 141    @@resource_proc_class.instance_variable_set('@add_method', false)
 142    @@resource_proc_class.instance_variable_set('@safe_mode', 4)
 143 
 144    class << @@resource_proc_class
 145      private :new
 146 
 147  =begin 
 148      CARRIER    = '.'.freeze
 149      METHOD_TBL = TkCore::INTERP.create_table
 150      ADD_METHOD = false
 151      SAFE_MODE  = 4
 152  =end
 153 
 154  =begin
 155      def __closed_block_check__(str)
 156        depth = 0
 157        str.scan(/[{}]/){|x|
 158          if x == "{"
 159            depth += 1
 160          elsif x == "}"
 161            depth -= 1
 162          end
 163          if depth <= 0 && !($' =~ /\A\s*\Z/)
 164            fail RuntimeError, "bad string for procedure : #{str.inspect}"
 165          end
 166        }
 167        str
 168      end
 169      private :__closed_block_check__
 170  =end
 171 
 172      def __check_proc_string__(str)
 173        # If you want to check the proc_string, do it in this method.
 174        # Please define this in the block given to 'new_proc_class' method. 
 175        str
 176      end
 177 
 178      def method_missing(id, *args)
 179        #res_proc, proc_str = self::METHOD_TBL[id]
 180        res_proc, proc_str = @method_tbl[id]
 181 
 182        proc_source = TkOptionDB.get(self::CARRIER, id.id2name, '').strip
 183        res_proc = nil if proc_str != proc_source # resource is changed
 184 
 185        # unless res_proc.kind_of?(Proc)
 186        unless TkComm._callback_entry?(res_proc)
 187          #if id == :new || !(self::METHOD_TBL.has_key?(id) || self::ADD_METHOD)
 188          if id == :new || !(@method_tbl.has_key?(id) || @add_method)
 189            raise NoMethodError, 
 190                  "not support resource-proc '#{id.id2name}' for #{self.name}"
 191          end
 192          proc_str = proc_source
 193          proc_str = '{' + proc_str + '}' unless /\A\{.*\}\Z/ =~ proc_str
 194          #proc_str = __closed_block_check__(proc_str)
 195          proc_str = __check_proc_string__(proc_str)
 196          res_proc = proc{ 
 197            begin
 198              #eval("$SAFE = #{self::SAFE_MODE};\nProc.new" + proc_str)
 199              eval("$SAFE = #{@safe_mode};\nProc.new" + proc_str)
 200            rescue SyntaxError=>err
 201              raise SyntaxError, 
 202                TkCore::INTERP._toUTF8(err.message.gsub(/\(eval\):\d:/, 
 203                                                        "(#{id.id2name}):"))
 204            end
 205          }.call
 206          #self::METHOD_TBL[id] = [res_proc, proc_source]
 207          @method_tbl[id] = [res_proc, proc_source]
 208        end
 209        res_proc.call(*args)
 210      end
 211 
 212      private :__check_proc_string__, :method_missing
 213    end
 214    @@resource_proc_class.freeze
 215 
 216  =begin
 217    def __create_new_class(klass, func, safe = 4, add = false, parent = nil)
 218      klass = klass.to_s if klass.kind_of? Symbol
 219      unless (?A..?Z) === klass[0]
 220        fail ArgumentError, "bad string '#{klass}' for class name"
 221      end
 222      unless func.kind_of? Array
 223        fail ArgumentError, "method-list must be Array"
 224      end
 225      func_str = func.join(' ')
 226      if parent == nil
 227        install_win(parent)
 228      elsif parent <= @@resource_proc_class
 229        install_win(parent::CARRIER)
 230      else
 231        fail ArgumentError, "parent must be Resource-Proc class"
 232      end
 233      carrier = Tk.tk_call_without_enc('frame', @path, '-class', klass)
 234 
 235      body = <<-"EOD"
 236        class #{klass} < TkOptionDB.module_eval('@@resource_proc_class')
 237          CARRIER    = '#{carrier}'.freeze
 238          METHOD_TBL = TkCore::INTERP.create_table
 239          ADD_METHOD = #{add}
 240          SAFE_MODE  = #{safe}
 241          %w(#{func_str}).each{|f| METHOD_TBL[f.intern] = nil }
 242        end
 243      EOD
 244 
 245      if parent.kind_of?(Class) && parent <= @@resource_proc_class
 246        parent.class_eval(body)
 247        eval(parent.name + '::' + klass)
 248      else
 249        eval(body)
 250        eval('TkOptionDB::' + klass)
 251      end
 252    end
 253  =end
 254    def __create_new_class(klass, func, safe = 4, add = false, parent = nil)
 255      if klass.kind_of?(TkWindow)
 256        carrier = klass.path
 257        CmdClassID.mutex.synchronize{
 258          klass = CmdClassID.join(TkCore::INTERP._ip_id_)
 259          CmdClassID[1].succ!
 260        }
 261        parent = nil # ignore parent
 262      else
 263        klass = klass.to_s if klass.kind_of?(Symbol)
 264        unless (?A..?Z) === klass[0]
 265          fail ArgumentError, "bad string '#{klass}' for class name"
 266        end
 267        if parent == nil
 268          install_win(nil)
 269        elsif parent.kind_of?(TkWindow)
 270          install_win(parent.path)
 271        elsif parent <= @@resource_proc_class
 272          install_win(parent::CARRIER)
 273        else
 274          fail ArgumentError, "parent must be Resource-Proc class"
 275        end
 276        carrier = Tk.tk_call_without_enc('frame', @path, '-class', klass)
 277      end
 278 
 279      unless func.kind_of?(Array)
 280        fail ArgumentError, "method-list must be Array"
 281      end
 282      func_str = func.join(' ')
 283 
 284      if parent.kind_of?(Class) && parent <= @@resource_proc_class
 285        cmd_klass = Class.new(parent)
 286      else
 287        cmd_klass = Class.new(TkOptionDB.module_eval('@@resource_proc_class'))
 288      end
 289      cmd_klass.const_set(:CARRIER, carrier.dup.freeze)
 290 
 291      cmd_klass.instance_variable_set('@method_tbl', TkCore::INTERP.create_table)
 292      cmd_klass.instance_variable_set('@add_method', add)
 293      cmd_klass.instance_variable_set('@safe_mode', safe)
 294      func.each{|f| 
 295        cmd_klass.instance_variable_get('@method_tbl')[f.to_s.intern] = nil
 296      }
 297  =begin
 298      cmd_klass.const_set(:METHOD_TBL, TkCore::INTERP.create_table)
 299      cmd_klass.const_set(:ADD_METHOD, add)
 300      cmd_klass.const_set(:SAFE_MODE, safe)
 301      func.each{|f| cmd_klass::METHOD_TBL[f.to_s.intern] = nil }
 302  =end
 303 
 304      cmd_klass
 305    end
 306    module_function :__create_new_class
 307    private_class_method :__create_new_class
 308 
 309    def __remove_methods_of_proc_class(klass)
 310      # for security, make these methods invalid
 311      class << klass
 312        def __null_method(*args); nil; end
 313        [ :class_eval, :name, :superclass, :clone, :dup, :autoload, :autoload?, 
 314          :ancestors, :const_defined?, :const_get, :const_set, :const_missing, 
 315          :class_variables, :constants, :included_modules, :instance_methods, 
 316          :method_defined?, :module_eval, :private_instance_methods, 
 317          :protected_instance_methods, :public_instance_methods, 
 318          :singleton_methods, :remove_const, :remove_method, :undef_method, 
 319          :to_s, :inspect, :display, :method, :methods, :respond_to?, 
 320          :instance_variable_get, :instance_variable_set, :instance_method, 
 321          :instance_eval, :instance_exec, :instance_variables, :kind_of?, :is_a?,
 322          :private_methods, :protected_methods, :public_methods ].each{|m|
 323          alias_method(m, :__null_method)
 324        }
 325      end
 326    end
 327    module_function :__remove_methods_of_proc_class
 328    private_class_method :__remove_methods_of_proc_class
 329 
 330    RAND_BASE_CNT = [0]
 331    RAND_BASE_HEAD = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 332    RAND_BASE_CHAR = RAND_BASE_HEAD + 'abcdefghijklmnopqrstuvwxyz0123456789_'
 333    def __get_random_basename
 334      name = '%s%03d' % [RAND_BASE_HEAD[rand(RAND_BASE_HEAD.size),1], 
 335                         RAND_BASE_CNT[0]]
 336      len = RAND_BASE_CHAR.size
 337      (6+rand(10)).times{
 338        name << RAND_BASE_CHAR[rand(len),1]
 339      }
 340      RAND_BASE_CNT[0] = RAND_BASE_CNT[0] + 1
 341      name
 342    end
 343    module_function :__get_random_basename
 344    private_class_method :__get_random_basename
 345 
 346    # define new proc class :
 347    # If you want to modify the new class or create a new subclass, 
 348    # you must do such operation in the block parameter. 
 349    # Because the created class is flozen after evaluating the block. 
 350    def new_proc_class(klass, func, safe = 4, add = false, parent = nil, &b)
 351      new_klass = __create_new_class(klass, func, safe, add, parent)
 352      new_klass.class_eval(&b) if block_given?
 353      __remove_methods_of_proc_class(new_klass)
 354      new_klass.freeze
 355      new_klass
 356    end
 357    module_function :new_proc_class
 358 
 359    def eval_under_random_base(parent = nil, &b)
 360      new_klass = __create_new_class(__get_random_basename(), 
 361                                     [], 4, false, parent)
 362      ret = new_klass.class_eval(&b) if block_given?
 363      __remove_methods_of_proc_class(new_klass)
 364      new_klass.freeze
 365      ret
 366    end
 367    module_function :eval_under_random_base
 368 
 369    def new_proc_class_random(klass, func, safe = 4, add = false, &b)
 370      eval_under_random_base(){
 371        TkOptionDB.new_proc_class(klass, func, safe, add, self, &b)
 372      }
 373    end
 374    module_function :new_proc_class_random
 375  end
 376  TkOption = TkOptionDB
 377  TkResourceDB = TkOptionDB