class Dir

Constants

MYDOCUMENTS

Public Class Methods

[](*glob_patterns) click to toggle source

Same as the standard MRI Dir[] method except that it handles backslashes in path names.

# File lib/win32/dir.rb, line 133
def [](*glob_patterns)
  temp = glob_patterns.map! { |pattern| "#{pattern}".tr("\\", "/") }
  old_ref(*temp)
end
Also aliased as: old_ref
create_junction(to, from) click to toggle source

Creates the symlink to, linked to the existing directory from. If the to directory already exists, it must be empty or an error is raised.

Example:

Dir.mkdir('C:/from')
Dir.create_junction('C:/to', 'C:/from')
# File lib/win32/dir.rb, line 198
def self.create_junction(to, from)
  to   = string_check(to).wincode
  from = string_check(from).wincode

  from_path = (0.chr * 1024).encode("UTF-16LE")

  length = GetFullPathNameW(from, from_path.size, from_path, nil)

  if length == 0 || length > from_path.size
    raise SystemCallError.new("GetFullPathNameW", FFI.errno)
  else
    from_path.strip!
  end

  to_path = (0.chr * 1024).encode("UTF-16LE")

  length = GetFullPathNameW(to, to_path.size, to_path, nil)

  if length == 0 || length > to_path.size
    raise SystemCallError.new("GetFullPathNameW", FFI.errno)
  else
    to_path.strip!
  end

  # You can create a junction to a directory that already exists, so
  # long as it's empty.
  unless CreateDirectoryW(to_path, nil)
    if FFI.errno != ERROR_ALREADY_EXISTS
      raise SystemCallError.new("CreateDirectoryW", FFI.errno)
    end
  end

  # Generic read & write + open existing + reparse point & backup semantics
  handle = CreateFileW(
    to_path,
    GENERIC_READ | GENERIC_WRITE,
    0,
    nil,
    OPEN_EXISTING,
    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
    0
  )

  if handle == INVALID_HANDLE_VALUE
    raise SystemCallError.new("CreateFileW", FFI.errno)
  end

  target = "\\??\\".encode("UTF-16LE") + from_path

  rdb = REPARSE_JDATA_BUFFER.new
  rdb[:ReparseTag] = 2684354563 # IO_REPARSE_TAG_MOUNT_POINT
  rdb[:ReparseDataLength] = target.bytesize + 12
  rdb[:Reserved] = 0
  rdb[:SubstituteNameOffset] = 0
  rdb[:SubstituteNameLength] = target.bytesize
  rdb[:PrintNameOffset] = target.bytesize + 2
  rdb[:PrintNameLength] = 0
  rdb[:PathBuffer] = target

  bytes = FFI::MemoryPointer.new(:ulong)

  begin
    bool = DeviceIoControl(
      handle,
      CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, 0),
      rdb,
      rdb[:ReparseDataLength] + rdb.header_size,
      nil,
      0,
      bytes,
      nil
    )

    error = FFI.errno

    unless bool
      RemoveDirectoryW(to_path)
      raise SystemCallError.new("DeviceIoControl", error)
    end
  ensure
    CloseHandle(handle)
  end

  self
end
empty?(path) click to toggle source

Returns whether or not path is empty. Returns false if path is not a directory, or contains any files other than '.' or '..'.

# File lib/win32/dir.rb, line 373
def self.empty?(path)
  PathIsDirectoryEmptyW("#{path}".wincode)
end
getwd() click to toggle source

Returns the present working directory. Unlike MRI, this method always normalizes the path.

Examples:

Dir.chdir("C:/Progra~1")
Dir.getwd # => C:\Program Files

Dir.chdir("C:/PROGRAM FILES")
Dir.getwd # => C:\Program Files
# File lib/win32/dir.rb, line 154
def getwd
  path1 = FFI::Buffer.new(:wint_t, 1024, true)
  path2 = FFI::Buffer.new(:wint_t, 1024, true)
  path3 = FFI::Buffer.new(:wint_t, 1024, true)

  length = GetCurrentDirectoryW(path1.size, path1)

  if length == 0 || length > path1.size
    raise SystemCallError.new("GetCurrentDirectoryW", FFI.errno)
  end

  length = GetShortPathNameW(path1, path2, path2.size)

  if length == 0 || length > path2.size
    raise SystemCallError.new("GetShortPathNameW", FFI.errno)
  end

  length = GetLongPathNameW(path2, path3, path3.size)

  if length == 0 || length > path3.size
    raise SystemCallError.new("GetLongPathNameW", FFI.errno)
  end

  path = path3.read_bytes(length * 2).wstrip

  begin
    path.encode(Encoding.default_external)
  rescue Encoding::UndefinedConversionError
    path.encode("UTF-8")
  end
end
Also aliased as: oldgetwd, pwd
glob(glob_pattern, flags = 0, &block) click to toggle source

Same as the standard MRI Dir.glob method except that it handles backslashes in path names.

# File lib/win32/dir.rb, line 118
def glob(glob_pattern, flags = 0, &block)
  if glob_pattern.is_a?(Array)
    temp = glob_pattern.map! { |pattern| string_check(pattern).tr("\\", "/") }
  else
    temp = string_check(glob_pattern).tr("\\", "/")
  end

  old_glob(temp, flags, &block)
end
Also aliased as: old_glob
junction?(path) click to toggle source

Returns whether or not path is a junction.

# File lib/win32/dir.rb, line 379
def self.junction?(path)
  string_check(path)
  bool = true

  attrib = GetFileAttributesW("#{path}".wincode)

  # Only directories with a reparse point attribute can be junctions
  if attrib == INVALID_FILE_ATTRIBUTES ||
      attrib & FILE_ATTRIBUTE_DIRECTORY == 0 ||
      attrib & FILE_ATTRIBUTE_REPARSE_POINT == 0
    bool = false
  end

  bool
end
Also aliased as: reparse_dir?
old_glob(glob_pattern, flags = 0, &block)
Alias for: glob
old_ref(*glob_patterns)
Alias for: []
oldgetwd()
Alias for: getwd
oldpwd()
Alias for: pwd
pwd()
Also aliased as: oldpwd
Alias for: getwd
read_junction(junction) click to toggle source

Returns the path that a given junction points to. Raises an Errno::ENOENT error if the given path does not exist. Returns false if it is not a junction.

Example:

Dir.mkdir('C:/from')
Dir.create_junction('C:/to', 'C:/from')
Dir.read_junction("c:/to") # => "c:/from"
# File lib/win32/dir.rb, line 294
def self.read_junction(junction)
  return false unless Dir.junction?(junction)

  junction = string_check(junction).wincode

  junction_path = (0.chr * 1024).encode("UTF-16LE")

  length = GetFullPathNameW(junction, junction_path.size, junction_path, nil)

  if length == 0 || length > junction_path.size
    raise SystemCallError.new("GetFullPathNameW", FFI.errno)
  else
    junction_path.strip!
  end

  # Generic read & write + open existing + reparse point & backup semantics
  handle = CreateFileW(
    junction_path,
    GENERIC_READ | GENERIC_WRITE,
    0,
    nil,
    OPEN_EXISTING,
    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
    0
  )

  if handle == INVALID_HANDLE_VALUE
    raise SystemCallError.new("CreateFileW", FFI.errno)
  end

  rdb = REPARSE_JDATA_BUFFER.new
  rdb[:ReparseTag] = 0
  rdb[:ReparseDataLength] = 0
  rdb[:Reserved] = 0
  rdb[:SubstituteNameOffset] = 0
  rdb[:SubstituteNameLength] = 0
  rdb[:PrintNameOffset] = 0
  rdb[:PrintNameLength] = 0
  rdb[:PathBuffer] = ""

  bytes = FFI::MemoryPointer.new(:ulong)

  begin
    bool = DeviceIoControl(
      handle,
      CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, 0),
      nil,
      0,
      rdb,
      1024,
      bytes,
      nil
    )

    error = FFI.errno

    unless bool
      raise SystemCallError.new("DeviceIoControl", error)
    end
  ensure
    CloseHandle(handle)
  end

  # MSDN says print and substitute names can be in any order
  jname = (rdb[:PathBuffer].to_ptr + rdb[:SubstituteNameOffset]).read_string(rdb[:SubstituteNameLength])
  jname = jname.bytes.to_a.pack("C*")
  jname = jname.force_encoding("UTF-16LE")
  raise "Invalid junction name: #{jname.encode("UTF-8")}" unless jname[0..3] == "\\??\\".encode("UTF-16LE")

  begin
    File.expand_path(jname[4..-1].encode(Encoding.default_external))
  rescue Encoding::UndefinedConversionError
    File.expand_path(jname[4..-1].encode("UTF-8"))
  end
end
reparse_dir?(path)
Alias for: junction?

Private Class Methods

CTL_CODE(device, function, method, access) click to toggle source

Macro from Windows header file, used by the create_junction method.

# File lib/win32/dir.rb, line 415
def self.CTL_CODE(device, function, method, access)
  ((device) << 16) | ((access) << 14) | ((function) << 2) | (method)
end
string_check(arg) click to toggle source

Simulate MRI's contortions for a stringiness check.

# File lib/win32/dir.rb, line 405
def string_check(arg)
  return arg if arg.is_a?(String)
  return arg.send(:to_str) if arg.respond_to?(:to_str, true) # MRI honors it, even if private
  return arg.to_path if arg.respond_to?(:to_path)

  raise TypeError
end