[git commit] cp: implement -t DIR

Denys Vlasenko vda.linux at googlemail.com
Mon Jun 21 17:38:39 UTC 2021


commit: https://git.busybox.net/busybox/commit/?id=0ec52d438a41be92d2d8e8651242b4d9faf23a6c
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

function                                             old     new   delta
packed_usage                                       33713   33734     +21
.rodata                                           103670  103672      +2
cp_main                                              506     500      -6
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/1 up/down: 23/-6)              Total: 17 bytes

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 coreutils/cp.c  | 130 +++++++++++++++++++++++++++++++-------------------------
 include/libbb.h |  17 +++++---
 2 files changed, 82 insertions(+), 65 deletions(-)

diff --git a/coreutils/cp.c b/coreutils/cp.c
index ac00e09bf..8b9e03c95 100644
--- a/coreutils/cp.c
+++ b/coreutils/cp.c
@@ -37,8 +37,55 @@
 
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */
 
+// Options of cp from GNU coreutils 6.10:
+// -a, --archive
+// -f, --force
+// -i, --interactive
+// -l, --link
+// -L, --dereference
+// -P, --no-dereference
+// -R, -r, --recursive
+// -s, --symbolic-link
+// -v, --verbose
+// -H	follow command-line symbolic links in SOURCE
+// -d	same as --no-dereference --preserve=links
+// -p	same as --preserve=mode,ownership,timestamps
+// -c	same as --preserve=context
+// -u, --update
+//	copy only when the SOURCE file is newer than the destination
+//	file or when the destination file is missing
+// --remove-destination
+//	remove each existing destination file before attempting to open
+// --parents
+//	use full source file name under DIRECTORY
+// -T, --no-target-directory
+//	treat DEST as a normal file
+// NOT SUPPORTED IN BBOX:
+// --backup[=CONTROL]
+//	make a backup of each existing destination file
+// -b	like --backup but does not accept an argument
+// --copy-contents
+//	copy contents of special files when recursive
+// --preserve[=ATTR_LIST]
+//	preserve attributes (default: mode,ownership,timestamps),
+//	if possible additional attributes: security context,links,all
+// --no-preserve=ATTR_LIST
+// --sparse=WHEN
+//	control creation of sparse files
+// --strip-trailing-slashes
+//	remove any trailing slashes from each SOURCE argument
+// -S, --suffix=SUFFIX
+//	override the usual backup suffix
+// -t, --target-directory=DIRECTORY
+//	copy all SOURCE arguments into DIRECTORY
+// -x, --one-file-system
+//	stay on this file system
+// -Z, --context=CONTEXT
+//	(SELinux) set SELinux security context of copy to CONTEXT
+
 //usage:#define cp_trivial_usage
-//usage:       "[-arPLHpfilsTu] SOURCE... DEST"
+//usage:       "[-arPLHpfilsTu] SOURCE DEST\n"
+//usage:       "or: cp [-arPLHpfilsu] SOURCE... { -t DIRECTORY | DIRECTORY }"
 //usage:#define cp_full_usage "\n\n"
 //usage:       "Copy SOURCEs to DEST\n"
 //usage:     "\n	-a	Same as -dpR"
@@ -53,7 +100,8 @@
 //usage:     "\n	-f	Overwrite"
 //usage:     "\n	-i	Prompt before overwrite"
 //usage:     "\n	-l,-s	Create (sym)links"
-//usage:     "\n	-T	Treat DEST as a normal file"
+//usage:     "\n	-T	Refuse to copy if DEST is a directory"
+//usage:     "\n	-t DIR	Copy all SOURCEs into DIR"
 //usage:     "\n	-u	Copy only newer files"
 
 #include "libbb.h"
@@ -73,14 +121,12 @@ int cp_main(int argc, char **argv)
 	int flags;
 	int status;
 	enum {
-		FILEUTILS_CP_OPTNUM = sizeof(FILEUTILS_CP_OPTSTR)-1,
 #if ENABLE_FEATURE_CP_LONG_OPTIONS
 		/*OPT_rmdest  = FILEUTILS_RMDEST = 1 << FILEUTILS_CP_OPTNUM */
 		OPT_parents = 1 << (FILEUTILS_CP_OPTNUM+1),
 		OPT_reflink = 1 << (FILEUTILS_CP_OPTNUM+2),
 #endif
 	};
-
 #if ENABLE_FEATURE_CP_LONG_OPTIONS
 # if ENABLE_FEATURE_CP_REFLINK
 	char *reflink = NULL;
@@ -94,7 +140,8 @@ int cp_main(int argc, char **argv)
 		// -r and -R are the same
 		// -R (and therefore -r) turns on -d (coreutils does this)
 		// -a = -pdR
-		"-2:l--s:s--l:Pd:rRd:Rd:apdR",
+	/* At least one argument. (Usually two+, but -t DIR can have only one) */
+		"-1:l--s:s--l:Pd:rRd:Rd:apdR",
 		"archive\0"        No_argument "a"
 		"force\0"          No_argument "f"
 		"interactive\0"    No_argument "i"
@@ -110,6 +157,9 @@ int cp_main(int argc, char **argv)
 		"parents\0"        No_argument "\xfe"
 # if ENABLE_FEATURE_CP_REFLINK
 		"reflink\0"        Optional_argument "\xfd"
+# endif
+		, &last
+# if ENABLE_FEATURE_CP_REFLINK
 		, &reflink
 # endif
 	);
@@ -128,55 +178,10 @@ int cp_main(int argc, char **argv)
 	flags = getopt32(argv, "^"
 		FILEUTILS_CP_OPTSTR
 		"\0"
-		"-2:l--s:s--l:Pd:rRd:Rd:apdR"
+		"-1:l--s:s--l:Pd:rRd:Rd:apdR"
+		, &last
 	);
 #endif
-	/* Options of cp from GNU coreutils 6.10:
-	 * -a, --archive
-	 * -f, --force
-	 * -i, --interactive
-	 * -l, --link
-	 * -L, --dereference
-	 * -P, --no-dereference
-	 * -R, -r, --recursive
-	 * -s, --symbolic-link
-	 * -v, --verbose
-	 * -H	follow command-line symbolic links in SOURCE
-	 * -d	same as --no-dereference --preserve=links
-	 * -p	same as --preserve=mode,ownership,timestamps
-	 * -c	same as --preserve=context
-	 * -u, --update
-	 *	copy only when the SOURCE file is newer than the destination
-	 *	file or when the destination file is missing
-	 * --remove-destination
-	 *	remove each existing destination file before attempting to open
-	 * --parents
-	 *	use full source file name under DIRECTORY
-	 * -T, --no-target-directory
-	 *	treat DEST as a normal file
-	 * NOT SUPPORTED IN BBOX:
-	 * --backup[=CONTROL]
-	 *	make a backup of each existing destination file
-	 * -b	like --backup but does not accept an argument
-	 * --copy-contents
-	 *	copy contents of special files when recursive
-	 * --preserve[=ATTR_LIST]
-	 *	preserve attributes (default: mode,ownership,timestamps),
-	 *	if possible additional attributes: security context,links,all
-	 * --no-preserve=ATTR_LIST
-	 * --sparse=WHEN
-	 *	control creation of sparse files
-	 * --strip-trailing-slashes
-	 *	remove any trailing slashes from each SOURCE argument
-	 * -S, --suffix=SUFFIX
-	 *	override the usual backup suffix
-	 * -t, --target-directory=DIRECTORY
-	 *	copy all SOURCE arguments into DIRECTORY
-	 * -x, --one-file-system
-	 *	stay on this file system
-	 * -Z, --context=CONTEXT
-	 *	(SELinux) set SELinux security context of copy to CONTEXT
-	 */
 	argc -= optind;
 	argv += optind;
 	/* Reverse this bit. If there is -d, bit is not set: */
@@ -195,15 +200,22 @@ int cp_main(int argc, char **argv)
 #endif
 
 	status = EXIT_SUCCESS;
-	last = argv[argc - 1];
-	/* If there are only two arguments and...  */
-	if (argc == 2) {
+	if (!(flags & FILEUTILS_TARGET_DIR)) {
+		last = argv[argc - 1];
+		if (argc < 2)
+			bb_show_usage();
+		if (argc != 2) {
+			if (flags & FILEUTILS_NO_TARGET_DIR)
+				bb_show_usage();
+			/* "cp A B C... DIR" - target must be dir */
+		} else /* argc == 2 */ {
+			/* "cp A B" - only case where target can be not a dir */
 			s_flags = cp_mv_stat2(*argv, &source_stat,
 					(flags & FILEUTILS_DEREFERENCE) ? stat : lstat);
-			if (s_flags < 0)
+			if (s_flags < 0) /* error other than ENOENT */
 				return EXIT_FAILURE;
 			d_flags = cp_mv_stat(last, &dest_stat);
-			if (d_flags < 0)
+			if (d_flags < 0) /* error other than ENOENT */
 				return EXIT_FAILURE;
 
 			if (flags & FILEUTILS_NO_TARGET_DIR) { /* -T */
@@ -235,9 +247,9 @@ int cp_main(int argc, char **argv)
 				dest = last;
 				goto DO_COPY; /* NB: argc==2 -> *++argv==last */
 			}
-	} else if (flags & FILEUTILS_NO_TARGET_DIR) {
-		bb_simple_error_msg_and_die("too many arguments");
+		}
 	}
+	/* else: last is DIR from "t -DIR" */
 
 	while (1) {
 #if ENABLE_FEATURE_CP_LONG_OPTIONS
diff --git a/include/libbb.h b/include/libbb.h
index 9a95a176d..e38e97ac2 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -460,13 +460,18 @@ enum {	/* cp.c, mv.c, install.c depend on these values. CAREFUL when changing th
 	FILEUTILS_VERBOSE         = (1 << 12) * ENABLE_FEATURE_VERBOSE,	/* -v */
 	FILEUTILS_UPDATE          = 1 << 13, /* -u */
 	FILEUTILS_NO_TARGET_DIR	  = 1 << 14, /* -T */
+	FILEUTILS_TARGET_DIR	  = 1 << 15, /* -t DIR */
 #if ENABLE_SELINUX
-	FILEUTILS_PRESERVE_SECURITY_CONTEXT = 1 << 15, /* -c */
+	FILEUTILS_PRESERVE_SECURITY_CONTEXT = 1 << 16, /* -c */
 #endif
-	FILEUTILS_RMDEST          = 1 << (16 - !ENABLE_SELINUX), /* --remove-destination */
-	/* bit 17 skipped for "cp --parents" */
-	FILEUTILS_REFLINK         = 1 << (18 - !ENABLE_SELINUX), /* cp --reflink=auto */
-	FILEUTILS_REFLINK_ALWAYS  = 1 << (19 - !ENABLE_SELINUX), /* cp --reflink[=always] */
+#define FILEUTILS_CP_OPTSTR "pdRfilsLHarPvuTt:" IF_SELINUX("c")
+/* How many bits in FILEUTILS_CP_OPTSTR? */
+	FILEUTILS_CP_OPTNUM       = 17 - !ENABLE_SELINUX,
+
+	FILEUTILS_RMDEST          = 1 << (17 - !ENABLE_SELINUX), /* --remove-destination */
+	/* bit 18 skipped for "cp --parents" */
+	FILEUTILS_REFLINK         = 1 << (19 - !ENABLE_SELINUX), /* cp --reflink=auto */
+	FILEUTILS_REFLINK_ALWAYS  = 1 << (20 - !ENABLE_SELINUX), /* cp --reflink[=always] */
 	/*
 	 * Hole. cp may have some bits set here,
 	 * they should not affect remove_file()/copy_file()
@@ -476,7 +481,7 @@ enum {	/* cp.c, mv.c, install.c depend on these values. CAREFUL when changing th
 #endif
 	FILEUTILS_IGNORE_CHMOD_ERR = 1 << 31,
 };
-#define FILEUTILS_CP_OPTSTR "pdRfilsLHarPvuT" IF_SELINUX("c")
+
 extern int remove_file(const char *path, int flags) FAST_FUNC;
 /* NB: without FILEUTILS_RECUR in flags, it will basically "cat"
  * the source, not copy (unless "source" is a directory).


More information about the busybox-cvs mailing list