The branch main has been updated by des:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=2d6b33f801d5352b8e078db83f6c90f6fe8291bb

commit 2d6b33f801d5352b8e078db83f6c90f6fe8291bb
Author:     Dag-Erling Smørgrav <d...@freebsd.org>
AuthorDate: 2025-07-09 17:06:07 +0000
Commit:     Dag-Erling Smørgrav <d...@freebsd.org>
CommitDate: 2025-07-09 17:07:13 +0000

    cp: Add an option to visit sources in order.
    
    This adds a --sort option which makes cp pass a comparison function to
    FTS, ensuring that sources are visited and traversed in a predictable
    order.  This will help make certain test cases more reliable.
    
    Sponsored by:   Klara, Inc.
    Reviewed by:    kevans
    Differential Revision:  https://reviews.freebsd.org/D51214
---
 bin/cp/cp.1             | 12 ++++++++++++
 bin/cp/cp.c             | 14 ++++++++++++--
 bin/cp/tests/cp_test.sh |  4 ++--
 3 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/bin/cp/cp.1 b/bin/cp/cp.1
index 6edc8e403acd..5231fa72621c 100644
--- a/bin/cp/cp.1
+++ b/bin/cp/cp.1
@@ -184,6 +184,18 @@ If the source file has both its set-user-ID and 
set-group-ID bits on,
 and either the user ID or group ID cannot be preserved, neither
 the set-user-ID nor set-group-ID bits are preserved in the copy's
 permissions.
+.It Fl -sort
+Visit and traverse sources in (non-localized) lexicographical order.
+Normally,
+.Nm
+visits the sources in the order they were listed on the command line,
+and if recursing, traverses their contents in whichever order they
+were returned in by the kernel, which may be the order in which they
+were created, lexicographical order, or something else entirely.
+With
+.Fl -sort ,
+the sources are both visited and traversed in lexicographical order.
+This is mostly useful for testing.
 .It Fl s , Fl -symbolic-link
 Create symbolic links to regular files in a hierarchy instead of copying.
 .It Fl v , Fl -verbose
diff --git a/bin/cp/cp.c b/bin/cp/cp.c
index a1b62084a790..38fe65399d06 100644
--- a/bin/cp/cp.c
+++ b/bin/cp/cp.c
@@ -71,7 +71,7 @@ static char dot[] = ".";
 #define END(buf) (buf + sizeof(buf))
 PATH_T to = { .dir = -1, .end = to.path };
 bool Nflag, fflag, iflag, lflag, nflag, pflag, sflag, vflag;
-static bool Hflag, Lflag, Pflag, Rflag, rflag;
+static bool Hflag, Lflag, Pflag, Rflag, rflag, Sflag;
 volatile sig_atomic_t info;
 
 enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
@@ -96,6 +96,7 @@ static const struct option long_opts[] =
        { "symbolic-link",      no_argument,            NULL,   's' },
        { "verbose",            no_argument,            NULL,   'v' },
        { "one-file-system",    no_argument,            NULL,   'x' },
+       { "sort",               no_argument,            NULL,   SORT_OPT },
        { 0 }
 };
 
@@ -167,6 +168,9 @@ main(int argc, char *argv[])
                case 'x':
                        fts_options |= FTS_XDEV;
                        break;
+               case SORT_OPT:
+                       Sflag = true;
+                       break;
                default:
                        usage();
                }
@@ -284,6 +288,12 @@ main(int argc, char *argv[])
            &to_stat)));
 }
 
+static int
+ftscmp(const FTSENT * const *a, const FTSENT * const *b)
+{
+       return (strcmp((*a)->fts_name, (*b)->fts_name));
+}
+
 static int
 copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
 {
@@ -327,7 +337,7 @@ copy(char *argv[], enum op type, int fts_options, struct 
stat *root_stat)
        }
 
        level = FTS_ROOTLEVEL;
-       if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL)
+       if ((ftsp = fts_open(argv, fts_options, Sflag ? ftscmp : NULL)) == NULL)
                err(1, "fts_open");
        for (badcp = rval = 0;
             (curr = fts_read(ftsp)) != NULL;
diff --git a/bin/cp/tests/cp_test.sh b/bin/cp/tests/cp_test.sh
index 6adbc45c5009..3c3dd309b9e4 100755
--- a/bin/cp/tests/cp_test.sh
+++ b/bin/cp/tests/cp_test.sh
@@ -657,7 +657,7 @@ unrdir_body()
        atf_check \
            -s exit:1 \
            -e match:"^cp: src/b: Permission denied" \
-           cp -R src dst
+           cp -R --sort src dst
        atf_check test -d dst/a
        atf_check cmp src/a/f dst/a/f
        atf_check test -d dst/b
@@ -681,7 +681,7 @@ unrfile_body()
        atf_check \
            -s exit:1 \
            -e match:"^cp: src/b: Permission denied" \
-           cp -R src dst
+           cp -R --sort src dst
        atf_check test -d dst
        atf_check cmp src/a dst/a
        atf_check test ! -e dst/b

Reply via email to