[前][次][番号順一覧][スレッド一覧]

ruby-changes:26237

From: usa <ko1@a...>
Date: Mon, 10 Dec 2012 18:01:38 +0900 (JST)
Subject: [ruby-changes:26237] usa:r38294 (trunk): * ext/fiddle/win32/*: library ports from DL to Fiddle.

usa	2012-12-10 18:01:27 +0900 (Mon, 10 Dec 2012)

  New Revision: 38294

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=38294

  Log:
    * ext/fiddle/win32/*: library ports from DL to Fiddle.
    
    * ext/dl/win32/extconf.rb: check fiddle.  often case dl compiled prior
      to fiddle, so this change is no meaning.  in most cases, simply
      fiddle/win32 overwrite dl/win32.

  Added directories:
    trunk/ext/fiddle/win32/
    trunk/ext/fiddle/win32/lib/
    trunk/ext/fiddle/win32/lib/win32/
  Added files:
    trunk/ext/fiddle/win32/extconf.rb
    trunk/ext/fiddle/win32/lib/win32/registry.rb
    trunk/ext/fiddle/win32/lib/win32/resolv.rb
  Modified files:
    trunk/ChangeLog
    trunk/ext/dl/win32/extconf.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 38293)
+++ ChangeLog	(revision 38294)
@@ -1,3 +1,11 @@
+Mon Dec 10 17:59:07 2012  NAKAMURA Usaku  <usa@r...>
+
+	* ext/fiddle/win32/*: library ports from DL to Fiddle.
+
+	* ext/dl/win32/extconf.rb: check fiddle.  often case dl compiled prior
+	  to fiddle, so this change is no meaning.  in most cases, simply
+	  fiddle/win32 overwrite dl/win32.
+
 Mon Dec 10 15:23:35 2012  Nobuyoshi Nakada  <nobu@r...>
 
 	* vm_trace.c (rb_threadptr_exec_event_hooks): exceptions in event
Index: ext/dl/win32/extconf.rb
===================================================================
--- ext/dl/win32/extconf.rb	(revision 38293)
+++ ext/dl/win32/extconf.rb	(revision 38294)
@@ -1,3 +1,3 @@
-if compiled?('dl') and $mswin||$bccwin||$mingw||$cygwin
+if compiled?('dl') and !complied?('fiddle') and $mswin||$bccwin||$mingw||$cygwin
   create_makefile('win32')
 end
Index: ext/fiddle/win32/lib/win32/registry.rb
===================================================================
--- ext/fiddle/win32/lib/win32/registry.rb	(revision 0)
+++ ext/fiddle/win32/lib/win32/registry.rb	(revision 38294)
@@ -0,0 +1,845 @@
+require 'fiddle/import'
+module Win32
+
+=begin rdoc
+= Win32 Registry
+
+win32/registry is registry accessor library for Win32 platform.
+It uses fiddle/import to call Win32 Registry APIs.
+
+== example
+  Win32::Registry::HKEY_CURRENT_USER.open('SOFTWARE\foo') do |reg|
+    value = reg['foo']                               # read a value
+    value = reg['foo', Win32::Registry::REG_SZ]      # read a value with type
+    type, value = reg.read('foo')                    # read a value
+    reg['foo'] = 'bar'                               # write a value
+    reg['foo', Win32::Registry::REG_SZ] = 'bar'      # write a value with type
+    reg.write('foo', Win32::Registry::REG_SZ, 'bar') # write a value
+
+    reg.each_value { |name, type, data| ... }        # Enumerate values
+    reg.each_key { |key, wtime| ... }                # Enumerate subkeys
+
+    reg.delete_value(name)                         # Delete a value
+    reg.delete_key(name)                           # Delete a subkey
+    reg.delete_key(name, true)                     # Delete a subkey recursively
+  end
+
+= Reference
+
+== Win32::Registry class
+
+--- info
+
+--- num_keys
+
+--- max_key_length
+
+--- num_values
+
+--- max_value_name_length
+
+--- max_value_length
+
+--- descriptor_length
+
+--- wtime
+    Returns an item of key information.
+
+=== constants
+--- HKEY_CLASSES_ROOT
+
+--- HKEY_CURRENT_USER
+
+--- HKEY_LOCAL_MACHINE
+
+--- HKEY_PERFORMANCE_DATA
+
+--- HKEY_CURRENT_CONFIG
+
+--- HKEY_DYN_DATA
+
+    Win32::Registry object whose key is predefined key.
+For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/predefined_keys.asp] article.
+
+=end rdoc
+
+  class Registry
+
+    #
+    # For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/registry.asp].
+    #
+    # --- HKEY_*
+    #
+    #     Predefined key ((*handle*)).
+    #     These are Integer, not Win32::Registry.
+    #
+    # --- REG_*
+    #
+    #     Registry value type.
+    #
+    # --- KEY_*
+    #
+    #     Security access mask.
+    #
+    # --- KEY_OPTIONS_*
+    #
+    #     Key options.
+    #
+    # --- REG_CREATED_NEW_KEY
+    #
+    # --- REG_OPENED_EXISTING_KEY
+    #
+    #     If the key is created newly or opened existing key.
+    #     See also Registry#disposition method.
+    module Constants
+      HKEY_CLASSES_ROOT = 0x80000000
+      HKEY_CURRENT_USER = 0x80000001
+      HKEY_LOCAL_MACHINE = 0x80000002
+      HKEY_USERS = 0x80000003
+      HKEY_PERFORMANCE_DATA = 0x80000004
+      HKEY_PERFORMANCE_TEXT = 0x80000050
+      HKEY_PERFORMANCE_NLSTEXT = 0x80000060
+      HKEY_CURRENT_CONFIG = 0x80000005
+      HKEY_DYN_DATA = 0x80000006
+
+      REG_NONE = 0
+      REG_SZ = 1
+      REG_EXPAND_SZ = 2
+      REG_BINARY = 3
+      REG_DWORD = 4
+      REG_DWORD_LITTLE_ENDIAN = 4
+      REG_DWORD_BIG_ENDIAN = 5
+      REG_LINK = 6
+      REG_MULTI_SZ = 7
+      REG_RESOURCE_LIST = 8
+      REG_FULL_RESOURCE_DESCRIPTOR = 9
+      REG_RESOURCE_REQUIREMENTS_LIST = 10
+      REG_QWORD = 11
+      REG_QWORD_LITTLE_ENDIAN = 11
+
+      STANDARD_RIGHTS_READ = 0x00020000
+      STANDARD_RIGHTS_WRITE = 0x00020000
+      KEY_QUERY_VALUE = 0x0001
+      KEY_SET_VALUE = 0x0002
+      KEY_CREATE_SUB_KEY = 0x0004
+      KEY_ENUMERATE_SUB_KEYS = 0x0008
+      KEY_NOTIFY = 0x0010
+      KEY_CREATE_LINK = 0x0020
+      KEY_READ = STANDARD_RIGHTS_READ |
+        KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY
+      KEY_WRITE = STANDARD_RIGHTS_WRITE |
+        KEY_SET_VALUE | KEY_CREATE_SUB_KEY
+      KEY_EXECUTE = KEY_READ
+      KEY_ALL_ACCESS = KEY_READ | KEY_WRITE | KEY_CREATE_LINK
+
+      REG_OPTION_RESERVED = 0x0000
+      REG_OPTION_NON_VOLATILE = 0x0000
+      REG_OPTION_VOLATILE = 0x0001
+      REG_OPTION_CREATE_LINK = 0x0002
+      REG_OPTION_BACKUP_RESTORE = 0x0004
+      REG_OPTION_OPEN_LINK = 0x0008
+      REG_LEGAL_OPTION = REG_OPTION_RESERVED |
+        REG_OPTION_NON_VOLATILE | REG_OPTION_CREATE_LINK |
+        REG_OPTION_BACKUP_RESTORE | REG_OPTION_OPEN_LINK
+
+      REG_CREATED_NEW_KEY = 1
+      REG_OPENED_EXISTING_KEY = 2
+
+      REG_WHOLE_HIVE_VOLATILE = 0x0001
+      REG_REFRESH_HIVE = 0x0002
+      REG_NO_LAZY_FLUSH = 0x0004
+      REG_FORCE_RESTORE = 0x0008
+
+      MAX_KEY_LENGTH = 514
+      MAX_VALUE_LENGTH = 32768
+    end
+    include Constants
+    include Enumerable
+
+    #
+    # Error
+    #
+    class Error < ::StandardError
+      module Kernel32
+        extend Fiddle::Importer
+        dlload "kernel32.dll"
+      end
+      FormatMessageA = Kernel32.extern "int FormatMessageA(int, void *, int, int, void *, int, void *)", :stdcall
+      def initialize(code)
+        @code = code
+        msg = "\0".force_encoding(Encoding::ASCII_8BIT) * 1024
+        len = FormatMessageA.call(0x1200, 0, code, 0, msg, 1024, 0)
+        msg = msg[0, len].force_encoding(Encoding.find(Encoding.locale_charmap))
+        super msg.tr("\r", '').chomp
+      end
+      attr_reader :code
+    end
+
+    #
+    # Predefined Keys
+    #
+    class PredefinedKey < Registry
+      def initialize(hkey, keyname)
+        @hkey = hkey
+        @parent = nil
+        @keyname = keyname
+        @disposition = REG_OPENED_EXISTING_KEY
+      end
+
+      # Predefined keys cannot be closed
+      def close
+        raise Error.new(5) ## ERROR_ACCESS_DENIED
+      end
+
+      # Fake #class method for Registry#open, Registry#create
+      def class
+        Registry
+      end
+
+      # Make all
+      Constants.constants.grep(/^HKEY_/) do |c|
+        Registry.const_set c, new(Constants.const_get(c), c.to_s)
+      end
+    end
+
+    #
+    # Win32 APIs
+    #
+    module API
+      extend Fiddle::Importer
+      dlload "advapi32.dll"
+      [
+        "long RegOpenKeyExA(void *, void *, long, long, void *)",
+        "long RegCreateKeyExA(void *, void *, long, long, long, long, void *, void *, void *)",
+        "long RegEnumValueA(void *, long, void *, void *, void *, void *, void *, void *)",
+        "long RegEnumKeyExA(void *, long, void *, void *, void *, void *, void *, void *)",
+        "long RegQueryValueExA(void *, void *, void *, void *, void *, void *)",
+        "long RegSetValueExA(void *, void *, long, long, void *, long)",
+        "long RegDeleteValue(void *, void *)",
+        "long RegDeleteKey(void *, void *)",
+        "long RegFlushKey(void *)",
+        "long RegCloseKey(void *)",
+        "long RegQueryInfoKey(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *)",
+      ].each do |fn|
+        cfunc = extern fn, :stdcall
+        const_set cfunc.name.intern, cfunc
+      end
+
+      module_function
+
+      def check(result)
+        raise Error, result, caller(2) if result != 0
+      end
+
+      def packdw(dw)
+        [dw].pack('V')
+      end
+
+      def unpackdw(dw)
+        dw += [0].pack('V')
+        dw.unpack('V')[0]
+      end
+
+      def packqw(qw)
+        [ qw & 0xFFFFFFFF, qw >> 32 ].pack('VV')
+      end
+
+      def unpackqw(qw)
+        qw = qw.unpack('VV')
+        (qw[1] << 32) | qw[0]
+      end
+
+      def OpenKey(hkey, name, opt, desired)
+        result = packdw(0)
+        check RegOpenKeyExA.call(hkey, name, opt, desired, result)
+        unpackdw(result)
+      end
+
+      def CreateKey(hkey, name, opt, desired)
+        result = packdw(0)
+        disp = packdw(0)
+        check RegCreateKeyExA.call(hkey, name, 0, 0, opt, desired,
+                                   0, result, disp)
+        [ unpackdw(result), unpackdw(disp) ]
+      end
+
+      def EnumValue(hkey, index)
+        name = ' ' * Constants::MAX_KEY_LENGTH
+        size = packdw(Constants::MAX_KEY_LENGTH)
+        check RegEnumValueA.call(hkey, index, name, size, 0, 0, 0, 0)
+        name[0, unpackdw(size)]
+      end
+
+      def EnumKey(hkey, index)
+        name = ' ' * Constants::MAX_KEY_LENGTH
+        size = packdw(Constants::MAX_KEY_LENGTH)
+        wtime = ' ' * 8
+        check RegEnumKeyExA.call(hkey, index, name, size, 0, 0, 0, wtime)
+        [ name[0, unpackdw(size)], unpackqw(wtime) ]
+      end
+
+      def QueryValue(hkey, name)
+        type = packdw(0)
+        size = packdw(0)
+        check RegQueryValueExA.call(hkey, name, 0, type, 0, size)
+        data = ' ' * unpackdw(size)
+        check RegQueryValueExA.call(hkey, name, 0, type, data, size)
+        [ unpackdw(type), data[0, unpackdw(size)] ]
+      end
+
+      def SetValue(hkey, name, type, data, size)
+        check RegSetValueExA.call(hkey, name, 0, type, data, size)
+      end
+
+      def DeleteValue(hkey, name)
+        check RegDeleteValue.call(hkey, name)
+      end
+
+      def DeleteKey(hkey, name)
+        check RegDeleteKey.call(hkey, name)
+      end
+
+      def FlushKey(hkey)
+        check RegFlushKey.call(hkey)
+      end
+
+      def CloseKey(hkey)
+        check RegCloseKey.call(hkey)
+      end
+
+      def QueryInfoKey(hkey)
+        subkeys = packdw(0)
+        maxsubkeylen = packdw(0)
+        values = packdw(0)
+        maxvaluenamelen = packdw(0)
+        maxvaluelen = packdw(0)
+        secdescs = packdw(0)
+        wtime = ' ' * 8
+        check RegQueryInfoKey.call(hkey, 0, 0, 0, subkeys, maxsubkeylen, 0,
+          values, maxvaluenamelen, maxvaluelen, secdescs, wtime)
+        [ unpackdw(subkeys), unpackdw(maxsubkeylen), unpackdw(values),
+          unpackdw(maxvaluenamelen), unpackdw(maxvaluelen),
+          unpackdw(secdescs), unpackqw(wtime) ]
+      end
+    end
+
+    #
+    # Replace %\w+% into the environment value of what is contained between the %'s
+    # This method is used for REG_EXPAND_SZ.
+    #
+    # For detail, see expandEnvironmentStrings[http://msdn.microsoft.com/library/en-us/sysinfo/base/expandenvironmentstrings.asp] \Win32 \API.
+    #
+    def self.expand_environ(str)
+      str.gsub(/%([^%]+)%/) { ENV[$1] || ENV[$1.upcase] || $& }
+    end
+
+    @@type2name = { }
+    %w[
+      REG_NONE REG_SZ REG_EXPAND_SZ REG_BINARY REG_DWORD
+      REG_DWORD_BIG_ENDIAN REG_LINK REG_MULTI_SZ
+      REG_RESOURCE_LIST REG_FULL_RESOURCE_DESCRIPTOR
+      REG_RESOURCE_REQUIREMENTS_LIST REG_QWORD
+    ].each do |type|
+      @@type2name[Constants.const_get(type)] = type
+    end
+
+    #
+    # Convert registry type value to readable string.
+    #
+    def self.type2name(type)
+      @@type2name[type] || type.to_s
+    end
+
+    #
+    # Convert 64-bit FILETIME integer into Time object.
+    #
+    def self.wtime2time(wtime)
+      Time.at((wtime - 116444736000000000) / 10000000)
+    end
+
+    #
+    # Convert Time object or Integer object into 64-bit FILETIME.
+    #
+    def self.time2wtime(time)
+      time.to_i * 10000000 + 116444736000000000
+    end
+
+    #
+    # constructor
+    #
+    private_class_method :new
+
+    #
+    # --- Registry.open(key, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED)
+    #
+    # --- Registry.open(key, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) { |reg| ... }
+    #
+    # Open the registry key subkey under key.
+    # key is Win32::Registry object of parent key.
+    # You can use predefined key HKEY_* (see Constants)
+    # desired and opt is access mask and key option.
+    # For detail, see the MSDN[http://msdn.microsoft.com/library/en-us/sysinfo/base/regopenkeyex.asp].
+    # If block is given, the key is closed automatically.
+    def self.open(hkey, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED)
+      subkey = subkey.chomp('\\')
+      newkey = API.OpenKey(hkey.hkey, subkey, opt, desired)
+      obj = new(newkey, hkey, subkey, REG_OPENED_EXISTING_KEY)
+      if block_given?
+        begin
+          yield obj
+        ensure
+          obj.close
+        end
+      else
+        obj
+      end
+    end
+
+    #
+    # --- Registry.create(key, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED)
+    #
+    # --- Registry.create(key, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) { |reg| ... }
+    #
+    # Create or open the registry key subkey under key.
+    # You can use predefined key HKEY_* (see Constants)
+    #
+    # If subkey is already exists, key is opened and Registry#created?
+    # method will return false.
+    #
+    # If block is given, the key is closed automatically.
+    #
+    def self.create(hkey, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED)
+      newkey, disp = API.CreateKey(hkey.hkey, subkey, opt, desired)
+      obj = new(newkey, hkey, subkey, disp)
+      if block_given?
+        begin
+          yield obj
+        ensure
+          obj.close
+        end
+      else
+        obj
+      end
+    end
+
+    #
+    # finalizer
+    #
+    @@final = proc { |hkey| proc { API.CloseKey(hkey[0]) if hkey[0] } }
+
+    #
+    # initialize
+    #
+    def initialize(hkey, parent, keyname, disposition)
+      @hkey = hkey
+      @parent = parent
+      @keyname = keyname
+      @disposition = disposition
+      @hkeyfinal = [ hkey ]
+      ObjectSpace.define_finalizer self, @@final.call(@hkeyfinal)
+    end
+
+    #  Returns key handle value.
+    attr_reader :hkey
+    # Win32::Registry object of parent key, or nil if predefeined key.
+    attr_reader :parent
+    # Same as subkey value of Registry.open or
+    # Registry.create method.
+    attr_reader :keyname
+    #  Disposition value (REG_CREATED_NEW_KEY or REG_OPENED_EXISTING_KEY).
+    attr_reader :disposition
+
+    #
+    # Returns if key is created ((*newly*)).
+    # (see Registry.create) -- basically you call create
+    # then when you call created? on the instance returned
+    # it will tell if it was successful or not
+    #
+    def created?
+      @disposition == REG_CREATED_NEW_KEY
+    end
+
+    #
+    # Returns if key is not closed.
+    #
+    def open?
+      !@hkey.nil?
+    end
+
+    #
+    # Full path of key such as 'HKEY_CURRENT_USER\SOFTWARE\foo\bar'.
+    #
+    def name
+      parent = self
+      name = @keyname
+      while parent = parent.parent
+        name = parent.keyname + '\\' + name
+      end
+      name
+    end
+
+    def inspect
+      "\#<Win32::Registry key=#{name.inspect}>"
+    end
+
+    #
+    # marshalling is not allowed
+    #
+    def _dump(depth)
+      raise TypeError, "can't dump Win32::Registry"
+    end
+
+    #
+    # Same as Win32::Registry.open (self, subkey, desired, opt)
+    #
+    def open(subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED, &blk)
+      self.class.open(self, subkey, desired, opt, &blk)
+    end
+
+    #
+    # Same as Win32::Registry.create (self, subkey, desired, opt)
+    #
+    def create(subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED, &blk)
+      self.class.create(self, subkey, desired, opt, &blk)
+    end
+
+    #
+    # Close key.
+    #
+    # After close, most method raise an error.
+    #
+    def close
+      API.CloseKey(@hkey)
+      @hkey = @parent = @keyname = nil
+      @hkeyfinal[0] = nil
+    end
+
+    #
+    # Enumerate values.
+    #
+    def each_value
+      index = 0
+      while true
+        begin
+          subkey = API.EnumValue(@hkey, index)
+        rescue Error
+          break
+        end
+        begin
+          type, data = read(subkey)
+        rescue Error
+          next
+        end
+        yield subkey, type, data
+        index += 1
+      end
+      index
+    end
+    alias each each_value
+
+    #
+    # Enumerate subkeys.
+    #
+    # subkey is String which contains name of subkey.
+    # wtime is last write time as FILETIME (64-bit integer).
+    # (see Registry.wtime2time)
+    #
+    def each_key
+      index = 0
+      while true
+        begin
+          subkey, wtime = API.EnumKey(@hkey, index)
+        rescue Error
+          break
+        end
+        yield subkey, wtime
+        index += 1
+      end
+      index
+    end
+
+    #
+    # return keys as an array
+    #
+    def keys
+      keys_ary = []
+      each_key { |key,| keys_ary << key }
+      keys_ary
+    end
+
+    # Read a registry value named name and return array of
+    # [ type, data ].
+    # When name is nil, the `default' value is read.
+    # type is value type. (see Win32::Registry::Constants module)
+    # data is value data, its class is:
+    # :REG_SZ, REG_EXPAND_SZ
+    #    String
+    # :REG_MULTI_SZ
+    #    Array of String
+    # :REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_QWORD
+    #    Integer
+    # :REG_BINARY
+    #    String (contains binary data)
+    #
+    # When rtype is specified, the value type must be included by
+    # rtype array, or TypeError is raised.
+    def read(name, *rtype)
+      type, data = API.QueryValue(@hkey, name)
+      unless rtype.empty? or rtype.include?(type)
+        raise TypeError, "Type mismatch (expect #{rtype.inspect} but #{type} present)"
+      end
+      case type
+      when REG_SZ, REG_EXPAND_SZ
+        [ type, data.chop ]
+      when REG_MULTI_SZ
+        [ type, data.split(/\0/) ]
+      when REG_BINARY
+        [ type, data ]
+      when REG_DWORD
+        [ type, API.unpackdw(data) ]
+      when REG_DWORD_BIG_ENDIAN
+        [ type, data.unpack('N')[0] ]
+      when REG_QWORD
+        [ type, API.unpackqw(data) ]
+      else
+        raise TypeError, "Type #{type} is not supported."
+      end
+    end
+
+    #
+    # Read a registry value named name and return its value data.
+    # The class of value is same as #read method returns.
+    #
+    # If the value type is REG_EXPAND_SZ, returns value data whose environment
+    # variables are replaced.
+    # If the value type is neither REG_SZ, REG_MULTI_SZ, REG_DWORD,
+    # REG_DWORD_BIG_ENDIAN, nor REG_QWORD, TypeError is raised.
+    #
+    # The meaning of rtype is same as #read method.
+    #
+    def [](name, *rtype)
+      type, data = read(name, *rtype)
+      case type
+      when REG_SZ, REG_DWORD, REG_QWORD, REG_MULTI_SZ
+        data
+      when REG_EXPAND_SZ
+        Registry.expand_environ(data)
+      else
+        raise TypeError, "Type #{type} is not supported."
+      end
+    end
+
+    # Read a REG_SZ(read_s), REG_DWORD(read_i), or REG_BINARY(read_bin)
+    # registry value named name.
+    #
+    # If the values type does not match, TypeError is raised.
+    def read_s(name)
+      read(name, REG_SZ)[1]
+    end
+
+    #
+    # Read a REG_SZ or REG_EXPAND_SZ registry value named name.
+    #
+    # If the value type is REG_EXPAND_SZ, environment variables are replaced.
+    # Unless the value type is REG_SZ or REG_EXPAND_SZ, TypeError is raised.
+    #
+    def read_s_expand(name)
+      type, data = read(name, REG_SZ, REG_EXPAND_SZ)
+      if type == REG_EXPAND_SZ
+        Registry.expand_environ(data)
+      else
+        data
+      end
+    end
+
+    #
+    # Read a REG_SZ(read_s), REG_DWORD(read (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]