pathname2 1.6.5-universal-mingw32 → 1.7.1-universal-mingw32

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NDcxYjA1NDMxNWUyNWYzNGZlOTE4NGJhNzM5NDUzNGFhNTZlMDY4Yg==
5
+ data.tar.gz: !binary |-
6
+ YTM1ZGMwYzJlODBlM2E0OGUyZjZmZmJmOTE1ZjU5Y2IyMjk3NWJjOA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MWEwM2Y2ZGZlY2I0NjhhNzY2ZWNjZjRiMzE2ZTIwNGY2NGZmNmE0MTI2NmJh
10
+ Y2ZhNmJkZTRjOTIxZmZiYmRhMzcyYTk1YjQ2MzAyZDgyYjJlM2E0MjViMmVh
11
+ ZGY1MjZmM2NjZTViM2Y4N2IzYzFjZmQzZjQwOTUxNWY4OTVjZGQ=
12
+ data.tar.gz: !binary |-
13
+ YzMwMDNmMmI0YTdlZThhMTAxYWIzN2Y1OGQzM2FkYmQ4NzlmNjYyOTZkMWU0
14
+ Y2RmNWJjZDUyYTNkM2RlODJiNjVlMjA5ZGM1YTdlZTIwZjdhNjkyZjdkMzUx
15
+ ZTdlM2UyMzczMDYzYjgwNzJmMzNjOTcwMTE0Y2M0NGUzNmU3MzY=
data/CHANGES CHANGED
@@ -1,3 +1,13 @@
1
+ == 1.7.1 - 28-Mar-2014
2
+ * Updated gemspec for Windows vs Unix.
3
+ * Updated README and MANIFEST.
4
+
5
+ == 1.7.0 - 28-Mar-2014
6
+ * Windows now uses FFI internally instead of win32-api.
7
+ * Added a custom pretty_print method for the pp library.
8
+ * Reorganized the test suite for Windows, and added many
9
+ more test tasks to the Rakefile.
10
+
1
11
  == 1.6.5 - 19-Sep-2011
2
12
  * Added the String#to_path instance method. This returns a Pathname
3
13
  object.
data/MANIFEST CHANGED
@@ -8,4 +8,31 @@
8
8
  * examples/example_pathname.rb
9
9
  * lib/pathname2.rb
10
10
  * test/test_pathname.rb
11
- * test/test_pathname_windows.rb
11
+ * test/test_version.rb
12
+ * test/windows/test_append.rb
13
+ * test/windows/test_aref.rb
14
+ * test/windows/test_ascend.rb
15
+ * test/windows/test_children.rb
16
+ * test/windows/test_clean.rb
17
+ * test/windows/test_clean_bang.rb
18
+ * test/windows/test_constructor.rb
19
+ * test/windows/test_descend.rb
20
+ * test/windows/test_drive_number.rb
21
+ * test/windows/test_each.rb
22
+ * test/windows/test_facade.rb
23
+ * test/windows/test_is_absolute.rb
24
+ * test/windows/test_is_relative.rb
25
+ * test/windows/test_is_root.rb
26
+ * test/windows/test_is_unc.rb
27
+ * test/windows/test_long_path.rb
28
+ * test/windows/test_misc.rb
29
+ * test/windows/test_parent.rb
30
+ * test/windows/test_pstrip.rb
31
+ * test/windows/test_pstrip_bang.rb
32
+ * test/windows/test_realpath.rb
33
+ * test/windows/test_relative_path_from.rb
34
+ * test/windows/test_root.rb
35
+ * test/windows/test_short_path.rb
36
+ * test/windows/test_to_a.rb
37
+ * test/windows/test_undecorate.rb
38
+ * test/windows/test_undecorate_bang.rb
data/README CHANGED
@@ -2,8 +2,9 @@
2
2
  A drop-in replacement for the current Pathname class.
3
3
 
4
4
  == Prerequisites
5
- * facade 1.0.4 or later
6
- * windows-pr 1.1.2 or later (Windows only)
5
+ * facade
6
+ * ffi (Windows only)
7
+ * test-unit (testing only)
7
8
 
8
9
  == Installation
9
10
 
@@ -76,9 +77,7 @@
76
77
  supposed to be private. See ruby-core:7383.
77
78
 
78
79
  Any other issues should be reported on the project page at
79
- http://www.rubyforge.org/projects/shards
80
-
81
- The git repo itself is located at https://github.com/djberg96/pathname2
80
+ https://github.com/djberg96/pathname2
82
81
 
83
82
  == Future Plans
84
83
  Suggestions welcome.
@@ -87,7 +86,7 @@
87
86
  Artistic 2.0
88
87
 
89
88
  == Copyright
90
- (C) 2003-2011 Daniel J. Berger
89
+ (C) 2003-2014 Daniel J. Berger
91
90
  All rights reserved.
92
91
 
93
92
  == Warranty
data/Rakefile CHANGED
@@ -8,7 +8,12 @@ namespace :gem do
8
8
  desc "Build the pathname2 gem"
9
9
  task :create => [:clean] do
10
10
  spec = eval(IO.read('pathname2.gemspec'))
11
- Gem::Builder.new(spec).build
11
+ if Gem::VERSION < "2.0"
12
+ Gem::Builder.new(spec).build
13
+ else
14
+ require 'rubygems/package'
15
+ Gem::Package.build(spec)
16
+ end
12
17
  end
13
18
 
14
19
  desc "Install the pathname2 gem"
@@ -23,13 +28,184 @@ Rake::TestTask.new('test') do |t|
23
28
  t.warning = true
24
29
  t.verbose = true
25
30
 
26
- if Config::CONFIG['host_os'] =~ /mswin|win32|dos|cygwin|mingw/i
27
- t.test_files = FileList['test/test_pathname_windows.rb']
31
+ if File::ALT_SEPARATOR
32
+ t.test_files = FileList["test/windows/*.rb"] + FileList["test/test_version.rb"]
28
33
  else
29
34
  t.test_files = FileList['test/test_pathname.rb']
30
35
  end
31
36
  end
32
37
 
38
+ namespace :test do
39
+ dir = File::ALT_SEPARATOR ? "windows" : "unix"
40
+ Rake::TestTask.new(:all) do |t|
41
+ t.warning = true
42
+ t.verbose = true
43
+ t.test_files = FileList["test/#{dir}/*.rb"] + FileList["test/test_version.rb"]
44
+ end
45
+
46
+ Rake::TestTask.new(:append) do |t|
47
+ t.warning = true
48
+ t.verbose = true
49
+ t.test_files = FileList["test/#{dir}/test_append.rb"]
50
+ end
51
+
52
+ Rake::TestTask.new(:aref) do |t|
53
+ t.warning = true
54
+ t.verbose = true
55
+ t.test_files = FileList["test/#{dir}/test_aref.rb"]
56
+ end
57
+
58
+ Rake::TestTask.new(:ascend) do |t|
59
+ t.warning = true
60
+ t.verbose = true
61
+ t.test_files = FileList["test/#{dir}/test_ascend.rb"]
62
+ end
63
+
64
+ Rake::TestTask.new(:children) do |t|
65
+ t.warning = true
66
+ t.verbose = true
67
+ t.test_files = FileList["test/#{dir}/test_children.rb"]
68
+ end
69
+
70
+ Rake::TestTask.new(:clean) do |t|
71
+ t.warning = true
72
+ t.verbose = true
73
+ t.test_files = FileList["test/#{dir}/test_clean.rb"]
74
+ end
75
+
76
+ Rake::TestTask.new(:clean!) do |t|
77
+ t.warning = true
78
+ t.verbose = true
79
+ t.test_files = FileList["test/#{dir}/test_clean_bang.rb"]
80
+ end
81
+
82
+ Rake::TestTask.new(:constructor) do |t|
83
+ t.warning = true
84
+ t.verbose = true
85
+ t.test_files = FileList["test/#{dir}/test_constructor.rb"]
86
+ end
87
+
88
+ Rake::TestTask.new(:descend) do |t|
89
+ t.warning = true
90
+ t.verbose = true
91
+ t.test_files = FileList["test/#{dir}/test_descend.rb"]
92
+ end
93
+
94
+ Rake::TestTask.new(:drive_number) do |t|
95
+ t.warning = true
96
+ t.verbose = true
97
+ t.test_files = FileList["test/#{dir}/test_drive_number.rb"]
98
+ end
99
+
100
+ Rake::TestTask.new(:each) do |t|
101
+ t.warning = true
102
+ t.verbose = true
103
+ t.test_files = FileList["test/#{dir}/test_each.rb"]
104
+ end
105
+
106
+ Rake::TestTask.new(:facade) do |t|
107
+ t.warning = true
108
+ t.verbose = true
109
+ t.test_files = FileList["test/#{dir}/test_facade.rb"]
110
+ end
111
+
112
+ Rake::TestTask.new(:is_absolute) do |t|
113
+ t.warning = true
114
+ t.verbose = true
115
+ t.test_files = FileList["test/#{dir}/test_is_absolute.rb"]
116
+ end
117
+
118
+ Rake::TestTask.new(:is_relative) do |t|
119
+ t.warning = true
120
+ t.verbose = true
121
+ t.test_files = FileList["test/#{dir}/test_is_relative.rb"]
122
+ end
123
+
124
+ Rake::TestTask.new(:is_root) do |t|
125
+ t.warning = true
126
+ t.verbose = true
127
+ t.test_files = FileList["test/#{dir}/test_is_root.rb"]
128
+ end
129
+
130
+ Rake::TestTask.new(:is_unc) do |t|
131
+ t.warning = true
132
+ t.verbose = true
133
+ t.test_files = FileList["test/#{dir}/test_is_unc.rb"]
134
+ end
135
+
136
+ Rake::TestTask.new(:long_path) do |t|
137
+ t.warning = true
138
+ t.verbose = true
139
+ t.test_files = FileList["test/#{dir}/test_long_path.rb"]
140
+ end
141
+
142
+ Rake::TestTask.new(:misc) do |t|
143
+ t.warning = true
144
+ t.verbose = true
145
+ t.test_files = FileList["test/#{dir}/test_misc.rb"]
146
+ end
147
+
148
+ Rake::TestTask.new(:parent) do |t|
149
+ t.warning = true
150
+ t.verbose = true
151
+ t.test_files = FileList["test/#{dir}/test_parent.rb"]
152
+ end
153
+
154
+ Rake::TestTask.new(:pstrip) do |t|
155
+ t.warning = true
156
+ t.verbose = true
157
+ t.test_files = FileList["test/#{dir}/test_pstrip.rb"]
158
+ end
159
+
160
+ Rake::TestTask.new(:pstrip!) do |t|
161
+ t.warning = true
162
+ t.verbose = true
163
+ t.test_files = FileList["test/#{dir}/test_pstrip_bang.rb"]
164
+ end
165
+
166
+ Rake::TestTask.new(:realpath) do |t|
167
+ t.warning = true
168
+ t.verbose = true
169
+ t.test_files = FileList["test/#{dir}/test_realpath.rb"]
170
+ end
171
+
172
+ Rake::TestTask.new(:relative_path_from) do |t|
173
+ t.warning = true
174
+ t.verbose = true
175
+ t.test_files = FileList["test/#{dir}/test_relative_path_from.rb"]
176
+ end
177
+
178
+ Rake::TestTask.new(:root) do |t|
179
+ t.warning = true
180
+ t.verbose = true
181
+ t.test_files = FileList["test/#{dir}/test_root.rb"]
182
+ end
183
+
184
+ Rake::TestTask.new(:short_path) do |t|
185
+ t.warning = true
186
+ t.verbose = true
187
+ t.test_files = FileList["test/#{dir}/test_short_path.rb"]
188
+ end
189
+
190
+ Rake::TestTask.new(:to_a) do |t|
191
+ t.warning = true
192
+ t.verbose = true
193
+ t.test_files = FileList["test/#{dir}/test_to_a.rb"]
194
+ end
195
+
196
+ Rake::TestTask.new(:undecorate) do |t|
197
+ t.warning = true
198
+ t.verbose = true
199
+ t.test_files = FileList["test/#{dir}/test_undecorate.rb"]
200
+ end
201
+
202
+ Rake::TestTask.new(:undecorate!) do |t|
203
+ t.warning = true
204
+ t.verbose = true
205
+ t.test_files = FileList["test/#{dir}/test_undecorate_bang.rb"]
206
+ end
207
+ end
208
+
33
209
  desc 'Run the Pathname benchmark suite'
34
210
  task :benchmark do
35
211
  sh 'ruby -Ilib benchmarks/bench_pathname.rb'
data/lib/pathname2.rb CHANGED
@@ -1,12 +1,12 @@
1
1
  # == Synopsis
2
2
  #
3
- # Pathname represents a path name on a filesystem. A Pathname can be
3
+ # Pathname represents a path name on a filesystem. A Pathname can be
4
4
  # relative or absolute. It does not matter whether the path exists or not.
5
5
  #
6
6
  # All functionality from File, FileTest, and Dir is included, using a facade
7
7
  # pattern.
8
8
  #
9
- # This class works on both Unix and Windows, including UNC path names. Note
9
+ # This class works on both Unix and Windows, including UNC path names. Note
10
10
  # that forward slashes are converted to backslashes on Windows systems.
11
11
  #
12
12
  # == Usage
@@ -27,24 +27,22 @@
27
27
  # path1 + path2 # "C:\\foo\\bar\\zap"
28
28
  # path1.exists? # Does the path exist?
29
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-2011 Daniel J. Berger.
38
- # Licensed under the same terms as Ruby itself.
39
- #
40
30
  require 'facade'
41
31
  require 'fileutils'
32
+ require 'pp'
42
33
 
43
34
  if File::ALT_SEPARATOR
44
- require 'windows/path'
45
- require 'windows/file'
46
- require 'windows/error'
47
- require 'windows/limits'
35
+ require 'ffi'
36
+ class String
37
+ # Convenience method for converting strings to UTF-16LE for wide character
38
+ # functions that require it.
39
+ def wincode
40
+ if self.encoding.name != 'UTF-16LE'
41
+ temp = self.dup
42
+ (temp.tr(File::SEPARATOR, File::ALT_SEPARATOR) << 0.chr).encode('UTF-16LE')
43
+ end
44
+ end
45
+ end
48
46
  end
49
47
 
50
48
  # You're mine now.
@@ -54,6 +52,8 @@ class Pathname < String
54
52
  class Error < StandardError; end
55
53
  extend Facade
56
54
 
55
+ undef_method :pretty_print
56
+
57
57
  facade File, File.methods(false).map{ |m| m.to_sym } - [
58
58
  :chmod, :lchmod, :chown, :lchown, :dirname, :fnmatch, :fnmatch?,
59
59
  :link, :open, :realpath, :rename, :symlink, :truncate, :utime,
@@ -69,16 +69,31 @@ class Pathname < String
69
69
  alias :_plus_ :+ # Used to prevent infinite loops in some cases
70
70
 
71
71
  if File::ALT_SEPARATOR
72
- include Windows::Path
73
- include Windows::File
74
- include Windows::Error
75
- include Windows::Limits
72
+ extend FFI::Library
73
+ ffi_lib :shlwapi
74
+
75
+ attach_function :PathAppendW, [:pointer, :pointer], :bool
76
+ attach_function :PathCanonicalizeW, [:pointer, :buffer_in], :bool
77
+ attach_function :PathCreateFromUrlW, [:buffer_in, :pointer, :pointer, :ulong], :long
78
+ attach_function :PathGetDriveNumberW, [:buffer_in], :int
79
+ attach_function :PathIsRelativeW, [:buffer_in], :bool
80
+ attach_function :PathIsRootW, [:buffer_in], :bool
81
+ attach_function :PathIsUNCW, [:buffer_in], :bool
82
+ attach_function :PathIsURLW, [:buffer_in], :bool
83
+ attach_function :PathRemoveBackslashW, [:buffer_in], :pointer
84
+ attach_function :PathStripToRootW, [:pointer], :bool
85
+ attach_function :PathUndecorateW, [:pointer], :void
86
+
87
+ ffi_lib :kernel32
88
+
89
+ attach_function :GetLongPathNameW, [:buffer_in, :buffer_out, :ulong], :ulong
90
+ attach_function :GetShortPathNameW, [:buffer_in, :pointer, :ulong], :ulong
76
91
  end
77
92
 
78
93
  public
79
94
 
80
95
  # The version of the pathname2 library
81
- VERSION = '1.6.5'
96
+ VERSION = '1.7.1'
82
97
 
83
98
  # The maximum length of a path
84
99
  MAXPATH = 1024 unless defined? MAXPATH # Yes, I willfully violate POSIX
@@ -102,8 +117,7 @@ class Pathname < String
102
117
  # example, all forward slashes are replaced with backslashes.
103
118
  #
104
119
  # File URL's will be converted to Pathname objects, e.g. the file URL
105
- # "file:///C:/Documents%20and%20Settings" will become
106
- # 'C:\Documents and Settings'.
120
+ # "file:///C:/Documents%20and%20Settings" will become 'C:\Documents and Settings'.
107
121
  #
108
122
  # Examples:
109
123
  #
@@ -115,7 +129,7 @@ class Pathname < String
115
129
  def initialize(path)
116
130
  if path.length > MAXPATH
117
131
  msg = "string too long. maximum string length is " + MAXPATH.to_s
118
- raise Error, msg
132
+ raise ArgumentError, msg
119
133
  end
120
134
 
121
135
  @sep = File::ALT_SEPARATOR || File::SEPARATOR
@@ -125,11 +139,15 @@ class Pathname < String
125
139
  # because Ruby's URI class does not (currently) parse absolute file URL's
126
140
  # properly when they include a drive letter.
127
141
  if @win
128
- if PathIsURL(path)
129
- buf = 0.chr * MAXPATH
130
- len = [buf.length].pack("l")
131
- if PathCreateFromUrl(path, buf, len, 0) == S_OK
132
- path = buf.strip
142
+ wpath = path.wincode
143
+
144
+ if PathIsURLW(wpath)
145
+ buf = FFI::MemoryPointer.new(:char, MAXPATH)
146
+ len = FFI::MemoryPointer.new(:ulong)
147
+ len.write_ulong(buf.size)
148
+
149
+ if PathCreateFromUrlW(wpath, buf, len, 0) == 0
150
+ path = buf.read_string(path.size * 2).tr(0.chr, '')
133
151
  else
134
152
  raise Error, "invalid file url: #{path}"
135
153
  end
@@ -137,16 +155,13 @@ class Pathname < String
137
155
  else
138
156
  if path.index('file:///', 0)
139
157
  require 'uri'
140
- if RUBY_VERSION.to_f >= 1.9
141
- path = URI::Parser.new.unescape(path)[7..-1] # Blech
142
- else
143
- path = URI.decode(URI.parse(path).path)
144
- end
158
+ path = URI::Parser.new.unescape(path)[7..-1]
145
159
  end
146
160
  end
147
161
 
148
162
  # Convert forward slashes to backslashes on Windows
149
- path = path.tr("/", @sep) if @win
163
+ path = path.tr(File::SEPARATOR, File::ALT_SEPARATOR) if @win
164
+
150
165
  super(path)
151
166
  end
152
167
 
@@ -184,7 +199,7 @@ class Pathname < String
184
199
  # the returned pathnames will contain the filename only.
185
200
  #
186
201
  # Note that the result never contain the entries '.' and '..' in the
187
- # the directory because they are not children. Also note that this method
202
+ # the directory because they are not children. Also note that this method
188
203
  # is *not* recursive.
189
204
  #
190
205
  # Example:
@@ -220,10 +235,12 @@ class Pathname < String
220
235
  unless @win
221
236
  raise NotImplementedError, "not supported on this platform"
222
237
  end
223
- buf = 0.chr * MAXPATH
224
- buf[0..self.length-1] = self
225
- PathUndecorate(buf)
226
- self.class.new(buf.split(0.chr).first)
238
+
239
+ wpath = FFI::MemoryPointer.from_string(self.wincode)
240
+
241
+ PathUndecorateW(wpath)
242
+
243
+ self.class.new(wpath.read_string(wpath.size).split("\000\000").first.tr(0.chr, ''))
227
244
  end
228
245
 
229
246
  # Windows only
@@ -231,14 +248,7 @@ class Pathname < String
231
248
  # Performs the substitution of Pathname#undecorate in place.
232
249
  #
233
250
  def undecorate!
234
- unless @win
235
- raise NotImplementedError, "not supported on this platform"
236
- end
237
- buf = 0.chr * MAXPATH
238
- buf[0..self.length-1] = self
239
- PathUndecorate(buf)
240
- replace(buf.split(0.chr).first)
241
- self
251
+ self.replace(undecorate)
242
252
  end
243
253
 
244
254
  # Windows only
@@ -251,13 +261,16 @@ class Pathname < String
251
261
  # path.short_path # => C:\Progra~1\Java.
252
262
  #
253
263
  def short_path
254
- unless @win
255
- raise NotImplementedError, "not supported on this platform"
256
- end
257
- buf = 0.chr * MAXPATH
258
- buf[0..self.length-1] = self
259
- GetShortPathName(self, buf, buf.length)
260
- self.class.new(buf.split(0.chr).first)
264
+ raise NotImplementedError, "not supported on this platform" unless @win
265
+
266
+ buf = FFI::MemoryPointer.new(:char, MAXPATH)
267
+ wpath = self.wincode
268
+
269
+ size = GetShortPathNameW(wpath, buf, buf.size)
270
+
271
+ raise SystemCallError.new('GetShortPathName', FFI.errno) if size == 0
272
+
273
+ self.class.new(buf.read_bytes(size * 2).delete(0.chr))
261
274
  end
262
275
 
263
276
  # Windows only
@@ -270,16 +283,19 @@ class Pathname < String
270
283
  # path.long_path # => C:\Program Files\Java.
271
284
  #
272
285
  def long_path
273
- unless @win
274
- raise NotImplementedError, "not supported on this platform"
275
- end
276
- buf = 0.chr * MAXPATH
277
- buf[0..self.length-1] = self
278
- GetLongPathName(self, buf, buf.length)
279
- self.class.new(buf.split(0.chr).first)
286
+ raise NotImplementedError, "not supported on this platform" unless @win
287
+
288
+ buf = FFI::MemoryPointer.new(:char, MAXPATH)
289
+ wpath = self.wincode
290
+
291
+ size = GetLongPathNameW(wpath, buf, buf.size)
292
+
293
+ raise SystemCallError.new('GetShortPathName', FFI.errno) if size == 0
294
+
295
+ self.class.new(buf.read_bytes(size * 2).delete(0.chr))
280
296
  end
281
297
 
282
- # Removes trailing slash, if present. Non-destructive.
298
+ # Removes all trailing slashes, if present. Non-destructive.
283
299
  #
284
300
  # Example:
285
301
  #
@@ -288,31 +304,20 @@ class Pathname < String
288
304
  #
289
305
  def pstrip
290
306
  str = self.dup
291
- if @win
292
- PathRemoveBackslash(str)
307
+ return str if str.empty?
308
+
309
+ while ["/", "\\"].include?(str.to_s[-1].chr)
293
310
  str.strip!
294
- else
295
- if str.to_s[-1].chr == @sep
296
- str.strip!
297
- str.chop!
298
- end
311
+ str.chop!
299
312
  end
313
+
300
314
  self.class.new(str)
301
315
  end
302
316
 
303
317
  # Performs the substitution of Pathname#pstrip in place.
304
318
  #
305
319
  def pstrip!
306
- if @win
307
- PathRemoveBackslash(self)
308
- strip!
309
- else
310
- if self.to_s[-1].chr == @sep
311
- strip!
312
- chop!
313
- end
314
- end
315
- self
320
+ self.replace(pstrip)
316
321
  end
317
322
 
318
323
  # Splits a pathname into strings based on the path separator.
@@ -323,7 +328,12 @@ class Pathname < String
323
328
  # Pathname.new('C:\WINNT\Fonts').to_a # => ['C:', 'WINNT', 'Fonts']
324
329
  #
325
330
  def to_a
326
- array = split(@sep) # Split string by path separator
331
+ # Split string by path separator
332
+ if @win
333
+ array = tr(File::SEPARATOR, File::ALT_SEPARATOR).split(@sep)
334
+ else
335
+ array = split(@sep)
336
+ end
327
337
  array.delete("") # Remove empty elements
328
338
  array
329
339
  end
@@ -481,7 +491,7 @@ class Pathname < String
481
491
  # Returns the root directory of the path, or '.' if there is no root
482
492
  # directory.
483
493
  #
484
- # On Unix, this means the '/' character. On Windows, this can refer
494
+ # On Unix, this means the '/' character. On Windows, this can refer
485
495
  # to the drive letter, or the server and share path if the path is a
486
496
  # UNC path.
487
497
  #
@@ -499,11 +509,9 @@ class Pathname < String
499
509
  dir = "."
500
510
 
501
511
  if @win
502
- buf = 0.chr * MAXPATH
503
- buf[0..self.length-1] = self
504
-
505
- if PathStripToRoot(buf)
506
- dir = buf.split(0.chr).first
512
+ wpath = FFI::MemoryPointer.from_string(self.wincode)
513
+ if PathStripToRootW(wpath)
514
+ dir = wpath.read_string(wpath.size).split("\000\000").first.tr(0.chr, '')
507
515
  end
508
516
  else
509
517
  dir = "/" if self =~ /^\//
@@ -521,7 +529,7 @@ class Pathname < String
521
529
  #
522
530
  def root?
523
531
  if @win
524
- PathIsRoot(self)
532
+ PathIsRootW(self.wincode)
525
533
  else
526
534
  self == root
527
535
  end
@@ -538,11 +546,8 @@ class Pathname < String
538
546
  # Pathname.new('C:\Program Files').unc? # => false
539
547
  #
540
548
  def unc?
541
- unless @win
542
- raise NotImplementedError, "not supported on this platform"
543
- end
544
-
545
- PathIsUNC(self)
549
+ raise NotImplementedError, "not supported on this platform" unless @win
550
+ PathIsUNCW(self.wincode)
546
551
  end
547
552
 
548
553
  # MS Windows only
@@ -559,12 +564,12 @@ class Pathname < String
559
564
  raise NotImplementedError, "not supported on this platform"
560
565
  end
561
566
 
562
- num = PathGetDriveNumber(self)
567
+ num = PathGetDriveNumberW(self.wincode)
563
568
  num >= 0 ? num : nil
564
569
  end
565
570
 
566
571
  # Compares two Pathname objects. Note that Pathnames may only be compared
567
- # against other Pathnames, not strings. Otherwise nil is returned.
572
+ # against other Pathnames, not strings. Otherwise nil is returned.
568
573
  #
569
574
  # Example:
570
575
  #
@@ -587,7 +592,8 @@ class Pathname < String
587
592
  # Pathname.new('/usr/local/bin').parent # => '/usr/local'
588
593
  #
589
594
  def parent
590
- self + ".." # Use our custom '+' method
595
+ return self if root?
596
+ self + ".." # Use our custom '+' method
591
597
  end
592
598
 
593
599
  # Returns a relative path from the argument to the receiver. If +self+
@@ -636,7 +642,7 @@ class Pathname < String
636
642
  dest_arr.delete('.')
637
643
  base_arr.delete('.')
638
644
 
639
- diff_arr = dest_arr - base_arr
645
+ # diff_arr = dest_arr - base_arr
640
646
 
641
647
  while !base_arr.empty? && !dest_arr.empty? && base_arr[0] == dest_arr[0]
642
648
  base_arr.shift
@@ -657,11 +663,11 @@ class Pathname < String
657
663
  end
658
664
  end
659
665
 
660
- # Adds two Pathname objects together, or a Pathname and a String. It
666
+ # Adds two Pathname objects together, or a Pathname and a String. It
661
667
  # also automatically cleans the Pathname.
662
668
  #
663
669
  # Adding a root path to an existing path merely replaces the current
664
- # path. Adding '.' to an existing path does nothing.
670
+ # path. Adding '.' to an existing path does nothing.
665
671
  #
666
672
  # Example:
667
673
  #
@@ -680,11 +686,15 @@ class Pathname < String
680
686
 
681
687
  # Use the builtin PathAppend() function if on Windows - much easier
682
688
  if @win
683
- buf = 0.chr * MAXPATH
684
- buf[0..self.length-1] = self
685
- PathAppend(buf, string)
686
- buf = buf.split("\0").first
687
- return self.class.new(buf) # PathAppend cleans automatically
689
+ path = FFI::MemoryPointer.new(:char, MAXPATH)
690
+ path.write_string(self.dup.wincode)
691
+ more = FFI::MemoryPointer.from_string(string.wincode)
692
+
693
+ PathAppendW(path, more)
694
+
695
+ path = path.read_string(path.size).split("\000\000").first.delete(0.chr)
696
+
697
+ return self.class.new(path) # PathAppend cleans automatically
688
698
  end
689
699
 
690
700
  # If the string is an absolute directory, return it
@@ -723,7 +733,7 @@ class Pathname < String
723
733
  #
724
734
  def relative?
725
735
  if @win
726
- PathIsRelative(self)
736
+ PathIsRelativeW(self.wincode)
727
737
  else
728
738
  root == "."
729
739
  end
@@ -741,9 +751,9 @@ class Pathname < String
741
751
  return self if self.empty?
742
752
 
743
753
  if @win
744
- path = 0.chr * MAXPATH
745
- if PathCanonicalize(path, self)
746
- return self.class.new(path.split(0.chr).first)
754
+ ptr = FFI::MemoryPointer.new(:char, MAXPATH)
755
+ if PathCanonicalizeW(ptr, self.wincode)
756
+ return self.class.new(ptr.read_string(ptr.size).delete(0.chr))
747
757
  else
748
758
  return self
749
759
  end
@@ -772,32 +782,7 @@ class Pathname < String
772
782
  # in place.
773
783
  #
774
784
  def clean!
775
- return self if self.empty?
776
-
777
- if @win
778
- path = 0.chr * MAXPATH
779
- if PathCanonicalize(path, self)
780
- replace(path.split(0.chr).first)
781
- end
782
- return self
783
- end
784
-
785
- final = []
786
-
787
- to_a.each{ |element|
788
- next if element == "."
789
- final.push(element)
790
- if element == ".." && self != ".."
791
- 2.times{ final.pop }
792
- end
793
- }
794
-
795
- final = final.join(@sep)
796
- final = root + final if root != "."
797
- final = "." if final.empty?
798
- replace(self.class.new(final))
799
-
800
- self
785
+ self.replace(clean)
801
786
  end
802
787
 
803
788
  alias cleanpath! clean!
@@ -828,6 +813,15 @@ class Pathname < String
828
813
  local_path
829
814
  end
830
815
 
816
+ # A custom pretty printer
817
+ def pretty_print(q)
818
+ if File::ALT_SEPARATOR
819
+ q.text(self.to_s.tr(File::SEPARATOR, File::ALT_SEPARATOR))
820
+ else
821
+ q.text(self.to_s)
822
+ end
823
+ end
824
+
831
825
  #-- Find facade
832
826
 
833
827
  # Pathname#find is an iterator to traverse a directory tree in a depth first