Class MOFile
In: lib/gettext/mo.rb
Parent: Hash

Methods

Classes and Modules

Class MOFile::HeaderRev1
Class MOFile::InvalidFormat

Constants

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.]

Attributes

charset  [R] 
filename  [R] 
last_modified  [RW] 
little_endian  [RW] 
nplurals  [R] 
path  [RW] 
plural  [R] 

Public Class methods

[Source]

    # 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

[Source]

    # 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

Public Instance methods

[Source]

     # 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

[Source]

    # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # File lib/gettext/mo.rb, line 289
289:   def set_comment(msgid_or_sym, comment)
290:     #Do nothing
291:   end

[Source]

    # File lib/gettext/mo.rb, line 60
60:   def update!
61:     if FileTest.exist?(@filename)
62:       st = File.stat(@filename)
63:       load(@filename) unless (@last_modified == [st.ctime, st.mtime])
64:     else
65:       puts "#{@filename} was lost." if $DEBUG
66:       clear
67:     end
68:     self
69:   end

[Validate]