OSDN Git Service

ActiveLdap 1.2.4
[redminele/redminele.git] / ruby / lib / ruby / gems / 1.8 / gems / fast_gettext-0.5.8 / lib / fast_gettext / vendor / mofile.rb
diff --git a/ruby/lib/ruby/gems/1.8/gems/fast_gettext-0.5.8/lib/fast_gettext/vendor/mofile.rb b/ruby/lib/ruby/gems/1.8/gems/fast_gettext-0.5.8/lib/fast_gettext/vendor/mofile.rb
new file mode 100644 (file)
index 0000000..54221a6
--- /dev/null
@@ -0,0 +1,296 @@
+=begin
+    mofile.rb - A simple class for operating GNU MO file.
+
+    Copyright (C) 2003-2008  Masao Mutoh
+    Copyright (C) 2002  Masahiro Sakai, Masao Mutoh
+    Copyright (C) 2001  Masahiro Sakai
+
+        Masahiro Sakai                  <s01397ms at sfc.keio.ac.jp>
+        Masao Mutoh                     <mutoh at highway.ne.jp>
+
+    You can redistribute this file and/or modify it under the same term
+    of Ruby.  License of Ruby is included with Ruby distribution in
+    the file "README".
+
+    $Id: mo.rb,v 1.10 2008/06/17 16:40:52 mutoh Exp $
+=end
+
+require 'iconv'
+require 'stringio'
+
+#Modifications:
+#  use Iconv or FastGettext::Icvon
+
+module FastGettext
+  module GetText
+    class MOFile < Hash
+      class InvalidFormat < RuntimeError; end;
+
+      attr_reader :filename
+
+      Header = Struct.new(:magic,
+                          :revision,
+                          :nstrings,
+                          :orig_table_offset,
+                          :translated_table_offset,
+                          :hash_table_size,
+                          :hash_table_offset)
+
+      # The following are only used in .mo files
+      # with minor revision >= 1.
+      class HeaderRev1 < Header
+        attr_accessor :n_sysdep_segments,
+        :sysdep_segments_offset,
+        :n_sysdep_strings,
+        :orig_sysdep_tab_offset,
+        :trans_sysdep_tab_offset
+      end
+
+      MAGIC_BIG_ENDIAN    = "\x95\x04\x12\xde"
+      MAGIC_LITTLE_ENDIAN = "\xde\x12\x04\x95"
+
+      def self.open(arg = nil, output_charset = nil)
+        result = self.new(output_charset)
+        result.load(arg)
+      end
+
+      def initialize(output_charset = nil)
+        @filename = nil
+        @last_modified = nil
+        @little_endian = true
+        @output_charset = output_charset
+        super()
+      end
+
+      def update!
+        if FileTest.exist?(@filename)
+          st = File.stat(@filename)
+          load(@filename) unless (@last_modified == [st.ctime, st.mtime])
+        else
+          warn "#{@filename} was lost." if $DEBUG
+          clear
+        end
+        self
+      end
+
+      def load(arg)
+        if arg.kind_of? String
+          begin
+            st = File.stat(arg)
+            @last_modified = [st.ctime, st.mtime]
+          rescue Exception
+          end
+          load_from_file(arg)
+        else
+          load_from_stream(arg)
+        end
+        @filename = arg
+        self
+      end
+
+      def load_from_stream(io)
+        magic = io.read(4)
+        case magic
+        when MAGIC_BIG_ENDIAN
+          @little_endian = false
+        when MAGIC_LITTLE_ENDIAN
+          @little_endian = true
+        else
+          raise InvalidFormat.new(sprintf("Unknown signature %s", magic.dump))
+        end
+
+        endian_type6 = @little_endian ? 'V6' : 'N6'
+        endian_type_astr = @little_endian ? 'V*' : 'N*'
+
+        header = HeaderRev1.new(magic, *(io.read(4 * 6).unpack(endian_type6)))
+
+        if header.revision == 1
+          # FIXME: It doesn't support sysdep correctly.
+          header.n_sysdep_segments = io.read(4).unpack(endian_type6)
+          header.sysdep_segments_offset = io.read(4).unpack(endian_type6)
+          header.n_sysdep_strings = io.read(4).unpack(endian_type6)
+          header.orig_sysdep_tab_offset = io.read(4).unpack(endian_type6)
+          header.trans_sysdep_tab_offset = io.read(4).unpack(endian_type6)
+        elsif header.revision > 1
+          raise InvalidFormat.new(sprintf("file format revision %d isn't supported", header.revision))
+        end
+        io.pos = header.orig_table_offset
+        orig_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
+
+        io.pos = header.translated_table_offset
+        trans_table_data = io.read((4 * 2) * header.nstrings).unpack(endian_type_astr)
+
+        original_strings = Array.new(header.nstrings)
+        for i in 0...header.nstrings
+          io.pos = orig_table_data[i * 2 + 1]
+          original_strings[i] = io.read(orig_table_data[i * 2 + 0])
+        end
+
+        clear
+        for i in 0...header.nstrings
+          io.pos = trans_table_data[i * 2 + 1]
+          str = io.read(trans_table_data[i * 2 + 0])
+
+          if (! original_strings[i]) || original_strings[i] == ""
+            if str
+              @charset = nil
+              @nplurals = nil
+              @plural = nil
+              str.each_line{|line|
+                if /^Content-Type:/i =~ line and /charset=((?:\w|-)+)/i =~ line
+                  @charset = $1
+                elsif /^Plural-Forms:\s*nplurals\s*\=\s*(\d*);\s*plural\s*\=\s*([^;]*)\n?/ =~ line
+                  @nplurals = $1
+                  @plural = $2
+                end
+                break if @charset and @nplurals
+              }
+              @nplurals = "1" unless @nplurals
+              @plural = "0" unless @plural
+            end
+          else
+            if @output_charset
+              begin
+                iconv = Iconv || FastGettext::Iconv
+                str = iconv.conv(@output_charset, @charset, str) if @charset
+              rescue iconv::Failure
+                if $DEBUG
+                  warn "@charset = ", @charset
+                  warn"@output_charset = ", @output_charset
+                  warn "msgid = ", original_strings[i]
+                  warn "msgstr = ", str
+                end
+              end
+            end
+          end
+          self[original_strings[i]] = str.freeze
+        end
+        self
+      end
+
+      # Is this number a prime number ?
+      # http://apidock.com/ruby/Prime
+      def prime?(number)
+        ('1' * number) !~ /^1?$|^(11+?)\1+$/
+      end
+
+      def next_prime(seed)
+        require 'mathn'
+        prime = Prime.new
+        while current = prime.succ
+          return current if current > seed
+        end
+      end
+
+      # 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.]
+      HASHWORDBITS = 32
+      def hash_string(str)
+        hval = 0
+        i = 0
+        str.each_byte do |b|
+          break if b == '\0'
+          hval <<= 4
+          hval += b.to_i
+          g = hval & (0xf << (HASHWORDBITS - 4))
+          if (g != 0)
+            hval ^= g >> (HASHWORDBITS - 8)
+            hval ^= g
+          end
+        end
+        hval
+      end
+
+      def save_to_stream(io)
+        #Save data as little endian format.
+        header_size = 4 * 7
+        table_size  = 4 * 2 * size
+
+        hash_table_size = next_prime((size * 4) / 3)
+        hash_table_size = 3 if hash_table_size <= 2
+        header = Header.new(
+                            MAGIC_LITTLE_ENDIAN,          # magic
+                            0,                            # revision
+                            size,                         # nstrings
+                            header_size,                  # orig_table_offset
+                            header_size + table_size,     # translated_table_offset
+                            hash_table_size,              # hash_table_size
+                            header_size + table_size * 2  # hash_table_offset
+                            )
+        io.write(header.to_a.pack('a4V*'))
+
+        ary = to_a
+        ary.sort!{|a, b| a[0] <=> b[0]} # sort by original string
+
+        pos = header.hash_table_size * 4 + header.hash_table_offset
+
+        orig_table_data = Array.new()
+        ary.each{|item, _|
+          orig_table_data.push(item.size)
+          orig_table_data.push(pos)
+          pos += item.size + 1 # +1 is <NUL>
+        }
+        io.write(orig_table_data.pack('V*'))
+
+        trans_table_data = Array.new()
+        ary.each{|_, item|
+          trans_table_data.push(item.size)
+          trans_table_data.push(pos)
+          pos += item.size + 1 # +1 is <NUL>
+        }
+        io.write(trans_table_data.pack('V*'))
+
+        hash_tab = Array.new(hash_table_size)
+        j = 0
+        ary[0...size].each {|key, _|
+          hash_val = hash_string(key)
+          idx = hash_val % hash_table_size
+          if hash_tab[idx] != nil
+            incr = 1 + (hash_val % (hash_table_size - 2))
+            begin
+              if (idx >= hash_table_size - incr)
+                idx -= hash_table_size - incr
+              else
+                idx += incr
+              end
+            end until (hash_tab[idx] == nil)
+          end
+          hash_tab[idx] = j + 1
+          j += 1
+        }
+        hash_tab.collect!{|i| i ? i : 0}
+
+        io.write(hash_tab.pack('V*'))
+
+        ary.each{|item, _| io.write(item); io.write("\0") }
+        ary.each{|_, item| io.write(item); io.write("\0") }
+
+        self
+      end
+
+      def load_from_file(filename)
+        @filename = filename
+        begin
+          File.open(filename, 'rb'){|f| load_from_stream(f)}
+        rescue => e
+          e.set_backtrace("File: #{@filename}")
+          raise e
+        end
+      end
+
+      def save_to_file(filename)
+        File.open(filename, 'wb'){|f| save_to_stream(f)}
+      end
+
+      def set_comment(msgid_or_sym, comment)
+        #Do nothing
+      end
+
+
+      attr_accessor :little_endian, :path, :last_modified
+      attr_reader :charset, :nplurals, :plural
+    end
+  end
+end
\ No newline at end of file