@@ -123,6 +123,8 @@ defmodule File do
123
123
124
124
@ type posix_time :: integer ( )
125
125
126
+ @ type on_conflict_callback :: ( Path . t ( ) , Path . t ( ) -> boolean )
127
+
126
128
@ doc """
127
129
Returns `true` if the path is a regular file.
128
130
@@ -776,11 +778,6 @@ defmodule File do
776
778
be a path to a non-existent file. If either is a directory, `{:error, :eisdir}`
777
779
will be returned.
778
780
779
- The `callback` function is invoked if the `destination_file` already exists.
780
- The function receives arguments for `source_file` and `destination_file`;
781
- it should return `true` if the existing file should be overwritten, `false` if
782
- otherwise. The default callback returns `true`.
783
-
784
781
The function returns `:ok` in case of success. Otherwise, it returns
785
782
`{:error, reason}`.
786
783
@@ -792,13 +789,30 @@ defmodule File do
792
789
whether the destination is an existing directory or not. We have chosen to
793
790
explicitly disallow copying to a destination which is a directory,
794
791
and an error will be returned if tried.
792
+
793
+ ## Options
794
+
795
+ * `:on_conflict` - (since v1.14.0) Invoked when a file already exists in the destination.
796
+ The function receives arguments for `source_file` and `destination_file`. It should
797
+ return `true` if the existing file should be overwritten, `false` if otherwise.
798
+ The default callback returns `true`. On earlier versions, this callback could be
799
+ given as third argument, but such behaviour is now deprecated.
800
+
795
801
"""
796
- @ spec cp ( Path . t ( ) , Path . t ( ) , ( Path . t ( ) , Path . t ( ) -> boolean ) ) :: :ok | { :error , posix }
797
- def cp ( source_file , destination_file , callback \\ fn _ , _ -> true end ) do
802
+ @ spec cp ( Path . t ( ) , Path . t ( ) , on_conflict: on_conflict_callback ) :: :ok | { :error , posix }
803
+ def cp ( source_file , destination_file , options \\ [ ] )
804
+
805
+ # TODO: Deprecate me on Elixir v1.19
806
+ def cp ( source_file , destination_file , callback ) when is_function ( callback , 2 ) do
807
+ cp ( source_file , destination_file , on_conflict: callback )
808
+ end
809
+
810
+ def cp ( source_file , destination_file , options ) when is_list ( options ) do
811
+ on_conflict = Keyword . get ( options , :on_conflict , fn _ , _ -> true end )
798
812
source_file = IO . chardata_to_string ( source_file )
799
813
destination_file = IO . chardata_to_string ( destination_file )
800
814
801
- case do_cp_file ( source_file , destination_file , callback , [ ] ) do
815
+ case do_cp_file ( source_file , destination_file , on_conflict , [ ] ) do
802
816
{ :error , reason , _ } -> { :error , reason }
803
817
_ -> :ok
804
818
end
@@ -814,9 +828,9 @@ defmodule File do
814
828
The same as `cp/3`, but raises a `File.CopyError` exception if it fails.
815
829
Returns `:ok` otherwise.
816
830
"""
817
- @ spec cp! ( Path . t ( ) , Path . t ( ) , ( Path . t ( ) , Path . t ( ) -> boolean ) ) :: :ok
818
- def cp! ( source_file , destination_file , callback \\ fn _ , _ -> true end ) do
819
- case cp ( source_file , destination_file , callback ) do
831
+ @ spec cp! ( Path . t ( ) , Path . t ( ) , on_conflict: on_conflict_callback ) :: :ok
832
+ def cp! ( source_file , destination_file , options \\ [ ] ) do
833
+ case cp ( source_file , destination_file , options ) do
820
834
:ok ->
821
835
:ok
822
836
@@ -839,18 +853,15 @@ defmodule File do
839
853
If `source` is a directory, or a symbolic link to it, then `destination` must
840
854
be an existent `directory` or a symbolic link to one, or a path to a non-existent directory.
841
855
842
- If the source is a file, it copies `source` to
843
- `destination`. If the `source` is a directory, it copies
844
- the contents inside source into the `destination` directory.
856
+ If the source is a file, it copies `source` to `destination`. If the `source`
857
+ is a directory, it copies the contents inside source into the `destination` directory.
845
858
846
- If a file already exists in the destination, it invokes `callback`.
847
- `callback` must be a function that takes two arguments: `source` and `destination`.
848
- The callback should return `true` if the existing file should be overwritten and `false` otherwise.
859
+ If a file already exists in the destination, it invokes the optional `on_conflict`
860
+ callback given as an option. See "Options" for more information.
849
861
850
- This function may fail while copying files,
851
- in such cases, it will leave the destination
852
- directory in a dirty state, where file which have already been copied
853
- won't be removed.
862
+ This function may fail while copying files, in such cases, it will leave the
863
+ destination directory in a dirty state, where file which have already been
864
+ copied won't be removed.
854
865
855
866
The function returns `{:ok, files_and_directories}` in case of
856
867
success, `files_and_directories` lists all files and directories copied in no
@@ -861,6 +872,19 @@ defmodule File do
861
872
explicitly disallow this behaviour. If `source` is a `file` and `destination`
862
873
is a directory, `{:error, :eisdir}` will be returned.
863
874
875
+ ## Options
876
+
877
+ * `:on_conflict` - (since v1.14.0) Invoked when a file already exists in the destination.
878
+ The function receives arguments for `source` and `destination`. It should return
879
+ `true` if the existing file should be overwritten, `false` if otherwise. The default
880
+ callback returns `true`. On earlier versions, this callback could be given as third
881
+ argument, but such behaviour is now deprecated.
882
+
883
+ * `:dereference_symlinks` - (since v1.14.0) By default, this function will copy symlinks
884
+ by creating symlinks that point to the same location. This option forces symlinks to be
885
+ dereferenced and have their contents copied instead when set to `true`. If the dereferenced
886
+ files do not exist, than the operation fails. The default is `false`.
887
+
864
888
## Examples
865
889
866
890
# Copies file "a.txt" to "b.txt"
@@ -870,14 +894,28 @@ defmodule File do
870
894
File.cp_r("samples", "tmp")
871
895
872
896
# Same as before, but asks the user how to proceed in case of conflicts
873
- File.cp_r("samples", "tmp", fn source, destination ->
897
+ File.cp_r("samples", "tmp", on_conflict: fn source, destination ->
874
898
IO.gets("Overwriting #{destination} by #{source}. Type y to confirm. ") == "y\n"
875
899
end)
876
900
877
901
"""
878
- @ spec cp_r ( Path . t ( ) , Path . t ( ) , ( Path . t ( ) , Path . t ( ) -> boolean ) ) ::
902
+ @ spec cp_r ( Path . t ( ) , Path . t ( ) ,
903
+ on_conflict: on_conflict_callback ,
904
+ dereference_symlinks: boolean ( )
905
+ ) ::
879
906
{ :ok , [ binary ] } | { :error , posix , binary }
880
- def cp_r ( source , destination , callback \\ fn _ , _ -> true end ) when is_function ( callback , 2 ) do
907
+
908
+ def cp_r ( source , destination , options \\ [ ] )
909
+
910
+ # TODO: Deprecate me on Elixir v1.19
911
+ def cp_r ( source , destination , callback ) when is_function ( callback , 2 ) do
912
+ cp_r ( source , destination , on_conflict: callback )
913
+ end
914
+
915
+ def cp_r ( source , destination , options ) when is_list ( options ) do
916
+ on_conflict = Keyword . get ( options , :on_conflict , fn _ , _ -> true end )
917
+ dereference? = Keyword . get ( options , :dereference_symlinks , false )
918
+
881
919
source =
882
920
source
883
921
|> IO . chardata_to_string ( )
@@ -888,7 +926,7 @@ defmodule File do
888
926
|> IO . chardata_to_string ( )
889
927
|> assert_no_null_byte! ( "File.cp_r/3" )
890
928
891
- case do_cp_r ( source , destination , callback , [ ] ) do
929
+ case do_cp_r ( source , destination , on_conflict , dereference? , [ ] ) do
892
930
{ :error , _ , _ } = error -> error
893
931
res -> { :ok , res }
894
932
end
@@ -898,9 +936,12 @@ defmodule File do
898
936
The same as `cp_r/3`, but raises a `File.CopyError` exception if it fails.
899
937
Returns the list of copied files otherwise.
900
938
"""
901
- @ spec cp_r! ( Path . t ( ) , Path . t ( ) , ( Path . t ( ) , Path . t ( ) -> boolean ) ) :: [ binary ]
902
- def cp_r! ( source , destination , callback \\ fn _ , _ -> true end ) do
903
- case cp_r ( source , destination , callback ) do
939
+ @ spec cp_r! ( Path . t ( ) , Path . t ( ) ,
940
+ on_conflict: on_conflict_callback ,
941
+ dereference_symlinks: boolean ( )
942
+ ) :: [ binary ]
943
+ def cp_r! ( source , destination , options \\ [ ] ) do
944
+ case cp_r ( source , destination , options ) do
904
945
{ :ok , files } ->
905
946
files
906
947
@@ -914,15 +955,21 @@ defmodule File do
914
955
end
915
956
end
916
957
917
- defp do_cp_r ( src , dest , callback , acc ) when is_list ( acc ) do
958
+ defp do_cp_r ( src , dest , on_conflict , dereference? , acc ) when is_list ( acc ) do
918
959
case :elixir_utils . read_link_type ( src ) do
919
960
{ :ok , :regular } ->
920
- do_cp_file ( src , dest , callback , acc )
961
+ do_cp_file ( src , dest , on_conflict , acc )
921
962
922
963
{ :ok , :symlink } ->
923
964
case :file . read_link ( src ) do
924
- { :ok , link } -> do_cp_link ( link , src , dest , callback , acc )
925
- { :error , reason } -> { :error , reason , src }
965
+ { :ok , link } when dereference? ->
966
+ do_cp_r ( Path . expand ( link , Path . dirname ( src ) ) , dest , on_conflict , dereference? , acc )
967
+
968
+ { :ok , link } ->
969
+ do_cp_link ( link , src , dest , on_conflict , acc )
970
+
971
+ { :error , reason } ->
972
+ { :error , reason , src }
926
973
end
927
974
928
975
{ :ok , :directory } ->
@@ -931,7 +978,7 @@ defmodule File do
931
978
case mkdir ( dest ) do
932
979
success when success in [ :ok , { :error , :eexist } ] ->
933
980
Enum . reduce ( files , [ dest | acc ] , fn x , acc ->
934
- do_cp_r ( Path . join ( src , x ) , Path . join ( dest , x ) , callback , acc )
981
+ do_cp_r ( Path . join ( src , x ) , Path . join ( dest , x ) , on_conflict , dereference? , acc )
935
982
end )
936
983
937
984
{ :error , reason } ->
@@ -950,9 +997,8 @@ defmodule File do
950
997
end
951
998
end
952
999
953
- # If we reach this clause, there was an error while
954
- # processing a file.
955
- defp do_cp_r ( _ , _ , _ , acc ) do
1000
+ # If we reach this clause, there was an error while processing a file.
1001
+ defp do_cp_r ( _ , _ , _ , _ , acc ) do
956
1002
acc
957
1003
end
958
1004
@@ -961,14 +1007,14 @@ defmodule File do
961
1007
end
962
1008
963
1009
# Both src and dest are files.
964
- defp do_cp_file ( src , dest , callback , acc ) do
1010
+ defp do_cp_file ( src , dest , on_conflict , acc ) do
965
1011
case :file . copy ( src , { dest , [ :exclusive ] } ) do
966
1012
{ :ok , _ } ->
967
1013
copy_file_mode! ( src , dest )
968
1014
[ dest | acc ]
969
1015
970
1016
{ :error , :eexist } ->
971
- if path_differs? ( src , dest ) and callback . ( src , dest ) do
1017
+ if path_differs? ( src , dest ) and on_conflict . ( src , dest ) do
972
1018
case copy ( src , dest ) do
973
1019
{ :ok , _ } ->
974
1020
copy_file_mode! ( src , dest )
@@ -987,13 +1033,13 @@ defmodule File do
987
1033
end
988
1034
989
1035
# Both src and dest are files.
990
- defp do_cp_link ( link , src , dest , callback , acc ) do
1036
+ defp do_cp_link ( link , src , dest , on_conflict , acc ) do
991
1037
case :file . make_symlink ( link , dest ) do
992
1038
:ok ->
993
1039
[ dest | acc ]
994
1040
995
1041
{ :error , :eexist } ->
996
- if path_differs? ( src , dest ) and callback . ( src , dest ) do
1042
+ if path_differs? ( src , dest ) and on_conflict . ( src , dest ) do
997
1043
# If rm/1 fails, :file.make_symlink/2 will fail
998
1044
_ = rm ( dest )
999
1045
0 commit comments