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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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