@@ -60,40 +60,70 @@ defmodule Path do
60
60
def absname ( path , relative_to ) do
61
61
path = IO . chardata_to_string ( path )
62
62
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 ] )
73
65
:volumerelative ->
74
66
relative_to = IO . chardata_to_string ( relative_to )
75
67
absname_vr ( split ( path ) , split ( relative_to ) , relative_to )
76
68
end
77
69
end
78
70
79
- ## Absolute path on current drive
71
+ # Absolute path on current drive
80
72
defp absname_vr ( [ "/" | rest ] , [ volume | _ ] , _relative ) ,
81
- do: join ( [ volume | rest ] )
73
+ do: absname_join ( [ volume | rest ] )
82
74
83
- ## Relative to current directory on current drive.
75
+ # Relative to current directory on current drive.
84
76
defp absname_vr ( [ << x , ?: >> | rest ] , [ << x , _ :: binary >> | _ ] , relative ) ,
85
- do: absname ( join ( rest ) , relative )
77
+ do: absname ( absname_join ( rest ) , relative )
86
78
87
- ## Relative to current directory on another drive.
79
+ # Relative to current directory on another drive.
88
80
defp absname_vr ( [ << x , ?: >> | name ] , _ , _relative ) do
89
81
cwd =
90
82
case :file . get_cwd ( [ x , ?: ] ) do
91
83
{ :ok , dir } -> IO . chardata_to_string ( dir )
92
84
{ :error , _ } -> << x , ?: , ?/ >>
93
85
end
94
- absname ( join ( name ) , cwd )
86
+ absname ( absname_join ( name ) , cwd )
95
87
end
96
88
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
+
97
127
@ doc """
98
128
Converts the path to an absolute one and expands
99
129
any `.` and `..` characters and a leading `~`.
@@ -106,7 +136,7 @@ defmodule Path do
106
136
"""
107
137
@ spec expand ( t ) :: binary
108
138
def expand ( path ) do
109
- normalize absname ( expand_home ( path ) , System . cwd! )
139
+ expand_dot absname ( expand_home ( path ) , System . cwd! )
110
140
end
111
141
112
142
@ doc """
@@ -133,7 +163,7 @@ defmodule Path do
133
163
"""
134
164
@ spec expand ( t , t ) :: binary
135
165
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! )
137
167
end
138
168
139
169
@ doc """
@@ -182,9 +212,13 @@ defmodule Path do
182
212
"""
183
213
@ spec relative ( t ) :: binary
184
214
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 )
188
222
end |> elem ( 1 ) |> IO . chardata_to_string
189
223
end
190
224
@@ -390,7 +424,7 @@ defmodule Path do
390
424
end
391
425
392
426
@ doc """
393
- Returns a string with one or more path components joined by the path separator .
427
+ Joins a list of strings .
394
428
395
429
This function should be used to convert a list of strings to a path.
396
430
Note that any trailing slash is removed on join.
@@ -411,52 +445,40 @@ defmodule Path do
411
445
def join ( [ name1 , name2 | rest ] ) , do:
412
446
join ( [ join ( name1 , name2 ) | rest ] )
413
447
def join ( [ name ] ) , do:
414
- do_join ( IO . chardata_to_string ( name ) , << >> , [ ] , major_os_type ( ) )
448
+ name
415
449
416
450
@ doc """
417
451
Joins two paths.
418
452
453
+ The right path will always be expanded to its relative format
454
+ and any trailing slash is removed on join.
455
+
419
456
## Examples
420
457
421
458
iex> Path.join("foo", "bar")
422
459
"foo/bar"
423
460
424
461
"""
425
462
@ 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 )
431
467
end
432
468
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
460
482
461
483
@ doc ~S"""
462
484
Splits the path into a list at the path separator.
@@ -568,7 +590,7 @@ defmodule Path do
568
590
|> Enum . map ( & IO . chardata_to_string / 1 )
569
591
end
570
592
571
- # Normalize the given path by expanding "..", "." and "~".
593
+ # expand_dot the given path by expanding "..", "." and "~".
572
594
573
595
defp chardata_to_list ( chardata ) do
574
596
case :unicode . characters_to_list ( chardata ) do
@@ -602,29 +624,34 @@ defmodule Path do
602
624
end
603
625
end
604
626
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
613
640
end
614
641
615
- defp normalize ( [ ".. " | t ] , [ _ | acc ] ) do
616
- normalize t , acc
642
+ defp expand_dot ( [ "." | t ] , acc ) do
643
+ expand_dot t , acc
617
644
end
618
645
619
- defp normalize ( [ "." | t ] , acc ) do
620
- normalize t , acc
646
+ defp expand_dot ( [ h | t ] , acc ) do
647
+ expand_dot t , [ "/" , h | acc ]
621
648
end
622
649
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 ) )
625
652
end
626
653
627
- defp normalize ( [ ] , acc ) do
628
- join :lists . reverse ( acc )
654
+ defp major_os_type do
655
+ :os . type |> elem ( 0 )
629
656
end
630
657
end
0 commit comments