summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coreutils/cp.c130
-rw-r--r--include/libbb.h17
2 files changed, 82 insertions, 65 deletions
diff --git a/coreutils/cp.c b/coreutils/cp.c
index ac00e09..8b9e03c 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 9a95a17..e38e97a 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).