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