Skip to content

Commit 9e742e5

Browse files
author
José Valim
committed
Path.join merges second path regardless if it is an absolute or relative path
Closes #790
1 parent 6715473 commit 9e742e5

File tree

2 files changed

+84
-9
lines changed

2 files changed

+84
-9
lines changed

lib/elixir/lib/path.ex

+80-9
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ defmodule Path do
107107
"""
108108
def type(name) when is_list(name) or is_binary(name) do
109109
case :os.type() do
110-
{ :unix, _ } -> unix_pathtype(name)
111110
{ :win32, _ } -> win32_pathtype(name)
111+
_ -> unix_pathtype(name)
112112
end |> elem(0)
113113
end
114114

@@ -131,8 +131,8 @@ defmodule Path do
131131
"""
132132
def relative(name) do
133133
case :os.type() do
134-
{ :unix, _ } -> unix_pathtype(name)
135134
{ :win32, _ } -> win32_pathtype(name)
135+
_ -> unix_pathtype(name)
136136
end |> elem(1)
137137
end
138138

@@ -194,11 +194,12 @@ defmodule Path do
194194
195195
"""
196196
def relative_to(path, from) when is_list(path) and is_binary(from) do
197-
relative_to(FN.split(list_to_binary(path)), FN.split(from), list_to_binary(path))
197+
path = filename_string_to_binary(path)
198+
relative_to(FN.split(path), FN.split(from), path)
198199
end
199200

200201
def relative_to(path, from) when is_binary(path) and is_list(from) do
201-
relative_to(FN.split(path), FN.split(list_to_binary(from)), path)
202+
relative_to(FN.split(path), FN.split(filename_string_to_binary(from)), path)
202203
end
203204

204205
def relative_to(path, from) do
@@ -329,9 +330,12 @@ defmodule Path do
329330
#=> "/foo/bar"
330331
331332
"""
332-
def join(paths) do
333-
FN.join(paths)
334-
end
333+
def join([name1, name2|rest]), do:
334+
join([join(name1, name2)|rest])
335+
def join([name]) when is_list(name), do:
336+
binary_to_filename_string(do_join(filename_string_to_binary(name), <<>>, [], major_os_type()))
337+
def join([name]) when is_binary(name), do:
338+
do_join(name, <<>>, [], major_os_type())
335339

336340
@doc """
337341
Joins two paths.
@@ -342,10 +346,59 @@ defmodule Path do
342346
#=> "foo/bar"
343347
344348
"""
345-
def join(left, right) do
346-
FN.join(left, right)
349+
def join(left, right) when is_binary(left) and is_binary(right), do:
350+
do_join(left, Path.relative(right), [], major_os_type())
351+
352+
def join(left, right) when is_binary(left) and is_list(right), do:
353+
join(left, filename_string_to_binary(right))
354+
355+
def join(left, right) when is_list(left) and is_binary(right), do:
356+
join(filename_string_to_binary(left), right)
357+
358+
def join(left, right) when is_list(left) and is_list(right), do:
359+
binary_to_filename_string join(filename_string_to_binary(left), filename_string_to_binary(right))
360+
361+
def join(left, right) when is_atom(left), do:
362+
join(atom_to_binary(left), right)
363+
364+
def join(left, right) when is_atom(right), do:
365+
join(left, atom_to_binary(right))
366+
367+
defp major_os_type do
368+
case :os.type do
369+
{ maj, _ } -> maj
370+
maj -> maj
371+
end
347372
end
348373

374+
defp do_join(<<uc_letter, ?:, rest :: binary>>, relativename, [], :win32) when uc_letter in ?A..?Z, do:
375+
do_join(rest, relativename, [?:, uc_letter+?a-?A], :win32)
376+
defp do_join(<<?\\,rest :: binary>>, relativename, result, :win32), do:
377+
do_join(<<?/,rest :: binary>>, relativename, result, :win32)
378+
defp do_join(<<?/,rest :: binary>>, relativename, [?., ?/|result], os_type), do:
379+
do_join(rest, relativename, [?/|result], os_type)
380+
defp do_join(<<?/,rest :: binary>>, relativename, [?/|result], os_type), do:
381+
do_join(rest, relativename, [?/|result], os_type)
382+
defp do_join(<<>>, <<>>, result, os_type), do:
383+
list_to_binary(maybe_remove_dirsep(result, os_type))
384+
defp do_join(<<>>, relativename, [?:|rest], :win32), do:
385+
do_join(relativename, <<>>, [?:|rest], :win32)
386+
defp do_join(<<>>, relativename, [?/|result], os_type), do:
387+
do_join(relativename, <<>>, [?/|result], os_type)
388+
defp do_join(<<>>, relativename, result, os_type), do:
389+
do_join(relativename, <<>>, [?/|result], os_type)
390+
defp do_join(<<char,rest :: binary>>, relativename, result, os_type) when is_integer(char), do:
391+
do_join(rest, relativename, [char|result], os_type)
392+
393+
defp maybe_remove_dirsep([?/, ?:, letter], :win32), do:
394+
[letter, ?:, ?/]
395+
defp maybe_remove_dirsep([?/], _), do:
396+
[?/]
397+
defp maybe_remove_dirsep([?/|name], _), do:
398+
:lists.reverse(name)
399+
defp maybe_remove_dirsep(name, _), do:
400+
:lists.reverse(name)
401+
349402
@doc """
350403
Returns a list with the path splitted by the path separator.
351404
If an empty string is given, then it returns the root path.
@@ -411,6 +464,24 @@ defmodule Path do
411464
defp get_cwd(path) when is_list(path), do: System.cwd! |> binary_to_list
412465
defp get_cwd(_), do: System.cwd!
413466

467+
defp binary_to_filename_string(binary) do
468+
case :unicode.characters_to_list(binary) do
469+
{ :error, _, _ } ->
470+
:erlang.error(:badarg)
471+
list when is_list(list) ->
472+
list
473+
end
474+
end
475+
476+
defp filename_string_to_binary(list) do
477+
case :unicode.characters_to_binary(:filename.flatten(list), :unicode, :file.native_name_encoding()) do
478+
{ :error, _, _ } ->
479+
:erlang.error(:badarg)
480+
bin when is_binary(bin) ->
481+
bin
482+
end
483+
end
484+
414485
# Normalize the given path by expanding "..", "." and "~".
415486

416487
defp normalize(path), do: do_normalize(FN.split(path))

lib/elixir/test/elixir/path_test.exs

+4
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,10 @@ defmodule PathTest do
218218
test :join_two_with_binary do
219219
assert Path.join("/foo", "bar") == "/foo/bar"
220220
assert Path.join("~", "foo") == "~/foo"
221+
222+
assert Path.join("", "bar") == "/bar"
223+
assert Path.join("/foo", "/bar") == "/foo/bar"
224+
assert Path.join("/foo", "./bar") == "/foo/bar"
221225
end
222226

223227
test :join_two_with_list do

0 commit comments

Comments
 (0)