pathname2 1.0.0 → 1.1.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 +3 -0
- data/lib/pathname2.rb +557 -536
- data/test/tc_pathname.rb +192 -182
- data/test/tc_pathname_win.rb +237 -227
- metadata +5 -5
data/CHANGES
CHANGED
data/lib/pathname2.rb
CHANGED
@@ -1,536 +1,557 @@
|
|
1
|
-
# == Synopsis
|
2
|
-
#
|
3
|
-
# Pathname represents a path name on a filesystem. A Pathname can be
|
4
|
-
# relative or absolute. It does not matter whether the path exists or not.
|
5
|
-
#
|
6
|
-
# All functionality from File, FileTest, and Dir is included, using a facade
|
7
|
-
# pattern.
|
8
|
-
#
|
9
|
-
# This class works on both Unix and Win32, including UNC path names. Note
|
10
|
-
# that forward slashes are converted to backslashes on Win32 systems.
|
11
|
-
#
|
12
|
-
# == Usage
|
13
|
-
#
|
14
|
-
# require "pathname"
|
15
|
-
#
|
16
|
-
# # Unix
|
17
|
-
# path1 = Pathname.new("/foo/bar/baz")
|
18
|
-
# path2 = Pathname.new("../zap")
|
19
|
-
#
|
20
|
-
# path1 + path2 # "/foo/bar/zap"
|
21
|
-
# path1.dirname # "/foo/bar"
|
22
|
-
#
|
23
|
-
# # Win32
|
24
|
-
# path1 = Pathname.new("C:\\foo\\bar\\baz")
|
25
|
-
# path2 = Pathname.new("..\\zap")
|
26
|
-
#
|
27
|
-
# path1 + path2 # "C:\\foo\\bar\\zap"
|
28
|
-
# path1.exists? # Does the path exist?
|
29
|
-
#
|
30
|
-
# == Author
|
31
|
-
#
|
32
|
-
# Daniel J. Berger
|
33
|
-
# djberg96 at gmail dot com
|
34
|
-
# imperator on IRC (irc.freenode.net)
|
35
|
-
#
|
36
|
-
# == Copyright
|
37
|
-
# Copyright (c) 2005 Daniel J. Berger.
|
38
|
-
# Licensed under the same terms as Ruby itself.
|
39
|
-
#
|
40
|
-
require "facade"
|
41
|
-
require "ftools"
|
42
|
-
require "fileutils"
|
43
|
-
|
44
|
-
class Pathname < String
|
45
|
-
extend Facade
|
46
|
-
facade File
|
47
|
-
facade Dir
|
48
|
-
|
49
|
-
if PLATFORM.match("mswin32")
|
50
|
-
require "Win32API"
|
51
|
-
@@PathStripToRoot = Win32API.new("shlwapi","PathStripToRoot","P","L")
|
52
|
-
@@PathIsUNC = Win32API.new("shlwapi","PathIsUNC","P","L")
|
53
|
-
@@PathIsURL = Win32API.new("shlwapi","PathIsURL","P","L")
|
54
|
-
@@PathCanonicalize = Win32API.new("shlwapi","PathCanonicalize","PP","L")
|
55
|
-
@@PathAppend = Win32API.new("shlwapi","PathAppend","PP","L")
|
56
|
-
@@PathIsRoot = Win32API.new("shlwapi","PathIsRoot","P","L")
|
57
|
-
@@PathIsDirectory = Win32API.new("shlwapi","PathIsDirectory","P","L")
|
58
|
-
@@PathIsRelative = Win32API.new("shlwapi","PathIsRelative","P","L")
|
59
|
-
@@PathFileExists = Win32API.new("shlwapi","PathFileExists","P","L")
|
60
|
-
@@PathUndecorate = Win32API.new("shlwapi","PathUndecorate","P","L")
|
61
|
-
|
62
|
-
@@PathGetDriveNumber =
|
63
|
-
Win32API.new("shlwapi","PathGetDriveNumber","P","L")
|
64
|
-
|
65
|
-
@@PathCreateFromUrl =
|
66
|
-
Win32API.new("shlwapi", "PathCreateFromUrl", "PPPL", "L")
|
67
|
-
|
68
|
-
@@PathRemoveBackslash =
|
69
|
-
Win32API.new("shlwapi", "PathRemoveBackslash", "P", "P")
|
70
|
-
end
|
71
|
-
|
72
|
-
VERSION = "1.
|
73
|
-
MAX_PATH = 260
|
74
|
-
|
75
|
-
# Creates and returns a new Pathname object.
|
76
|
-
#
|
77
|
-
# On Win32 systems, all forward slashes are replaced with backslashes.
|
78
|
-
def initialize(path)
|
79
|
-
if path.length > MAX_PATH
|
80
|
-
msg = "string too long. maximum string length is " + MAX_PATH.to_s
|
81
|
-
raise PathnameError, msg
|
82
|
-
end
|
83
|
-
|
84
|
-
@sep = File::ALT_SEPARATOR || File::SEPARATOR
|
85
|
-
@win32 = PLATFORM.match("mswin32")
|
86
|
-
|
87
|
-
# Handle File URL's for Windows
|
88
|
-
if @win32
|
89
|
-
if @@PathIsURL.call(path) > 0
|
90
|
-
buf = 0.chr * MAX_PATH
|
91
|
-
len = [buf.length].pack("l")
|
92
|
-
if @@PathCreateFromUrl.call(path, buf, len, 0) >= 0
|
93
|
-
path = buf.strip
|
94
|
-
else
|
95
|
-
raise PathnameError, "invalid file url: #{path}"
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# Convert forward slashes to backslashes on Win32
|
101
|
-
path = path.tr("/", @sep) if @win32
|
102
|
-
super(path)
|
103
|
-
end
|
104
|
-
|
105
|
-
# Win32 only
|
106
|
-
#
|
107
|
-
# Removes the decoration from a path string. For example,
|
108
|
-
# C:\Path\File[5].txt would become C:\Path\File.txt.
|
109
|
-
def undecorate
|
110
|
-
unless @win32
|
111
|
-
raise NotImplementedError, "not supported on this platform"
|
112
|
-
end
|
113
|
-
buf = 0.chr * MAX_PATH
|
114
|
-
buf[0..self.length-1] = self
|
115
|
-
@@PathUndecorate.call(buf)
|
116
|
-
Pathname.new(buf.split(0.chr).first)
|
117
|
-
end
|
118
|
-
|
119
|
-
# Removes trailing slash, if present.
|
120
|
-
def pstrip
|
121
|
-
if @win32
|
122
|
-
@@PathRemoveBackslash.call(self)
|
123
|
-
self.strip!
|
124
|
-
else
|
125
|
-
if self[-1] == @sep
|
126
|
-
self.strip!
|
127
|
-
self.chop!
|
128
|
-
end
|
129
|
-
end
|
130
|
-
self
|
131
|
-
end
|
132
|
-
|
133
|
-
# Splits a pathname into pieces based on the path separator. For example,
|
134
|
-
# "/foo/bar/baz" would return a three element array of ['foo','bar','baz'].
|
135
|
-
def to_a
|
136
|
-
array = split(@sep) # Split string by path separator
|
137
|
-
array.delete("") # Remove empty elements
|
138
|
-
array
|
139
|
-
end
|
140
|
-
|
141
|
-
# Yields each component of the path name to a block.
|
142
|
-
def each
|
143
|
-
self.to_a.each{ |element| yield element }
|
144
|
-
end
|
145
|
-
|
146
|
-
# Returns the root directory of the path, or '.' if there is no root
|
147
|
-
# directory.
|
148
|
-
#
|
149
|
-
# On Unix, this means the '/' character. On Win32 systems, this can
|
150
|
-
# refer to the drive letter, or the server and share path if the path
|
151
|
-
# is a UNC path.
|
152
|
-
def root
|
153
|
-
dir = "."
|
154
|
-
if @win32
|
155
|
-
buf = 0.chr * MAX_PATH
|
156
|
-
buf[0..self.length-1] = self
|
157
|
-
|
158
|
-
if @@PathStripToRoot.call(buf) > 0
|
159
|
-
dir = Pathname.new(buf.split(0.chr).first)
|
160
|
-
end
|
161
|
-
else
|
162
|
-
dir = File.dirname(self)
|
163
|
-
while dir != "/" && dir != "."
|
164
|
-
dir = File.dirname(dir)
|
165
|
-
end
|
166
|
-
end
|
167
|
-
dir = "." if dir.empty?
|
168
|
-
dir
|
169
|
-
end
|
170
|
-
|
171
|
-
# Returns whether or not the path consists only of a root directory.
|
172
|
-
def root?
|
173
|
-
if @win32
|
174
|
-
@@PathIsRoot.call(self) > 0
|
175
|
-
else
|
176
|
-
self == root
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
# Win32 only
|
181
|
-
#
|
182
|
-
# Determines if the string is a valid Universal Naming Convention (UNC)
|
183
|
-
# for a server and share path.
|
184
|
-
def unc?
|
185
|
-
unless @win32
|
186
|
-
raise NotImplementedError, "not supported on this platform"
|
187
|
-
end
|
188
|
-
|
189
|
-
@@PathIsUNC.call(self) > 0
|
190
|
-
end
|
191
|
-
|
192
|
-
# Win32 only
|
193
|
-
#
|
194
|
-
# Returns the drive number that corresponds to the root, or nil if not
|
195
|
-
# applicable.
|
196
|
-
#
|
197
|
-
# For example, Pathname.new("C:\\foo").drive_number would return 2.
|
198
|
-
def drive_number
|
199
|
-
unless @win32
|
200
|
-
raise NotImplementedError, "not supported on this platform"
|
201
|
-
end
|
202
|
-
|
203
|
-
num = @@PathGetDriveNumber.call(self)
|
204
|
-
num >= 0 ? num : nil
|
205
|
-
end
|
206
|
-
|
207
|
-
# Pathnames may only be compared against other Pathnames, not strings.
|
208
|
-
def <=>(string)
|
209
|
-
return nil unless string.kind_of?(Pathname)
|
210
|
-
super
|
211
|
-
end
|
212
|
-
|
213
|
-
# Adds two Pathname objects together, or a Pathname and a String. It
|
214
|
-
# also automatically cleans the Pathname.
|
215
|
-
#
|
216
|
-
# Example:
|
217
|
-
# path1 = '/foo/bar'
|
218
|
-
# path2 = '../baz'
|
219
|
-
# path1 + path2 # '/foo/baz'
|
220
|
-
#
|
221
|
-
# Adding a root path to an existing path merely replaces the current
|
222
|
-
# path. Adding '.' to an existing path does nothing.
|
223
|
-
def +(string)
|
224
|
-
unless string.kind_of?(Pathname)
|
225
|
-
string = Pathname.new(string)
|
226
|
-
end
|
227
|
-
|
228
|
-
# Any path plus "." is the same directory
|
229
|
-
return self if string == "."
|
230
|
-
|
231
|
-
# Use the builtin PathAppend method if on Windows - much easier
|
232
|
-
if @win32
|
233
|
-
buf = 0.chr * MAX_PATH
|
234
|
-
buf[0..self.length-1] = self
|
235
|
-
@@PathAppend.call(buf, string << 0.chr)
|
236
|
-
buf.strip!
|
237
|
-
return Pathname.new(buf) # PathAppend cleans automatically
|
238
|
-
end
|
239
|
-
|
240
|
-
# If the string is an absolute directory, return it
|
241
|
-
return string if string.absolute?
|
242
|
-
|
243
|
-
array = self.to_a + string.to_a
|
244
|
-
new_string = array.join(@sep)
|
245
|
-
|
246
|
-
unless self.relative? || @win32
|
247
|
-
new_string = @sep + new_string # Add root path back if needed
|
248
|
-
end
|
249
|
-
|
250
|
-
Pathname.new(new_string).clean
|
251
|
-
end
|
252
|
-
|
253
|
-
# Returns whether or not the path is an absolute path.
|
254
|
-
def absolute?
|
255
|
-
!relative?
|
256
|
-
end
|
257
|
-
|
258
|
-
# Returns whether or not the path is a relative path.
|
259
|
-
def relative?
|
260
|
-
if @win32
|
261
|
-
@@PathIsRelative.call(self) > 0
|
262
|
-
else
|
263
|
-
root == "."
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
# Removes unnecessary '.' paths and ellides '..' paths appropriately.
|
268
|
-
def clean
|
269
|
-
return self if self.empty?
|
270
|
-
|
271
|
-
if @win32
|
272
|
-
path = 0.chr * MAX_PATH
|
273
|
-
if @@PathCanonicalize.call(path, self) > 0
|
274
|
-
return Pathname.new(path.split(0.chr).first)
|
275
|
-
else
|
276
|
-
return self
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
final = []
|
281
|
-
self.to_a.each{ |element|
|
282
|
-
next if element == "."
|
283
|
-
final.push(element)
|
284
|
-
if element == ".." && self != ".."
|
285
|
-
2.times{ final.pop }
|
286
|
-
end
|
287
|
-
}
|
288
|
-
final = final.join(@sep)
|
289
|
-
final = root + final if root != "."
|
290
|
-
final = "." if final.empty?
|
291
|
-
Pathname.new(final)
|
292
|
-
end
|
293
|
-
alias :cleanpath :clean
|
294
|
-
|
295
|
-
#--
|
296
|
-
|
297
|
-
#
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
#
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
end
|
327
|
-
|
328
|
-
#
|
329
|
-
def
|
330
|
-
|
331
|
-
end
|
332
|
-
|
333
|
-
#
|
334
|
-
def
|
335
|
-
|
336
|
-
end
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
#
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
#
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
#
|
446
|
-
def
|
447
|
-
|
448
|
-
end
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
# FileUtils.
|
456
|
-
def
|
457
|
-
FileUtils.
|
458
|
-
end
|
459
|
-
|
460
|
-
# FileUtils.
|
461
|
-
def
|
462
|
-
FileUtils.
|
463
|
-
end
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
# FileUtils.
|
482
|
-
def
|
483
|
-
FileUtils.
|
484
|
-
end
|
485
|
-
|
486
|
-
# FileUtils.
|
487
|
-
def
|
488
|
-
FileUtils.
|
489
|
-
end
|
490
|
-
|
491
|
-
# FileUtils.
|
492
|
-
def
|
493
|
-
FileUtils.
|
494
|
-
end
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
# FileUtils.
|
503
|
-
def
|
504
|
-
FileUtils.
|
505
|
-
end
|
506
|
-
|
507
|
-
# FileUtils.
|
508
|
-
def
|
509
|
-
FileUtils.
|
510
|
-
end
|
511
|
-
|
512
|
-
# FileUtils.
|
513
|
-
def
|
514
|
-
FileUtils.
|
515
|
-
end
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
end
|
1
|
+
# == Synopsis
|
2
|
+
#
|
3
|
+
# Pathname represents a path name on a filesystem. A Pathname can be
|
4
|
+
# relative or absolute. It does not matter whether the path exists or not.
|
5
|
+
#
|
6
|
+
# All functionality from File, FileTest, and Dir is included, using a facade
|
7
|
+
# pattern.
|
8
|
+
#
|
9
|
+
# This class works on both Unix and Win32, including UNC path names. Note
|
10
|
+
# that forward slashes are converted to backslashes on Win32 systems.
|
11
|
+
#
|
12
|
+
# == Usage
|
13
|
+
#
|
14
|
+
# require "pathname"
|
15
|
+
#
|
16
|
+
# # Unix
|
17
|
+
# path1 = Pathname.new("/foo/bar/baz")
|
18
|
+
# path2 = Pathname.new("../zap")
|
19
|
+
#
|
20
|
+
# path1 + path2 # "/foo/bar/zap"
|
21
|
+
# path1.dirname # "/foo/bar"
|
22
|
+
#
|
23
|
+
# # Win32
|
24
|
+
# path1 = Pathname.new("C:\\foo\\bar\\baz")
|
25
|
+
# path2 = Pathname.new("..\\zap")
|
26
|
+
#
|
27
|
+
# path1 + path2 # "C:\\foo\\bar\\zap"
|
28
|
+
# path1.exists? # Does the path exist?
|
29
|
+
#
|
30
|
+
# == Author
|
31
|
+
#
|
32
|
+
# Daniel J. Berger
|
33
|
+
# djberg96 at gmail dot com
|
34
|
+
# imperator on IRC (irc.freenode.net)
|
35
|
+
#
|
36
|
+
# == Copyright
|
37
|
+
# Copyright (c) 2005 Daniel J. Berger.
|
38
|
+
# Licensed under the same terms as Ruby itself.
|
39
|
+
#
|
40
|
+
require "facade"
|
41
|
+
require "ftools"
|
42
|
+
require "fileutils"
|
43
|
+
|
44
|
+
class Pathname < String
|
45
|
+
extend Facade
|
46
|
+
facade File
|
47
|
+
facade Dir
|
48
|
+
|
49
|
+
if PLATFORM.match("mswin32")
|
50
|
+
require "Win32API"
|
51
|
+
@@PathStripToRoot = Win32API.new("shlwapi","PathStripToRoot","P","L")
|
52
|
+
@@PathIsUNC = Win32API.new("shlwapi","PathIsUNC","P","L")
|
53
|
+
@@PathIsURL = Win32API.new("shlwapi","PathIsURL","P","L")
|
54
|
+
@@PathCanonicalize = Win32API.new("shlwapi","PathCanonicalize","PP","L")
|
55
|
+
@@PathAppend = Win32API.new("shlwapi","PathAppend","PP","L")
|
56
|
+
@@PathIsRoot = Win32API.new("shlwapi","PathIsRoot","P","L")
|
57
|
+
@@PathIsDirectory = Win32API.new("shlwapi","PathIsDirectory","P","L")
|
58
|
+
@@PathIsRelative = Win32API.new("shlwapi","PathIsRelative","P","L")
|
59
|
+
@@PathFileExists = Win32API.new("shlwapi","PathFileExists","P","L")
|
60
|
+
@@PathUndecorate = Win32API.new("shlwapi","PathUndecorate","P","L")
|
61
|
+
|
62
|
+
@@PathGetDriveNumber =
|
63
|
+
Win32API.new("shlwapi","PathGetDriveNumber","P","L")
|
64
|
+
|
65
|
+
@@PathCreateFromUrl =
|
66
|
+
Win32API.new("shlwapi", "PathCreateFromUrl", "PPPL", "L")
|
67
|
+
|
68
|
+
@@PathRemoveBackslash =
|
69
|
+
Win32API.new("shlwapi", "PathRemoveBackslash", "P", "P")
|
70
|
+
end
|
71
|
+
|
72
|
+
VERSION = "1.1.0"
|
73
|
+
MAX_PATH = 260
|
74
|
+
|
75
|
+
# Creates and returns a new Pathname object.
|
76
|
+
#
|
77
|
+
# On Win32 systems, all forward slashes are replaced with backslashes.
|
78
|
+
def initialize(path)
|
79
|
+
if path.length > MAX_PATH
|
80
|
+
msg = "string too long. maximum string length is " + MAX_PATH.to_s
|
81
|
+
raise PathnameError, msg
|
82
|
+
end
|
83
|
+
|
84
|
+
@sep = File::ALT_SEPARATOR || File::SEPARATOR
|
85
|
+
@win32 = PLATFORM.match("mswin32")
|
86
|
+
|
87
|
+
# Handle File URL's for Windows
|
88
|
+
if @win32
|
89
|
+
if @@PathIsURL.call(path) > 0
|
90
|
+
buf = 0.chr * MAX_PATH
|
91
|
+
len = [buf.length].pack("l")
|
92
|
+
if @@PathCreateFromUrl.call(path, buf, len, 0) >= 0
|
93
|
+
path = buf.strip
|
94
|
+
else
|
95
|
+
raise PathnameError, "invalid file url: #{path}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Convert forward slashes to backslashes on Win32
|
101
|
+
path = path.tr("/", @sep) if @win32
|
102
|
+
super(path)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Win32 only
|
106
|
+
#
|
107
|
+
# Removes the decoration from a path string. For example,
|
108
|
+
# C:\Path\File[5].txt would become C:\Path\File.txt.
|
109
|
+
def undecorate
|
110
|
+
unless @win32
|
111
|
+
raise NotImplementedError, "not supported on this platform"
|
112
|
+
end
|
113
|
+
buf = 0.chr * MAX_PATH
|
114
|
+
buf[0..self.length-1] = self
|
115
|
+
@@PathUndecorate.call(buf)
|
116
|
+
Pathname.new(buf.split(0.chr).first)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Removes trailing slash, if present.
|
120
|
+
def pstrip
|
121
|
+
if @win32
|
122
|
+
@@PathRemoveBackslash.call(self)
|
123
|
+
self.strip!
|
124
|
+
else
|
125
|
+
if self[-1] == @sep
|
126
|
+
self.strip!
|
127
|
+
self.chop!
|
128
|
+
end
|
129
|
+
end
|
130
|
+
self
|
131
|
+
end
|
132
|
+
|
133
|
+
# Splits a pathname into pieces based on the path separator. For example,
|
134
|
+
# "/foo/bar/baz" would return a three element array of ['foo','bar','baz'].
|
135
|
+
def to_a
|
136
|
+
array = split(@sep) # Split string by path separator
|
137
|
+
array.delete("") # Remove empty elements
|
138
|
+
array
|
139
|
+
end
|
140
|
+
|
141
|
+
# Yields each component of the path name to a block.
|
142
|
+
def each
|
143
|
+
self.to_a.each{ |element| yield element }
|
144
|
+
end
|
145
|
+
|
146
|
+
# Returns the root directory of the path, or '.' if there is no root
|
147
|
+
# directory.
|
148
|
+
#
|
149
|
+
# On Unix, this means the '/' character. On Win32 systems, this can
|
150
|
+
# refer to the drive letter, or the server and share path if the path
|
151
|
+
# is a UNC path.
|
152
|
+
def root
|
153
|
+
dir = "."
|
154
|
+
if @win32
|
155
|
+
buf = 0.chr * MAX_PATH
|
156
|
+
buf[0..self.length-1] = self
|
157
|
+
|
158
|
+
if @@PathStripToRoot.call(buf) > 0
|
159
|
+
dir = Pathname.new(buf.split(0.chr).first)
|
160
|
+
end
|
161
|
+
else
|
162
|
+
dir = File.dirname(self)
|
163
|
+
while dir != "/" && dir != "."
|
164
|
+
dir = File.dirname(dir)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
dir = "." if dir.empty?
|
168
|
+
dir
|
169
|
+
end
|
170
|
+
|
171
|
+
# Returns whether or not the path consists only of a root directory.
|
172
|
+
def root?
|
173
|
+
if @win32
|
174
|
+
@@PathIsRoot.call(self) > 0
|
175
|
+
else
|
176
|
+
self == root
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Win32 only
|
181
|
+
#
|
182
|
+
# Determines if the string is a valid Universal Naming Convention (UNC)
|
183
|
+
# for a server and share path.
|
184
|
+
def unc?
|
185
|
+
unless @win32
|
186
|
+
raise NotImplementedError, "not supported on this platform"
|
187
|
+
end
|
188
|
+
|
189
|
+
@@PathIsUNC.call(self) > 0
|
190
|
+
end
|
191
|
+
|
192
|
+
# Win32 only
|
193
|
+
#
|
194
|
+
# Returns the drive number that corresponds to the root, or nil if not
|
195
|
+
# applicable.
|
196
|
+
#
|
197
|
+
# For example, Pathname.new("C:\\foo").drive_number would return 2.
|
198
|
+
def drive_number
|
199
|
+
unless @win32
|
200
|
+
raise NotImplementedError, "not supported on this platform"
|
201
|
+
end
|
202
|
+
|
203
|
+
num = @@PathGetDriveNumber.call(self)
|
204
|
+
num >= 0 ? num : nil
|
205
|
+
end
|
206
|
+
|
207
|
+
# Pathnames may only be compared against other Pathnames, not strings.
|
208
|
+
def <=>(string)
|
209
|
+
return nil unless string.kind_of?(Pathname)
|
210
|
+
super
|
211
|
+
end
|
212
|
+
|
213
|
+
# Adds two Pathname objects together, or a Pathname and a String. It
|
214
|
+
# also automatically cleans the Pathname.
|
215
|
+
#
|
216
|
+
# Example:
|
217
|
+
# path1 = '/foo/bar'
|
218
|
+
# path2 = '../baz'
|
219
|
+
# path1 + path2 # '/foo/baz'
|
220
|
+
#
|
221
|
+
# Adding a root path to an existing path merely replaces the current
|
222
|
+
# path. Adding '.' to an existing path does nothing.
|
223
|
+
def +(string)
|
224
|
+
unless string.kind_of?(Pathname)
|
225
|
+
string = Pathname.new(string)
|
226
|
+
end
|
227
|
+
|
228
|
+
# Any path plus "." is the same directory
|
229
|
+
return self if string == "."
|
230
|
+
|
231
|
+
# Use the builtin PathAppend method if on Windows - much easier
|
232
|
+
if @win32
|
233
|
+
buf = 0.chr * MAX_PATH
|
234
|
+
buf[0..self.length-1] = self
|
235
|
+
@@PathAppend.call(buf, string << 0.chr)
|
236
|
+
buf.strip!
|
237
|
+
return Pathname.new(buf) # PathAppend cleans automatically
|
238
|
+
end
|
239
|
+
|
240
|
+
# If the string is an absolute directory, return it
|
241
|
+
return string if string.absolute?
|
242
|
+
|
243
|
+
array = self.to_a + string.to_a
|
244
|
+
new_string = array.join(@sep)
|
245
|
+
|
246
|
+
unless self.relative? || @win32
|
247
|
+
new_string = @sep + new_string # Add root path back if needed
|
248
|
+
end
|
249
|
+
|
250
|
+
Pathname.new(new_string).clean
|
251
|
+
end
|
252
|
+
|
253
|
+
# Returns whether or not the path is an absolute path.
|
254
|
+
def absolute?
|
255
|
+
!relative?
|
256
|
+
end
|
257
|
+
|
258
|
+
# Returns whether or not the path is a relative path.
|
259
|
+
def relative?
|
260
|
+
if @win32
|
261
|
+
@@PathIsRelative.call(self) > 0
|
262
|
+
else
|
263
|
+
root == "."
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# Removes unnecessary '.' paths and ellides '..' paths appropriately.
|
268
|
+
def clean
|
269
|
+
return self if self.empty?
|
270
|
+
|
271
|
+
if @win32
|
272
|
+
path = 0.chr * MAX_PATH
|
273
|
+
if @@PathCanonicalize.call(path, self) > 0
|
274
|
+
return Pathname.new(path.split(0.chr).first)
|
275
|
+
else
|
276
|
+
return self
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
final = []
|
281
|
+
self.to_a.each{ |element|
|
282
|
+
next if element == "."
|
283
|
+
final.push(element)
|
284
|
+
if element == ".." && self != ".."
|
285
|
+
2.times{ final.pop }
|
286
|
+
end
|
287
|
+
}
|
288
|
+
final = final.join(@sep)
|
289
|
+
final = root + final if root != "."
|
290
|
+
final = "." if final.empty?
|
291
|
+
Pathname.new(final)
|
292
|
+
end
|
293
|
+
alias :cleanpath :clean
|
294
|
+
|
295
|
+
#-- Find facade
|
296
|
+
|
297
|
+
# Pathname#find is an iterator to traverse a directory tree in a depth first
|
298
|
+
# manner. It yields a Pathname for each file under the directory passed to
|
299
|
+
# Pathname.new.
|
300
|
+
#
|
301
|
+
# Since it is implemented by the Find module, Find.prune can be used to
|
302
|
+
# control the traverse.
|
303
|
+
#
|
304
|
+
# If +self+ is ".", yielded pathnames begin with a filename in the current
|
305
|
+
# current directory, not ".".
|
306
|
+
#
|
307
|
+
def find(&block)
|
308
|
+
require "find"
|
309
|
+
if self == "."
|
310
|
+
Find.find(self){ |f| yield Pathname.new(f.sub(%r{\A\./}, '')) }
|
311
|
+
else
|
312
|
+
Find.find(self) {|f| yield Pathname.new(f) }
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
#-- IO methods not handled by facade
|
317
|
+
|
318
|
+
# IO.foreach
|
319
|
+
def foreach(*args, &block)
|
320
|
+
IO.foreach(self, *args, &block)
|
321
|
+
end
|
322
|
+
|
323
|
+
# IO.read
|
324
|
+
def read(*args)
|
325
|
+
IO.read(self, *args)
|
326
|
+
end
|
327
|
+
|
328
|
+
# IO.readlines
|
329
|
+
def readlines(*args)
|
330
|
+
IO.readlines(self, *args)
|
331
|
+
end
|
332
|
+
|
333
|
+
# IO.sysopen
|
334
|
+
def sysopen(*args)
|
335
|
+
IO.sysopen(self, *args)
|
336
|
+
end
|
337
|
+
|
338
|
+
#-- Dir methods not handled by facade
|
339
|
+
|
340
|
+
# Dir.glob
|
341
|
+
def glob(*args)
|
342
|
+
if block_given?
|
343
|
+
Dir.glob(*args){ |file| yield Pathname.new(file) }
|
344
|
+
else
|
345
|
+
Dir.glob(*args).map{ |file| Pathname.new(file) }
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
# Dir.chdir
|
350
|
+
def chdir(&block)
|
351
|
+
Dir.chdir(self, &block)
|
352
|
+
end
|
353
|
+
|
354
|
+
# Dir.entries
|
355
|
+
def entries
|
356
|
+
Dir.entries(self).map{ |file| Pathname.new(file) }
|
357
|
+
end
|
358
|
+
|
359
|
+
# Dir.mkdir
|
360
|
+
def mkdir(*args)
|
361
|
+
Dir.mkdir(self, *args)
|
362
|
+
end
|
363
|
+
|
364
|
+
# Dir.opendir
|
365
|
+
def opendir(&block)
|
366
|
+
Dir.open(self, &block)
|
367
|
+
end
|
368
|
+
|
369
|
+
#-- File methods not handled by facade
|
370
|
+
|
371
|
+
# File.chmod
|
372
|
+
def chmod(mode)
|
373
|
+
File.chmod(mode, self)
|
374
|
+
end
|
375
|
+
|
376
|
+
# File.lchmod
|
377
|
+
def lchmod(mode)
|
378
|
+
File.lchmod(mode, self)
|
379
|
+
end
|
380
|
+
|
381
|
+
# File.chown
|
382
|
+
def chown(owner, group)
|
383
|
+
File.chown(owner, group, self)
|
384
|
+
end
|
385
|
+
|
386
|
+
# File.lchown
|
387
|
+
def lchown(owner, group)
|
388
|
+
File.lchown(owner, group, self)
|
389
|
+
end
|
390
|
+
|
391
|
+
# File.fnmatch
|
392
|
+
def fnmatch(pattern, *args)
|
393
|
+
File.fnmatch(pattern, self, *args)
|
394
|
+
end
|
395
|
+
|
396
|
+
# File.fnmatch?
|
397
|
+
def fnmatch?(pattern, *args)
|
398
|
+
File.fnmatch?(pattern, self, *args)
|
399
|
+
end
|
400
|
+
|
401
|
+
# File.link
|
402
|
+
def link(old)
|
403
|
+
File.link(old, self)
|
404
|
+
end
|
405
|
+
|
406
|
+
# File.open
|
407
|
+
def open(*args, &block)
|
408
|
+
File.open(self, *args, &block)
|
409
|
+
end
|
410
|
+
|
411
|
+
# File.rename
|
412
|
+
def rename(name)
|
413
|
+
File.rename(self, name)
|
414
|
+
end
|
415
|
+
|
416
|
+
# File.symlink
|
417
|
+
def symlink(old)
|
418
|
+
File.symlink(old, self)
|
419
|
+
end
|
420
|
+
|
421
|
+
# File.truncate
|
422
|
+
def truncate(length)
|
423
|
+
File.truncate(self, length)
|
424
|
+
end
|
425
|
+
|
426
|
+
# File.utime
|
427
|
+
def utime(atime, mtime)
|
428
|
+
File.utime(atime, mtime, self)
|
429
|
+
end
|
430
|
+
|
431
|
+
# File.basename
|
432
|
+
def basename(*args)
|
433
|
+
File.basename(self, *args)
|
434
|
+
end
|
435
|
+
|
436
|
+
# File.expand_path
|
437
|
+
def expand_path(*args)
|
438
|
+
File.expand_path(self, *args)
|
439
|
+
end
|
440
|
+
|
441
|
+
#--
|
442
|
+
# ftools facade
|
443
|
+
#++
|
444
|
+
|
445
|
+
# ftools File.copy
|
446
|
+
def copy(*args)
|
447
|
+
File.copy(self, *args)
|
448
|
+
end
|
449
|
+
|
450
|
+
#--
|
451
|
+
# FileUtils facade. Note that methods already covered by File and Dir
|
452
|
+
# are not defined here (pwd, mkdir, etc).
|
453
|
+
#++
|
454
|
+
|
455
|
+
# FileUtils.cd
|
456
|
+
def cd(*args, &block)
|
457
|
+
FileUtils.cd(self, *args, &block)
|
458
|
+
end
|
459
|
+
|
460
|
+
# FileUtils.mkdir_p
|
461
|
+
def mkdir_p(*args)
|
462
|
+
FileUtils.mkdir_p(self, *args)
|
463
|
+
end
|
464
|
+
alias mkpath mkdir_p
|
465
|
+
|
466
|
+
# FileUtils.ln
|
467
|
+
def ln(*args)
|
468
|
+
FileUtils.ln(self, *args)
|
469
|
+
end
|
470
|
+
|
471
|
+
# FileUtils.ln_s
|
472
|
+
def ln_s(*args)
|
473
|
+
FileUtils.ln_s(self, *args)
|
474
|
+
end
|
475
|
+
|
476
|
+
# FileUtils.ln_sf
|
477
|
+
def ln_sf(*args)
|
478
|
+
FileUtils.ln_sf(self, *args)
|
479
|
+
end
|
480
|
+
|
481
|
+
# FileUtils.cp
|
482
|
+
def cp(*args)
|
483
|
+
FileUtils.cp(self, *args)
|
484
|
+
end
|
485
|
+
|
486
|
+
# FileUtils.cp_r
|
487
|
+
def cp_r(*args)
|
488
|
+
FileUtils.cp_r(self, *args)
|
489
|
+
end
|
490
|
+
|
491
|
+
# FileUtils.mv
|
492
|
+
def mv(*args)
|
493
|
+
FileUtils.mv(self, *args)
|
494
|
+
end
|
495
|
+
|
496
|
+
# FileUtils.rm
|
497
|
+
def rm(*args)
|
498
|
+
FileUtils.rm(self, *args)
|
499
|
+
end
|
500
|
+
alias remove rm
|
501
|
+
|
502
|
+
# FileUtils.rm_f
|
503
|
+
def rm_f(*args)
|
504
|
+
FileUtils.rm_f(self, *args)
|
505
|
+
end
|
506
|
+
|
507
|
+
# FileUtils.rm_r
|
508
|
+
def rm_r(*args)
|
509
|
+
FileUtils.rm_r(self, *args)
|
510
|
+
end
|
511
|
+
|
512
|
+
# FileUtils.rm_rf
|
513
|
+
def rm_rf(*args)
|
514
|
+
FileUtils.rm_rf(self, *args)
|
515
|
+
end
|
516
|
+
alias rmtree rm_rf
|
517
|
+
|
518
|
+
# FileUtils.install
|
519
|
+
def install(*args)
|
520
|
+
FileUtils.install(self, *args)
|
521
|
+
end
|
522
|
+
|
523
|
+
# FileUtils.touch
|
524
|
+
def touch(*args)
|
525
|
+
FileUtils.touch(*args)
|
526
|
+
end
|
527
|
+
|
528
|
+
# FileUtils.compare_file
|
529
|
+
def compare_file(file)
|
530
|
+
FileUtils.compare_file(self, file)
|
531
|
+
end
|
532
|
+
|
533
|
+
# FileUtils.uptodate?
|
534
|
+
def uptodate?(*args)
|
535
|
+
FileUtils.uptodate(self, *args)
|
536
|
+
end
|
537
|
+
|
538
|
+
# FileUtils.copy_file
|
539
|
+
def copy_file(*args)
|
540
|
+
FileUtils.copy_file(self, *args)
|
541
|
+
end
|
542
|
+
|
543
|
+
# FileUtils.remove_dir
|
544
|
+
def remove_dir(*args)
|
545
|
+
FileUtils.remove_dir(self, *args)
|
546
|
+
end
|
547
|
+
|
548
|
+
# FileUtils.remove_file
|
549
|
+
def remove_file(*args)
|
550
|
+
FileUtils.remove_dir(self, *args)
|
551
|
+
end
|
552
|
+
|
553
|
+
# FileUtils.copy_entry
|
554
|
+
def copy_entry(*args)
|
555
|
+
FileUtils.copy_entry(self, *args)
|
556
|
+
end
|
557
|
+
end
|