update.
[chise/ruby.git] / chise / libchise_r.rb
1 # Copyright (C) 2002-2004 Kouichirou Eto, All rights reserved.
2 # libchise extension compatible library.
3
4 require "bdb"
5 require "pathname"
6 require "fileutils"
7 require "chise/config"
8 require "chise/path"
9
10 module CHISE
11   module ChiseValue; end
12   module TableAccessModule; end
13
14   module EachEntryModule
15     def each_entry(subdir)
16       #dir = @location + subdir
17       dir = DataSource::DB_DIR.path + subdir
18       dir.each_entry {|f|
19         next if f.to_s == "." || f.to_s == ".."
20         next if f.to_s =~ /\.txt\Z/
21         yield(f.unescape_win_filename.unescape.to_s)
22       }
23     end
24   end
25
26   class DataSource_R
27     NONE = 0
28     Berkeley_DB = 1
29     DB_DIR = "/cygdrive/c/chise/chise-db"
30
31     def initialize(type=Berkeley_DB, loc=DB_DIR, subtype=0, modemask=0755)
32       @type = type
33       #loc = Config.instance.db_dir if loc.nil?
34       @location = loc.path
35       @subtype = subtype
36       @modemask = modemask
37       @fdb = {}
38       @cdb = {}
39     end
40     attr_reader :type, :location, :subtype, :modemask
41
42     def close() end
43
44     def get_feature(f)
45       @fdb[f] = Feature.new(self, f) if @fdb[f].nil?
46       @fdb[f]
47     end
48
49     def get_ccs(ccs)
50       @cdb[ccs] = CCS.new(self, ccs) if @cdb[ccs].nil?
51       @cdb[ccs]
52     end
53
54     def each_feature_name
55       each_entry("character/feature") {|f| yield f }
56     end
57
58     def load_feature(cid, name)
59       feature = get_feature(name)
60       return nil if feature.nil?
61       feature.get_value(cid)
62     end
63
64     def decode_char(ccs, code_point)
65       ccst = get_ccs(ccs)
66       return nil if ccst.nil?
67       ccst.decode(code_point)
68     end
69
70     private
71     include EachEntryModule
72   end
73
74   class AttributeTable
75     def initialize(dir, cat, keytype, name, amask, mmask)
76       @dir, @cat, @keytype, @name, @amask, @mmask = dir, cat, keytype, name, amask, mmask
77       @db = nil
78       setup_db
79       at_exit {
80         close
81       }
82     end
83
84     def close
85       return if @db.nil?
86       begin
87         @db.sync
88         @db.close
89         # @db = nil
90       rescue => e
91         p e
92       end
93     end
94
95     def get(k)    setup_db; @db.get(k);    end
96     def put(k, v) setup_db; @db.put(k, v); end
97
98     def each() setup_db; @db.each {|k, v| yield(k, v) } end
99
100     private
101     def setup_db
102       return if @db
103       dbdir  = @dir + @cat + @keytype
104       path = dbdir + @name.path.escape.escape_win_filename
105       #TODO: should make dir.
106       if @amask == BDB::RDONLY
107         raise unless FileTest.exist?(path.to_s)
108       end
109       #qp path.to_s
110       @db = BDB::Hash.open(path.to_s, nil, @amask)
111     end
112   end
113
114   module TableAccessModule
115     def reset
116       @db = nil
117       @access = 0
118     end
119
120     def sync
121       @db.close if @db
122       reset
123       true
124     end
125     alias close sync
126
127     def setup_db(writable=nil)
128       if writable
129         sync if @access & BDB::CREATE == 0
130         access = BDB::CREATE
131       else
132         access = BDB::RDONLY
133       end
134
135       return true if @db
136
137       begin
138         db_dir = @ds.location
139         modemask = @ds.modemask
140       rescue
141         db_dir = CHISE::DataSource::DB_DIR.path
142         modemask = 0755
143       end
144
145       #qp db_dir, @category, @keyvalue, @name, @access, modemask
146       begin
147         @db = AttributeTable.new(db_dir, @category, @keyvalue,
148                                  @name, access, modemask)
149         return false if @db.nil?
150         @access = access
151       rescue => e
152         #puts $!, $@
153         @db = nil
154         return false
155       end
156       true
157     end
158   end
159
160   class Feature_R
161     include ChiseValue
162     include TableAccessModule
163
164     def initialize(ds, name)
165       @ds, @name = ds, name
166       @category, @keyvalue = "character", "feature"
167       reset
168     end
169
170     def get_value(cid)
171       setup_db
172       return nil if @db.nil?
173       @db.get(format_char_id(cid))
174     end
175
176     def set_value(cid, value)
177       setup_db(true)
178       raise "@db is nil." if @db.nil?
179       @db.put(format_char_id(cid), value)
180       true
181     end
182
183     def each_char
184       setup_db
185       raise "@db is nil." if @db.nil?
186       @db.each {|k, v|
187         yield(parse_c_string(k), v)
188       }
189     end
190   end
191
192   class CCS_R
193     include ChiseValue
194     include TableAccessModule
195
196     def initialize(ds, name)
197       @ds, @name = ds, name
198       @category, @keyvalue = "character", "by_feature"
199       reset
200     end
201
202     def set(code_point, cid)
203       setup_db(true)
204       raise "@db is nil." if @db.nil?
205       parse_c_string(@db.get(code_point.to_s))
206       @db.put(code_point.to_s, format_char_id(cid))
207       true
208     end
209
210     def decode(code_point)
211       setup_db
212       return nil if @db.nil?
213       parse_c_string(@db.get(code_point.to_s))
214     end
215
216     def set_decoded_char(code_point, cid)
217       setup_db(true)
218       raise "@db is nil." if @db.nil?
219       @db.put(code_point.to_s, format_char_id(cid))
220     end
221
222     def each_char
223       setup_db
224       if @db.nil?
225         #raise "@db is nil."+@name
226         p "@db is nil."+@name
227         return nil
228       end
229       @db.each {|code_point, cid|
230         yield(code_point, parse_c_string(cid))
231       }
232     end
233   end
234
235   module ChiseValue
236     def parse_c_string(str)
237       return nil if str.nil?
238
239       i = 0
240       c = str[i]
241       i += 1
242       len = str.length
243
244       raise unless 2 <= len && c == ?\?
245
246       c = str[i]
247       i += 1
248
249       if (c == ?\\)
250         raise if (len < 3)
251         c = str[i]
252         i += 1
253         if (c == ?^)
254           raise if (len < 4)
255           c = str[i]
256           i += 1
257           if c == ?\?
258             return 0x7F
259           else
260             return c & (0x80 | 0x1F)
261           end
262         end
263         # raise # ?
264       end
265
266       if ( c < 0xC0 )
267         cid = c
268         counter = 0
269       elsif ( c < 0xE0 )
270         cid = c & 0x1f
271         counter = 1
272       elsif ( c < 0xF0 )
273         cid = c & 0x0f
274         counter = 2
275       elsif ( c < 0xF8 )
276         cid = c & 0x07
277         counter = 3
278       elsif ( c < 0xFC )
279         cid = c & 0x03
280         counter = 4
281       else
282         cid = c & 0x01
283         counter = 5
284       end
285
286       if (counter + 2 <= len)
287         (0...counter).each {|j|
288           cid = (cid << 6) | (str[j + i] & 0x3F)
289         }
290         return cid
291       end
292
293       raise
294     end
295
296     def format_char_id(cid)
297       case cid
298       when ?\t  then return "?\t"
299       when ?\n  then return "?\n"
300       when ?\r  then return "?\r"
301       when 0x1C then return "?\^\\"
302       end
303
304       if cid <= 0x1F
305         return "?\\^"+(?@+cid).chr
306       elsif (cid == ?\s) || (cid == ?\") ||
307           (cid == ?\#) || (cid == ?\') ||
308           (cid == ?\() || (cid == ?\)) ||
309           (cid == ?\,) || (cid == ?\.) ||
310           (cid == ?\;) || (cid == ?\?) ||
311           (cid == ?\[) || (cid == ?\\) ||
312           (cid == ?\]) || (cid == ?\`)
313         return "?\\"+cid.chr
314       elsif (cid <= 0x7E)
315         return("?"+cid.chr)
316       elsif (cid == 0x7F)
317         return "?\\^?"+0.chr
318       elsif (cid <= 0x9F)
319         dest = "?\\^"
320         dest += (((cid + ?@) >> 6) | 0xC0).chr
321         dest += (((cid + ?@) & 0x3F) | 0x80).chr
322         return dest
323       elsif (cid <= 0x7FF)
324         dest = "?  "
325         dest[1] = (cid >> 6) | 0xC0
326         dest[2] = (cid & 0x3F) | 0x80
327         return dest
328       elsif (cid <= 0xFFFF)
329         dest = "?   "
330         dest[1] =  (cid >> 12) | 0xE0
331         dest[2] = ((cid >>  6) & 0x3F) | 0x80
332         dest[3] =  (cid        & 0x3F) | 0x80
333         return dest
334       elsif (cid <= 0x1FFFFF)
335         dest = "?    "
336         dest[1] =  (cid >> 18) | 0xF0
337         dest[2] = ((cid >> 12) & 0x3F) | 0x80
338         dest[3] = ((cid >>  6) & 0x3F) | 0x80
339         dest[4] =  (cid        & 0x3F) | 0x80
340         return dest
341       elsif (cid <= 0x3FFFFFF)
342         dest = "?     "
343         dest[1] =  (cid >> 24) | 0xF8
344         dest[2] = ((cid >> 18) & 0x3F) | 0x80
345         dest[3] = ((cid >> 12) & 0x3F) | 0x80
346         dest[4] = ((cid >>  6) & 0x3F) | 0x80
347         dest[5] =  (cid        & 0x3F) | 0x80
348         return dest
349       else
350         dest = "?      "
351         dest[1] =  (cid >> 30) | 0xFC
352         dest[2] = ((cid >> 24) & 0x3F) | 0x80
353         dest[3] = ((cid >> 18) & 0x3F) | 0x80
354         dest[4] = ((cid >> 12) & 0x3F) | 0x80
355         dest[5] = ((cid >>  6) & 0x3F) | 0x80
356         dest[6] =  (cid        & 0x3F) | 0x80
357         return dest
358       end
359       raise
360     end
361   end
362 end