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

Methods

Classes and Modules

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 42
42:   def initialize(output_charset = nil)
43:     @filename = nil
44:     @last_modified = nil
45:     @little_endian = true
46:     @output_charset = output_charset
47:     super()
48:   end

[Source]

    # File lib/gettext/mo.rb, line 37
37:   def self.open(arg = nil, output_charset = nil)
38:     result = self.new(output_charset)
39:     result.load(arg)
40:   end

Public Instance methods

[Source]

     # File lib/gettext/mo.rb, line 170
170:   def hash_string(str)
171:     hval = 0
172:     i = 0
173:     str.each_byte do |b|
174:       break if b == '\0'
175:       hval <<= 4
176:       hval += b.to_i
177:       g = hval & (0xf << (HASHWORDBITS - 4))
178:       if (g != 0)
179:         hval ^= g >> (HASHWORDBITS - 8)
180:         hval ^= g
181:       end
182:     end
183:     hval
184:   end

[Source]

    # File lib/gettext/mo.rb, line 61
61:   def load(arg)
62:     if arg.kind_of? String
63:       begin
64:         st = File.stat(arg)
65:         @last_modified = [st.ctime, st.mtime]
66:       rescue Exception
67:       end
68:       load_from_file(arg)
69:     else
70:       load_from_stream(arg)
71:     end
72:     @filename = arg
73:     self
74:   end

[Source]

     # File lib/gettext/mo.rb, line 253
253:   def load_from_file(filename)
254:     @filename = filename
255:     File.open(filename, 'rb'){|f| load_from_stream(f)}
256:   end

[Source]

     # File lib/gettext/mo.rb, line 76
 76:   def load_from_stream(io)
 77:     magic = io.read(4)
 78:     case magic
 79:     when MAGIC_BIG_ENDIAN
 80:       @little_endian = false
 81:     when MAGIC_LITTLE_ENDIAN
 82:       @little_endian = true
 83:     else
 84:       raise InvalidFormat.new(sprintf("Unknown signature %s", magic.dump))
 85:     end
 86: 
 87:     header = Header.new(magic, *(io.read(4 * 6).unpack(@little_endian ? 'V6' : 'N6')))
 88:     raise InvalidFormat.new(sprintf("file format revision %d isn't supported", header.revision)) if header.revision > 0
 89: 
 90:     io.pos = header.orig_table_offset
 91:     orig_table_data = io.read((4 * 2) * header.nstrings).unpack(@little_endian ? 'V*' : 'N*')
 92: 
 93:     io.pos = header.translated_table_offset
 94:     trans_table_data = io.read((4 * 2) * header.nstrings).unpack(@little_endian ? 'V*' : 'N*')
 95: 
 96:     original_strings = Array.new(header.nstrings)
 97:     for i in 0...header.nstrings
 98:       io.pos = orig_table_data[i * 2 + 1]
 99:       original_strings[i] = io.read(orig_table_data[i * 2 + 0])
100:     end
101: 
102:     clear
103:     for i in 0...header.nstrings
104:       io.pos = trans_table_data[i * 2 + 1]
105:       str = io.read(trans_table_data[i * 2 + 0])
106: 
107:       if (! original_strings[i]) || original_strings[i] == ""
108:         if str
109:           @charset = nil
110:           @nplurals = nil
111:           @plural = nil
112:           str.each_line{|line|
113:             if /^Content-Type:/i =~ line and /charset=((?:\w|-)+)/i =~ line
114:               @charset = $1
115:             elsif /^Plural-Forms:\s*nplurals\s*\=\s*(\d*);\s*plural\s*\=\s*([^;]*)\n?/ =~ line
116:               @nplurals = $1
117:               @plural = $2
118:             end
119:             break if @charset and @nplurals
120:           }
121:           @nplurals = "1" unless @nplurals
122:           @plural = "0" unless @plural
123:         end
124:       else
125:         if @output_charset
126:           begin
127:             str = Iconv.conv(@output_charset, @charset, str) if @charset
128:           rescue Iconv::Failure
129:             if $DEBUG
130:               $stderr.print "@charset = ", @charset, "\n"
131:               $stderr.print "@output_charset = ", @output_charset, "\n"
132:               $stderr.print "msgid = ", original_strings[i], "\n"
133:               $stderr.print "msgstr = ", str, "\n"
134:             end
135:           end
136:         end
137:       end
138:       self[original_strings[i]] = str
139:     end
140:     self
141:   end

From gettext-0.12.1/gettext-tools/lib/hash.c

[Source]

     # File lib/gettext/mo.rb, line 157
157:   def next_prime(seed)
158:     seed |= 1
159:     while (! prime?(seed))
160:       seed += 2
161:     end
162:     seed
163:   end

From gettext-0.12.1/gettext-tools/lib/hash.c

[Source]

     # File lib/gettext/mo.rb, line 144
144:   def prime?(candidate)
145:     divn = 3
146:     sq = divn * divn
147: 
148:     while (sq < candidate && candidate % divn != 0)
149:       divn += 1
150:       sq += 4 * divn
151:       divn += 1
152:     end
153:     candidate % divn != 0
154:   end

[Source]

     # File lib/gettext/mo.rb, line 258
258:   def save_to_file(filename)
259:     File.open(filename, 'wb'){|f| save_to_stream(f)}
260:   end

[Source]

     # File lib/gettext/mo.rb, line 186
186:   def save_to_stream(io)
187:     #Save data as little endian format.
188:     header_size = 4 * 7
189:     table_size  = 4 * 2 * size
190: 
191:     hash_table_size = next_prime((size * 4) / 3) 
192:     hash_table_size = 3 if hash_table_size <= 2
193:     header = Header.new(
194:       MAGIC_LITTLE_ENDIAN,          # magic
195:       0,                            # revision
196:       size,                         # nstrings
197:       header_size,                  # orig_table_offset
198:       header_size + table_size,     # translated_table_offset
199:       hash_table_size,              # hash_table_size
200:       header_size + table_size * 2  # hash_table_offset
201:     )
202:     io.write(header.to_a.pack('a4V*'))
203: 
204:     ary = to_a
205:     ary.sort!{|a, b| a[0] <=> b[0]} # sort by original string
206: 
207:     pos = header.hash_table_size * 4 + header.hash_table_offset
208: 
209:     orig_table_data = Array.new()
210:     ary.each{|item, _|
211:       orig_table_data.push(item.size)
212:       orig_table_data.push(pos)
213:       pos += item.size + 1 # +1 is <NUL>
214:     }
215:     io.write(orig_table_data.pack('V*'))
216: 
217:     trans_table_data = Array.new()
218:     ary.each{|_, item|
219:       trans_table_data.push(item.size)
220:       trans_table_data.push(pos)
221:       pos += item.size + 1 # +1 is <NUL>
222:     }
223:     io.write(trans_table_data.pack('V*'))
224: 
225:     hash_tab = Array.new(hash_table_size)
226:     j = 0
227:     ary[0...size].each {|key, _|
228:       hash_val = hash_string(key)
229:       idx = hash_val % hash_table_size
230:       if hash_tab[idx] != nil
231:         incr = 1 + (hash_val % (hash_table_size - 2))
232:         begin
233:           if (idx >= hash_table_size - incr)
234:             idx -= hash_table_size - incr
235:           else
236:             idx += incr
237:           end
238:         end until (hash_tab[idx] == nil)
239:       end
240:       hash_tab[idx] = j + 1
241:       j += 1
242:     }
243:     hash_tab.collect!{|i| i ? i : 0}
244: 
245:     io.write(hash_tab.pack('V*'))
246: 
247:     ary.each{|item, _| io.write(item); io.write("\0") }
248:     ary.each{|_, item| io.write(item); io.write("\0") }
249: 
250:     self
251:   end

[Source]

     # File lib/gettext/mo.rb, line 262
262:   def set_comment(msgid_or_sym, comment)
263:     #Do nothing
264:   end

[Source]

    # File lib/gettext/mo.rb, line 50
50:   def update!
51:     if FileTest.exist?(@filename)
52:       st = File.stat(@filename)
53:       load(@filename) unless (@last_modified == [st.ctime, st.mtime])
54:     else
55:       puts "#{@filename} was lost." if $DEBUG
56:       clear
57:     end
58:     self
59:   end

[Validate]