|
1583 | 1583 | <span id="1583">1583</span>
|
1584 | 1584 | <span id="1584">1584</span>
|
1585 | 1585 | <span id="1585">1585</span>
|
| 1586 | +<span id="1586">1586</span> |
| 1587 | +<span id="1587">1587</span> |
| 1588 | +<span id="1588">1588</span> |
| 1589 | +<span id="1589">1589</span> |
| 1590 | +<span id="1590">1590</span> |
| 1591 | +<span id="1591">1591</span> |
| 1592 | +<span id="1592">1592</span> |
| 1593 | +<span id="1593">1593</span> |
| 1594 | +<span id="1594">1594</span> |
| 1595 | +<span id="1595">1595</span> |
| 1596 | +<span id="1596">1596</span> |
| 1597 | +<span id="1597">1597</span> |
| 1598 | +<span id="1598">1598</span> |
| 1599 | +<span id="1599">1599</span> |
| 1600 | +<span id="1600">1600</span> |
| 1601 | +<span id="1601">1601</span> |
| 1602 | +<span id="1602">1602</span> |
| 1603 | +<span id="1603">1603</span> |
| 1604 | +<span id="1604">1604</span> |
| 1605 | +<span id="1605">1605</span> |
| 1606 | +<span id="1606">1606</span> |
| 1607 | +<span id="1607">1607</span> |
| 1608 | +<span id="1608">1608</span> |
| 1609 | +<span id="1609">1609</span> |
| 1610 | +<span id="1610">1610</span> |
| 1611 | +<span id="1611">1611</span> |
| 1612 | +<span id="1612">1612</span> |
| 1613 | +<span id="1613">1613</span> |
| 1614 | +<span id="1614">1614</span> |
| 1615 | +<span id="1615">1615</span> |
| 1616 | +<span id="1616">1616</span> |
| 1617 | +<span id="1617">1617</span> |
| 1618 | +<span id="1618">1618</span> |
| 1619 | +<span id="1619">1619</span> |
| 1620 | +<span id="1620">1620</span> |
| 1621 | +<span id="1621">1621</span> |
| 1622 | +<span id="1622">1622</span> |
| 1623 | +<span id="1623">1623</span> |
| 1624 | +<span id="1624">1624</span> |
| 1625 | +<span id="1625">1625</span> |
| 1626 | +<span id="1626">1626</span> |
| 1627 | +<span id="1627">1627</span> |
| 1628 | +<span id="1628">1628</span> |
| 1629 | +<span id="1629">1629</span> |
| 1630 | +<span id="1630">1630</span> |
| 1631 | +<span id="1631">1631</span> |
| 1632 | +<span id="1632">1632</span> |
| 1633 | +<span id="1633">1633</span> |
| 1634 | +<span id="1634">1634</span> |
| 1635 | +<span id="1635">1635</span> |
| 1636 | +<span id="1636">1636</span> |
| 1637 | +<span id="1637">1637</span> |
| 1638 | +<span id="1638">1638</span> |
| 1639 | +<span id="1639">1639</span> |
| 1640 | +<span id="1640">1640</span> |
| 1641 | +<span id="1641">1641</span> |
| 1642 | +<span id="1642">1642</span> |
| 1643 | +<span id="1643">1643</span> |
| 1644 | +<span id="1644">1644</span> |
| 1645 | +<span id="1645">1645</span> |
| 1646 | +<span id="1646">1646</span> |
| 1647 | +<span id="1647">1647</span> |
| 1648 | +<span id="1648">1648</span> |
| 1649 | +<span id="1649">1649</span> |
| 1650 | +<span id="1650">1650</span> |
| 1651 | +<span id="1651">1651</span> |
| 1652 | +<span id="1652">1652</span> |
| 1653 | +<span id="1653">1653</span> |
| 1654 | +<span id="1654">1654</span> |
| 1655 | +<span id="1655">1655</span> |
| 1656 | +<span id="1656">1656</span> |
| 1657 | +<span id="1657">1657</span> |
| 1658 | +<span id="1658">1658</span> |
| 1659 | +<span id="1659">1659</span> |
| 1660 | +<span id="1660">1660</span> |
| 1661 | +<span id="1661">1661</span> |
| 1662 | +<span id="1662">1662</span> |
| 1663 | +<span id="1663">1663</span> |
| 1664 | +<span id="1664">1664</span> |
| 1665 | +<span id="1665">1665</span> |
| 1666 | +<span id="1666">1666</span> |
| 1667 | +<span id="1667">1667</span> |
| 1668 | +<span id="1668">1668</span> |
| 1669 | +<span id="1669">1669</span> |
| 1670 | +<span id="1670">1670</span> |
| 1671 | +<span id="1671">1671</span> |
| 1672 | +<span id="1672">1672</span> |
1586 | 1673 | </pre><pre class="rust"><code><span class="attribute">#![allow(clippy::missing_safety_doc)]
|
1587 | 1674 | #![allow(clippy::extra_unused_lifetimes)]
|
1588 | 1675 |
|
|
1594 | 1681 | // For the full copyright and license information, please view the LICENSE file
|
1595 | 1682 | // that was distributed with this source code.
|
1596 | 1683 |
|
1597 |
| -// spell-checker:ignore (ToDO) copydir ficlone ftruncate linkgs lstat nlink nlinks pathbuf pwrite reflink strs xattrs symlinked fiemap |
| 1684 | +// spell-checker:ignore (ToDO) copydir ficlone fiemap ftruncate linkgs lstat nlink nlinks pathbuf pwrite reflink strs xattrs symlinked deduplicated advcpmv |
1598 | 1685 |
|
1599 | 1686 | </span><span class="attribute">#[macro_use]
|
1600 | 1687 | </span><span class="kw">extern crate </span>quick_error;
|
|
1618 | 1705 |
|
1619 | 1706 | <span class="kw">use </span>clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
|
1620 | 1707 | <span class="kw">use </span>filetime::FileTime;
|
| 1708 | +<span class="kw">use </span>indicatif::{ProgressBar, ProgressStyle}; |
1621 | 1709 | <span class="attribute">#[cfg(unix)]
|
1622 | 1710 | </span><span class="kw">use </span>libc::mkfifo;
|
1623 | 1711 | <span class="kw">use </span>quick_error::ResultExt;
|
|
1799 | 1887 | target_dir: <span class="prelude-ty">Option</span><String>,
|
1800 | 1888 | update: bool,
|
1801 | 1889 | verbose: bool,
|
| 1890 | + progress_bar: bool, |
1802 | 1891 | }
|
1803 | 1892 |
|
1804 | 1893 | <span class="kw">static </span>ABOUT: <span class="kw-2">&</span>str = <span class="string">"Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY."</span>;
|
|
1829 | 1918 | <span class="kw">pub const </span>PARENT: <span class="kw-2">&</span>str = <span class="string">"parent"</span>;
|
1830 | 1919 | <span class="kw">pub const </span>PARENTS: <span class="kw-2">&</span>str = <span class="string">"parents"</span>;
|
1831 | 1920 | <span class="kw">pub const </span>PATHS: <span class="kw-2">&</span>str = <span class="string">"paths"</span>;
|
| 1921 | + <span class="kw">pub const </span>PROGRESS_BAR: <span class="kw-2">&</span>str = <span class="string">"progress"</span>; |
1832 | 1922 | <span class="kw">pub const </span>PRESERVE: <span class="kw-2">&</span>str = <span class="string">"preserve"</span>;
|
1833 | 1923 | <span class="kw">pub const </span>PRESERVE_DEFAULT_ATTRIBUTES: <span class="kw-2">&</span>str = <span class="string">"preserve-default-attributes"</span>;
|
1834 | 1924 | <span class="kw">pub const </span>RECURSIVE: <span class="kw-2">&</span>str = <span class="string">"recursive"</span>;
|
|
2124 | 2214 | )
|
2125 | 2215 | <span class="comment">// END TODO
|
2126 | 2216 | </span>.arg(
|
| 2217 | + <span class="comment">// The 'g' short flag is modeled after advcpmv |
| 2218 | + // See this repo: https://github.com/jarun/advcpmv |
| 2219 | + </span>Arg::new(options::PROGRESS_BAR) |
| 2220 | + .long(options::PROGRESS_BAR) |
| 2221 | + .short(<span class="string">'g'</span>) |
| 2222 | + .action(clap::ArgAction::SetTrue) |
| 2223 | + .help( |
| 2224 | + <span class="string">"Display a progress bar. \n\ |
| 2225 | + Note: this feature is not supported by GNU coreutils."</span>, |
| 2226 | + ), |
| 2227 | + ) |
| 2228 | + .arg( |
2127 | 2229 | Arg::new(options::PATHS)
|
2128 | 2230 | .action(ArgAction::Append)
|
2129 | 2231 | .value_hint(clap::ValueHint::AnyPath),
|
|
2402 | 2504 | preserve_attributes,
|
2403 | 2505 | recursive,
|
2404 | 2506 | target_dir,
|
| 2507 | + progress_bar: matches.get_flag(options::PROGRESS_BAR), |
2405 | 2508 | };
|
2406 | 2509 |
|
2407 | 2510 | <span class="prelude-val">Ok</span>(options)
|
|
2520 | 2623 | /// `Err(Error::NotAllFilesCopied)` if at least one non-fatal error was
|
2521 | 2624 | /// encountered.
|
2522 | 2625 | ///
|
2523 |
| -/// Behavior depends on `options`, see [`Options`] for details. |
| 2626 | +/// Behavior depends on path`options`, see [`Options`] for details. |
2524 | 2627 | ///
|
2525 | 2628 | /// [`Options`]: ./struct.Options.html
|
2526 | 2629 | </span><span class="kw">fn </span>copy(sources: <span class="kw-2">&</span>[Source], target: <span class="kw-2">&</span>TargetSlice, options: <span class="kw-2">&</span>Options) -> CopyResult<()> {
|
|
2534 | 2637 | <span class="kw">let </span><span class="kw-2">mut </span>non_fatal_errors = <span class="bool-val">false</span>;
|
2535 | 2638 | <span class="kw">let </span><span class="kw-2">mut </span>seen_sources = HashSet::with_capacity(sources.len());
|
2536 | 2639 | <span class="kw">let </span><span class="kw-2">mut </span>symlinked_files = HashSet::new();
|
2537 |
| - <span class="kw">for </span>source <span class="kw">in </span>sources { |
| 2640 | + |
| 2641 | + <span class="kw">let </span>progress_bar = <span class="kw">if </span>options.progress_bar { |
| 2642 | + <span class="prelude-val">Some</span>( |
| 2643 | + ProgressBar::new(disk_usage(sources, options.recursive)<span class="question-mark">?</span>) |
| 2644 | + .with_style( |
| 2645 | + ProgressStyle::with_template( |
| 2646 | + <span class="string">"{msg}: [{elapsed_precise}] {wide_bar} {bytes:>7}/{total_bytes:7}"</span>, |
| 2647 | + ) |
| 2648 | + .unwrap(), |
| 2649 | + ) |
| 2650 | + .with_message(uucore::util_name()), |
| 2651 | + ) |
| 2652 | + } <span class="kw">else </span>{ |
| 2653 | + <span class="prelude-val">None |
| 2654 | + </span>}; |
| 2655 | + |
| 2656 | + <span class="kw">for </span>source <span class="kw">in </span>sources.iter() { |
2538 | 2657 | <span class="kw">if </span>seen_sources.contains(source) {
|
2539 | 2658 | <span class="comment">// FIXME: compare sources by the actual file they point to, not their path. (e.g. dir/file == dir/../dir/file in most cases)
|
2540 | 2659 | </span><span class="macro">show_warning!</span>(<span class="string">"source {} specified more than once"</span>, source.quote());
|
|
2545 | 2664 | preserve_hardlinks(<span class="kw-2">&mut </span>hard_links, source, <span class="kw-2">&</span>dest, <span class="kw-2">&mut </span>found_hard_link)<span class="question-mark">?</span>;
|
2546 | 2665 | }
|
2547 | 2666 | <span class="kw">if </span>!found_hard_link {
|
2548 |
| - <span class="kw">if let </span><span class="prelude-val">Err</span>(error) = |
2549 |
| - copy_source(source, target, <span class="kw-2">&</span>target_type, options, <span class="kw-2">&mut </span>symlinked_files) |
2550 |
| - { |
| 2667 | + <span class="kw">if let </span><span class="prelude-val">Err</span>(error) = copy_source( |
| 2668 | + <span class="kw-2">&</span>progress_bar, |
| 2669 | + source, |
| 2670 | + target, |
| 2671 | + <span class="kw-2">&</span>target_type, |
| 2672 | + options, |
| 2673 | + <span class="kw-2">&mut </span>symlinked_files, |
| 2674 | + ) { |
2551 | 2675 | <span class="kw">match </span>error {
|
2552 | 2676 | <span class="comment">// When using --no-clobber, we don't want to show
|
2553 | 2677 | // an error message
|
|
2602 | 2726 | }
|
2603 | 2727 |
|
2604 | 2728 | <span class="kw">fn </span>copy_source(
|
| 2729 | + progress_bar: <span class="kw-2">&</span><span class="prelude-ty">Option</span><ProgressBar>, |
2605 | 2730 | source: <span class="kw-2">&</span>SourceSlice,
|
2606 | 2731 | target: <span class="kw-2">&</span>TargetSlice,
|
2607 | 2732 | target_type: <span class="kw-2">&</span>TargetType,
|
|
2611 | 2736 | <span class="kw">let </span>source_path = Path::new(<span class="kw-2">&</span>source);
|
2612 | 2737 | <span class="kw">if </span>source_path.is_dir() {
|
2613 | 2738 | <span class="comment">// Copy as directory
|
2614 |
| - </span>copy_directory(source, target, options, symlinked_files, <span class="bool-val">true</span>) |
| 2739 | + </span>copy_directory(progress_bar, source, target, options, symlinked_files, <span class="bool-val">true</span>) |
2615 | 2740 | } <span class="kw">else </span>{
|
2616 | 2741 | <span class="comment">// Copy as file
|
2617 | 2742 | </span><span class="kw">let </span>dest = construct_dest_path(source_path, target, target_type, options)<span class="question-mark">?</span>;
|
2618 |
| - copy_file(source_path, dest.as_path(), options, symlinked_files, <span class="bool-val">true</span>) |
| 2743 | + copy_file( |
| 2744 | + progress_bar, |
| 2745 | + source_path, |
| 2746 | + dest.as_path(), |
| 2747 | + options, |
| 2748 | + symlinked_files, |
| 2749 | + <span class="bool-val">true</span>, |
| 2750 | + ) |
2619 | 2751 | }
|
2620 | 2752 | }
|
2621 | 2753 |
|
|
2862 | 2994 | /// The original permissions of `source` will be copied to `dest`
|
2863 | 2995 | /// after a successful copy.
|
2864 | 2996 | </span><span class="kw">fn </span>copy_file(
|
| 2997 | + progress_bar: <span class="kw-2">&</span><span class="prelude-ty">Option</span><ProgressBar>, |
2865 | 2998 | source: <span class="kw-2">&</span>Path,
|
2866 | 2999 | dest: <span class="kw-2">&</span>Path,
|
2867 | 3000 | options: <span class="kw-2">&</span>Options,
|
|
3037 | 3170 | </span>fs::set_permissions(dest, dest_permissions).ok();
|
3038 | 3171 | }
|
3039 | 3172 | copy_attributes(source, dest, <span class="kw-2">&</span>options.preserve_attributes)<span class="question-mark">?</span>;
|
| 3173 | + |
| 3174 | + <span class="kw">if let </span><span class="prelude-val">Some</span>(progress_bar) = progress_bar { |
| 3175 | + progress_bar.inc(fs::metadata(source)<span class="question-mark">?</span>.len()); |
| 3176 | + } |
| 3177 | + |
3040 | 3178 | <span class="prelude-val">Ok</span>(())
|
3041 | 3179 | }
|
3042 | 3180 |
|
|
3156 | 3294 | <span class="prelude-val">Ok</span>(target.join(local_to_root))
|
3157 | 3295 | }
|
3158 | 3296 |
|
| 3297 | +<span class="doccomment">/// Get the total size of a slice of files and directories. |
| 3298 | +/// |
| 3299 | +/// This function is much like the `du` utility, by recursively getting the sizes of files in directories. |
| 3300 | +/// Files are not deduplicated when appearing in multiple sources. If `recursive` is set to `false`, the |
| 3301 | +/// directories in `paths` will be ignored. |
| 3302 | +</span><span class="kw">fn </span>disk_usage(paths: <span class="kw-2">&</span>[PathBuf], recursive: bool) -> io::Result<u64> { |
| 3303 | + <span class="kw">let </span><span class="kw-2">mut </span>total = <span class="number">0</span>; |
| 3304 | + <span class="kw">for </span>p <span class="kw">in </span>paths { |
| 3305 | + <span class="kw">let </span>md = fs::metadata(p)<span class="question-mark">?</span>; |
| 3306 | + <span class="kw">if </span>md.file_type().is_dir() { |
| 3307 | + <span class="kw">if </span>recursive { |
| 3308 | + total += disk_usage_directory(p)<span class="question-mark">?</span>; |
| 3309 | + } |
| 3310 | + } <span class="kw">else </span>{ |
| 3311 | + total += md.len(); |
| 3312 | + } |
| 3313 | + } |
| 3314 | + <span class="prelude-val">Ok</span>(total) |
| 3315 | +} |
| 3316 | + |
| 3317 | +<span class="doccomment">/// A helper for `disk_usage` specialized for directories. |
| 3318 | +</span><span class="kw">fn </span>disk_usage_directory(p: <span class="kw-2">&</span>Path) -> io::Result<u64> { |
| 3319 | + <span class="kw">let </span><span class="kw-2">mut </span>total = <span class="number">0</span>; |
| 3320 | + |
| 3321 | + <span class="kw">for </span>entry <span class="kw">in </span>fs::read_dir(p)<span class="question-mark">? </span>{ |
| 3322 | + <span class="kw">let </span>entry = entry<span class="question-mark">?</span>; |
| 3323 | + <span class="kw">if </span>entry.file_type()<span class="question-mark">?</span>.is_dir() { |
| 3324 | + total += disk_usage_directory(<span class="kw-2">&</span>entry.path())<span class="question-mark">?</span>; |
| 3325 | + } <span class="kw">else </span>{ |
| 3326 | + total += entry.metadata()<span class="question-mark">?</span>.len(); |
| 3327 | + } |
| 3328 | + } |
| 3329 | + |
| 3330 | + <span class="prelude-val">Ok</span>(total) |
| 3331 | +} |
| 3332 | + |
3159 | 3333 | <span class="attribute">#[test]
|
3160 | 3334 | </span><span class="kw">fn </span>test_cp_localize_to_target() {
|
3161 | 3335 | <span class="macro">assert!</span>(
|
|
0 commit comments