cp: implement -t DIR

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@googlemail.com>
This commit is contained in:
Denys Vlasenko 2021-06-21 19:35:33 +02:00
parent 1de709fda2
commit 0ec52d438a
2 changed files with 82 additions and 65 deletions

View File

@ -37,8 +37,55 @@
/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */ /* 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:#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:#define cp_full_usage "\n\n"
//usage: "Copy SOURCEs to DEST\n" //usage: "Copy SOURCEs to DEST\n"
//usage: "\n -a Same as -dpR" //usage: "\n -a Same as -dpR"
@ -53,7 +100,8 @@
//usage: "\n -f Overwrite" //usage: "\n -f Overwrite"
//usage: "\n -i Prompt before overwrite" //usage: "\n -i Prompt before overwrite"
//usage: "\n -l,-s Create (sym)links" //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" //usage: "\n -u Copy only newer files"
#include "libbb.h" #include "libbb.h"
@ -73,14 +121,12 @@ int cp_main(int argc, char **argv)
int flags; int flags;
int status; int status;
enum { enum {
FILEUTILS_CP_OPTNUM = sizeof(FILEUTILS_CP_OPTSTR)-1,
#if ENABLE_FEATURE_CP_LONG_OPTIONS #if ENABLE_FEATURE_CP_LONG_OPTIONS
/*OPT_rmdest = FILEUTILS_RMDEST = 1 << FILEUTILS_CP_OPTNUM */ /*OPT_rmdest = FILEUTILS_RMDEST = 1 << FILEUTILS_CP_OPTNUM */
OPT_parents = 1 << (FILEUTILS_CP_OPTNUM+1), OPT_parents = 1 << (FILEUTILS_CP_OPTNUM+1),
OPT_reflink = 1 << (FILEUTILS_CP_OPTNUM+2), OPT_reflink = 1 << (FILEUTILS_CP_OPTNUM+2),
#endif #endif
}; };
#if ENABLE_FEATURE_CP_LONG_OPTIONS #if ENABLE_FEATURE_CP_LONG_OPTIONS
# if ENABLE_FEATURE_CP_REFLINK # if ENABLE_FEATURE_CP_REFLINK
char *reflink = NULL; char *reflink = NULL;
@ -94,7 +140,8 @@ int cp_main(int argc, char **argv)
// -r and -R are the same // -r and -R are the same
// -R (and therefore -r) turns on -d (coreutils does this) // -R (and therefore -r) turns on -d (coreutils does this)
// -a = -pdR // -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" "archive\0" No_argument "a"
"force\0" No_argument "f" "force\0" No_argument "f"
"interactive\0" No_argument "i" "interactive\0" No_argument "i"
@ -110,6 +157,9 @@ int cp_main(int argc, char **argv)
"parents\0" No_argument "\xfe" "parents\0" No_argument "\xfe"
# if ENABLE_FEATURE_CP_REFLINK # if ENABLE_FEATURE_CP_REFLINK
"reflink\0" Optional_argument "\xfd" "reflink\0" Optional_argument "\xfd"
# endif
, &last
# if ENABLE_FEATURE_CP_REFLINK
, &reflink , &reflink
# endif # endif
); );
@ -128,55 +178,10 @@ int cp_main(int argc, char **argv)
flags = getopt32(argv, "^" flags = getopt32(argv, "^"
FILEUTILS_CP_OPTSTR FILEUTILS_CP_OPTSTR
"\0" "\0"
"-2:l--s:s--l:Pd:rRd:Rd:apdR" "-1:l--s:s--l:Pd:rRd:Rd:apdR"
, &last
); );
#endif #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; argc -= optind;
argv += optind; argv += optind;
/* Reverse this bit. If there is -d, bit is not set: */ /* Reverse this bit. If there is -d, bit is not set: */
@ -195,15 +200,22 @@ int cp_main(int argc, char **argv)
#endif #endif
status = EXIT_SUCCESS; status = EXIT_SUCCESS;
last = argv[argc - 1]; if (!(flags & FILEUTILS_TARGET_DIR)) {
/* If there are only two arguments and... */ last = argv[argc - 1];
if (argc == 2) { 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, s_flags = cp_mv_stat2(*argv, &source_stat,
(flags & FILEUTILS_DEREFERENCE) ? stat : lstat); (flags & FILEUTILS_DEREFERENCE) ? stat : lstat);
if (s_flags < 0) if (s_flags < 0) /* error other than ENOENT */
return EXIT_FAILURE; return EXIT_FAILURE;
d_flags = cp_mv_stat(last, &dest_stat); d_flags = cp_mv_stat(last, &dest_stat);
if (d_flags < 0) if (d_flags < 0) /* error other than ENOENT */
return EXIT_FAILURE; return EXIT_FAILURE;
if (flags & FILEUTILS_NO_TARGET_DIR) { /* -T */ if (flags & FILEUTILS_NO_TARGET_DIR) { /* -T */
@ -235,9 +247,9 @@ int cp_main(int argc, char **argv)
dest = last; dest = last;
goto DO_COPY; /* NB: argc==2 -> *++argv==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) { while (1) {
#if ENABLE_FEATURE_CP_LONG_OPTIONS #if ENABLE_FEATURE_CP_LONG_OPTIONS

View File

@ -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_VERBOSE = (1 << 12) * ENABLE_FEATURE_VERBOSE, /* -v */
FILEUTILS_UPDATE = 1 << 13, /* -u */ FILEUTILS_UPDATE = 1 << 13, /* -u */
FILEUTILS_NO_TARGET_DIR = 1 << 14, /* -T */ FILEUTILS_NO_TARGET_DIR = 1 << 14, /* -T */
FILEUTILS_TARGET_DIR = 1 << 15, /* -t DIR */
#if ENABLE_SELINUX #if ENABLE_SELINUX
FILEUTILS_PRESERVE_SECURITY_CONTEXT = 1 << 15, /* -c */ FILEUTILS_PRESERVE_SECURITY_CONTEXT = 1 << 16, /* -c */
#endif #endif
FILEUTILS_RMDEST = 1 << (16 - !ENABLE_SELINUX), /* --remove-destination */ #define FILEUTILS_CP_OPTSTR "pdRfilsLHarPvuTt:" IF_SELINUX("c")
/* bit 17 skipped for "cp --parents" */ /* How many bits in FILEUTILS_CP_OPTSTR? */
FILEUTILS_REFLINK = 1 << (18 - !ENABLE_SELINUX), /* cp --reflink=auto */ FILEUTILS_CP_OPTNUM = 17 - !ENABLE_SELINUX,
FILEUTILS_REFLINK_ALWAYS = 1 << (19 - !ENABLE_SELINUX), /* cp --reflink[=always] */
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, * Hole. cp may have some bits set here,
* they should not affect remove_file()/copy_file() * 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 #endif
FILEUTILS_IGNORE_CHMOD_ERR = 1 << 31, 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; extern int remove_file(const char *path, int flags) FAST_FUNC;
/* NB: without FILEUTILS_RECUR in flags, it will basically "cat" /* NB: without FILEUTILS_RECUR in flags, it will basically "cat"
* the source, not copy (unless "source" is a directory). * the source, not copy (unless "source" is a directory).