|
611 | 611 | <a href="#611" id="611">611</a>
|
612 | 612 | <a href="#612" id="612">612</a>
|
613 | 613 | <a href="#613" id="613">613</a>
|
614 |
| -<a href="#614" id="614">614</a> |
615 |
| -<a href="#615" id="615">615</a> |
616 | 614 | </pre><pre class="rust"><code><span class="comment">// This file is part of the uutils coreutils package.
|
617 | 615 | //
|
618 | 616 | // (c) Orvar Segerström <orvarsegerstrom@gmail.com>
|
|
640 | 638 | <span class="kw">use </span>uucore::backup_control::{<span class="self">self</span>, BackupMode};
|
641 | 639 | <span class="kw">use </span>uucore::display::Quotable;
|
642 | 640 | <span class="kw">use </span>uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError, UUsageError};
|
| 641 | +<span class="kw">use </span>uucore::fs::are_hardlinks_to_same_file; |
643 | 642 | <span class="kw">use </span>uucore::update_control::{<span class="self">self</span>, UpdateMode};
|
644 | 643 | <span class="kw">use </span>uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show};
|
645 | 644 |
|
|
852 | 851 | }
|
853 | 852 | }
|
854 | 853 |
|
855 |
| -<span class="kw">fn </span>exec(files: <span class="kw-2">&</span>[OsString], b: <span class="kw-2">&</span>Behavior) -> UResult<()> { |
856 |
| - <span class="kw">let </span>paths: Vec<PathBuf> = { |
857 |
| - <span class="kw">let </span>paths = files.iter().map(Path::new); |
| 854 | +<span class="kw">fn </span>parse_paths(files: <span class="kw-2">&</span>[OsString], b: <span class="kw-2">&</span>Behavior) -> Vec<PathBuf> { |
| 855 | + <span class="kw">let </span>paths = files.iter().map(Path::new); |
858 | 856 |
|
859 |
| - <span class="comment">// Strip slashes from path, if strip opt present |
860 |
| - </span><span class="kw">if </span>b.strip_slashes { |
861 |
| - paths |
862 |
| - .map(|p| p.components().as_path().to_owned()) |
863 |
| - .collect::<Vec<PathBuf>>() |
864 |
| - } <span class="kw">else </span>{ |
865 |
| - paths.map(|p| p.to_owned()).collect::<Vec<PathBuf>>() |
866 |
| - } |
867 |
| - }; |
| 857 | + <span class="kw">if </span>b.strip_slashes { |
| 858 | + paths |
| 859 | + .map(|p| p.components().as_path().to_owned()) |
| 860 | + .collect::<Vec<PathBuf>>() |
| 861 | + } <span class="kw">else </span>{ |
| 862 | + paths.map(|p| p.to_owned()).collect::<Vec<PathBuf>>() |
| 863 | + } |
| 864 | +} |
868 | 865 |
|
869 |
| - <span class="kw">if let </span><span class="prelude-val">Some</span>(<span class="kw-2">ref </span>name) = b.target_dir { |
870 |
| - <span class="kw">return </span>move_files_into_dir(<span class="kw-2">&</span>paths, <span class="kw-2">&</span>PathBuf::from(name), b); |
| 866 | +<span class="kw">fn </span>handle_two_paths(source: <span class="kw-2">&</span>Path, target: <span class="kw-2">&</span>Path, b: <span class="kw-2">&</span>Behavior) -> UResult<()> { |
| 867 | + <span class="kw">if </span>source.symlink_metadata().is_err() { |
| 868 | + <span class="kw">return </span><span class="prelude-val">Err</span>(MvError::NoSuchFile(source.quote().to_string()).into()); |
871 | 869 | }
|
872 |
| - <span class="kw">match </span>paths.len() { |
873 |
| - <span class="comment">/* case 0/1 are not possible thanks to clap */ |
874 |
| - </span><span class="number">2 </span>=> { |
875 |
| - <span class="kw">let </span>source = <span class="kw-2">&</span>paths[<span class="number">0</span>]; |
876 |
| - <span class="kw">let </span>target = <span class="kw-2">&</span>paths[<span class="number">1</span>]; |
877 |
| - <span class="comment">// Here we use the `symlink_metadata()` method instead of `exists()`, |
878 |
| - // since it handles dangling symlinks correctly. The method gives an |
879 |
| - // `Ok()` results unless the source does not exist, or the user |
880 |
| - // lacks permission to access metadata. |
881 |
| - </span><span class="kw">if </span>source.symlink_metadata().is_err() { |
882 |
| - <span class="kw">return </span><span class="prelude-val">Err</span>(MvError::NoSuchFile(source.quote().to_string()).into()); |
883 |
| - } |
884 | 870 |
|
885 |
| - <span class="comment">// GNU semantics are: if the source and target are the same, no move occurs and we print an error |
886 |
| - </span><span class="kw">if </span>source.eq(target) { |
887 |
| - <span class="comment">// Done to match GNU semantics for the dot file |
888 |
| - </span><span class="kw">if </span>source.eq(Path::new(<span class="string">"."</span>)) || source.ends_with(<span class="string">"/."</span>) || source.is_file() { |
889 |
| - <span class="kw">return </span><span class="prelude-val">Err</span>(MvError::SameFile( |
890 |
| - source.quote().to_string(), |
891 |
| - target.quote().to_string(), |
892 |
| - ) |
893 |
| - .into()); |
894 |
| - } <span class="kw">else </span>{ |
895 |
| - <span class="kw">return </span><span class="prelude-val">Err</span>(MvError::SelfSubdirectory(source.display().to_string()).into()); |
896 |
| - } |
897 |
| - } |
| 871 | + <span class="kw">if </span>(source.eq(target) || are_hardlinks_to_same_file(source, target)) |
| 872 | + && b.backup != BackupMode::SimpleBackup |
| 873 | + { |
| 874 | + <span class="kw">if </span>source.eq(Path::new(<span class="string">"."</span>)) || source.ends_with(<span class="string">"/."</span>) || source.is_file() { |
| 875 | + <span class="kw">return </span><span class="prelude-val">Err</span>( |
| 876 | + MvError::SameFile(source.quote().to_string(), target.quote().to_string()).into(), |
| 877 | + ); |
| 878 | + } <span class="kw">else </span>{ |
| 879 | + <span class="kw">return </span><span class="prelude-val">Err</span>(MvError::SelfSubdirectory(source.display().to_string()).into()); |
| 880 | + } |
| 881 | + } |
898 | 882 |
|
899 |
| - <span class="kw">if </span>target.is_dir() { |
900 |
| - <span class="kw">if </span>b.no_target_dir { |
901 |
| - <span class="kw">if </span>source.is_dir() { |
902 |
| - rename(source, target, b, <span class="prelude-val">None</span>).map_err_context(|| { |
903 |
| - <span class="macro">format!</span>(<span class="string">"cannot move {} to {}"</span>, source.quote(), target.quote()) |
904 |
| - }) |
905 |
| - } <span class="kw">else </span>{ |
906 |
| - <span class="prelude-val">Err</span>(MvError::DirectoryToNonDirectory(target.quote().to_string()).into()) |
907 |
| - } |
908 |
| - } <span class="kw">else </span>{ |
909 |
| - move_files_into_dir(<span class="kw-2">&</span>[source.clone()], target, b) |
910 |
| - } |
911 |
| - } <span class="kw">else if </span>target.exists() && source.is_dir() { |
912 |
| - <span class="kw">match </span>b.overwrite { |
913 |
| - OverwriteMode::NoClobber => <span class="kw">return </span><span class="prelude-val">Ok</span>(()), |
914 |
| - OverwriteMode::Interactive => { |
915 |
| - <span class="kw">if </span>!<span class="macro">prompt_yes!</span>(<span class="string">"overwrite {}? "</span>, target.quote()) { |
916 |
| - <span class="kw">return </span><span class="prelude-val">Err</span>(io::Error::new(io::ErrorKind::Other, <span class="string">""</span>).into()); |
917 |
| - } |
918 |
| - } |
919 |
| - OverwriteMode::Force => {} |
920 |
| - }; |
921 |
| - <span class="prelude-val">Err</span>(MvError::NonDirectoryToDirectory( |
922 |
| - source.quote().to_string(), |
923 |
| - target.quote().to_string(), |
924 |
| - ) |
925 |
| - .into()) |
| 883 | + <span class="kw">if </span>target.is_dir() { |
| 884 | + <span class="kw">if </span>b.no_target_dir { |
| 885 | + <span class="kw">if </span>source.is_dir() { |
| 886 | + rename(source, target, b, <span class="prelude-val">None</span>).map_err_context(|| { |
| 887 | + <span class="macro">format!</span>(<span class="string">"cannot move {} to {}"</span>, source.quote(), target.quote()) |
| 888 | + }) |
926 | 889 | } <span class="kw">else </span>{
|
927 |
| - rename(source, target, b, <span class="prelude-val">None</span>).map_err(|e| USimpleError::new(<span class="number">1</span>, <span class="macro">format!</span>(<span class="string">"{e}"</span>))) |
| 890 | + <span class="prelude-val">Err</span>(MvError::DirectoryToNonDirectory(target.quote().to_string()).into()) |
928 | 891 | }
|
| 892 | + } <span class="kw">else </span>{ |
| 893 | + move_files_into_dir(<span class="kw-2">&</span>[source.to_path_buf()], target, b) |
929 | 894 | }
|
930 |
| - <span class="kw">_ </span>=> { |
931 |
| - <span class="kw">if </span>b.no_target_dir { |
932 |
| - <span class="kw">return </span><span class="prelude-val">Err</span>(UUsageError::new( |
933 |
| - <span class="number">1</span>, |
934 |
| - <span class="macro">format!</span>(<span class="string">"mv: extra operand {}"</span>, files[<span class="number">2</span>].quote()), |
935 |
| - )); |
| 895 | + } <span class="kw">else if </span>target.exists() && source.is_dir() { |
| 896 | + <span class="kw">match </span>b.overwrite { |
| 897 | + OverwriteMode::NoClobber => <span class="kw">return </span><span class="prelude-val">Ok</span>(()), |
| 898 | + OverwriteMode::Interactive => { |
| 899 | + <span class="kw">if </span>!<span class="macro">prompt_yes!</span>(<span class="string">"overwrite {}? "</span>, target.quote()) { |
| 900 | + <span class="kw">return </span><span class="prelude-val">Err</span>(io::Error::new(io::ErrorKind::Other, <span class="string">""</span>).into()); |
| 901 | + } |
936 | 902 | }
|
937 |
| - <span class="kw">let </span>target_dir = paths.last().unwrap(); |
938 |
| - <span class="kw">let </span>sources = <span class="kw-2">&</span>paths[..paths.len() - <span class="number">1</span>]; |
| 903 | + OverwriteMode::Force => {} |
| 904 | + }; |
| 905 | + <span class="prelude-val">Err</span>(MvError::NonDirectoryToDirectory( |
| 906 | + source.quote().to_string(), |
| 907 | + target.quote().to_string(), |
| 908 | + ) |
| 909 | + .into()) |
| 910 | + } <span class="kw">else </span>{ |
| 911 | + rename(source, target, b, <span class="prelude-val">None</span>).map_err(|e| USimpleError::new(<span class="number">1</span>, <span class="macro">format!</span>(<span class="string">"{e}"</span>))) |
| 912 | + } |
| 913 | +} |
939 | 914 |
|
940 |
| - move_files_into_dir(sources, target_dir, b) |
941 |
| - } |
| 915 | +<span class="kw">fn </span>handle_multiple_paths(paths: <span class="kw-2">&</span>[PathBuf], b: <span class="kw-2">&</span>Behavior) -> UResult<()> { |
| 916 | + <span class="kw">if </span>b.no_target_dir { |
| 917 | + <span class="kw">return </span><span class="prelude-val">Err</span>(UUsageError::new( |
| 918 | + <span class="number">1</span>, |
| 919 | + <span class="macro">format!</span>(<span class="string">"mv: extra operand {}"</span>, paths[<span class="number">2</span>].quote()), |
| 920 | + )); |
| 921 | + } |
| 922 | + <span class="kw">let </span>target_dir = paths.last().unwrap(); |
| 923 | + <span class="kw">let </span>sources = <span class="kw-2">&</span>paths[..paths.len() - <span class="number">1</span>]; |
| 924 | + |
| 925 | + move_files_into_dir(sources, target_dir, b) |
| 926 | +} |
| 927 | + |
| 928 | +<span class="kw">fn </span>exec(files: <span class="kw-2">&</span>[OsString], b: <span class="kw-2">&</span>Behavior) -> UResult<()> { |
| 929 | + <span class="kw">let </span>paths = parse_paths(files, b); |
| 930 | + |
| 931 | + <span class="kw">if let </span><span class="prelude-val">Some</span>(<span class="kw-2">ref </span>name) = b.target_dir { |
| 932 | + <span class="kw">return </span>move_files_into_dir(<span class="kw-2">&</span>paths, <span class="kw-2">&</span>PathBuf::from(name), b); |
| 933 | + } |
| 934 | + |
| 935 | + <span class="kw">match </span>paths.len() { |
| 936 | + <span class="number">2 </span>=> handle_two_paths(<span class="kw-2">&</span>paths[<span class="number">0</span>], <span class="kw-2">&</span>paths[<span class="number">1</span>], b), |
| 937 | + <span class="kw">_ </span>=> handle_multiple_paths(<span class="kw-2">&</span>paths, b), |
942 | 938 | }
|
943 | 939 | }
|
944 | 940 |
|
|
0 commit comments