win32-file-stat 1.3.4 → 1.3.5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,9 @@
1
+ == 1.3.5 - 21-Nov-2011
2
+ * Fixed a bug in the dev method for 1.9.x.
3
+ * Fixed some method redefinition warnings.
4
+ * Refactored the Rakefile and gemspec. The old install task has been
5
+ removed from the Rakefile.
6
+
1
7
  == 1.3.4 - 13-Aug-2009
2
8
  * Changed license to Artistic 2.0.
3
9
  * Some gemspec updates, including the addition of a license, an updated
@@ -91,4 +97,4 @@
91
97
  * Some optimization in the constructor.
92
98
 
93
99
  == 1.0.0 - 13-Apr-2006
94
- * Initial release
100
+ * Initial release
data/Rakefile CHANGED
@@ -1,24 +1,26 @@
1
1
  require 'rake'
2
+ require 'rake/clean'
2
3
  require 'rake/testtask'
3
- require 'rbconfig'
4
- include Config
5
4
 
6
- desc 'Install the win32-file-stat library (non-gem)'
7
- task :install do
8
- install_dir = File.join(CONFIG['sitelibdir'], 'win32', 'file')
9
- file = 'lib\win32\file\stat.rb'
10
- FileUtils.mkdir_p(install_dir)
11
- FileUtils.cp(file, install_dir, :verbose => true)
12
- end
5
+ CLEAN.include("**/*.gem", "**/*.rbx", "**/*.rbc")
6
+
7
+ namespace :gem do
8
+ desc "Create the win32-file-stat gem"
9
+ task :create => [:clean] do
10
+ spec = eval(IO.read("win32-file-stat.gemspec"))
11
+ Gem::Builder.new(spec).build
12
+ end
13
13
 
14
- desc 'Install the win32-file-stat library as a gem'
15
- task :install_gem do
16
- ruby 'win32-file-stat.gemspec'
17
- file = Dir["win32-file-stat*.gem"].first
18
- sh "gem install #{file}"
14
+ desc "Install the win32-file-stat gem"
15
+ task :install => [:create] do
16
+ file = Dir["win32-file-stat*.gem"].first
17
+ sh "gem install #{file}"
18
+ end
19
19
  end
20
20
 
21
21
  Rake::TestTask.new do |t|
22
- t.verbose = true
23
- t.warning = true
24
- end
22
+ t.verbose = true
23
+ t.warning = true
24
+ end
25
+
26
+ task :default => :test
@@ -14,727 +14,736 @@ require 'windows/ntfs/winternl'
14
14
  require 'pp'
15
15
 
16
16
  class File::Stat
17
- include Windows::MSVCRT::Buffer
18
- include Windows::MSVCRT::File
19
- include Windows::DeviceIO
20
- include Windows::FileSystem
21
- include Windows::Path
22
- include Windows::File
23
- include Windows::Error
24
- include Windows::Handle
25
- include Windows::Volume
26
- include Windows::Process
27
- include Windows::Security
28
- include Windows::Time
29
- include Windows::NTFS::Winternl
30
- include Comparable
31
-
32
- # The version of the win32-file-stat library
33
- VERSION = '1.3.4'
34
-
35
- private
36
-
37
- # :stopdoc:
38
-
39
- # Defined in Ruby's win32.h. Not meant for public consumption.
40
- S_IWGRP = 0020
41
- S_IWOTH = 0002
42
-
43
- # This is the only way to avoid a -w warning for initialize. We remove
44
- # it later, after we've defined our initialize method.
45
- alias old_init initialize
46
-
47
- # Make this library -w clean
48
- undef_method(:atime, :blksize, :blockdev?, :blocks, :chardev?, :ctime)
49
- undef_method(:dev, :directory?, :executable?, :file?, :ftype, :gid, :ino)
50
- undef_method(:executable_real?, :grpowned?, :mode, :mtime, :nlink, :owned?)
51
- undef_method(:pipe?, :readable?, :rdev, :readable_real?, :setgid?, :setuid?)
52
- undef_method(:size, :size?, :socket?, :sticky?, :symlink?, :uid, :writable?)
53
- undef_method(:writable_real?, :zero?)
54
- undef_method(:pretty_print, :inspect, :<=>)
55
-
56
- public
57
-
58
- # Always nil. Provided for interface compatibility only.
59
- attr_reader :dev_major
60
- attr_reader :dev_minor
61
- attr_reader :rdev_major
62
- attr_reader :rdev_minor
63
-
64
- # :startdoc:
65
-
66
- # Creates and returns a File::Stat object, which encapsulate common status
67
- # information for File objects on MS Windows sytems. The information is
68
- # recorded at the moment the File::Stat object is created; changes made to
69
- # the file after that point will not be reflected.
70
- #
71
- def initialize(file)
72
- @file = File.expand_path(file)
73
- @file = @file.tr('/', "\\")
74
- @file = multi_to_wide(@file)
75
-
76
- @file_type = get_file_type(@file)
77
- @chardev = @file_type == FILE_TYPE_CHAR
78
-
79
- case GetDriveTypeW(@file)
80
- when DRIVE_REMOVABLE, DRIVE_CDROM, DRIVE_RAMDISK
81
- @blockdev = true
82
- else
83
- @blockdev = false
84
- end
17
+ include Windows::MSVCRT::Buffer
18
+ include Windows::MSVCRT::File
19
+ include Windows::DeviceIO
20
+ include Windows::FileSystem
21
+ include Windows::Path
22
+ include Windows::File
23
+ include Windows::Error
24
+ include Windows::Handle
25
+ include Windows::Volume
26
+ include Windows::Process
27
+ include Windows::Security
28
+ include Windows::Time
29
+ include Windows::NTFS::Winternl
30
+ include Comparable
31
+
32
+ # The version of the win32-file-stat library
33
+ VERSION = '1.3.5'
34
+
35
+ private
36
+
37
+ # :stopdoc:
38
+
39
+ # Defined in Ruby's win32.h. Not meant for public consumption.
40
+ S_IWGRP = 0020
41
+ S_IWOTH = 0002
42
+
43
+ # This is the only way to avoid a -w warning for initialize. We remove
44
+ # it later, after we've defined our initialize method.
45
+ alias old_init initialize
46
+
47
+ # Make this library -w clean
48
+ undef_method(:atime, :blksize, :blockdev?, :blocks, :chardev?, :ctime)
49
+ undef_method(:dev, :directory?, :executable?, :file?, :ftype, :gid, :ino)
50
+ undef_method(:executable_real?, :grpowned?, :mode, :mtime, :nlink, :owned?)
51
+ undef_method(:pipe?, :readable?, :rdev, :readable_real?, :setgid?, :setuid?)
52
+ undef_method(:size, :size?, :socket?, :sticky?, :symlink?, :uid, :writable?)
53
+ undef_method(:dev_major, :dev_minor, :rdev_major, :rdev_minor)
54
+ undef_method(:writable_real?, :zero?)
55
+ undef_method(:pretty_print, :inspect, :<=>)
56
+
57
+ public
58
+
59
+ # Always nil. Provided for interface compatibility only.
60
+ attr_reader :dev_major
61
+ attr_reader :dev_minor
62
+ attr_reader :rdev_major
63
+ attr_reader :rdev_minor
64
+
65
+ # :startdoc:
66
+
67
+ # Creates and returns a File::Stat object, which encapsulate common status
68
+ # information for File objects on MS Windows sytems. The information is
69
+ # recorded at the moment the File::Stat object is created; changes made to
70
+ # the file after that point will not be reflected.
71
+ #
72
+ def initialize(file)
73
+ @file = File.expand_path(file)
74
+ @file = @file.tr('/', "\\")
75
+ @file = multi_to_wide(@file)
76
+
77
+ @file_type = get_file_type(@file)
78
+ @chardev = @file_type == FILE_TYPE_CHAR
79
+
80
+ case GetDriveTypeW(@file)
81
+ when DRIVE_REMOVABLE, DRIVE_CDROM, DRIVE_RAMDISK
82
+ @blockdev = true
83
+ else
84
+ @blockdev = false
85
+ end
85
86
 
86
- # The stat struct in stat.h only has 11 members on Windows
87
- stat_buf = [0,0,0,0,0,0,0,0,0,0,0].pack('ISSsssIQQQQ')
88
-
89
- # The stat64 function doesn't seem to like character devices
90
- if wstat64(@file, stat_buf) != 0
91
- raise ArgumentError, get_last_error unless @chardev
92
- end
93
-
94
- # Some bytes skipped (padding for struct alignment)
95
- @dev = stat_buf[0, 4].unpack('I').first # Drive number
96
- @ino = stat_buf[4, 2].unpack('S').first # Meaningless
97
- @mode = stat_buf[6, 2].unpack('S').first # File mode bit mask
98
- @nlink = stat_buf[8, 2].unpack('s').first # Always 1
99
- @uid = stat_buf[10, 2].unpack('s').first # Always 0
100
- @gid = stat_buf[12, 2].unpack('s').first # Always 0
101
- @rdev = stat_buf[16, 4].unpack('I').first # Same as dev
102
- @size = stat_buf[24, 8].unpack('Q').first # Size of file in bytes
87
+ # The stat struct in stat.h only has 11 members on Windows
88
+ stat_buf = [0,0,0,0,0,0,0,0,0,0,0].pack('ISSsssIQQQQ')
89
+
90
+ # The stat64 function doesn't seem to like character devices
91
+ if wstat64(@file, stat_buf) != 0
92
+ raise ArgumentError, get_last_error unless @chardev
93
+ end
94
+
95
+ # Some bytes skipped (padding for struct alignment)
96
+ @dev = stat_buf[0, 4].unpack('I').first # Drive number
97
+ @ino = stat_buf[4, 2].unpack('S').first # Meaningless
98
+ @mode = stat_buf[6, 2].unpack('S').first # File mode bit mask
99
+ @nlink = stat_buf[8, 2].unpack('s').first # Always 1
100
+ @uid = stat_buf[10, 2].unpack('s').first # Always 0
101
+ @gid = stat_buf[12, 2].unpack('s').first # Always 0
102
+ @rdev = stat_buf[16, 4].unpack('I').first # Same as dev
103
+ @size = stat_buf[24, 8].unpack('Q').first # Size of file in bytes
103
104
 
104
- # This portion can fail in rare, FS related instances. If it does, set
105
- # the various times to Time.at(0).
106
- begin
107
- @atime = Time.at(stat_buf[32, 8].unpack('Q').first) # Access time
108
- @mtime = Time.at(stat_buf[40, 8].unpack('Q').first) # Mod time
109
- @ctime = Time.at(stat_buf[48, 8].unpack('Q').first) # Creation time
110
- rescue
111
- @atime = Time.at(0)
112
- @mtime = Time.at(0)
113
- @ctime = Time.at(0)
114
- end
105
+ # This portion can fail in rare, FS related instances. If it does, set
106
+ # the various times to Time.at(0).
107
+ begin
108
+ @atime = Time.at(stat_buf[32, 8].unpack('Q').first) # Access time
109
+ @mtime = Time.at(stat_buf[40, 8].unpack('Q').first) # Mod time
110
+ @ctime = Time.at(stat_buf[48, 8].unpack('Q').first) # Creation time
111
+ rescue
112
+ @atime = Time.at(0)
113
+ @mtime = Time.at(0)
114
+ @ctime = Time.at(0)
115
+ end
115
116
 
116
- @mode = 33188 if @chardev
117
+ @mode = 33188 if @chardev
117
118
 
118
- attributes = GetFileAttributesW(@file)
119
- error_num = GetLastError()
119
+ attributes = GetFileAttributesW(@file)
120
+ error_num = GetLastError()
120
121
 
121
- # Locked files.
122
- if error_num == ERROR_SHARING_VIOLATION
123
- buffer = 0.chr * 512
122
+ # Locked files.
123
+ if error_num == ERROR_SHARING_VIOLATION
124
+ buffer = 0.chr * 512
124
125
 
125
- begin
126
- handle = FindFirstFileW(@file, buffer)
126
+ begin
127
+ handle = FindFirstFileW(@file, buffer)
127
128
 
128
- if handle == INVALID_HANDLE_VALUE
129
- raise SystemCallError, get_last_error()
130
- end
131
- ensure
132
- FindClose(handle) if handle != INVALID_HANDLE_VALUE
133
- end
134
-
135
- attributes = buffer[0,4].unpack('L').first
136
- st = 0.chr * 16
137
- FileTimeToSystemTime(buffer[4,8],st)
138
- y,m,w,d,h,n,s,i = st.unpack('SSSSSSSS')
139
- @ctime = Time.local(y,m,d,h,n,s)
140
-
141
- st = 0.chr * 16
142
- FileTimeToSystemTime(buffer[12,8],st)
143
- y,m,w,d,h,n,s,i = st.unpack('SSSSSSSS')
144
- @atime = Time.local(y,m,d,h,n,s)
145
-
146
- st = 0.chr * 16
147
- FileTimeToSystemTime(buffer[20,8],st)
148
- y,m,w,d,h,n,s,i = st.unpack('SSSSSSSS')
149
- @mtime = Time.local(y,m,d,h,n,s)
129
+ if handle == INVALID_HANDLE_VALUE
130
+ raise SystemCallError, get_last_error()
131
+ end
132
+ ensure
133
+ FindClose(handle) if handle != INVALID_HANDLE_VALUE
150
134
  end
151
135
 
152
- # Ignore errors caused by empty/open/used block devices.
153
- if attributes == INVALID_FILE_ATTRIBUTES
154
- unless error_num == ERROR_NOT_READY
155
- raise ArgumentError, get_last_error(error_num)
156
- end
136
+ attributes = buffer[0,4].unpack('L').first
137
+ st = 0.chr * 16
138
+ FileTimeToSystemTime(buffer[4,8],st)
139
+ y,m,w,d,h,n,s,i = st.unpack('SSSSSSSS')
140
+ @ctime = Time.local(y,m,d,h,n,s)
141
+
142
+ st = 0.chr * 16
143
+ FileTimeToSystemTime(buffer[12,8],st)
144
+ y,m,w,d,h,n,s,i = st.unpack('SSSSSSSS')
145
+ @atime = Time.local(y,m,d,h,n,s)
146
+
147
+ st = 0.chr * 16
148
+ FileTimeToSystemTime(buffer[20,8],st)
149
+ y,m,w,d,h,n,s,i = st.unpack('SSSSSSSS')
150
+ @mtime = Time.local(y,m,d,h,n,s)
151
+ end
152
+
153
+ # Ignore errors caused by empty/open/used block devices.
154
+ if attributes == INVALID_FILE_ATTRIBUTES
155
+ unless error_num == ERROR_NOT_READY
156
+ raise ArgumentError, get_last_error(error_num)
157
157
  end
158
+ end
158
159
 
159
- @blksize = get_blksize(@file)
160
+ @blksize = get_blksize(@file)
160
161
 
161
- # This is a reasonable guess
162
- case @blksize
163
- when nil
164
- @blocks = nil
165
- when 0
166
- @blocks = 0
167
- else
168
- @blocks = (@size.to_f / @blksize.to_f).ceil
169
- end
162
+ # This is a reasonable guess
163
+ case @blksize
164
+ when nil
165
+ @blocks = nil
166
+ when 0
167
+ @blocks = 0
168
+ else
169
+ @blocks = (@size.to_f / @blksize.to_f).ceil
170
+ end
170
171
 
171
- @readonly = attributes & FILE_ATTRIBUTE_READONLY > 0
172
- @hidden = attributes & FILE_ATTRIBUTE_HIDDEN > 0
173
- @system = attributes & FILE_ATTRIBUTE_SYSTEM > 0
174
- @archive = attributes & FILE_ATTRIBUTE_ARCHIVE > 0
175
- @directory = attributes & FILE_ATTRIBUTE_DIRECTORY > 0
176
- @encrypted = attributes & FILE_ATTRIBUTE_ENCRYPTED > 0
177
- @normal = attributes & FILE_ATTRIBUTE_NORMAL > 0
178
- @temporary = attributes & FILE_ATTRIBUTE_TEMPORARY > 0
179
- @sparse = attributes & FILE_ATTRIBUTE_SPARSE_FILE > 0
180
- @reparse_point = attributes & FILE_ATTRIBUTE_REPARSE_POINT > 0
181
- @compressed = attributes & FILE_ATTRIBUTE_COMPRESSED > 0
182
- @offline = attributes & FILE_ATTRIBUTE_OFFLINE > 0
183
- @indexed = attributes & ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED > 0
172
+ @readonly = attributes & FILE_ATTRIBUTE_READONLY > 0
173
+ @hidden = attributes & FILE_ATTRIBUTE_HIDDEN > 0
174
+ @system = attributes & FILE_ATTRIBUTE_SYSTEM > 0
175
+ @archive = attributes & FILE_ATTRIBUTE_ARCHIVE > 0
176
+ @directory = attributes & FILE_ATTRIBUTE_DIRECTORY > 0
177
+ @encrypted = attributes & FILE_ATTRIBUTE_ENCRYPTED > 0
178
+ @normal = attributes & FILE_ATTRIBUTE_NORMAL > 0
179
+ @temporary = attributes & FILE_ATTRIBUTE_TEMPORARY > 0
180
+ @sparse = attributes & FILE_ATTRIBUTE_SPARSE_FILE > 0
181
+ @reparse_point = attributes & FILE_ATTRIBUTE_REPARSE_POINT > 0
182
+ @compressed = attributes & FILE_ATTRIBUTE_COMPRESSED > 0
183
+ @offline = attributes & FILE_ATTRIBUTE_OFFLINE > 0
184
+ @indexed = attributes & ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED > 0
184
185
 
185
- @executable = GetBinaryTypeW(@file, '')
186
- @regular = @file_type == FILE_TYPE_DISK
187
- @pipe = @file_type == FILE_TYPE_PIPE
186
+ @executable = GetBinaryTypeW(@file, '')
187
+ @regular = @file_type == FILE_TYPE_DISK
188
+ @pipe = @file_type == FILE_TYPE_PIPE
188
189
 
189
- # Not supported and/or meaningless
190
- @dev_major = nil
191
- @dev_minor = nil
192
- @grpowned = true
193
- @owned = true
194
- @readable = true
195
- @readable_real = true
196
- @rdev_major = nil
197
- @rdev_minor = nil
198
- @setgid = false
199
- @setuid = false
200
- @sticky = false
201
- @symlink = false
202
- @writable = true
203
- @writable_real = true
204
- end
205
-
206
- ## Comparable
207
-
208
- # Compares two File::Stat objects. Comparsion is based on mtime only.
209
- #
210
- def <=>(other)
211
- @mtime.to_i <=> other.mtime.to_i
212
- end
213
-
214
- ## Miscellaneous
215
-
216
- # Returns whether or not the file is a block device. For MS Windows a
217
- # block device is a removable drive, cdrom or ramdisk.
218
- #
219
- def blockdev?
220
- @blockdev
221
- end
222
-
223
- # Returns whether or not the file is a character device.
224
- #
225
- def chardev?
226
- @chardev
227
- end
228
-
229
- # Returns whether or not the file is executable. Generally speaking, this
230
- # means .bat, .cmd, .com, and .exe files.
231
- #
232
- def executable?
233
- @executable
234
- end
235
-
236
- alias :executable_real? :executable?
237
-
238
- # Returns whether or not the file is a regular file, as opposed to a pipe,
239
- # socket, etc.
240
- #
241
- def file?
242
- @regular
243
- end
244
-
245
- # Identifies the type of file. The return string is one of: file,
246
- # directory, characterSpecial, socket or unknown.
247
- #
248
- def ftype
249
- return 'directory' if directory?
250
- case @file_type
251
- when FILE_TYPE_CHAR
252
- 'characterSpecial'
253
- when FILE_TYPE_DISK
254
- 'file'
255
- when FILE_TYPE_PIPE
256
- 'socket'
257
- else
258
- if blockdev?
259
- 'blockSpecial'
260
- else
261
- 'unknown'
262
- end
190
+ # Not supported and/or meaningless
191
+ @dev_major = nil
192
+ @dev_minor = nil
193
+ @grpowned = true
194
+ @owned = true
195
+ @readable = true
196
+ @readable_real = true
197
+ @rdev_major = nil
198
+ @rdev_minor = nil
199
+ @setgid = false
200
+ @setuid = false
201
+ @sticky = false
202
+ @symlink = false
203
+ @writable = true
204
+ @writable_real = true
205
+ end
206
+
207
+ ## Comparable
208
+
209
+ # Compares two File::Stat objects. Comparsion is based on mtime only.
210
+ #
211
+ def <=>(other)
212
+ @mtime.to_i <=> other.mtime.to_i
213
+ end
214
+
215
+ ## Miscellaneous
216
+
217
+ # Returns whether or not the file is a block device. For MS Windows a
218
+ # block device is a removable drive, cdrom or ramdisk.
219
+ #
220
+ def blockdev?
221
+ @blockdev
222
+ end
223
+
224
+ # Returns whether or not the file is a character device.
225
+ #
226
+ def chardev?
227
+ @chardev
228
+ end
229
+
230
+ # Returns whether or not the file is executable. Generally speaking, this
231
+ # means .bat, .cmd, .com, and .exe files.
232
+ #
233
+ def executable?
234
+ @executable
235
+ end
236
+
237
+ alias :executable_real? :executable?
238
+
239
+ # Returns whether or not the file is a regular file, as opposed to a pipe,
240
+ # socket, etc.
241
+ #
242
+ def file?
243
+ @regular
244
+ end
245
+
246
+ # Identifies the type of file. The return string is one of 'file',
247
+ # 'directory', 'characterSpecial', 'socket' or 'unknown'.
248
+ #
249
+ def ftype
250
+ return 'directory' if directory?
251
+ case @file_type
252
+ when FILE_TYPE_CHAR
253
+ 'characterSpecial'
254
+ when FILE_TYPE_DISK
255
+ 'file'
256
+ when FILE_TYPE_PIPE
257
+ 'socket'
258
+ else
259
+ if blockdev?
260
+ 'blockSpecial'
261
+ else
262
+ 'unknown'
263
+ end
264
+ end
265
+ end
266
+
267
+ # Meaningless on Windows.
268
+ #
269
+ def grpowned?
270
+ @grpowned
271
+ end
272
+
273
+ # Always true on Windows
274
+ def owned?
275
+ @owned
276
+ end
277
+
278
+ # Returns whether or not the file is a pipe.
279
+ #
280
+ def pipe?
281
+ @pipe
282
+ end
283
+
284
+ alias :socket? :pipe?
285
+
286
+ # Meaningless on Windows
287
+ #
288
+ def readable?
289
+ @readable
290
+ end
291
+
292
+ # Meaningless on Windows
293
+ #
294
+ def readable_real?
295
+ @readable_real
296
+ end
297
+
298
+ # Meaningless on Windows
299
+ #
300
+ def setgid?
301
+ @setgid
302
+ end
303
+
304
+ # Meaningless on Windows
305
+ #
306
+ def setuid?
307
+ @setuid
308
+ end
309
+
310
+ # Returns nil if statfile is a zero-length file; otherwise, returns the
311
+ # file size. Usable as a condition in tests.
312
+ #
313
+ def size?
314
+ @size > 0 ? @size : nil
315
+ end
316
+
317
+ # Meaningless on Windows.
318
+ #
319
+ def sticky?
320
+ @sticky
321
+ end
322
+
323
+ # Meaningless on Windows at the moment. This may change in the future.
324
+ #
325
+ def symlink?
326
+ @symlink
327
+ end
328
+
329
+ # Meaningless on Windows.
330
+ #
331
+ def writable?
332
+ @writable
333
+ end
334
+
335
+ # Meaningless on Windows.
336
+ #
337
+ def writable_real?
338
+ @writable_real
339
+ end
340
+
341
+ # Returns whether or not the file size is zero.
342
+ #
343
+ def zero?
344
+ @size == 0
345
+ end
346
+
347
+ ## Attribute members
348
+
349
+ # Returns whether or not the file is an archive file.
350
+ #
351
+ def archive?
352
+ @archive
353
+ end
354
+
355
+ # Returns whether or not the file is compressed.
356
+ #
357
+ def compressed?
358
+ @compressed
359
+ end
360
+
361
+ # Returns whether or not the file is a directory.
362
+ #
363
+ def directory?
364
+ @directory
365
+ end
366
+
367
+ # Returns whether or not the file in encrypted.
368
+ #
369
+ def encrypted?
370
+ @encrypted
371
+ end
372
+
373
+ # Returns whether or not the file is hidden.
374
+ #
375
+ def hidden?
376
+ @hidden
377
+ end
378
+
379
+ # Returns whether or not the file is content indexed.
380
+ #
381
+ def indexed?
382
+ @indexed
383
+ end
384
+
385
+ alias :content_indexed? :indexed?
386
+
387
+ # Returns whether or not the file is 'normal'. This is only true if
388
+ # virtually all other attributes are false.
389
+ #
390
+ def normal?
391
+ @normal
392
+ end
393
+
394
+ # Returns whether or not the file is offline.
395
+ #
396
+ def offline?
397
+ @offline
398
+ end
399
+
400
+ # Returns whether or not the file is readonly.
401
+ #
402
+ def readonly?
403
+ @readonly
404
+ end
405
+
406
+ alias :read_only? :readonly?
407
+
408
+ # Returns whether or not the file is a reparse point.
409
+ #
410
+ def reparse_point?
411
+ @reparse_point
412
+ end
413
+
414
+ # Returns whether or not the file is a sparse file. In most cases a sparse
415
+ # file is an image file.
416
+ #
417
+ def sparse?
418
+ @sparse
419
+ end
420
+
421
+ # Returns whether or not the file is a system file.
422
+ #
423
+ def system?
424
+ @system
425
+ end
426
+
427
+ # Returns whether or not the file is being used for temporary storage.
428
+ #
429
+ def temporary?
430
+ @temporary
431
+ end
432
+
433
+ ## Standard stat members
434
+
435
+ # Returns a Time object containing the last access time.
436
+ #
437
+ def atime
438
+ @atime
439
+ end
440
+
441
+ # Returns the file system's block size, or nil if it cannot be determined.
442
+ #
443
+ def blksize
444
+ @blksize
445
+ end
446
+
447
+ # Returns the number of blocks used by the file, where a block is defined
448
+ # as size divided by blksize, rounded up.
449
+ #
450
+ #--
451
+ # This is a fudge. A search of the internet reveals different ways people
452
+ # have defined st_blocks on MS Windows.
453
+ #
454
+ def blocks
455
+ @blocks
456
+ end
457
+
458
+ # Returns a Time object containing the time that the file status associated
459
+ # with the file was changed.
460
+ #
461
+ def ctime
462
+ @ctime
463
+ end
464
+
465
+ # Drive letter (A-Z) of the disk containing the file. If the path is a
466
+ # UNC path then the drive number (probably -1) is returned instead.
467
+ #
468
+ def dev
469
+ if PathIsUNCW(@file)
470
+ @dev
471
+ else
472
+ if RUBY_VERSION.to_f >= 1.9
473
+ (@dev + 'A'.ord).chr + ':'
474
+ else
475
+ (@dev + ?A).chr + ':'
263
476
  end
264
- end
265
-
266
- # Meaningless on Windows.
267
- #
268
- def grpowned?
269
- @grpowned
270
- end
271
-
272
- # Always true on Windows
273
- def owned?
274
- @owned
275
- end
276
-
277
- # Returns whether or not the file is a pipe.
278
- #
279
- def pipe?
280
- @pipe
281
- end
282
-
283
- alias :socket? :pipe?
284
-
285
- # Meaningless on Windows
286
- #
287
- def readable?
288
- @readable
289
- end
290
-
291
- # Meaningless on Windows
292
- #
293
- def readable_real?
294
- @readable_real
295
- end
296
-
297
- # Meaningless on Windows
298
- #
299
- def setgid?
300
- @setgid
301
- end
302
-
303
- # Meaningless on Windows
304
- #
305
- def setuid?
306
- @setuid
307
- end
308
-
309
- # Returns nil if statfile is a zero-length file; otherwise, returns the
310
- # file size. Usable as a condition in tests.
311
- #
312
- def size?
313
- @size > 0 ? @size : nil
314
- end
315
-
316
- # Meaningless on Windows.
317
- #
318
- def sticky?
319
- @sticky
320
- end
321
-
322
- # Meaningless on Windows at the moment. This may change in the future.
323
- #
324
- def symlink?
325
- @symlink
326
- end
327
-
328
- # Meaningless on Windows.
329
- #
330
- def writable?
331
- @writable
332
- end
333
-
334
- # Meaningless on Windows.
335
- #
336
- def writable_real?
337
- @writable_real
338
- end
339
-
340
- # Returns whether or not the file size is zero.
341
- #
342
- def zero?
343
- @size == 0
344
- end
345
-
346
- ## Attribute members
347
-
348
- # Returns whether or not the file is an archive file.
349
- #
350
- def archive?
351
- @archive
352
- end
353
-
354
- # Returns whether or not the file is compressed.
355
- #
356
- def compressed?
357
- @compressed
358
- end
359
-
360
- # Returns whether or not the file is a directory.
361
- #
362
- def directory?
363
- @directory
364
- end
365
-
366
- # Returns whether or not the file in encrypted.
367
- #
368
- def encrypted?
369
- @encrypted
370
- end
371
-
372
- # Returns whether or not the file is hidden.
373
- #
374
- def hidden?
375
- @hidden
376
- end
377
-
378
- # Returns whether or not the file is content indexed.
379
- #
380
- def indexed?
381
- @indexed
382
- end
383
- alias :content_indexed? :indexed?
384
-
385
- # Returns whether or not the file is 'normal'. This is only true if
386
- # virtually all other attributes are false.
387
- #
388
- def normal?
389
- @normal
390
- end
391
-
392
- # Returns whether or not the file is offline.
393
- #
394
- def offline?
395
- @offline
396
- end
397
-
398
- # Returns whether or not the file is readonly.
399
- #
400
- def readonly?
401
- @readonly
402
- end
403
-
404
- alias :read_only? :readonly?
405
-
406
- # Returns whether or not the file is a reparse point.
407
- #
408
- def reparse_point?
409
- @reparse_point
410
- end
411
-
412
- # Returns whether or not the file is a sparse file. In most cases a sparse
413
- # file is an image file.
414
- #
415
- def sparse?
416
- @sparse
417
- end
418
-
419
- # Returns whether or not the file is a system file.
420
- #
421
- def system?
422
- @system
423
- end
424
-
425
- # Returns whether or not the file is being used for temporary storage.
426
- #
427
- def temporary?
428
- @temporary
429
- end
430
-
431
- ## Standard stat members
432
-
433
- # Returns a Time object containing the last access time.
434
- #
435
- def atime
436
- @atime
437
- end
438
-
439
- # Returns the file system's block size, or nil if it cannot be determined.
440
- #
441
- def blksize
442
- @blksize
443
- end
444
-
445
- # Returns the number of blocks used by the file, where a block is defined
446
- # as size divided by blksize, rounded up.
447
- #
448
- #--
449
- # This is a fudge. A search of the internet reveals different ways people
450
- # have defined st_blocks on MS Windows.
451
- #
452
- def blocks
453
- @blocks
454
- end
455
-
456
- # Returns a Time object containing the time that the file status associated
457
- # with the file was changed.
458
- #
459
- def ctime
460
- @ctime
461
- end
462
-
463
- # Drive letter (A-Z) of the disk containing the file. If the path is a
464
- # UNC path then the drive number (probably -1) is returned instead.
465
- #
466
- def dev
467
- if PathIsUNCW(@file)
468
- @dev
477
+ end
478
+ end
479
+
480
+ # Group ID. Always 0.
481
+ #
482
+ def gid
483
+ @gid
484
+ end
485
+
486
+ # Inode number. Meaningless on NTFS.
487
+ #
488
+ def ino
489
+ @ino
490
+ end
491
+
492
+ # Bit mask for file-mode information.
493
+ #
494
+ # :no-doc:
495
+ # This was taken from rb_win32_stat() in win32.c. I'm not entirely
496
+ # sure what the point is.
497
+ #
498
+ def mode
499
+ @mode &= ~(S_IWGRP | S_IWOTH)
500
+ end
501
+
502
+ # Returns a Time object containing the modification time.
503
+ #
504
+ def mtime
505
+ @mtime
506
+ end
507
+
508
+ # Drive number of the disk containing the file.
509
+ #
510
+ def rdev
511
+ @rdev
512
+ end
513
+
514
+ # Always 1
515
+ #
516
+ def nlink
517
+ @nlink
518
+ end
519
+
520
+ # Returns the size of the file, in bytes.
521
+ #
522
+ def size
523
+ @size
524
+ end
525
+
526
+ # User ID. Always 0.
527
+ #
528
+ def uid
529
+ @uid
530
+ end
531
+
532
+ # Returns a stringified version of a File::Stat object.
533
+ #
534
+ def inspect
535
+ members = %w[
536
+ archive? atime blksize blockdev? blocks compressed? ctime dev
537
+ encrypted? gid hidden? indexed? ino mode mtime rdev nlink normal?
538
+ offline? readonly? reparse_point? size sparse? system? temporary?
539
+ uid
540
+ ]
541
+
542
+ str = "#<#{self.class}"
543
+
544
+ members.sort.each{ |mem|
545
+ if mem == 'mode'
546
+ str << " #{mem}=" << sprintf("0%o", send(mem.intern))
547
+ elsif mem[-1].chr == '?'
548
+ str << " #{mem.chop}=" << send(mem.intern).to_s
469
549
  else
470
- (@dev + ?A).chr + ':'
550
+ str << " #{mem}=" << send(mem.intern).to_s
471
551
  end
472
- end
473
-
474
- # Group ID. Always 0.
475
- #
476
- def gid
477
- @gid
478
- end
479
-
480
- # Inode number. Meaningless on NTFS.
481
- #
482
- def ino
483
- @ino
484
- end
485
-
486
- # Bit mask for file-mode information.
487
- #
488
- # :no-doc:
489
- # This was taken from rb_win32_stat() in win32.c. I'm not entirely
490
- # sure what the point is.
491
- #
492
- def mode
493
- @mode &= ~(S_IWGRP | S_IWOTH)
494
- end
495
-
496
- # Returns a Time object containing the modification time.
497
- #
498
- def mtime
499
- @mtime
500
- end
501
-
502
- # Drive number of the disk containing the file.
503
- #
504
- def rdev
505
- @rdev
506
- end
507
-
508
- # Always 1
509
- #
510
- def nlink
511
- @nlink
512
- end
513
-
514
- # Returns the size of the file, in bytes.
515
- #
516
- def size
517
- @size
518
- end
519
-
520
- # User ID. Always 0.
521
- #
522
- def uid
523
- @uid
524
- end
525
-
526
- # Returns a stringified version of a File::Stat object.
527
- #
528
- def inspect
529
- members = %w/
530
- archive? atime blksize blockdev? blocks compressed? ctime dev
531
- encrypted? gid hidden? indexed? ino mode mtime rdev nlink normal?
532
- offline? readonly? reparse_point? size sparse? system? temporary?
533
- uid
534
- /
535
- str = "#<#{self.class}"
536
- members.sort.each{ |mem|
537
- if mem == 'mode'
538
- str << " #{mem}=" << sprintf("0%o", send(mem.intern))
539
- elsif mem[-1].chr == '?'
540
- str << " #{mem.chop}=" << send(mem.intern).to_s
541
- else
542
- str << " #{mem}=" << send(mem.intern).to_s
543
- end
544
- }
545
- str
546
- end
547
-
548
- # A custom pretty print method. This was necessary not only to handle
549
- # the additional attributes, but to work around an error caused by the
550
- # builtin method for the current File::Stat class (see pp.rb).
551
- #
552
- def pretty_print(q)
553
- members = %w/
554
- archive? atime blksize blockdev? blocks compressed? ctime dev
555
- encrypted? gid hidden? indexed? ino mode mtime rdev nlink normal?
556
- offline? readonly? reparse_point? size sparse? system? temporary?
557
- uid
558
- /
559
-
560
- q.object_group(self){
561
- q.breakable
562
- members.each{ |mem|
563
- q.group{
564
- q.text("#{mem}".ljust(15) + "=> ")
565
- if mem == 'mode'
566
- q.text(sprintf("0%o", send(mem.intern)))
567
- else
568
- val = self.send(mem.intern)
569
- if val.nil?
570
- q.text('nil')
571
- else
572
- q.text(val.to_s)
573
- end
574
- end
575
- }
576
- q.comma_breakable unless mem == members.last
577
- }
552
+ }
553
+
554
+ str
555
+ end
556
+
557
+ # A custom pretty print method. This was necessary not only to handle
558
+ # the additional attributes, but to work around an error caused by the
559
+ # builtin method for the current File::Stat class (see pp.rb).
560
+ #
561
+ def pretty_print(q)
562
+ members = %w[
563
+ archive? atime blksize blockdev? blocks compressed? ctime dev
564
+ encrypted? gid hidden? indexed? ino mode mtime rdev nlink normal?
565
+ offline? readonly? reparse_point? size sparse? system? temporary?
566
+ uid
567
+ ]
568
+
569
+ q.object_group(self){
570
+ q.breakable
571
+ members.each{ |mem|
572
+ q.group{
573
+ q.text("#{mem}".ljust(15) + "=> ")
574
+ if mem == 'mode'
575
+ q.text(sprintf("0%o", send(mem.intern)))
576
+ else
577
+ val = self.send(mem.intern)
578
+ if val.nil?
579
+ q.text('nil')
580
+ else
581
+ q.text(val.to_s)
582
+ end
583
+ end
584
+ }
585
+ q.comma_breakable unless mem == members.last
578
586
  }
579
- end
587
+ }
588
+ end
580
589
 
581
- # Since old_init was added strictly to avoid a warning, we remove it now.
582
- remove_method(:old_init)
590
+ # Since old_init was added strictly to avoid a warning, we remove it now.
591
+ remove_method(:old_init)
583
592
 
584
- private
593
+ private
585
594
 
586
- # Returns the file system's block size.
587
- #
588
- def get_blksize(file)
589
- size = nil
590
-
591
- sectors = [0].pack('L')
592
- bytes = [0].pack('L')
593
- free = [0].pack('L')
594
- total = [0].pack('L')
595
+ # Returns the file system's block size.
596
+ #
597
+ def get_blksize(file)
598
+ size = nil
599
+
600
+ sectors = [0].pack('L')
601
+ bytes = [0].pack('L')
602
+ free = [0].pack('L')
603
+ total = [0].pack('L')
595
604
 
596
- # If there's a drive letter it must contain a trailing backslash.
597
- # The dup is necessary here because the function modifies the argument.
598
- file = file.dup
605
+ # If there's a drive letter it must contain a trailing backslash.
606
+ # The dup is necessary here because the function modifies the argument.
607
+ file = file.dup
599
608
 
600
- if PathStripToRootA(wide_to_multi(file))
601
- file = file[/^[^\0]*/] << ':'
602
- file << "\\" unless file[-1].chr == "\\"
603
- else
604
- file = nil # Default to the root drive on relative paths
605
- end
609
+ if PathStripToRootA(wide_to_multi(file))
610
+ file = file[/^[^\0]*/] << ':'
611
+ file << "\\" unless file[-1].chr == "\\"
612
+ else
613
+ file = nil # Default to the root drive on relative paths
614
+ end
606
615
 
607
- # Don't check for an error here. Just default to nil.
608
- if GetDiskFreeSpaceA(file, sectors, bytes, free, total)
609
- size = sectors.unpack('L').first * bytes.unpack('L').first
610
- end
616
+ # Don't check for an error here. Just default to nil.
617
+ if GetDiskFreeSpaceA(file, sectors, bytes, free, total)
618
+ size = sectors.unpack('L').first * bytes.unpack('L').first
619
+ end
611
620
 
612
- size
613
- end
614
-
615
- # Private method to get a HANDLE when CreateFile() won't cut it.
616
- #
617
- def get_handle(file)
618
- file = file.upcase
619
-
620
- begin
621
- hdlTokenHandle = 0.chr * 4
622
-
623
- OpenProcessToken(
624
- GetCurrentProcess(),
625
- TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
626
- hdlTokenHandle
627
- )
628
-
629
- hdlTokenHandle = hdlTokenHandle.unpack('L').first
630
-
631
- # Get the LUID for shutdown privilege.
632
- tmpLuid = 0.chr * 8
633
- LookupPrivilegeValue("", "SeDebugPrivilege", tmpLuid)
634
- tkp = [1].pack('L') + tmpLuid + [SE_PRIVILEGE_ENABLED].pack('L')
635
-
636
- # Enable the shutdown privilege in the access token of this process.
637
- AdjustTokenPrivileges(hdlTokenHandle, 0,tkp, tkp.length , nil, nil)
638
- ensure
639
- CloseHandle(hdlTokenHandle)
640
- end
621
+ size
622
+ end
623
+
624
+ # Private method to get a HANDLE when CreateFile() won't cut it.
625
+ #
626
+ def get_handle(file)
627
+ file = file.upcase
628
+
629
+ begin
630
+ hdlTokenHandle = 0.chr * 4
631
+
632
+ OpenProcessToken(
633
+ GetCurrentProcess(),
634
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
635
+ hdlTokenHandle
636
+ )
637
+
638
+ hdlTokenHandle = hdlTokenHandle.unpack('L').first
639
+
640
+ # Get the LUID for shutdown privilege.
641
+ tmpLuid = 0.chr * 8
642
+ LookupPrivilegeValue("", "SeDebugPrivilege", tmpLuid)
643
+ tkp = [1].pack('L') + tmpLuid + [SE_PRIVILEGE_ENABLED].pack('L')
644
+
645
+ # Enable the shutdown privilege in the access token of this process.
646
+ AdjustTokenPrivileges(hdlTokenHandle, 0,tkp, tkp.length , nil, nil)
647
+ ensure
648
+ CloseHandle(hdlTokenHandle)
649
+ end
641
650
 
642
- # First call is to get the required length
643
- handle_info = 0.chr * 4096
644
- required = 0.chr * 4
645
- NtQuerySystemInformation(16, handle_info, 4096, required)
646
-
647
- # Second call is the actual call
648
- handle_info = 0.chr * required.unpack('L').first
649
- NtQuerySystemInformation(16, handle_info, handle_info.length, required)
650
-
651
- count = handle_info[0,4].unpack('L').first
652
-
653
- for i in 0...count
654
- pid, type, handle, addr, access = handle_info[4+i*16,16].unpack('LSSLL')
655
- if access & 0xffff == 3
656
- begin
657
- process = OpenProcess(0x40,1,pid)
658
- dup_handle = 0.chr * 4
659
-
660
- DuplicateHandle(
661
- process,
662
- handle,
663
- GetCurrentProcess(),
664
- dup_handle,
665
- 0,
666
- 1,
667
- 2
668
- )
669
- ensure
670
- CloseHandle(process)
671
- end
672
-
673
- handle = dup_handle.unpack('L').first
674
- buffer = 0.chr * 0x2000
675
- NtQueryObject(handle, 1, buffer, 0x2000, nil)
676
- len = buffer[0,2].unpack('S').first
677
-
678
- if len>0
679
- if buffer[8..-1].upcase[file]
680
- return handle
681
- end
682
- end
683
- CloseHandle(handle)
684
- end
685
- end
686
-
687
- return 0
688
- end
689
-
690
- # Returns the file's type (as a numeric).
691
- #
692
- def get_file_type(file)
693
- begin
694
- handle = CreateFileW(
695
- file,
696
- 0,
651
+ # First call is to get the required length
652
+ handle_info = 0.chr * 4096
653
+ required = 0.chr * 4
654
+ NtQuerySystemInformation(16, handle_info, 4096, required)
655
+
656
+ # Second call is the actual call
657
+ handle_info = 0.chr * required.unpack('L').first
658
+ NtQuerySystemInformation(16, handle_info, handle_info.length, required)
659
+
660
+ count = handle_info[0,4].unpack('L').first
661
+
662
+ for i in 0...count
663
+ pid, type, handle, addr, access = handle_info[4+i*16,16].unpack('LSSLL')
664
+ if access & 0xffff == 3
665
+ begin
666
+ process = OpenProcess(0x40,1,pid)
667
+ dup_handle = 0.chr * 4
668
+
669
+ DuplicateHandle(
670
+ process,
671
+ handle,
672
+ GetCurrentProcess(),
673
+ dup_handle,
697
674
  0,
698
- nil,
699
- OPEN_EXISTING,
700
- FILE_FLAG_BACKUP_SEMANTICS, # Need this for directories
701
- nil
702
- )
703
-
704
- error_num = GetLastError()
705
-
706
- # CreateFile() chokes on locked files
707
- if error_num == ERROR_SHARING_VIOLATION
708
- drive = file[0,4] + 0.chr * 2
709
- device = 0.chr * 512
710
- QueryDosDeviceW(drive, device, 256)
711
- file = device.strip + 0.chr + file[4..-1]
712
- handle = get_handle(file)
713
- end
714
-
715
- # We raise a SystemCallError explicitly here in order to maintain
716
- # compatibility with the FileUtils module.
717
- if handle == INVALID_HANDLE_VALUE
718
- raise SystemCallError, get_last_error(error_num)
719
- end
675
+ 1,
676
+ 2
677
+ )
678
+ ensure
679
+ CloseHandle(process)
680
+ end
681
+
682
+ handle = dup_handle.unpack('L').first
683
+ buffer = 0.chr * 0x2000
684
+ NtQueryObject(handle, 1, buffer, 0x2000, nil)
685
+ len = buffer[0,2].unpack('S').first
686
+
687
+ if len>0
688
+ if buffer[8..-1].upcase[file]
689
+ return handle
690
+ end
691
+ end
692
+ CloseHandle(handle)
693
+ end
694
+ end
695
+
696
+ return 0
697
+ end
698
+
699
+ # Returns the file's type (as a numeric).
700
+ #
701
+ def get_file_type(file)
702
+ begin
703
+ handle = CreateFileW(
704
+ file,
705
+ 0,
706
+ 0,
707
+ nil,
708
+ OPEN_EXISTING,
709
+ FILE_FLAG_BACKUP_SEMANTICS, # Need this for directories
710
+ nil
711
+ )
720
712
 
721
- file_type = GetFileType(handle)
722
- error_num = GetLastError()
723
- ensure
724
- CloseHandle(handle)
713
+ error_num = GetLastError()
714
+
715
+ # CreateFile() chokes on locked files
716
+ if error_num == ERROR_SHARING_VIOLATION
717
+ drive = file[0,4] + 0.chr * 2
718
+ device = 0.chr * 512
719
+ QueryDosDeviceW(drive, device, 256)
720
+ file = device.strip + 0.chr + file[4..-1]
721
+ handle = get_handle(file)
725
722
  end
726
723
 
727
- if file_type == FILE_TYPE_UNKNOWN && error_num != NO_ERROR
728
- raise SystemCallError, get_last_error(error_num)
724
+ # We raise a SystemCallError explicitly here in order to maintain
725
+ # compatibility with the FileUtils module.
726
+ if handle == INVALID_HANDLE_VALUE
727
+ raise SystemCallError, get_last_error(error_num)
729
728
  end
730
729
 
731
- file_type
732
- end
730
+ file_type = GetFileType(handle)
731
+ error_num = GetLastError()
732
+ ensure
733
+ CloseHandle(handle)
734
+ end
735
+
736
+ if file_type == FILE_TYPE_UNKNOWN && error_num != NO_ERROR
737
+ raise SystemCallError, get_last_error(error_num)
738
+ end
739
+
740
+ file_type
741
+ end
733
742
 
734
- private
743
+ private
735
744
 
736
- # Verifies that a value is either true or false
737
- def check_bool(val)
738
- raise TypeError unless val == true || val == false
739
- end
740
- end
745
+ # Verifies that a value is either true or false
746
+ def check_bool(val)
747
+ raise TypeError unless val == true || val == false
748
+ end
749
+ end