win32-dir 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGES +121 -116
- data/MANIFEST +10 -8
- data/README +273 -273
- data/Rakefile +34 -38
- data/certs/djberg96_pub.pem +21 -0
- data/examples/dir_example.rb +22 -22
- data/lib/win32-dir.rb +1 -0
- data/lib/win32/dir.rb +417 -417
- data/lib/win32/dir/constants.rb +26 -26
- data/lib/win32/dir/functions.rb +66 -66
- data/lib/win32/dir/structs.rb +37 -37
- data/test/test_win32_dir.rb +466 -466
- data/win32-dir.gemspec +30 -29
- metadata +28 -4
- metadata.gz.sig +2 -0
data/Rakefile
CHANGED
@@ -1,38 +1,34 @@
|
|
1
|
-
require 'rake'
|
2
|
-
require 'rake/clean'
|
3
|
-
require 'rake/testtask'
|
4
|
-
|
5
|
-
CLEAN.include('**/*.gem', '**/*.log')
|
6
|
-
|
7
|
-
namespace 'gem' do
|
8
|
-
desc "Create the win32-dir gem"
|
9
|
-
task :create => [:clean] do
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
t.verbose = true
|
36
|
-
end
|
37
|
-
|
38
|
-
task :default => :test
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
CLEAN.include('**/*.gem', '**/*.log')
|
6
|
+
|
7
|
+
namespace 'gem' do
|
8
|
+
desc "Create the win32-dir gem"
|
9
|
+
task :create => [:clean] do
|
10
|
+
require 'rubygems/package'
|
11
|
+
Dir["*.gem"].each{ |f| File.delete(f) }
|
12
|
+
spec = eval(IO.read('win32-dir.gemspec'))
|
13
|
+
spec.signing_key = File.join(Dir.home, '.ssh', 'gem-private_key.pem')
|
14
|
+
Gem::Package.build(spec)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Install the win32-dir gem"
|
18
|
+
task :install => [:create] do
|
19
|
+
file = Dir["*.gem"].first
|
20
|
+
sh "gem install -l #{file}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "Run the example program"
|
25
|
+
task :example do
|
26
|
+
sh "ruby -Ilib examples/dir_example.rb"
|
27
|
+
end
|
28
|
+
|
29
|
+
Rake::TestTask.new do |t|
|
30
|
+
t.warning = true
|
31
|
+
t.verbose = true
|
32
|
+
end
|
33
|
+
|
34
|
+
task :default => :test
|
@@ -0,0 +1,21 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MREwDwYDVQQDDAhkamJl
|
3
|
+
cmc5NjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29t
|
4
|
+
MB4XDTE1MDkwMjIwNDkxOFoXDTE2MDkwMTIwNDkxOFowPzERMA8GA1UEAwwIZGpi
|
5
|
+
ZXJnOTYxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv
|
6
|
+
bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMyTkvXqRp6hLs9eoJOS
|
7
|
+
Hmi8kRYbq9Vkf15/hMxJpotYMgJVHHWrmDcC5Dye2PbnXjTkKf266Zw0PtT9h+lI
|
8
|
+
S3ts9HO+vaCFSMwFFZmnWJSpQ3CNw2RcHxjWkk9yF7imEM8Kz9ojhiDXzBetdV6M
|
9
|
+
gr0lV/alUr7TNVBDngbXEfTWscyXh1qd7xZ4EcOdsDktCe5G45N/o3662tPQvJsi
|
10
|
+
FOF0CM/KuBsa/HL1/eoEmF4B3EKIRfTHrQ3hu20Kv3RJ88QM4ec2+0dd97uX693O
|
11
|
+
zv6981fyEg+aXLkxrkViM/tz2qR2ZE0jPhHTREPYeMEgptRkTmWSKAuLVWrJEfgl
|
12
|
+
DtkCAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFEwe
|
13
|
+
nn6bfJADmuIDiMSOzedOrL+xMB0GA1UdEQQWMBSBEmRqYmVyZzk2QGdtYWlsLmNv
|
14
|
+
bTAdBgNVHRIEFjAUgRJkamJlcmc5NkBnbWFpbC5jb20wDQYJKoZIhvcNAQEFBQAD
|
15
|
+
ggEBAHmNOCWoDVD75zHFueY0viwGDVP1BNGFC+yXcb7u2GlK+nEMCORqzURbYPf7
|
16
|
+
tL+/hzmePIRz7i30UM//64GI1NLv9jl7nIwjhPpXpf7/lu2I9hOTsvwSumb5UiKC
|
17
|
+
/sqBxI3sfj9pr79Wpv4MuikX1XPik7Ncb7NPsJPw06Lvyc3Hkg5X2XpPtLtS+Gr2
|
18
|
+
wKJnmzb5rIPS1cmsqv0M9LPWflzfwoZ/SpnmhagP+g05p8bRNKjZSA2iImM/GyYZ
|
19
|
+
EJYzxdPOrx2n6NYR3Hk+vHP0U7UBSveI6+qx+ndQYaeyCn+GRX2PKS9h66YF/Q1V
|
20
|
+
tGSHgAmcLlkdGgan182qsE/4kKM=
|
21
|
+
-----END CERTIFICATE-----
|
data/examples/dir_example.rb
CHANGED
@@ -1,23 +1,23 @@
|
|
1
|
-
####################################################################
|
2
|
-
# dir_example.rb
|
3
|
-
#
|
4
|
-
# Generic test script for general futzing. Modify as you see fit.
|
5
|
-
# You can run this via the 'rake example' task.
|
6
|
-
####################################################################
|
7
|
-
require 'win32/dir'
|
8
|
-
|
9
|
-
puts "Admin Tools:\t\t" + Dir::ADMINTOOLS
|
10
|
-
puts "Common Admin Tools:\t" + Dir::COMMON_ADMINTOOLS
|
11
|
-
puts "App Data:\t\t" + Dir::APPDATA
|
12
|
-
puts "Common App Data:\t" + Dir::COMMON_APPDATA
|
13
|
-
puts "Common Documents:\t" + Dir::COMMON_DOCUMENTS
|
14
|
-
puts "Cookies:\t\t" + Dir::COOKIES
|
15
|
-
puts "History:\t\t" + Dir::HISTORY
|
16
|
-
puts "Internet Cache:\t\t" + Dir::INTERNET_CACHE
|
17
|
-
puts "Local App Data:\t\t" + Dir::LOCAL_APPDATA
|
18
|
-
puts "My Pictures:\t\t" + Dir::MYPICTURES
|
19
|
-
puts "Personal:\t\t" + Dir::PERSONAL
|
20
|
-
puts "Program Files:\t\t" + Dir::PROGRAM_FILES
|
21
|
-
puts "Program Files Common:\t" + Dir::PROGRAM_FILES_COMMON
|
22
|
-
puts "System:\t\t\t" + Dir::SYSTEM
|
1
|
+
####################################################################
|
2
|
+
# dir_example.rb
|
3
|
+
#
|
4
|
+
# Generic test script for general futzing. Modify as you see fit.
|
5
|
+
# You can run this via the 'rake example' task.
|
6
|
+
####################################################################
|
7
|
+
require 'win32/dir'
|
8
|
+
|
9
|
+
puts "Admin Tools:\t\t" + Dir::ADMINTOOLS
|
10
|
+
puts "Common Admin Tools:\t" + Dir::COMMON_ADMINTOOLS
|
11
|
+
puts "App Data:\t\t" + Dir::APPDATA
|
12
|
+
puts "Common App Data:\t" + Dir::COMMON_APPDATA
|
13
|
+
puts "Common Documents:\t" + Dir::COMMON_DOCUMENTS
|
14
|
+
puts "Cookies:\t\t" + Dir::COOKIES
|
15
|
+
puts "History:\t\t" + Dir::HISTORY
|
16
|
+
puts "Internet Cache:\t\t" + Dir::INTERNET_CACHE
|
17
|
+
puts "Local App Data:\t\t" + Dir::LOCAL_APPDATA
|
18
|
+
puts "My Pictures:\t\t" + Dir::MYPICTURES
|
19
|
+
puts "Personal:\t\t" + Dir::PERSONAL
|
20
|
+
puts "Program Files:\t\t" + Dir::PROGRAM_FILES
|
21
|
+
puts "Program Files Common:\t" + Dir::PROGRAM_FILES_COMMON
|
22
|
+
puts "System:\t\t\t" + Dir::SYSTEM
|
23
23
|
puts "Windows:\t\t" + Dir::WINDOWS
|
data/lib/win32-dir.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'win32/dir'
|
data/lib/win32/dir.rb
CHANGED
@@ -1,417 +1,417 @@
|
|
1
|
-
require_relative 'dir/constants'
|
2
|
-
require_relative 'dir/functions'
|
3
|
-
require_relative 'dir/structs'
|
4
|
-
|
5
|
-
class Dir
|
6
|
-
include Dir::Structs
|
7
|
-
include Dir::Constants
|
8
|
-
extend Dir::Functions
|
9
|
-
|
10
|
-
# The version of the win32-dir library.
|
11
|
-
VERSION = '0.5.
|
12
|
-
|
13
|
-
# CSIDL constants
|
14
|
-
csidl = Hash[
|
15
|
-
'DESKTOP', 0x0000,
|
16
|
-
'INTERNET', 0x0001,
|
17
|
-
'PROGRAMS', 0x0002,
|
18
|
-
'CONTROLS', 0x0003,
|
19
|
-
'PRINTERS', 0x0004,
|
20
|
-
'PERSONAL', 0x0005,
|
21
|
-
'FAVORITES', 0x0006,
|
22
|
-
'STARTUP', 0x0007,
|
23
|
-
'RECENT', 0x0008,
|
24
|
-
'SENDTO', 0x0009,
|
25
|
-
'BITBUCKET', 0x000a,
|
26
|
-
'STARTMENU', 0x000b,
|
27
|
-
'MYDOCUMENTS', 0x000c,
|
28
|
-
'MYMUSIC', 0x000d,
|
29
|
-
'MYVIDEO', 0x000e,
|
30
|
-
'DESKTOPDIRECTORY', 0x0010,
|
31
|
-
'DRIVES', 0x0011,
|
32
|
-
'NETWORK', 0x0012,
|
33
|
-
'NETHOOD', 0x0013,
|
34
|
-
'FONTS', 0x0014,
|
35
|
-
'TEMPLATES', 0x0015,
|
36
|
-
'COMMON_STARTMENU', 0x0016,
|
37
|
-
'COMMON_PROGRAMS', 0X0017,
|
38
|
-
'COMMON_STARTUP', 0x0018,
|
39
|
-
'COMMON_FAVORITES', 0x001f,
|
40
|
-
'COMMON_DESKTOPDIRECTORY', 0x0019,
|
41
|
-
'APPDATA', 0x001a,
|
42
|
-
'PRINTHOOD', 0x001b,
|
43
|
-
'LOCAL_APPDATA', 0x001c,
|
44
|
-
'ALTSTARTUP', 0x001d,
|
45
|
-
'COMMON_ALTSTARTUP', 0x001e,
|
46
|
-
'INTERNET_CACHE', 0x0020,
|
47
|
-
'COOKIES', 0x0021,
|
48
|
-
'HISTORY', 0x0022,
|
49
|
-
'COMMON_APPDATA', 0x0023,
|
50
|
-
'WINDOWS', 0x0024,
|
51
|
-
'SYSTEM', 0x0025,
|
52
|
-
'PROGRAM_FILES', 0x0026,
|
53
|
-
'MYPICTURES', 0x0027,
|
54
|
-
'PROFILE', 0x0028,
|
55
|
-
'SYSTEMX86', 0x0029,
|
56
|
-
'PROGRAM_FILESX86', 0x002a,
|
57
|
-
'PROGRAM_FILES_COMMON', 0x002b,
|
58
|
-
'PROGRAM_FILES_COMMONX86', 0x002c,
|
59
|
-
'COMMON_TEMPLATES', 0x002d,
|
60
|
-
'COMMON_DOCUMENTS', 0x002e,
|
61
|
-
'CONNECTIONS', 0x0031,
|
62
|
-
'COMMON_MUSIC', 0x0035,
|
63
|
-
'COMMON_PICTURES', 0x0036,
|
64
|
-
'COMMON_VIDEO', 0x0037,
|
65
|
-
'RESOURCES', 0x0038,
|
66
|
-
'RESOURCES_LOCALIZED', 0x0039,
|
67
|
-
'COMMON_OEM_LINKS', 0x003a,
|
68
|
-
'CDBURN_AREA', 0x003b,
|
69
|
-
'COMMON_ADMINTOOLS', 0x002f,
|
70
|
-
'ADMINTOOLS', 0x0030
|
71
|
-
]
|
72
|
-
|
73
|
-
# Dynamically set each of the CSIDL constants
|
74
|
-
csidl.each{ |key, value|
|
75
|
-
buf = 0.chr * 1024
|
76
|
-
path = nil
|
77
|
-
buf.encode!('UTF-16LE')
|
78
|
-
|
79
|
-
if SHGetFolderPathW(0, value, 0, 0, buf) == 0 # Current path
|
80
|
-
path = buf.strip
|
81
|
-
elsif SHGetFolderPathW(0, value, 0, 1, buf) == 0 # Default path
|
82
|
-
path = buf.strip
|
83
|
-
else
|
84
|
-
ptr = FFI::MemoryPointer.new(:long)
|
85
|
-
info = SHFILEINFO.new
|
86
|
-
flags = SHGFI_DISPLAYNAME | SHGFI_PIDL
|
87
|
-
|
88
|
-
if SHGetFolderLocation(0, value, 0, 0, ptr) == 0
|
89
|
-
if SHGetFileInfo(ptr.read_long, 0, info, info.size, flags) != 0
|
90
|
-
path = info[:szDisplayName].to_s
|
91
|
-
path.force_encoding(Encoding.default_external)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
begin
|
97
|
-
Dir.const_set(key, path.encode(Encoding.default_external)) if path
|
98
|
-
rescue Encoding::UndefinedConversionError
|
99
|
-
Dir.const_set(key, path.encode('UTF-8')) if path
|
100
|
-
end
|
101
|
-
}
|
102
|
-
|
103
|
-
# Set Dir::MYDOCUMENTS to the same as Dir::PERSONAL if undefined
|
104
|
-
unless defined? MYDOCUMENTS
|
105
|
-
MYDOCUMENTS = PERSONAL
|
106
|
-
end
|
107
|
-
|
108
|
-
class << self
|
109
|
-
alias old_glob glob
|
110
|
-
|
111
|
-
# Same as the standard MRI Dir.glob method except that it handles
|
112
|
-
# backslashes in path names.
|
113
|
-
#
|
114
|
-
def glob(glob_pattern, flags = 0, &block)
|
115
|
-
if glob_pattern.is_a?(Array)
|
116
|
-
temp = glob_pattern.map!{ |pattern| string_check(pattern).tr("\\", "/") }
|
117
|
-
else
|
118
|
-
temp = string_check(glob_pattern).tr("\\", "/")
|
119
|
-
end
|
120
|
-
|
121
|
-
old_glob(temp, flags, &block)
|
122
|
-
end
|
123
|
-
|
124
|
-
alias old_ref []
|
125
|
-
|
126
|
-
# Same as the standard MRI Dir[] method except that it handles
|
127
|
-
# backslashes in path names.
|
128
|
-
#
|
129
|
-
def [](*glob_patterns)
|
130
|
-
temp = glob_patterns.map!{ |pattern| "#{pattern}".tr("\\", "/") }
|
131
|
-
old_ref(*temp)
|
132
|
-
end
|
133
|
-
|
134
|
-
# JRuby normalizes the path by default.
|
135
|
-
unless RUBY_PLATFORM == 'java'
|
136
|
-
alias oldgetwd getwd
|
137
|
-
alias oldpwd pwd
|
138
|
-
|
139
|
-
# Returns the present working directory. Unlike MRI, this method always
|
140
|
-
# normalizes the path.
|
141
|
-
#
|
142
|
-
# Examples:
|
143
|
-
#
|
144
|
-
# Dir.chdir("C:/Progra~1")
|
145
|
-
# Dir.getwd # => C:\Program Files
|
146
|
-
#
|
147
|
-
# Dir.chdir("C:/PROGRAM FILES")
|
148
|
-
# Dir.getwd # => C:\Program Files
|
149
|
-
#
|
150
|
-
def getwd
|
151
|
-
path1 = FFI::Buffer.new(:wint_t, 1024, true)
|
152
|
-
path2 = FFI::Buffer.new(:wint_t, 1024, true)
|
153
|
-
path3 = FFI::Buffer.new(:wint_t, 1024, true)
|
154
|
-
|
155
|
-
length = GetCurrentDirectoryW(path1.size, path1)
|
156
|
-
|
157
|
-
if length == 0 || length > path1.size
|
158
|
-
raise SystemCallError.new("GetCurrentDirectoryW", FFI.errno)
|
159
|
-
end
|
160
|
-
|
161
|
-
length = GetShortPathNameW(path1, path2, path2.size)
|
162
|
-
|
163
|
-
if length == 0 || length > path2.size
|
164
|
-
raise SystemCallError.new("GetShortPathNameW", FFI.errno)
|
165
|
-
end
|
166
|
-
|
167
|
-
length = GetLongPathNameW(path2, path3, path3.size)
|
168
|
-
|
169
|
-
if length == 0 || length > path3.size
|
170
|
-
raise SystemCallError.new("GetLongPathNameW", FFI.errno)
|
171
|
-
end
|
172
|
-
|
173
|
-
path = path3.read_bytes(length * 2).wstrip
|
174
|
-
|
175
|
-
begin
|
176
|
-
path.encode(Encoding.default_external)
|
177
|
-
rescue Encoding::UndefinedConversionError
|
178
|
-
path.encode('UTF-8')
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
alias :pwd :getwd
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
# Creates the symlink +to+, linked to the existing directory +from+. If the
|
187
|
-
# +to+ directory already exists, it must be empty or an error is raised.
|
188
|
-
#
|
189
|
-
# Example:
|
190
|
-
#
|
191
|
-
# Dir.mkdir('C:/from')
|
192
|
-
# Dir.create_junction('C:/to', 'C:/from')
|
193
|
-
#
|
194
|
-
def self.create_junction(to, from)
|
195
|
-
to = string_check(to).wincode
|
196
|
-
from = string_check(from).wincode
|
197
|
-
|
198
|
-
from_path = (0.chr * 1024).encode('UTF-16LE')
|
199
|
-
|
200
|
-
length = GetFullPathNameW(from, from_path.size, from_path, nil)
|
201
|
-
|
202
|
-
if length == 0 || length > from_path.size
|
203
|
-
raise SystemCallError.new("GetFullPathNameW", FFI.errno)
|
204
|
-
else
|
205
|
-
from_path.strip!
|
206
|
-
end
|
207
|
-
|
208
|
-
to_path = (0.chr * 1024).encode('UTF-16LE')
|
209
|
-
|
210
|
-
length = GetFullPathNameW(to, to_path.size, to_path, nil)
|
211
|
-
|
212
|
-
if length == 0 || length > to_path.size
|
213
|
-
raise SystemCallError.new("GetFullPathNameW", FFI.errno)
|
214
|
-
else
|
215
|
-
to_path.strip!
|
216
|
-
end
|
217
|
-
|
218
|
-
# You can create a junction to a directory that already exists, so
|
219
|
-
# long as it's empty.
|
220
|
-
unless CreateDirectoryW(to_path, nil)
|
221
|
-
if FFI.errno != ERROR_ALREADY_EXISTS
|
222
|
-
raise SystemCallError.new("CreateDirectoryW", FFI.errno)
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
begin
|
227
|
-
# Generic read & write + open existing + reparse point & backup semantics
|
228
|
-
handle = CreateFileW(
|
229
|
-
to_path,
|
230
|
-
GENERIC_READ | GENERIC_WRITE,
|
231
|
-
0,
|
232
|
-
nil,
|
233
|
-
OPEN_EXISTING,
|
234
|
-
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
|
235
|
-
0
|
236
|
-
)
|
237
|
-
|
238
|
-
if handle == INVALID_HANDLE_VALUE
|
239
|
-
raise SystemCallError.new("CreateFileW", FFI.errno)
|
240
|
-
end
|
241
|
-
|
242
|
-
target = "\\??\\".encode('UTF-16LE') + from_path
|
243
|
-
|
244
|
-
rdb = REPARSE_JDATA_BUFFER.new
|
245
|
-
rdb[:ReparseTag] = 2684354563 # IO_REPARSE_TAG_MOUNT_POINT
|
246
|
-
rdb[:ReparseDataLength] = target.bytesize + 12
|
247
|
-
rdb[:Reserved] = 0
|
248
|
-
rdb[:SubstituteNameOffset] = 0
|
249
|
-
rdb[:SubstituteNameLength] = target.bytesize
|
250
|
-
rdb[:PrintNameOffset] = target.bytesize + 2
|
251
|
-
rdb[:PrintNameLength] = 0
|
252
|
-
rdb[:PathBuffer] = target
|
253
|
-
|
254
|
-
bytes = FFI::MemoryPointer.new(:ulong)
|
255
|
-
|
256
|
-
begin
|
257
|
-
bool = DeviceIoControl(
|
258
|
-
handle,
|
259
|
-
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, 0),
|
260
|
-
rdb,
|
261
|
-
rdb[:ReparseDataLength] + rdb.header_size,
|
262
|
-
nil,
|
263
|
-
0,
|
264
|
-
bytes,
|
265
|
-
nil
|
266
|
-
)
|
267
|
-
|
268
|
-
error = FFI.errno
|
269
|
-
|
270
|
-
unless bool
|
271
|
-
RemoveDirectoryW(to_path)
|
272
|
-
raise SystemCallError.new("DeviceIoControl", error)
|
273
|
-
end
|
274
|
-
ensure
|
275
|
-
CloseHandle(handle)
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
self
|
280
|
-
end
|
281
|
-
|
282
|
-
# Returns the path that a given junction points to. Raises an
|
283
|
-
# Errno::ENOENT error if the given path does not exist. Returns false
|
284
|
-
# if it is not a junction.
|
285
|
-
#
|
286
|
-
# Example:
|
287
|
-
#
|
288
|
-
# Dir.mkdir('C:/from')
|
289
|
-
# Dir.create_junction('C:/to', 'C:/from')
|
290
|
-
# Dir.read_junction("c:/to") # => "c:/from"
|
291
|
-
#
|
292
|
-
def self.read_junction(junction)
|
293
|
-
return false unless Dir.junction?(junction)
|
294
|
-
|
295
|
-
junction = string_check(junction).wincode
|
296
|
-
|
297
|
-
junction_path = (0.chr * 1024).encode('UTF-16LE')
|
298
|
-
|
299
|
-
length = GetFullPathNameW(junction, junction_path.size, junction_path, nil)
|
300
|
-
|
301
|
-
if length == 0 || length > junction_path.size
|
302
|
-
raise SystemCallError.new("GetFullPathNameW", FFI.errno)
|
303
|
-
else
|
304
|
-
junction_path.strip!
|
305
|
-
end
|
306
|
-
|
307
|
-
begin
|
308
|
-
# Generic read & write + open existing + reparse point & backup semantics
|
309
|
-
handle = CreateFileW(
|
310
|
-
junction_path,
|
311
|
-
GENERIC_READ | GENERIC_WRITE,
|
312
|
-
0,
|
313
|
-
nil,
|
314
|
-
OPEN_EXISTING,
|
315
|
-
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
|
316
|
-
0
|
317
|
-
)
|
318
|
-
|
319
|
-
if handle == INVALID_HANDLE_VALUE
|
320
|
-
raise SystemCallError.new("CreateFileW", FFI.errno)
|
321
|
-
end
|
322
|
-
|
323
|
-
rdb = REPARSE_JDATA_BUFFER.new
|
324
|
-
rdb[:ReparseTag] = 0
|
325
|
-
rdb[:ReparseDataLength] = 0
|
326
|
-
rdb[:Reserved] = 0
|
327
|
-
rdb[:SubstituteNameOffset] = 0
|
328
|
-
rdb[:SubstituteNameLength] = 0
|
329
|
-
rdb[:PrintNameOffset] = 0
|
330
|
-
rdb[:PrintNameLength] = 0
|
331
|
-
rdb[:PathBuffer] = ''
|
332
|
-
|
333
|
-
bytes = FFI::MemoryPointer.new(:ulong)
|
334
|
-
|
335
|
-
begin
|
336
|
-
bool = DeviceIoControl(
|
337
|
-
handle,
|
338
|
-
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, 0),
|
339
|
-
nil,
|
340
|
-
0,
|
341
|
-
rdb,
|
342
|
-
1024,
|
343
|
-
bytes,
|
344
|
-
nil
|
345
|
-
)
|
346
|
-
|
347
|
-
error = FFI.errno
|
348
|
-
|
349
|
-
unless bool
|
350
|
-
raise SystemCallError.new("DeviceIoControl", error)
|
351
|
-
end
|
352
|
-
ensure
|
353
|
-
CloseHandle(handle)
|
354
|
-
end
|
355
|
-
end
|
356
|
-
|
357
|
-
# MSDN says print and substitute names can be in any order
|
358
|
-
jname = (rdb[:PathBuffer].to_ptr + rdb[:SubstituteNameOffset]).read_string(rdb[:SubstituteNameLength])
|
359
|
-
jname = jname.bytes.to_a.pack('C*')
|
360
|
-
jname = jname.force_encoding("UTF-16LE")
|
361
|
-
raise "Invalid junction name: #{jname.encode('UTF-8')}" unless jname[0..3] == "\\??\\".encode("UTF-16LE")
|
362
|
-
begin
|
363
|
-
File.expand_path(jname[4..-1].encode(Encoding.default_external))
|
364
|
-
rescue Encoding::UndefinedConversionError
|
365
|
-
File.expand_path(jname[4..-1].encode('UTF-8'))
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
|
-
# Returns whether or not +path+ is empty. Returns false if +path+ is not
|
370
|
-
# a directory, or contains any files other than '.' or '..'.
|
371
|
-
#
|
372
|
-
def self.empty?(path)
|
373
|
-
PathIsDirectoryEmptyW("#{path}".wincode)
|
374
|
-
end
|
375
|
-
|
376
|
-
# Returns whether or not +path+ is a junction.
|
377
|
-
#
|
378
|
-
def self.junction?(path)
|
379
|
-
string_check(path)
|
380
|
-
bool = true
|
381
|
-
|
382
|
-
attrib = GetFileAttributesW("#{path}".wincode)
|
383
|
-
|
384
|
-
# Only directories with a reparse point attribute can be junctions
|
385
|
-
if attrib == INVALID_FILE_ATTRIBUTES ||
|
386
|
-
attrib & FILE_ATTRIBUTE_DIRECTORY == 0 ||
|
387
|
-
attrib & FILE_ATTRIBUTE_REPARSE_POINT == 0
|
388
|
-
then
|
389
|
-
bool = false
|
390
|
-
end
|
391
|
-
|
392
|
-
bool
|
393
|
-
end
|
394
|
-
|
395
|
-
# Class level aliases
|
396
|
-
#
|
397
|
-
class << self
|
398
|
-
alias reparse_dir? junction?
|
399
|
-
end
|
400
|
-
|
401
|
-
private
|
402
|
-
|
403
|
-
class << self
|
404
|
-
# Simulate MRI's contortions for a stringiness check.
|
405
|
-
def string_check(arg)
|
406
|
-
return arg if arg.is_a?(String)
|
407
|
-
return arg.send(:to_str) if arg.respond_to?(:to_str, true) # MRI honors it, even if private
|
408
|
-
return arg.to_path if arg.respond_to?(:to_path)
|
409
|
-
raise TypeError
|
410
|
-
end
|
411
|
-
end
|
412
|
-
|
413
|
-
# Macro from Windows header file, used by the create_junction method.
|
414
|
-
def self.CTL_CODE(device, function, method, access)
|
415
|
-
((device) << 16) | ((access) << 14) | ((function) << 2) | (method)
|
416
|
-
end
|
417
|
-
end
|
1
|
+
require_relative 'dir/constants'
|
2
|
+
require_relative 'dir/functions'
|
3
|
+
require_relative 'dir/structs'
|
4
|
+
|
5
|
+
class Dir
|
6
|
+
include Dir::Structs
|
7
|
+
include Dir::Constants
|
8
|
+
extend Dir::Functions
|
9
|
+
|
10
|
+
# The version of the win32-dir library.
|
11
|
+
VERSION = '0.5.1'
|
12
|
+
|
13
|
+
# CSIDL constants
|
14
|
+
csidl = Hash[
|
15
|
+
'DESKTOP', 0x0000,
|
16
|
+
'INTERNET', 0x0001,
|
17
|
+
'PROGRAMS', 0x0002,
|
18
|
+
'CONTROLS', 0x0003,
|
19
|
+
'PRINTERS', 0x0004,
|
20
|
+
'PERSONAL', 0x0005,
|
21
|
+
'FAVORITES', 0x0006,
|
22
|
+
'STARTUP', 0x0007,
|
23
|
+
'RECENT', 0x0008,
|
24
|
+
'SENDTO', 0x0009,
|
25
|
+
'BITBUCKET', 0x000a,
|
26
|
+
'STARTMENU', 0x000b,
|
27
|
+
'MYDOCUMENTS', 0x000c,
|
28
|
+
'MYMUSIC', 0x000d,
|
29
|
+
'MYVIDEO', 0x000e,
|
30
|
+
'DESKTOPDIRECTORY', 0x0010,
|
31
|
+
'DRIVES', 0x0011,
|
32
|
+
'NETWORK', 0x0012,
|
33
|
+
'NETHOOD', 0x0013,
|
34
|
+
'FONTS', 0x0014,
|
35
|
+
'TEMPLATES', 0x0015,
|
36
|
+
'COMMON_STARTMENU', 0x0016,
|
37
|
+
'COMMON_PROGRAMS', 0X0017,
|
38
|
+
'COMMON_STARTUP', 0x0018,
|
39
|
+
'COMMON_FAVORITES', 0x001f,
|
40
|
+
'COMMON_DESKTOPDIRECTORY', 0x0019,
|
41
|
+
'APPDATA', 0x001a,
|
42
|
+
'PRINTHOOD', 0x001b,
|
43
|
+
'LOCAL_APPDATA', 0x001c,
|
44
|
+
'ALTSTARTUP', 0x001d,
|
45
|
+
'COMMON_ALTSTARTUP', 0x001e,
|
46
|
+
'INTERNET_CACHE', 0x0020,
|
47
|
+
'COOKIES', 0x0021,
|
48
|
+
'HISTORY', 0x0022,
|
49
|
+
'COMMON_APPDATA', 0x0023,
|
50
|
+
'WINDOWS', 0x0024,
|
51
|
+
'SYSTEM', 0x0025,
|
52
|
+
'PROGRAM_FILES', 0x0026,
|
53
|
+
'MYPICTURES', 0x0027,
|
54
|
+
'PROFILE', 0x0028,
|
55
|
+
'SYSTEMX86', 0x0029,
|
56
|
+
'PROGRAM_FILESX86', 0x002a,
|
57
|
+
'PROGRAM_FILES_COMMON', 0x002b,
|
58
|
+
'PROGRAM_FILES_COMMONX86', 0x002c,
|
59
|
+
'COMMON_TEMPLATES', 0x002d,
|
60
|
+
'COMMON_DOCUMENTS', 0x002e,
|
61
|
+
'CONNECTIONS', 0x0031,
|
62
|
+
'COMMON_MUSIC', 0x0035,
|
63
|
+
'COMMON_PICTURES', 0x0036,
|
64
|
+
'COMMON_VIDEO', 0x0037,
|
65
|
+
'RESOURCES', 0x0038,
|
66
|
+
'RESOURCES_LOCALIZED', 0x0039,
|
67
|
+
'COMMON_OEM_LINKS', 0x003a,
|
68
|
+
'CDBURN_AREA', 0x003b,
|
69
|
+
'COMMON_ADMINTOOLS', 0x002f,
|
70
|
+
'ADMINTOOLS', 0x0030
|
71
|
+
]
|
72
|
+
|
73
|
+
# Dynamically set each of the CSIDL constants
|
74
|
+
csidl.each{ |key, value|
|
75
|
+
buf = 0.chr * 1024
|
76
|
+
path = nil
|
77
|
+
buf.encode!('UTF-16LE')
|
78
|
+
|
79
|
+
if SHGetFolderPathW(0, value, 0, 0, buf) == 0 # Current path
|
80
|
+
path = buf.strip
|
81
|
+
elsif SHGetFolderPathW(0, value, 0, 1, buf) == 0 # Default path
|
82
|
+
path = buf.strip
|
83
|
+
else
|
84
|
+
ptr = FFI::MemoryPointer.new(:long)
|
85
|
+
info = SHFILEINFO.new
|
86
|
+
flags = SHGFI_DISPLAYNAME | SHGFI_PIDL
|
87
|
+
|
88
|
+
if SHGetFolderLocation(0, value, 0, 0, ptr) == 0
|
89
|
+
if SHGetFileInfo(ptr.read_long, 0, info, info.size, flags) != 0
|
90
|
+
path = info[:szDisplayName].to_s
|
91
|
+
path.force_encoding(Encoding.default_external)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
begin
|
97
|
+
Dir.const_set(key, path.encode(Encoding.default_external)) if path
|
98
|
+
rescue Encoding::UndefinedConversionError
|
99
|
+
Dir.const_set(key, path.encode('UTF-8')) if path
|
100
|
+
end
|
101
|
+
}
|
102
|
+
|
103
|
+
# Set Dir::MYDOCUMENTS to the same as Dir::PERSONAL if undefined
|
104
|
+
unless defined? MYDOCUMENTS
|
105
|
+
MYDOCUMENTS = PERSONAL
|
106
|
+
end
|
107
|
+
|
108
|
+
class << self
|
109
|
+
alias old_glob glob
|
110
|
+
|
111
|
+
# Same as the standard MRI Dir.glob method except that it handles
|
112
|
+
# backslashes in path names.
|
113
|
+
#
|
114
|
+
def glob(glob_pattern, flags = 0, &block)
|
115
|
+
if glob_pattern.is_a?(Array)
|
116
|
+
temp = glob_pattern.map!{ |pattern| string_check(pattern).tr("\\", "/") }
|
117
|
+
else
|
118
|
+
temp = string_check(glob_pattern).tr("\\", "/")
|
119
|
+
end
|
120
|
+
|
121
|
+
old_glob(temp, flags, &block)
|
122
|
+
end
|
123
|
+
|
124
|
+
alias old_ref []
|
125
|
+
|
126
|
+
# Same as the standard MRI Dir[] method except that it handles
|
127
|
+
# backslashes in path names.
|
128
|
+
#
|
129
|
+
def [](*glob_patterns)
|
130
|
+
temp = glob_patterns.map!{ |pattern| "#{pattern}".tr("\\", "/") }
|
131
|
+
old_ref(*temp)
|
132
|
+
end
|
133
|
+
|
134
|
+
# JRuby normalizes the path by default.
|
135
|
+
unless RUBY_PLATFORM == 'java'
|
136
|
+
alias oldgetwd getwd
|
137
|
+
alias oldpwd pwd
|
138
|
+
|
139
|
+
# Returns the present working directory. Unlike MRI, this method always
|
140
|
+
# normalizes the path.
|
141
|
+
#
|
142
|
+
# Examples:
|
143
|
+
#
|
144
|
+
# Dir.chdir("C:/Progra~1")
|
145
|
+
# Dir.getwd # => C:\Program Files
|
146
|
+
#
|
147
|
+
# Dir.chdir("C:/PROGRAM FILES")
|
148
|
+
# Dir.getwd # => C:\Program Files
|
149
|
+
#
|
150
|
+
def getwd
|
151
|
+
path1 = FFI::Buffer.new(:wint_t, 1024, true)
|
152
|
+
path2 = FFI::Buffer.new(:wint_t, 1024, true)
|
153
|
+
path3 = FFI::Buffer.new(:wint_t, 1024, true)
|
154
|
+
|
155
|
+
length = GetCurrentDirectoryW(path1.size, path1)
|
156
|
+
|
157
|
+
if length == 0 || length > path1.size
|
158
|
+
raise SystemCallError.new("GetCurrentDirectoryW", FFI.errno)
|
159
|
+
end
|
160
|
+
|
161
|
+
length = GetShortPathNameW(path1, path2, path2.size)
|
162
|
+
|
163
|
+
if length == 0 || length > path2.size
|
164
|
+
raise SystemCallError.new("GetShortPathNameW", FFI.errno)
|
165
|
+
end
|
166
|
+
|
167
|
+
length = GetLongPathNameW(path2, path3, path3.size)
|
168
|
+
|
169
|
+
if length == 0 || length > path3.size
|
170
|
+
raise SystemCallError.new("GetLongPathNameW", FFI.errno)
|
171
|
+
end
|
172
|
+
|
173
|
+
path = path3.read_bytes(length * 2).wstrip
|
174
|
+
|
175
|
+
begin
|
176
|
+
path.encode(Encoding.default_external)
|
177
|
+
rescue Encoding::UndefinedConversionError
|
178
|
+
path.encode('UTF-8')
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
alias :pwd :getwd
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Creates the symlink +to+, linked to the existing directory +from+. If the
|
187
|
+
# +to+ directory already exists, it must be empty or an error is raised.
|
188
|
+
#
|
189
|
+
# Example:
|
190
|
+
#
|
191
|
+
# Dir.mkdir('C:/from')
|
192
|
+
# Dir.create_junction('C:/to', 'C:/from')
|
193
|
+
#
|
194
|
+
def self.create_junction(to, from)
|
195
|
+
to = string_check(to).wincode
|
196
|
+
from = string_check(from).wincode
|
197
|
+
|
198
|
+
from_path = (0.chr * 1024).encode('UTF-16LE')
|
199
|
+
|
200
|
+
length = GetFullPathNameW(from, from_path.size, from_path, nil)
|
201
|
+
|
202
|
+
if length == 0 || length > from_path.size
|
203
|
+
raise SystemCallError.new("GetFullPathNameW", FFI.errno)
|
204
|
+
else
|
205
|
+
from_path.strip!
|
206
|
+
end
|
207
|
+
|
208
|
+
to_path = (0.chr * 1024).encode('UTF-16LE')
|
209
|
+
|
210
|
+
length = GetFullPathNameW(to, to_path.size, to_path, nil)
|
211
|
+
|
212
|
+
if length == 0 || length > to_path.size
|
213
|
+
raise SystemCallError.new("GetFullPathNameW", FFI.errno)
|
214
|
+
else
|
215
|
+
to_path.strip!
|
216
|
+
end
|
217
|
+
|
218
|
+
# You can create a junction to a directory that already exists, so
|
219
|
+
# long as it's empty.
|
220
|
+
unless CreateDirectoryW(to_path, nil)
|
221
|
+
if FFI.errno != ERROR_ALREADY_EXISTS
|
222
|
+
raise SystemCallError.new("CreateDirectoryW", FFI.errno)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
begin
|
227
|
+
# Generic read & write + open existing + reparse point & backup semantics
|
228
|
+
handle = CreateFileW(
|
229
|
+
to_path,
|
230
|
+
GENERIC_READ | GENERIC_WRITE,
|
231
|
+
0,
|
232
|
+
nil,
|
233
|
+
OPEN_EXISTING,
|
234
|
+
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
|
235
|
+
0
|
236
|
+
)
|
237
|
+
|
238
|
+
if handle == INVALID_HANDLE_VALUE
|
239
|
+
raise SystemCallError.new("CreateFileW", FFI.errno)
|
240
|
+
end
|
241
|
+
|
242
|
+
target = "\\??\\".encode('UTF-16LE') + from_path
|
243
|
+
|
244
|
+
rdb = REPARSE_JDATA_BUFFER.new
|
245
|
+
rdb[:ReparseTag] = 2684354563 # IO_REPARSE_TAG_MOUNT_POINT
|
246
|
+
rdb[:ReparseDataLength] = target.bytesize + 12
|
247
|
+
rdb[:Reserved] = 0
|
248
|
+
rdb[:SubstituteNameOffset] = 0
|
249
|
+
rdb[:SubstituteNameLength] = target.bytesize
|
250
|
+
rdb[:PrintNameOffset] = target.bytesize + 2
|
251
|
+
rdb[:PrintNameLength] = 0
|
252
|
+
rdb[:PathBuffer] = target
|
253
|
+
|
254
|
+
bytes = FFI::MemoryPointer.new(:ulong)
|
255
|
+
|
256
|
+
begin
|
257
|
+
bool = DeviceIoControl(
|
258
|
+
handle,
|
259
|
+
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, 0),
|
260
|
+
rdb,
|
261
|
+
rdb[:ReparseDataLength] + rdb.header_size,
|
262
|
+
nil,
|
263
|
+
0,
|
264
|
+
bytes,
|
265
|
+
nil
|
266
|
+
)
|
267
|
+
|
268
|
+
error = FFI.errno
|
269
|
+
|
270
|
+
unless bool
|
271
|
+
RemoveDirectoryW(to_path)
|
272
|
+
raise SystemCallError.new("DeviceIoControl", error)
|
273
|
+
end
|
274
|
+
ensure
|
275
|
+
CloseHandle(handle)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
self
|
280
|
+
end
|
281
|
+
|
282
|
+
# Returns the path that a given junction points to. Raises an
|
283
|
+
# Errno::ENOENT error if the given path does not exist. Returns false
|
284
|
+
# if it is not a junction.
|
285
|
+
#
|
286
|
+
# Example:
|
287
|
+
#
|
288
|
+
# Dir.mkdir('C:/from')
|
289
|
+
# Dir.create_junction('C:/to', 'C:/from')
|
290
|
+
# Dir.read_junction("c:/to") # => "c:/from"
|
291
|
+
#
|
292
|
+
def self.read_junction(junction)
|
293
|
+
return false unless Dir.junction?(junction)
|
294
|
+
|
295
|
+
junction = string_check(junction).wincode
|
296
|
+
|
297
|
+
junction_path = (0.chr * 1024).encode('UTF-16LE')
|
298
|
+
|
299
|
+
length = GetFullPathNameW(junction, junction_path.size, junction_path, nil)
|
300
|
+
|
301
|
+
if length == 0 || length > junction_path.size
|
302
|
+
raise SystemCallError.new("GetFullPathNameW", FFI.errno)
|
303
|
+
else
|
304
|
+
junction_path.strip!
|
305
|
+
end
|
306
|
+
|
307
|
+
begin
|
308
|
+
# Generic read & write + open existing + reparse point & backup semantics
|
309
|
+
handle = CreateFileW(
|
310
|
+
junction_path,
|
311
|
+
GENERIC_READ | GENERIC_WRITE,
|
312
|
+
0,
|
313
|
+
nil,
|
314
|
+
OPEN_EXISTING,
|
315
|
+
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
|
316
|
+
0
|
317
|
+
)
|
318
|
+
|
319
|
+
if handle == INVALID_HANDLE_VALUE
|
320
|
+
raise SystemCallError.new("CreateFileW", FFI.errno)
|
321
|
+
end
|
322
|
+
|
323
|
+
rdb = REPARSE_JDATA_BUFFER.new
|
324
|
+
rdb[:ReparseTag] = 0
|
325
|
+
rdb[:ReparseDataLength] = 0
|
326
|
+
rdb[:Reserved] = 0
|
327
|
+
rdb[:SubstituteNameOffset] = 0
|
328
|
+
rdb[:SubstituteNameLength] = 0
|
329
|
+
rdb[:PrintNameOffset] = 0
|
330
|
+
rdb[:PrintNameLength] = 0
|
331
|
+
rdb[:PathBuffer] = ''
|
332
|
+
|
333
|
+
bytes = FFI::MemoryPointer.new(:ulong)
|
334
|
+
|
335
|
+
begin
|
336
|
+
bool = DeviceIoControl(
|
337
|
+
handle,
|
338
|
+
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, 0),
|
339
|
+
nil,
|
340
|
+
0,
|
341
|
+
rdb,
|
342
|
+
1024,
|
343
|
+
bytes,
|
344
|
+
nil
|
345
|
+
)
|
346
|
+
|
347
|
+
error = FFI.errno
|
348
|
+
|
349
|
+
unless bool
|
350
|
+
raise SystemCallError.new("DeviceIoControl", error)
|
351
|
+
end
|
352
|
+
ensure
|
353
|
+
CloseHandle(handle)
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
# MSDN says print and substitute names can be in any order
|
358
|
+
jname = (rdb[:PathBuffer].to_ptr + rdb[:SubstituteNameOffset]).read_string(rdb[:SubstituteNameLength])
|
359
|
+
jname = jname.bytes.to_a.pack('C*')
|
360
|
+
jname = jname.force_encoding("UTF-16LE")
|
361
|
+
raise "Invalid junction name: #{jname.encode('UTF-8')}" unless jname[0..3] == "\\??\\".encode("UTF-16LE")
|
362
|
+
begin
|
363
|
+
File.expand_path(jname[4..-1].encode(Encoding.default_external))
|
364
|
+
rescue Encoding::UndefinedConversionError
|
365
|
+
File.expand_path(jname[4..-1].encode('UTF-8'))
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
# Returns whether or not +path+ is empty. Returns false if +path+ is not
|
370
|
+
# a directory, or contains any files other than '.' or '..'.
|
371
|
+
#
|
372
|
+
def self.empty?(path)
|
373
|
+
PathIsDirectoryEmptyW("#{path}".wincode)
|
374
|
+
end
|
375
|
+
|
376
|
+
# Returns whether or not +path+ is a junction.
|
377
|
+
#
|
378
|
+
def self.junction?(path)
|
379
|
+
string_check(path)
|
380
|
+
bool = true
|
381
|
+
|
382
|
+
attrib = GetFileAttributesW("#{path}".wincode)
|
383
|
+
|
384
|
+
# Only directories with a reparse point attribute can be junctions
|
385
|
+
if attrib == INVALID_FILE_ATTRIBUTES ||
|
386
|
+
attrib & FILE_ATTRIBUTE_DIRECTORY == 0 ||
|
387
|
+
attrib & FILE_ATTRIBUTE_REPARSE_POINT == 0
|
388
|
+
then
|
389
|
+
bool = false
|
390
|
+
end
|
391
|
+
|
392
|
+
bool
|
393
|
+
end
|
394
|
+
|
395
|
+
# Class level aliases
|
396
|
+
#
|
397
|
+
class << self
|
398
|
+
alias reparse_dir? junction?
|
399
|
+
end
|
400
|
+
|
401
|
+
private
|
402
|
+
|
403
|
+
class << self
|
404
|
+
# Simulate MRI's contortions for a stringiness check.
|
405
|
+
def string_check(arg)
|
406
|
+
return arg if arg.is_a?(String)
|
407
|
+
return arg.send(:to_str) if arg.respond_to?(:to_str, true) # MRI honors it, even if private
|
408
|
+
return arg.to_path if arg.respond_to?(:to_path)
|
409
|
+
raise TypeError
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
# Macro from Windows header file, used by the create_junction method.
|
414
|
+
def self.CTL_CODE(device, function, method, access)
|
415
|
+
((device) << 16) | ((access) << 14) | ((function) << 2) | (method)
|
416
|
+
end
|
417
|
+
end
|