win32-dir 0.3.7 → 0.4.0

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.
data/CHANGES CHANGED
@@ -1,3 +1,9 @@
1
+ == 0.4.0 - 29-Jun-2012
2
+ * Conversion to FFI. Should work with JRuby now.
3
+ * If current versions of Dir::XXX constant values cannot be found
4
+ then default values are tried.
5
+ * Now requires Ruby 1.9 or later.
6
+
1
7
  == 0.3.7 - 18-Jul-2010
2
8
  * Modified Dir.glob and Dir[] to handle backslashes in path names.
3
9
  * Added tests for the modified Dir.glob and Dir[] behavior.
data/MANIFEST CHANGED
@@ -1,8 +1,8 @@
1
- * README
2
- * MANIFEST
3
- * CHANGES
4
- * Rakefile
5
- * win32-dir.gemspec
6
- * examples/dir_example.rb
7
- * lib/win32/dir.rb
8
- * test/test_win32_dir.rb
1
+ * README
2
+ * MANIFEST
3
+ * CHANGES
4
+ * Rakefile
5
+ * win32-dir.gemspec
6
+ * examples/dir_example.rb
7
+ * lib/win32/dir.rb
8
+ * test/test_win32_dir.rb
data/README CHANGED
@@ -233,16 +233,11 @@ Dir::TEMPLATES
233
233
  only instead of an actual path.
234
234
 
235
235
  == Known Bugs
236
- The Unicode support is not quite there for Dir.create_junction. It creates
237
- the directory and junction fine, but the +to+ name appears to get garbled
238
- with regards to the character set.
239
-
240
- Please log any other bug reports on the RubyForge project page at
241
- http://www.rubyforge.net/projects/win32utils.
236
+ Please log any bug reports on the project page at
237
+ http://www.github.com/djberg96/win32-dir
242
238
 
243
239
  == Future Plans
244
- Fix the Unicode issue with Dir.create_junction.
245
- Other suggestions welcome.
240
+ Suggestions welcome.
246
241
 
247
242
  == Acknowledgements
248
243
  Shashank Date and Zach Dennis for the suggestion and supporting comments
@@ -258,7 +253,7 @@ Dir::TEMPLATES
258
253
  Artistic 2.0
259
254
 
260
255
  == Copyright
261
- (C) 2003-2010 Daniel J. Berger, All Rights Reserved
256
+ (C) 2003-2012 Daniel J. Berger, All Rights Reserved
262
257
 
263
258
  == Warranty
264
259
  This package is provided "as is" and without any express or
data/Rakefile CHANGED
@@ -1,9 +1,12 @@
1
1
  require 'rake'
2
+ require 'rake/clean'
2
3
  require 'rake/testtask'
3
4
 
5
+ CLEAN.include('**/*.gem', '**/*.log')
6
+
4
7
  namespace 'gem' do
5
8
  desc "Create the win32-dir gem"
6
- task :build do
9
+ task :build => [:clean] do
7
10
  Dir["*.gem"].each{ |f| File.delete(f) }
8
11
  spec = eval(IO.read('win32-dir.gemspec'))
9
12
  Gem::Builder.new(spec).build
@@ -25,3 +28,5 @@ Rake::TestTask.new do |t|
25
28
  t.warning = true
26
29
  t.verbose = true
27
30
  end
31
+
32
+ task :default => :test
@@ -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 CHANGED
@@ -1,119 +1,188 @@
1
- require 'windows/directory'
2
- require 'windows/shell'
3
- require 'windows/file'
4
- require 'windows/error'
5
- require 'windows/device_io'
6
- require 'windows/unicode'
7
- require 'windows/directory'
8
- require 'windows/handle'
9
- require 'windows/path'
10
- require 'windows/limits'
11
- require 'windows/system_info'
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'dir', 'constants')
2
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'dir', 'functions')
3
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'dir', 'structs')
12
4
 
13
5
  class Dir
14
- include Windows::Directory
15
- include Windows::Shell
16
- include Windows::Error
17
- include Windows::File
18
- include Windows::DeviceIO
19
- include Windows::Limits
20
-
21
- extend Windows::Directory
22
- extend Windows::Shell
23
- extend Windows::File
24
- extend Windows::Error
25
- extend Windows::DeviceIO
26
- extend Windows::Unicode
27
- extend Windows::Handle
28
- extend Windows::Path
29
- extend Windows::Limits
30
- extend Windows::SystemInfo
31
-
6
+ include Dir::Structs
7
+ include Dir::Constants
8
+ extend Dir::Functions
9
+
10
+ private_class_method(
11
+ :SHGetFolderPathW,
12
+ :SHGetFolderLocation,
13
+ :SHGetFileInfo,
14
+ :PathIsDirectoryEmptyW,
15
+ :CloseHandle,
16
+ :CreateDirectoryW,
17
+ :CreateFileW,
18
+ :DeviceIoControl,
19
+ :GetCurrentDirectoryW,
20
+ :GetFileAttributesW,
21
+ :GetLastError,
22
+ :GetShortPathNameW,
23
+ :GetLongPathNameW,
24
+ :GetFullPathNameW,
25
+ :RemoveDirectoryW
26
+ )
27
+
32
28
  # The version of the win32-dir library.
33
- VERSION = '0.3.7'
34
-
35
- # Dynamically set each of the CSIDL_ constants
36
- constants.grep(/CSIDL/).each{ |constant|
37
- path = 0.chr * MAXPATH
38
- nconst = constant.to_s.split('CSIDL_').last # to_s call for 1.9.x
39
-
40
- if SHGetFolderPath(0, const_get(constant), 0, 1, path) != 0
41
- path = nil
42
- else
43
- path.strip!
44
- end
29
+ VERSION = '0.4.0'
30
+
31
+ # CSIDL constants
32
+ csidl = Hash[
33
+ 'DESKTOP', 0x0000,
34
+ 'INTERNET', 0x0001,
35
+ 'PROGRAMS', 0x0002,
36
+ 'CONTROLS', 0x0003,
37
+ 'PRINTERS', 0x0004,
38
+ 'PERSONAL', 0x0005,
39
+ 'FAVORITES', 0x0006,
40
+ 'STARTUP', 0x0007,
41
+ 'RECENT', 0x0008,
42
+ 'SENDTO', 0x0009,
43
+ 'BITBUCKET', 0x000a,
44
+ 'STARTMENU', 0x000b,
45
+ 'MYDOCUMENTS', 0x000c,
46
+ 'MYMUSIC', 0x000d,
47
+ 'MYVIDEO', 0x000e,
48
+ 'DESKTOPDIRECTORY', 0x0010,
49
+ 'DRIVES', 0x0011,
50
+ 'NETWORK', 0x0012,
51
+ 'NETHOOD', 0x0013,
52
+ 'FONTS', 0x0014,
53
+ 'TEMPLATES', 0x0015,
54
+ 'COMMON_STARTMENU', 0x0016,
55
+ 'COMMON_PROGRAMS', 0X0017,
56
+ 'COMMON_STARTUP', 0x0018,
57
+ 'COMMON_FAVORITES', 0x001f,
58
+ 'COMMON_DESKTOPDIRECTORY', 0x0019,
59
+ 'APPDATA', 0x001a,
60
+ 'PRINTHOOD', 0x001b,
61
+ 'LOCAL_APPDATA', 0x001c,
62
+ 'ALTSTARTUP', 0x001d,
63
+ 'COMMON_ALTSTARTUP', 0x001e,
64
+ 'INTERNET_CACHE', 0x0020,
65
+ 'COOKIES', 0x0021,
66
+ 'HISTORY', 0x0022,
67
+ 'COMMON_APPDATA', 0x0023,
68
+ 'WINDOWS', 0x0024,
69
+ 'SYSTEM', 0x0025,
70
+ 'PROGRAM_FILES', 0x0026,
71
+ 'MYPICTURES', 0x0027,
72
+ 'PROFILE', 0x0028,
73
+ 'SYSTEMX86', 0x0029,
74
+ 'PROGRAM_FILESX86', 0x002a,
75
+ 'PROGRAM_FILES_COMMON', 0x002b,
76
+ 'PROGRAM_FILES_COMMONX86', 0x002c,
77
+ 'COMMON_TEMPLATES', 0x002d,
78
+ 'COMMON_DOCUMENTS', 0x002e,
79
+ 'CONNECTIONS', 0x0031,
80
+ 'COMMON_MUSIC', 0x0035,
81
+ 'COMMON_PICTURES', 0x0036,
82
+ 'COMMON_VIDEO', 0x0037,
83
+ 'RESOURCES', 0x0038,
84
+ 'RESOURCES_LOCALIZED', 0x0039,
85
+ 'COMMON_OEM_LINKS', 0x003a,
86
+ 'CDBURN_AREA', 0x003b,
87
+ 'COMMON_ADMINTOOLS', 0x002f,
88
+ 'ADMINTOOLS', 0x0030
89
+ ]
45
90
 
46
- # Try another approach for virtual folders
47
- if path.nil?
48
- ppidl = 0.chr * 4 # PIDLIST_ABSOLUTE
91
+ # Dynamically set each of the CSIDL constants
92
+ csidl.each{ |key, value|
93
+ buf = 0.chr * 1024
94
+ path = nil
95
+ buf.encode!('UTF-16LE')
49
96
 
50
- if SHGetFolderLocation(0, const_get(constant), 0, 0, ppidl) == S_OK
51
- info = 0.chr * 692 # sizeof(SHFILEINFO)
52
- flags = SHGFI_DISPLAYNAME | SHGFI_PIDL
53
- SHGetFileInfo(ppidl.unpack('L')[0], 0, info, 692, flags)
54
- path = info[12..-1].strip
97
+ if SHGetFolderPathW(0, value, 0, 0, buf) == 0 # Current path
98
+ path = buf.strip
99
+ elsif SHGetFolderPathW(0, value, 0, 1, buf) == 0 # Default path
100
+ path = buf.strip
101
+ else
102
+ ptr = FFI::MemoryPointer.new(:long)
103
+ info = SHFILEINFO.new
104
+ flags = SHGFI_DISPLAYNAME | SHGFI_PIDL
105
+
106
+ if SHGetFolderLocation(0, value, 0, 0, ptr) == 0
107
+ if SHGetFileInfo(ptr.read_long, 0, info, info.size, flags) != 0
108
+ path = info[:szDisplayName].to_s
109
+ end
55
110
  end
56
111
  end
57
112
 
58
- Dir.const_set(nconst, path) if path
113
+ Dir.const_set(key, path) if path
59
114
  }
60
115
 
61
116
  # Set Dir::MYDOCUMENTS to the same as Dir::PERSONAL if undefined
62
117
  unless defined? MYDOCUMENTS
63
- # Same as Dir::PERSONAL
64
118
  MYDOCUMENTS = PERSONAL
65
119
  end
66
-
120
+
67
121
  class << self
68
- remove_method :getwd
69
- remove_method :pwd
70
- alias :old_glob :glob
71
- alias :old_ref :[]
72
- remove_method :glob
73
- remove_method :[]
74
- end
122
+ alias old_glob glob
75
123
 
76
- # Same as the standard MRI Dir.glob method except that it handles
77
- # backslashes in path names.
78
- #
79
- def self.glob(glob_pattern, flags = 0, &block)
80
- glob_pattern = glob_pattern.tr("\\", "/")
81
- old_glob(glob_pattern, flags, &block)
82
- end
124
+ # Same as the standard MRI Dir.glob method except that it handles
125
+ # backslashes in path names.
126
+ #
127
+ def glob(glob_pattern, flags = 0, &block)
128
+ glob_pattern = glob_pattern.tr("\\", "/")
129
+ old_glob(glob_pattern, flags, &block)
130
+ end
83
131
 
84
- # Same as the standard MRI Dir[] method except that it handles
85
- # backslashes in path names.
86
- #
87
- def self.[](glob_pattern)
88
- glob_pattern = glob_pattern.tr("\\", "/")
89
- old_ref(glob_pattern)
90
- end
91
-
92
- # Returns the present working directory. Unlike MRI, this method always
93
- # normalizes the path.
94
- #
95
- # Examples:
96
- #
97
- # Dir.chdir("C:/Progra~1")
98
- # Dir.getwd # => C:\Program Files
99
- #
100
- # Dir.chdir("C:/PROGRAM FILES")
101
- # Dir.getwd # => C:\Program Files
102
- #
103
- def self.getwd
104
- path1 = 0.chr * MAXPATH
105
- path2 = 0.chr * MAXPATH
106
- path3 = 0.chr * MAXPATH
132
+ alias old_ref []
107
133
 
108
- GetCurrentDirectory(MAXPATH, path1)
109
- GetShortPathName(path1, path2, MAXPATH)
110
- GetLongPathName(path2, path3, MAXPATH)
134
+ # Same as the standard MRI Dir[] method except that it handles
135
+ # backslashes in path names.
136
+ #
137
+ def [](glob_pattern)
138
+ glob_pattern = glob_pattern.tr("\\", "/")
139
+ old_ref(glob_pattern)
140
+ end
111
141
 
112
- path3[/^[^\0]*/]
113
- end
114
-
115
- class << self
116
- alias :pwd :getwd
142
+ # JRuby normalizes the path by default.
143
+ unless RUBY_PLATFORM == 'java'
144
+ alias oldgetwd getwd
145
+ alias oldpwd pwd
146
+
147
+ # Returns the present working directory. Unlike MRI, this method always
148
+ # normalizes the path.
149
+ #
150
+ # Examples:
151
+ #
152
+ # Dir.chdir("C:/Progra~1")
153
+ # Dir.getwd # => C:\Program Files
154
+ #
155
+ # Dir.chdir("C:/PROGRAM FILES")
156
+ # Dir.getwd # => C:\Program Files
157
+ #
158
+ def getwd
159
+ path1 = 0.chr * 1024
160
+ path2 = 0.chr * 1024
161
+ path3 = 0.chr * 1024
162
+
163
+ path1.encode!('UTF-16LE')
164
+
165
+ if GetCurrentDirectoryW(path1.size, path1) == 0
166
+ raise SystemCallError, FFI.errno, "GetCurrentDirectoryW"
167
+ end
168
+
169
+ path2.encode!('UTF-16LE')
170
+
171
+ if GetShortPathNameW(path1, path2, path2.size) == 0
172
+ raise SystemCallError, FFi.errno, "GetShortPathNameW"
173
+ end
174
+
175
+ path3.encode!('UTF-16LE')
176
+
177
+ if GetLongPathNameW(path2, path3, path3.size) == 0
178
+ raise SystemCallError, FFI.errno, "GetLongPathNameW"
179
+ end
180
+
181
+ path3.strip.encode(Encoding.default_external)
182
+ end
183
+
184
+ alias :pwd :getwd
185
+ end
117
186
  end
118
187
 
119
188
  # Creates the symlink +to+, linked to the existing directory +from+. If the
@@ -123,121 +192,140 @@ class Dir
123
192
  #
124
193
  # Dir.mkdir('C:/from')
125
194
  # Dir.create_junction('C:/to', 'C:/from')
126
- #
195
+ #
127
196
  def self.create_junction(to, from)
128
- to = to.tr(File::SEPARATOR, File::ALT_SEPARATOR) # Normalize path
129
- from = from.tr(File::SEPARATOR, File::ALT_SEPARATOR) # Normalize path
130
-
131
- to_path = 0.chr * MAXPATH
132
- from_path = 0.chr * MAXPATH
133
- buf_target = 0.chr * MAXPATH
134
-
135
- length = GetFullPathName(from, from_path.size, from_path, 0)
197
+ to = to.tr(File::SEPARATOR, File::ALT_SEPARATOR) + "\0" # Normalize path
198
+ from = from.tr(File::SEPARATOR, File::ALT_SEPARATOR) + "\0" # Normalize path
199
+
200
+ to.encode!('UTF-16LE')
201
+ from.encode!('UTF-16LE')
202
+
203
+ from_path = 0.chr * 1024
204
+ from_path.encode!('UTF-16LE')
205
+
206
+ length = GetFullPathNameW(from, from_path.size, from_path, nil)
136
207
 
137
208
  if length == 0
138
- raise StandardError, 'GetFullPathName() failed: ' + get_last_error
209
+ raise SystemCallError, FFI.errno, "GetFullPathNameW"
139
210
  else
140
- from_path = from_path[0..length-1]
211
+ from_path.strip!
141
212
  end
142
-
143
- length = GetFullPathName(to, to_path.size, to_path, 0)
213
+
214
+ to_path = 0.chr * 1024
215
+ to.encode!('UTF-16LE')
216
+ to_path.encode!('UTF-16LE')
217
+
218
+ length = GetFullPathNameW(to, to_path.size, to_path, nil)
144
219
 
145
220
  if length == 0
146
- raise StandardError, 'GetFullPathName() failed: ' + get_last_error
221
+ raise SystemCallError, FFI.errno, "GetFullPathNameW"
147
222
  else
148
- to_path = to_path[0..length-1]
223
+ to_path.strip!
149
224
  end
150
225
 
151
226
  # You can create a junction to a directory that already exists, so
152
227
  # long as it's empty.
153
- rv = CreateDirectory(to_path, 0)
154
-
155
- if rv == 0 && rv != ERROR_ALREADY_EXISTS
156
- raise StandardError, 'CreateDirectory() failed: ' + get_last_error
228
+ unless CreateDirectoryW(to_path, nil)
229
+ if FFI.errno != ERROR_ALREADY_EXISTS
230
+ raise SystemCallError, FFI.errno, "CreateDirectoryW"
231
+ end
157
232
  end
158
-
159
- handle = CreateFile(
160
- to_path,
161
- GENERIC_READ | GENERIC_WRITE,
162
- 0,
163
- 0,
164
- OPEN_EXISTING,
165
- FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
166
- 0
167
- )
168
-
169
- if handle == INVALID_HANDLE_VALUE
170
- raise StandardError, 'CreateFile() failed: ' + get_last_error
171
- end
172
-
173
- buf_target = buf_target.split(0.chr).first
174
- buf_target = "\\??\\" << from_path
175
- wide_string = multi_to_wide(buf_target)[0..-3]
176
-
177
- # REPARSE_JDATA_BUFFER
178
- rdb = [
179
- "0xA0000003L".hex, # ReparseTag (IO_REPARSE_TAG_MOUNT_POINT)
180
- wide_string.size + 12, # ReparseDataLength
181
- 0, # Reserved
182
- 0, # SubstituteNameOffset
183
- wide_string.size, # SubstituteNameLength
184
- wide_string.size + 2, # PrintNameOffset
185
- 0, # PrintNameLength
186
- wide_string # PathBuffer
187
- ].pack('LSSSSSSa' + (wide_string.size + 4).to_s)
188
-
189
- bytes = [0].pack('L')
190
233
 
191
234
  begin
192
- bool = DeviceIoControl(
193
- handle,
194
- CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, 0),
195
- rdb,
196
- rdb.size,
235
+ # Generic read & write + open existing + reparse point & backup semantics
236
+ handle = CreateFileW(
237
+ to_path,
238
+ GENERIC_READ | GENERIC_WRITE,
197
239
  0,
198
- 0,
199
- bytes,
240
+ nil,
241
+ OPEN_EXISTING,
242
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
200
243
  0
201
244
  )
202
-
203
- unless bool
204
- error = 'DeviceIoControl() failed: ' + get_last_error
205
- RemoveDirectory(to_path)
206
- raise error
245
+
246
+ if handle == INVALID_HANDLE_VALUE
247
+ raise SystemCallError, FFI.errno, "CreateFileW"
248
+ end
249
+
250
+ target = "\\??\\".encode('UTF-16LE') + from_path
251
+
252
+ rdb = REPARSE_JDATA_BUFFER.new
253
+ rdb[:ReparseTag] = 2684354563 # IO_REPARSE_TAG_MOUNT_POINT
254
+ rdb[:ReparseDataLength] = target.bytesize + 12
255
+ rdb[:Reserved] = 0
256
+ rdb[:SubstituteNameOffset] = 0
257
+ rdb[:SubstituteNameLength] = target.bytesize
258
+ rdb[:PrintNameOffset] = target.bytesize + 2
259
+ rdb[:PrintNameLength] = 0
260
+ rdb[:PathBuffer] = target
261
+
262
+ bytes = FFI::MemoryPointer.new(:ulong)
263
+
264
+ begin
265
+ bool = DeviceIoControl(
266
+ handle,
267
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, 0),
268
+ rdb,
269
+ rdb[:ReparseDataLength] + 8,
270
+ nil,
271
+ 0,
272
+ bytes,
273
+ nil
274
+ )
275
+
276
+ error = FFI.errno
277
+
278
+ unless bool
279
+ RemoveDirectoryW(to_path)
280
+ raise SystemCallError, error, "DeviceIoControl"
281
+ end
282
+ ensure
283
+ CloseHandle(handle)
207
284
  end
208
- ensure
209
- CloseHandle(handle)
210
285
  end
211
-
212
- self
286
+
287
+ self
213
288
  end
214
-
289
+
215
290
  # Returns whether or not +path+ is empty. Returns false if +path+ is not
216
291
  # a directory, or contains any files other than '.' or '..'.
217
- #
292
+ #
218
293
  def self.empty?(path)
219
- PathIsDirectoryEmpty(path)
294
+ path = path + "\0"
295
+ path = path.encode('UTF-16LE')
296
+ PathIsDirectoryEmptyW(path)
220
297
  end
221
-
298
+
222
299
  # Returns whether or not +path+ is a junction.
223
- #
300
+ #
224
301
  def self.junction?(path)
225
- bool = true
226
- attrib = GetFileAttributes(path)
227
-
302
+ bool = true
303
+ path = path + "\0"
304
+ path.encode!('UTF-16LE')
305
+
306
+ attrib = GetFileAttributesW(path)
307
+
308
+ # Only directories with a reparse point attribute can be junctions
228
309
  if attrib == INVALID_FILE_ATTRIBUTES ||
229
310
  attrib & FILE_ATTRIBUTE_DIRECTORY == 0 ||
230
311
  attrib & FILE_ATTRIBUTE_REPARSE_POINT == 0
231
312
  then
232
- bool = false
313
+ bool = false
233
314
  end
234
-
315
+
235
316
  bool
236
- end
237
-
317
+ end
318
+
238
319
  # Class level aliases
239
320
  #
240
321
  class << self
241
322
  alias reparse_dir? junction?
242
323
  end
324
+
325
+ private
326
+
327
+ # Macro from Windows header file, used by the create_junction method.
328
+ def self.CTL_CODE(device, function, method, access)
329
+ ((device) << 16) | ((access) << 14) | ((function) << 2) | (method)
330
+ end
243
331
  end