| Class | MOFile |
| In: |
lib/gettext/mo.rb
|
| Parent: | Hash |
| Header | = | Struct.new(:magic, :revision, :nstrings, :orig_table_offset, :translated_table_offset, :hash_table_size, :hash_table_offset) | ||
| MAGIC_BIG_ENDIAN | = | "\x95\x04\x12\xde" | ||
| MAGIC_LITTLE_ENDIAN | = | "\xde\x12\x04\x95" | ||
| HASHWORDBITS | = | 32 | From gettext-0.12.1/gettext-runtime/intl/hash-string.h Defines the so called `hashpjw’ function by P.J. Weinberger [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools, 1986, 1987 Bell Telephone Laboratories, Inc.] |
| charset | [R] | |
| filename | [R] | |
| last_modified | [RW] | |
| little_endian | [RW] | |
| nplurals | [R] | |
| path | [RW] | |
| plural | [R] |
# File lib/gettext/mo.rb, line 52
52: def initialize(output_charset = nil)
53: @filename = nil
54: @last_modified = nil
55: @little_endian = true
56: @output_charset = output_charset
57: super()
58: end
# File lib/gettext/mo.rb, line 47
47: def self.open(arg = nil, output_charset = nil)
48: result = self.new(output_charset)
49: result.load(arg)
50: end
# File lib/gettext/mo.rb, line 192
192: def hash_string(str)
193: hval = 0
194: i = 0
195: str.each_byte do |b|
196: break if b == '\0'
197: hval <<= 4
198: hval += b.to_i
199: g = hval & (0xf << (HASHWORDBITS - 4))
200: if (g != 0)
201: hval ^= g >> (HASHWORDBITS - 8)
202: hval ^= g
203: end
204: end
205: hval
206: end
# File lib/gettext/mo.rb, line 71
71: def load(arg)
72: if arg.kind_of? String
73: begin
74: st = File.stat(arg)
75: @last_modified = [st.ctime, st.mtime]
76: rescue Exception
77: end
78: load_from_file(arg)
79: else
80: load_from_stream(arg)
81: end
82: @filename = arg
83: self
84: end
# File lib/gettext/mo.rb, line 275
275: def load_from_file(filename)
276: @filename = filename
277: begin
278: File.open(filename, 'rb'){|f| load_from_stream(f)}
279: rescue => e
280: e.set_backtrace("File: #{@filename}")
281: raise e
282: end
283: end
# File lib/gettext/mo.rb, line 86
86: def load_from_stream(io)
87: magic = io.read(4)
88: case magic
89: when MAGIC_BIG_ENDIAN
90: @little_endian = false
91: when MAGIC_LITTLE_ENDIAN
92: @little_endian = true
93: else
94: raise InvalidFormat.new(sprintf("Unknown signature %s", magic.dump))
95: end
96:
97: endian_type6 = @little_endian ? 'V6' : 'N6'
98: endian_type_astr = @little_endian ? 'V*' : 'N*'
99:
100: header = HeaderRev1.new(magic, *(io.read(4 * 6).unpack(endian_type6)))
101:
102: if header.revision == 1
103: # FIXME: It doesn't support sysdep correctly.
104: header.n_sysdep_segments = io.read(4).unpack(endian_type6)
105: header.sysdep_segments_offset = io.read(4).unpack(endian_type6)
106: header.n_sysdep_strings = io.read(4).unpack(endian_type6)
107: header.orig_sysdep_tab_offset = io.read(4).unpack(endian_type6)
108: header.trans_sysdep_tab_offset = io.read(4).unpack(endian_type6)
109: elsif header.revision > 1
110: raise InvalidFormat.new(sprintf("file format revision %d isn't supported", header.revision))
111: end
112: io.pos = header.orig_table_offset
113: orig_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
114:
115: io.pos = header.translated_table_offset
116: trans_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
117:
118: original_strings = Array.new(header.nstrings)
119: for i in 0...header.nstrings
120: io.pos = orig_table_data[i * 2 + 1]
121: original_strings[i] = io.read(orig_table_data[i * 2 + 0])
122: end
123:
124: clear
125: for i in 0...header.nstrings
126: io.pos = trans_table_data[i * 2 + 1]
127: str = io.read(trans_table_data[i * 2 + 0])
128:
129: if (! original_strings[i]) || original_strings[i] == ""
130: if str
131: @charset = nil
132: @nplurals = nil
133: @plural = nil
134: str.each_line{|line|
135: if /^Content-Type:/i =~ line and /charset=((?:\w|-)+)/i =~ line
136: @charset = $1
137: elsif /^Plural-Forms:\s*nplurals\s*\=\s*(\d*);\s*plural\s*\=\s*([^;]*)\n?/ =~ line
138: @nplurals = $1
139: @plural = $2
140: end
141: break if @charset and @nplurals
142: }
143: @nplurals = "1" unless @nplurals
144: @plural = "0" unless @plural
145: end
146: else
147: if @output_charset
148: begin
149: str = Iconv.conv(@output_charset, @charset, str) if @charset
150: rescue Iconv::Failure
151: if $DEBUG
152: $stderr.print "@charset = ", @charset, "\n"
153: $stderr.print "@output_charset = ", @output_charset, "\n"
154: $stderr.print "msgid = ", original_strings[i], "\n"
155: $stderr.print "msgstr = ", str, "\n"
156: end
157: end
158: end
159: end
160: self[original_strings[i]] = str.freeze
161: end
162: self
163: end
From gettext-0.12.1/gettext-tools/lib/hash.c
# File lib/gettext/mo.rb, line 179
179: def next_prime(seed)
180: seed |= 1
181: while (! prime?(seed))
182: seed += 2
183: end
184: seed
185: end
From gettext-0.12.1/gettext-tools/lib/hash.c
# File lib/gettext/mo.rb, line 166
166: def prime?(candidate)
167: divn = 3
168: sq = divn * divn
169:
170: while (sq < candidate && candidate % divn != 0)
171: divn += 1
172: sq += 4 * divn
173: divn += 1
174: end
175: candidate % divn != 0
176: end
# File lib/gettext/mo.rb, line 285
285: def save_to_file(filename)
286: File.open(filename, 'wb'){|f| save_to_stream(f)}
287: end
# File lib/gettext/mo.rb, line 208
208: def save_to_stream(io)
209: #Save data as little endian format.
210: header_size = 4 * 7
211: table_size = 4 * 2 * size
212:
213: hash_table_size = next_prime((size * 4) / 3)
214: hash_table_size = 3 if hash_table_size <= 2
215: header = Header.new(
216: MAGIC_LITTLE_ENDIAN, # magic
217: 0, # revision
218: size, # nstrings
219: header_size, # orig_table_offset
220: header_size + table_size, # translated_table_offset
221: hash_table_size, # hash_table_size
222: header_size + table_size * 2 # hash_table_offset
223: )
224: io.write(header.to_a.pack('a4V*'))
225:
226: ary = to_a
227: ary.sort!{|a, b| a[0] <=> b[0]} # sort by original string
228:
229: pos = header.hash_table_size * 4 + header.hash_table_offset
230:
231: orig_table_data = Array.new()
232: ary.each{|item, _|
233: orig_table_data.push(item.size)
234: orig_table_data.push(pos)
235: pos += item.size + 1 # +1 is <NUL>
236: }
237: io.write(orig_table_data.pack('V*'))
238:
239: trans_table_data = Array.new()
240: ary.each{|_, item|
241: trans_table_data.push(item.size)
242: trans_table_data.push(pos)
243: pos += item.size + 1 # +1 is <NUL>
244: }
245: io.write(trans_table_data.pack('V*'))
246:
247: hash_tab = Array.new(hash_table_size)
248: j = 0
249: ary[0...size].each {|key, _|
250: hash_val = hash_string(key)
251: idx = hash_val % hash_table_size
252: if hash_tab[idx] != nil
253: incr = 1 + (hash_val % (hash_table_size - 2))
254: begin
255: if (idx >= hash_table_size - incr)
256: idx -= hash_table_size - incr
257: else
258: idx += incr
259: end
260: end until (hash_tab[idx] == nil)
261: end
262: hash_tab[idx] = j + 1
263: j += 1
264: }
265: hash_tab.collect!{|i| i ? i : 0}
266:
267: io.write(hash_tab.pack('V*'))
268:
269: ary.each{|item, _| io.write(item); io.write("\0") }
270: ary.each{|_, item| io.write(item); io.write("\0") }
271:
272: self
273: end