@@ -60,40 +60,70 @@ defmodule Path do
6060 def absname ( path , relative_to ) do
6161 path = IO . chardata_to_string ( path )
6262 case type ( path ) do
63- :relative -> join ( relative_to , path )
64- :absolute ->
65- cond do
66- path == "/" ->
67- path
68- :binary . last ( path ) == ?/ ->
69- binary_part ( path , 0 , byte_size ( path ) - 1 )
70- true ->
71- path
72- end
63+ :relative -> absname_join ( relative_to , path )
64+ :absolute -> absname_join ( [ path ] )
7365 :volumerelative ->
7466 relative_to = IO . chardata_to_string ( relative_to )
7567 absname_vr ( split ( path ) , split ( relative_to ) , relative_to )
7668 end
7769 end
7870
79- ## Absolute path on current drive
71+ # Absolute path on current drive
8072 defp absname_vr ( [ "/" | rest ] , [ volume | _ ] , _relative ) ,
81- do: join ( [ volume | rest ] )
73+ do: absname_join ( [ volume | rest ] )
8274
83- ## Relative to current directory on current drive.
75+ # Relative to current directory on current drive.
8476 defp absname_vr ( [ << x , ?: >> | rest ] , [ << x , _ :: binary >> | _ ] , relative ) ,
85- do: absname ( join ( rest ) , relative )
77+ do: absname ( absname_join ( rest ) , relative )
8678
87- ## Relative to current directory on another drive.
79+ # Relative to current directory on another drive.
8880 defp absname_vr ( [ << x , ?: >> | name ] , _ , _relative ) do
8981 cwd =
9082 case :file . get_cwd ( [ x , ?: ] ) do
9183 { :ok , dir } -> IO . chardata_to_string ( dir )
9284 { :error , _ } -> << x , ?: , ?/ >>
9385 end
94- absname ( join ( name ) , cwd )
86+ absname ( absname_join ( name ) , cwd )
9587 end
9688
89+ # Joins a list
90+ defp absname_join ( [ name1 , name2 | rest ] ) , do:
91+ absname_join ( [ absname_join ( name1 , name2 ) | rest ] )
92+ defp absname_join ( [ name ] ) , do:
93+ do_absname_join ( IO . chardata_to_string ( name ) , << >> , [ ] , major_os_type ( ) )
94+
95+ # Joins two paths
96+ defp absname_join ( left , right ) ,
97+ do: do_absname_join ( IO . chardata_to_string ( left ) , relative ( right ) , [ ] , major_os_type ( ) )
98+
99+ defp do_absname_join ( << uc_letter , ?: , rest :: binary >> , relativename , [ ] , :win32 ) when uc_letter in ?A .. ?Z , do:
100+ do_absname_join ( rest , relativename , [ ?: , uc_letter + ?a - ?A ] , :win32 )
101+ defp do_absname_join ( << ?\\ , rest :: binary >> , relativename , result , :win32 ) , do:
102+ do_absname_join ( << ?/ , rest :: binary >> , relativename , result , :win32 )
103+ defp do_absname_join ( << ?/ , rest :: binary >> , relativename , [ ?. , ?/ | result ] , os_type ) , do:
104+ do_absname_join ( rest , relativename , [ ?/ | result ] , os_type )
105+ defp do_absname_join ( << ?/ , rest :: binary >> , relativename , [ ?/ | result ] , os_type ) , do:
106+ do_absname_join ( rest , relativename , [ ?/ | result ] , os_type )
107+ defp do_absname_join ( << >> , << >> , result , os_type ) , do:
108+ IO . iodata_to_binary ( reverse_maybe_remove_dirsep ( result , os_type ) )
109+ defp do_absname_join ( << >> , relativename , [ ?: | rest ] , :win32 ) , do:
110+ do_absname_join ( relativename , << >> , [ ?: | rest ] , :win32 )
111+ defp do_absname_join ( << >> , relativename , [ ?/ | result ] , os_type ) , do:
112+ do_absname_join ( relativename , << >> , [ ?/ | result ] , os_type )
113+ defp do_absname_join ( << >> , relativename , result , os_type ) , do:
114+ do_absname_join ( relativename , << >> , [ ?/ | result ] , os_type )
115+ defp do_absname_join ( << char , rest :: binary >> , relativename , result , os_type ) , do:
116+ do_absname_join ( rest , relativename , [ char | result ] , os_type )
117+
118+ defp reverse_maybe_remove_dirsep ( [ ?/ , ?: , letter ] , :win32 ) , do:
119+ [ letter , ?: , ?/ ]
120+ defp reverse_maybe_remove_dirsep ( [ ?/ ] , _ ) , do:
121+ [ ?/ ]
122+ defp reverse_maybe_remove_dirsep ( [ ?/ | name ] , _ ) , do:
123+ :lists . reverse ( name )
124+ defp reverse_maybe_remove_dirsep ( name , _ ) , do:
125+ :lists . reverse ( name )
126+
97127 @ doc """
98128 Converts the path to an absolute one and expands
99129 any `.` and `..` characters and a leading `~`.
@@ -106,7 +136,7 @@ defmodule Path do
106136 """
107137 @ spec expand ( t ) :: binary
108138 def expand ( path ) do
109- normalize absname ( expand_home ( path ) , System . cwd! )
139+ expand_dot absname ( expand_home ( path ) , System . cwd! )
110140 end
111141
112142 @ doc """
@@ -133,7 +163,7 @@ defmodule Path do
133163 """
134164 @ spec expand ( t , t ) :: binary
135165 def expand ( path , relative_to ) do
136- normalize absname ( absname ( expand_home ( path ) , expand_home ( relative_to ) ) , System . cwd! )
166+ expand_dot absname ( absname ( expand_home ( path ) , expand_home ( relative_to ) ) , System . cwd! )
137167 end
138168
139169 @ doc """
@@ -182,9 +212,13 @@ defmodule Path do
182212 """
183213 @ spec relative ( t ) :: binary
184214 def relative ( name ) do
185- case :os . type ( ) do
186- { :win32 , _ } -> win32_pathtype ( name )
187- _ -> unix_pathtype ( name )
215+ relative ( name , major_os_type ( ) )
216+ end
217+
218+ defp relative ( name , major_os_type ) do
219+ case major_os_type do
220+ :win32 -> win32_pathtype ( name )
221+ _ -> unix_pathtype ( name )
188222 end |> elem ( 1 ) |> IO . chardata_to_string
189223 end
190224
@@ -390,7 +424,7 @@ defmodule Path do
390424 end
391425
392426 @ doc """
393- Returns a string with one or more path components joined by the path separator .
427+ Joins a list of strings .
394428
395429 This function should be used to convert a list of strings to a path.
396430 Note that any trailing slash is removed on join.
@@ -411,52 +445,40 @@ defmodule Path do
411445 def join ( [ name1 , name2 | rest ] ) , do:
412446 join ( [ join ( name1 , name2 ) | rest ] )
413447 def join ( [ name ] ) , do:
414- do_join ( IO . chardata_to_string ( name ) , << >> , [ ] , major_os_type ( ) )
448+ name
415449
416450 @ doc """
417451 Joins two paths.
418452
453+ The right path will always be expanded to its relative format
454+ and any trailing slash is removed on join.
455+
419456 ## Examples
420457
421458 iex> Path.join("foo", "bar")
422459 "foo/bar"
423460
424461 """
425462 @ spec join ( t , t ) :: binary
426- def join ( left , right ) ,
427- do: do_join ( IO . chardata_to_string ( left ) , relative ( right ) , [ ] , major_os_type ( ) )
428-
429- defp major_os_type do
430- :os . type |> elem ( 0 )
463+ def join ( left , right ) do
464+ left = IO . chardata_to_string ( left )
465+ os_type = major_os_type ( )
466+ do_join ( left , right , os_type ) |> remove_dirsep ( os_type )
431467 end
432468
433- defp do_join ( << uc_letter , ?: , rest :: binary >> , relativename , [ ] , :win32 ) when uc_letter in ?A .. ?Z , do:
434- do_join ( rest , relativename , [ ?: , uc_letter + ?a - ?A ] , :win32 )
435- defp do_join ( << ?\\ , rest :: binary >> , relativename , result , :win32 ) , do:
436- do_join ( << ?/ , rest :: binary >> , relativename , result , :win32 )
437- defp do_join ( << ?/ , rest :: binary >> , relativename , [ ?. , ?/ | result ] , os_type ) , do:
438- do_join ( rest , relativename , [ ?/ | result ] , os_type )
439- defp do_join ( << ?/ , rest :: binary >> , relativename , [ ?/ | result ] , os_type ) , do:
440- do_join ( rest , relativename , [ ?/ | result ] , os_type )
441- defp do_join ( << >> , << >> , result , os_type ) , do:
442- IO . iodata_to_binary ( maybe_remove_dirsep ( result , os_type ) )
443- defp do_join ( << >> , relativename , [ ?: | rest ] , :win32 ) , do:
444- do_join ( relativename , << >> , [ ?: | rest ] , :win32 )
445- defp do_join ( << >> , relativename , [ ?/ | result ] , os_type ) , do:
446- do_join ( relativename , << >> , [ ?/ | result ] , os_type )
447- defp do_join ( << >> , relativename , result , os_type ) , do:
448- do_join ( relativename , << >> , [ ?/ | result ] , os_type )
449- defp do_join ( << char , rest :: binary >> , relativename , result , os_type ) , do:
450- do_join ( rest , relativename , [ char | result ] , os_type )
451-
452- defp maybe_remove_dirsep ( [ ?/ , ?: , letter ] , :win32 ) , do:
453- [ letter , ?: , ?/ ]
454- defp maybe_remove_dirsep ( [ ?/ ] , _ ) , do:
455- [ ?/ ]
456- defp maybe_remove_dirsep ( [ ?/ | name ] , _ ) , do:
457- :lists . reverse ( name )
458- defp maybe_remove_dirsep ( name , _ ) , do:
459- :lists . reverse ( name )
469+ defp do_join ( "" , right , os_type ) , do: relative ( right , os_type )
470+ defp do_join ( left , "" , _os_type ) , do: left
471+ defp do_join ( left , right , os_type ) , do: remove_dirsep ( left , os_type ) <> "/" <> relative ( right , os_type )
472+
473+ defp remove_dirsep ( "" , _os_type ) , do: ""
474+ defp remove_dirsep ( bin , os_type ) do
475+ last = :binary . last ( bin )
476+ if last == ?/ or ( last == ?\\ and os_type == :win32 ) do
477+ binary_part ( bin , 0 , byte_size ( bin ) - 1 )
478+ else
479+ bin
480+ end
481+ end
460482
461483 @ doc ~S"""
462484 Splits the path into a list at the path separator.
@@ -568,7 +590,7 @@ defmodule Path do
568590 |> Enum . map ( & IO . chardata_to_string / 1 )
569591 end
570592
571- # Normalize the given path by expanding "..", "." and "~".
593+ # expand_dot the given path by expanding "..", "." and "~".
572594
573595 defp chardata_to_list ( chardata ) do
574596 case :unicode . characters_to_list ( chardata ) do
@@ -602,29 +624,34 @@ defmodule Path do
602624 end
603625 end
604626
605- defp normalize ( path ) , do: normalize ( split ( path ) , [ ] )
606-
607- defp normalize ( [ ".." | t ] , [ "/" | _ ] = acc ) do
608- normalize t , acc
609- end
610-
611- defp normalize ( [ ".." | t ] , [ << letter , ?: , ?/ >> | _ ] = acc ) when letter in ?a .. ?z do
612- normalize t , acc
627+ defp expand_dot ( << "/../" , rest :: binary >> ) ,
628+ do: expand_dot ( "/" <> rest )
629+ defp expand_dot ( << letter , ":/../" , rest :: binary >> ) when letter in ?a .. ?z ,
630+ do: expand_dot ( << letter , ":/" , rest :: binary >> )
631+ defp expand_dot ( "/.." ) ,
632+ do: "/"
633+ defp expand_dot ( << letter , ":/.." >> ) when letter in ?a .. ?z ,
634+ do: expand_dot ( << letter , ":/" >> )
635+ defp expand_dot ( path ) ,
636+ do: expand_dot ( :binary . split ( path , "/" , [ :global ] ) , [ ] )
637+
638+ defp expand_dot ( [ ".." | t ] , [ _ , _ | acc ] ) do
639+ expand_dot t , acc
613640 end
614641
615- defp normalize ( [ ".. " | t ] , [ _ | acc ] ) do
616- normalize t , acc
642+ defp expand_dot ( [ "." | t ] , acc ) do
643+ expand_dot t , acc
617644 end
618645
619- defp normalize ( [ "." | t ] , acc ) do
620- normalize t , acc
646+ defp expand_dot ( [ h | t ] , acc ) do
647+ expand_dot t , [ "/" , h | acc ]
621648 end
622649
623- defp normalize ( [ h | t ] , acc ) do
624- normalize t , [ h | acc ]
650+ defp expand_dot ( [ ] , [ "/" | acc ] ) do
651+ IO . iodata_to_binary ( :lists . reverse ( acc ) )
625652 end
626653
627- defp normalize ( [ ] , acc ) do
628- join :lists . reverse ( acc )
654+ defp major_os_type do
655+ :os . type |> elem ( 0 )
629656 end
630657end
0 commit comments