aboutsummaryrefslogtreecommitdiff
path: root/bin/cp
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2025-07-09 17:06:07 +0000
committerDag-Erling Smørgrav <des@FreeBSD.org>2025-07-09 17:07:13 +0000
commit2d6b33f801d5352b8e078db83f6c90f6fe8291bb (patch)
tree12f783fa47dcd4267a8a0172304db3a6a38fdcd5 /bin/cp
parentc3efa16dc9bcdcbe128c6b3c4b000867bbaf7991 (diff)
Diffstat (limited to 'bin/cp')
-rw-r--r--bin/cp/cp.112
-rw-r--r--bin/cp/cp.c14
-rwxr-xr-xbin/cp/tests/cp_test.sh4
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();
}
@@ -285,6 +289,12 @@ main(int argc, char *argv[])
}
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)
{
char rootname[NAME_MAX];
@@ -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