i
[chise/ruby.git] / chise / rbchise.rb
index 789dbe9..f12c238 100755 (executable)
 # "rbchise.so" ext compatible library by eto 2003-0317
 
 require "bdb"
-require "chise/config"
+require "pathname"
+require "fileutils"
+require "chise/util"
 
 module CHISE
   class DataSource
     NONE = 0
     Berkeley_DB = 1
 
-    def initialize(type = Berkeley_DB, location = nil)
-      @type, @location = type, location
-      @location = Config.instance.db_dir if @location.nil?
-      @fnames = {}
-      @cnames_names = {}
-      at_exit {
-       @fnames.each {|k, db| db.close }
-       @cnames.each {|k, db| db.close }
-      }
+    def initialize(type=Berkeley_DB, loc=nil, subtype=0, modemask=0755)
+      @type = type
+      loc = Config.instance.db_dir if loc.nil?
+      @location = loc.path
+      @subtype = subtype
+      @modemask = modemask
+      @fdb = {}
+      @cdb = {}
     end
+    attr_reader :type, :location, :subtype, :modemask
 
-    def get_feature(feature)
-      @fnames[feature] = open_feature(feature) if @fnames[feature].nil?
-      @fnames[feature] 
+    def get_feature(f)
+      @fdb[f] = FeatureTable.new(self, f) if @fdb[f].nil?
+      @fdb[f] 
     end
 
-    def decode_char(name, code_point)
-      ccs = get_ccs(name)
-      ccs.decode(code_point)
+    def get_ccs(ccs)
+      @cdb[ccs] = CCSTable.new(self, ccs) if @cdb[ccs].nil?
+      @cdb[ccs] 
     end
 
-    def get_ccs(name)
-      db = open(name, "system-char-id")
-      CCSTable.new(name, db)
+    def each_feature
+      dir = @location + "character/feature"
+      dir.each_entry {|f|
+       next if f.to_s == "." || f.to_s == ".."
+       f = f.unescape_win_filename
+       f = f.unescape
+       yield(f.to_s)
+      }
     end
 
-    def open_feature_table(feature)
-      db = open("system-char-id", feature)
-      FeatureTable.new(feature, db)
+    def load_feature(name, cid)
+      ft = get_feature(name)
+      return nil if ft.nil?
+      ft.get_value(cid)
     end
 
-    def open(from, to) # real_subtpe, accessmask, modemask
-      name = from+"/"+to
-      return @dbs[name] if @dbs[name]
-      file = @location+"/"+name
-      @dbs[name] = BDB::Hash.open(file, nil, 0)
+    def decode_char(ccs, code_point)
+      ct = get_ccs(ccs)
+      return nil if ct.nil?
+      ct.decode(code_point)
     end
   end
 
-  class AttributeTable # abstract class
+  module ChiseValue; end
+
+  class AttributeTable
+    def initialize(dir, cat, keytype, name, amask, mmask)
+      dbdir  = dir + cat + keytype
+      #FileUtils.mkdir_p(dbdir.to_s) unless dbdir.directory?
+      name = name.path
+      name = name.escape
+      name = name.escape_win_filename
+      path = dbdir + name
+#     qp path, amask, mmask
+      raise unless path.exist?
+#     @db = BDB::Hash.open(path.to_s, amask, mmask)
+      @db = BDB::Hash.open(path.to_s)
+      at_exit {
+       close
+      }
+    end
+
+    def close
+      return if @db.nil?
+      begin
+       @db.sync
+       @db.close
+      rescue
+      end
+    end
+
+    def get(k)
+      @db.get(k)
+    end
+
+    def put(k, v)
+      @db.put(k, v)
+    end
+
+    def each
+      @db.each {|k, v| yield(k, v) }
+    end
   end
-  
-  class CCSTable < AttributeTable
-    def initialize(ccs, db)
-      @ccs, @db = ccs, db
+
+  module TableAccessModule
+    def initialize(ds, name)
+      @ds, @name = ds, name
+      @db = nil
+      @access = 0
     end
 
-    def get_char(code_point)
-      @db.get(code_point)
+    def sync
+      @db.close if @db
+      @db = nil
+      @access = 0
     end
+    alias close sync
 
-    def put_char(code_point, cid)
-      @db.put(code_point, cid)
+    def setup_db_exec(writable, cat, key)
+      if writable
+       sync if @access & BDB::CREATE == 0
+       @access = BDB::CREATE
+      else
+       @access = BDB::RDONLY
+      end
+
+      return if @db
+
+      begin
+       @db = AttributeTable.new(@ds.location, cat, key,
+                                @name, @access, @ds.modemask)
+      rescue
+       @db = nil
+      end
+      #raise if @db.nil?
     end
   end
 
-  class FeatureTable < AttributeTable
-    def initialize(feature, db)
-      @feature, @db = feature, db
+  class FeatureTable
+    include ChiseValue
+    include TableAccessModule
+
+    def set_value(cid, value)
+      setup_db(true)
+      return nil if @db.nil?
+      key = format_char_id(cid)
+      @db.put(key, value)
     end
 
-    def get_value(char_id)
-      @db.get(char_id)
+    def get_value(cid)
+      setup_db
+      return nil if @db.nil?
+      key = format_char_id(cid)
+      @db.get(key)
     end
 
     def each
+      setup_db
+      return nil if @db.nil?
+      @db.each {|k, v|
+       cid = parse_c_string(k)
+       yield(cid, v)
+      }
+    end
+
+    private
+    def setup_db(writable=nil)
+      setup_db_exec(writable, "character", "feature")
+    end
+  end
+
+  class CCSTable
+    include ChiseValue
+    include TableAccessModule
+
+    def decode(code_point)
+      setup_db
+      k = code_point.to_s
+      v = @db.get(k)
+      return nil if v.nil?
+      cid = parse_c_string(v)
+      cid
+    end
+
+    def set_decoded_char(code_point, cid)
+      setup_db(true)
+      k = code_point.to_s
+      v = format_char_id(cid)
+      @db.put(k, v)
+    end
+
+    private
+    def setup_db(writable=nil)
+      setup_db_exec(writable, "character", "by_feature")
+    end
+  end
+
+  module ChiseValue
+    def parse_c_string(str)
+      i = 0
+      c = str[i]
+      i += 1
+      len = str.length
+
+      raise unless 2 <= len && c == ?\?
+
+      c = str[i]
+      i += 1
+
+      if (c == ?\\)
+       raise if (len < 3)
+       c = str[i]
+       i += 1
+       if (c == ?^)
+         raise if (len < 4)
+         c = str[i]
+         i += 1
+         if c == ?\?
+           return 0x7F
+         else
+           return c & (0x80 | 0x1F)
+         end
+       end
+       # raise # ?
+      end
+
+      if ( c < 0xC0 )
+       cid = c
+       counter = 0
+      elsif ( c < 0xE0 )
+       cid = c & 0x1f
+       counter = 1
+      elsif ( c < 0xF0 )
+       cid = c & 0x0f
+       counter = 2
+      elsif ( c < 0xF8 )
+       cid = c & 0x07
+       counter = 3
+      elsif ( c < 0xFC )
+       cid = c & 0x03
+       counter = 4
+      else
+       cid = c & 0x01
+       counter = 5
+      end
+
+      if (counter + 2 <= len)
+       (0...counter).each {|j|
+         cid = (cid << 6) | (str[j + i] & 0x3F)
+       }
+       return cid
+      end
+
+      raise
+    end
+
+    def format_char_id(cid)
+      case cid
+      when ?\t  then return "?\t"
+      when ?\n  then return "?\n"
+      when ?\r  then return "?\r"
+      when 0x1C then return "?\^\\"
+      end
+
+      if cid <= 0x1F
+       return "?\\^"+(?@+cid).chr
+      elsif (cid == ?\s) || (cid == ?\") ||
+         (cid == ?\#) || (cid == ?\') ||
+         (cid == ?\() || (cid == ?\)) ||
+         (cid == ?\,) || (cid == ?\.) ||
+         (cid == ?\;) || (cid == ?\?) ||
+         (cid == ?\[) || (cid == ?\\) ||
+         (cid == ?\]) || (cid == ?\`)
+       return "?\\"+cid.chr
+      elsif (cid <= 0x7E)
+       return("?"+cid.chr)
+      elsif (cid == 0x7F)
+       return "?\\^?"+0.chr
+      elsif (cid <= 0x9F)
+       dest = "?\\^"
+       dest += (((cid + ?@) >> 6) | 0xC0).chr
+       dest += (((cid + ?@) & 0x3F) | 0x80).chr
+       return dest
+      elsif (cid <= 0x7FF)
+       dest = "?  "
+       dest[1] = (cid >> 6) | 0xC0
+       dest[2] = (cid & 0x3F) | 0x80
+       return dest
+      elsif (cid <= 0xFFFF)
+       dest = "?   "
+       dest[1] =  (cid >> 12) | 0xE0
+       dest[2] = ((cid >>  6) & 0x3F) | 0x80
+       dest[3] =  (cid        & 0x3F) | 0x80
+       return dest
+      elsif (cid <= 0x1FFFFF)
+       dest = "?    "
+       dest[1] =  (cid >> 18) | 0xF0
+       dest[2] = ((cid >> 12) & 0x3F) | 0x80
+       dest[3] = ((cid >>  6) & 0x3F) | 0x80
+       dest[4] =  (cid        & 0x3F) | 0x80
+       return dest
+      elsif (cid <= 0x3FFFFFF)
+       dest = "?     "
+       dest[1] =  (cid >> 24) | 0xF8
+       dest[2] = ((cid >> 18) & 0x3F) | 0x80
+       dest[3] = ((cid >> 12) & 0x3F) | 0x80
+       dest[4] = ((cid >>  6) & 0x3F) | 0x80
+       dest[5] =  (cid        & 0x3F) | 0x80
+       return dest
+      else
+       dest = "?      "
+       dest[1] =  (cid >> 30) | 0xFC
+       dest[2] = ((cid >> 24) & 0x3F) | 0x80
+       dest[3] = ((cid >> 18) & 0x3F) | 0x80
+       dest[4] = ((cid >> 12) & 0x3F) | 0x80
+       dest[5] = ((cid >>  6) & 0x3F) | 0x80
+       dest[6] =  (cid        & 0x3F) | 0x80
+       return dest
+      end
+      raise
     end
   end
 end