git: 2d6b33f801d5 - main - cp: Add an option to visit sources in order.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 09 Jul 2025 17:09:55 UTC
The branch main has been updated by des:
URL: https://cgit.FreeBSD.org/src/commit/?id=2d6b33f801d5352b8e078db83f6c90f6fe8291bb
commit 2d6b33f801d5352b8e078db83f6c90f6fe8291bb
Author: Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2025-07-09 17:06:07 +0000
Commit: Dag-Erling Smørgrav <des@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