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