git: 19413ce66cc0 - stable/12 - cp: Make -P work without -R as per POSIX

From: Kyle Evans <kevans_at_FreeBSD.org>
Date: Mon, 25 Apr 2022 00:03:20 UTC
The branch stable/12 has been updated by kevans:

URL: https://cgit.FreeBSD.org/src/commit/?id=19413ce66cc02d676e0778eb9519284e47d7d8e1

commit 19413ce66cc02d676e0778eb9519284e47d7d8e1
Author:     Cameron Katri <me@cameronkatri.com>
AuthorDate: 2022-02-23 18:55:13 +0000
Commit:     Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2022-04-25 00:02:30 +0000

    cp: Make -P work without -R as per POSIX
    
    According to POSIX, cp should allow the `-P` flag to work whether `-R`
    is specified or not.  Currently, the `-P` option only works along with
    `-R`.
    
    PR:             199466
    Reviewed by:    kevans
    
    (cherry picked from commit 97e13037915c22162f199461f56951793d669f57)
---
 bin/cp/cp.1             | 16 ++++++++++++----
 bin/cp/cp.c             | 13 ++++++++-----
 bin/cp/tests/cp_test.sh | 11 +++++++++++
 3 files changed, 31 insertions(+), 9 deletions(-)

diff --git a/bin/cp/cp.1 b/bin/cp/cp.1
index f7e2d639def7..f6ff23a16f41 100644
--- a/bin/cp/cp.1
+++ b/bin/cp/cp.1
@@ -32,7 +32,7 @@
 .\"	@(#)cp.1	8.3 (Berkeley) 4/18/94
 .\" $FreeBSD$
 .\"
-.Dd June 6, 2015
+.Dd February 23, 2022
 .Dt CP 1
 .Os
 .Sh NAME
@@ -55,6 +55,14 @@
 .Op Fl f | i | n
 .Op Fl alpsvx
 .Ar source_file ... target_directory
+.Nm
+.Op Fl f | i | n
+.Op Fl alPpsvx
+.Ar source_file target_file
+.Nm
+.Op Fl f | i | n
+.Op Fl alPpsvx
+.Ar source_file ... target_directory
 .Sh DESCRIPTION
 In the first synopsis form, the
 .Nm
@@ -84,10 +92,10 @@ If the
 .Fl R
 option is specified, all symbolic links are followed.
 .It Fl P
-If the
+No symbolic links are followed.
+This is the default if the
 .Fl R
-option is specified, no symbolic links are followed.
-This is the default.
+option is specified.
 .It Fl R
 If
 .Ar source_file
diff --git a/bin/cp/cp.c b/bin/cp/cp.c
index 2c4e2bd7bd3c..e38cd97f4369 100644
--- a/bin/cp/cp.c
+++ b/bin/cp/cp.c
@@ -100,21 +100,23 @@ main(int argc, char *argv[])
 {
 	struct stat to_stat, tmp_stat;
 	enum op type;
-	int ch, fts_options, r, have_trailing_slash;
+	int Pflag, ch, fts_options, r, have_trailing_slash;
 	char *target;
 
 	fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
+	Pflag = 0;
 	while ((ch = getopt(argc, argv, "HLPRafilnprsvx")) != -1)
 		switch (ch) {
 		case 'H':
 			Hflag = 1;
-			Lflag = 0;
+			Lflag = Pflag = 0;
 			break;
 		case 'L':
 			Lflag = 1;
-			Hflag = 0;
+			Hflag = Pflag = 0;
 			break;
 		case 'P':
+			Pflag = 1;
 			Hflag = Lflag = 0;
 			break;
 		case 'R':
@@ -123,6 +125,7 @@ main(int argc, char *argv[])
 		case 'a':
 			pflag = 1;
 			Rflag = 1;
+			Pflag = 1;
 			Hflag = Lflag = 0;
 			break;
 		case 'f':
@@ -145,7 +148,7 @@ main(int argc, char *argv[])
 			break;
 		case 'r':
 			rflag = Lflag = 1;
-			Hflag = 0;
+			Hflag = Pflag = 0;
 			break;
 		case 's':
 			sflag = 1;
@@ -179,7 +182,7 @@ main(int argc, char *argv[])
 			fts_options &= ~FTS_PHYSICAL;
 			fts_options |= FTS_LOGICAL;
 		}
-	} else {
+	} else if (!Pflag) {
 		fts_options &= ~FTS_PHYSICAL;
 		fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
 	}
diff --git a/bin/cp/tests/cp_test.sh b/bin/cp/tests/cp_test.sh
index a9eb65f463d8..7362168d7303 100755
--- a/bin/cp/tests/cp_test.sh
+++ b/bin/cp/tests/cp_test.sh
@@ -199,6 +199,16 @@ recursive_link_Lflag_body()
 	    '(' ! -L foo-mirror/foo/baz ')'
 }
 
+atf_test_case standalone_Pflag
+standalone_Pflag_body()
+{
+	echo "foo" > bar
+	ln -s bar foo
+
+	atf_check cp -P foo baz
+	atf_check -o inline:'Symbolic Link\n' stat -f %SHT baz
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case basic
@@ -211,4 +221,5 @@ atf_init_test_cases()
 	atf_add_test_case recursive_link_dflt
 	atf_add_test_case recursive_link_Hflag
 	atf_add_test_case recursive_link_Lflag
+	atf_add_test_case standalone_Pflag
 }