cp: add -u/--update and --remove-destination
authorDenys Vlasenko <vda.linux@googlemail.com>
Thu, 26 May 2016 22:46:38 +0000 (00:46 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Thu, 26 May 2016 22:46:38 +0000 (00:46 +0200)
Based on the patch by wdlkmpx@gmail.com

function                                             old     new   delta
copy_file                                           1546    1644     +98
add_partition                                       1270    1362     +92
ask_and_unlink                                        95     133     +38
do_iproute                                           132     157     +25
decode_one_format                                    710     715      +5
cp_main                                              369     374      +5
ubirename_main                                       198     202      +4
read_package_field                                   232     230      -2
bb_make_directory                                    421     412      -9
packed_usage                                       30505   30476     -29
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 7/3 up/down: 267/-40)           Total: 227 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
coreutils/cp.c
include/libbb.h
libbb/copy_file.c

index 247ed0fda0632ca9dcbd08e130e9e46a21e3d666..2630c0d59614c6bb30160a627ba53ca6ebe01239 100644 (file)
@@ -31,6 +31,7 @@
 //usage:     "\n       -f      Overwrite"
 //usage:     "\n       -i      Prompt before overwrite"
 //usage:     "\n       -l,-s   Create (sym)links"
+//usage:     "\n       -u      Copy only newer files"
 
 #include "libbb.h"
 #include "libcoreutils/coreutils.h"
@@ -49,12 +50,10 @@ int cp_main(int argc, char **argv)
        int flags;
        int status;
        enum {
-               OPT_a = 1 << (sizeof(FILEUTILS_CP_OPTSTR)-1),
-               OPT_r = 1 << (sizeof(FILEUTILS_CP_OPTSTR)),
-               OPT_P = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+1),
-               OPT_v = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+2),
+               FILEUTILS_CP_OPTNUM = sizeof(FILEUTILS_CP_OPTSTR)-1,
 #if ENABLE_FEATURE_CP_LONG_OPTIONS
-               OPT_parents = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+3),
+               /*OPT_rmdest  = FILEUTILS_RMDEST = 1 << FILEUTILS_CP_OPTNUM */
+               OPT_parents = 1 << (FILEUTILS_CP_OPTNUM+1),
 #endif
        };
 
@@ -76,10 +75,12 @@ int cp_main(int argc, char **argv)
                "recursive\0"      No_argument "R"
                "symbolic-link\0"  No_argument "s"
                "verbose\0"        No_argument "v"
-               "parents\0"        No_argument "\xff"
+               "update\0"         No_argument "u"
+               "remove-destination\0" No_argument "\xff"
+               "parents\0"        No_argument "\xfe"
                ;
 #endif
-       flags = getopt32(argv, FILEUTILS_CP_OPTSTR "arPv");
+       flags = getopt32(argv, FILEUTILS_CP_OPTSTR);
        /* Options of cp from GNU coreutils 6.10:
         * -a, --archive
         * -f, --force
@@ -94,6 +95,11 @@ int cp_main(int argc, char **argv)
         * -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
         * NOT SUPPORTED IN BBOX:
@@ -106,8 +112,6 @@ int cp_main(int argc, char **argv)
         *      preserve attributes (default: mode,ownership,timestamps),
         *      if possible additional attributes: security context,links,all
         * --no-preserve=ATTR_LIST
-        * --remove-destination
-        *      remove  each existing destination file before attempting to open
         * --sparse=WHEN
         *      control creation of sparse files
         * --strip-trailing-slashes
@@ -118,9 +122,6 @@ int cp_main(int argc, char **argv)
         *      copy all SOURCE arguments into DIRECTORY
         * -T, --no-target-directory
         *      treat DEST as a normal file
-        * -u, --update
-        *      copy only when the SOURCE file is newer than the destination
-        *      file or when the destination file is missing
         * -x, --one-file-system
         *      stay on this file system
         * -Z, --context=CONTEXT
@@ -156,11 +157,16 @@ int cp_main(int argc, char **argv)
                        return EXIT_FAILURE;
 
 #if ENABLE_FEATURE_CP_LONG_OPTIONS
+               //bb_error_msg("flags:%x FILEUTILS_RMDEST:%x OPT_parents:%x",
+               //      flags, FILEUTILS_RMDEST, OPT_parents);
                if (flags & OPT_parents) {
                        if (!(d_flags & 2)) {
                                bb_error_msg_and_die("with --parents, the destination must be a directory");
                        }
                }
+               if (flags & FILEUTILS_RMDEST) {
+                       flags |= FILEUTILS_FORCE;
+               }
 #endif
 
                /* ...if neither is a directory...  */
index fd40ef74c04de7d349fd87cef9509c40e562c6c6..a21f4204a426bd61408ae5c1ae36bb0be526a9cc 100644 (file)
@@ -351,7 +351,7 @@ extern char *strrstr(const char *haystack, const char *needle) FAST_FUNC;
 //TODO: supply a pointer to char[11] buffer (avoid statics)?
 extern const char *bb_mode_string(mode_t mode) FAST_FUNC;
 extern int is_directory(const char *name, int followLinks) FAST_FUNC;
-enum { /* DO NOT CHANGE THESE VALUES!  cp.c, mv.c, install.c depend on them. */
+enum { /* cp.c, mv.c, install.c depend on these values. CAREFUL when changing them! */
        FILEUTILS_PRESERVE_STATUS = 1 << 0, /* -p */
        FILEUTILS_DEREFERENCE     = 1 << 1, /* !-d */
        FILEUTILS_RECUR           = 1 << 2, /* -R */
@@ -361,15 +361,25 @@ enum {    /* DO NOT CHANGE THESE VALUES!  cp.c, mv.c, install.c depend on them. */
        FILEUTILS_MAKE_SOFTLINK   = 1 << 6, /* -s */
        FILEUTILS_DEREF_SOFTLINK  = 1 << 7, /* -L */
        FILEUTILS_DEREFERENCE_L0  = 1 << 8, /* -H */
+       /* -a = -pdR (mapped in cp.c) */
+       /* -r = -dR  (mapped in cp.c) */
+       /* -P = -d   (mapped in cp.c) */
+       FILEUTILS_VERBOSE         = (1 << 12) * ENABLE_FEATURE_VERBOSE, /* -v */
+       FILEUTILS_UPDATE          = 1 << 13, /* -u */
 #if ENABLE_SELINUX
-       FILEUTILS_PRESERVE_SECURITY_CONTEXT = 1 << 9, /* -c */
-       FILEUTILS_SET_SECURITY_CONTEXT = 1 << 10,
+       FILEUTILS_PRESERVE_SECURITY_CONTEXT = 1 << 14, /* -c */
 #endif
-       FILEUTILS_IGNORE_CHMOD_ERR = 1 << 11,
-       /* -v */
-       FILEUTILS_VERBOSE         = (1 << 12) * ENABLE_FEATURE_VERBOSE,
+       FILEUTILS_RMDEST          = 1 << (15 - !ENABLE_SELINUX), /* --remove-destination */
+       /*
+        * Hole. cp may have some bits set here,
+        * they should not affect remove_file()/copy_file()
+        */
+#if ENABLE_SELINUX
+       FILEUTILS_SET_SECURITY_CONTEXT = 1 << 30,
+#endif
+       FILEUTILS_IGNORE_CHMOD_ERR = 1 << 31,
 };
-#define FILEUTILS_CP_OPTSTR "pdRfilsLH" IF_SELINUX("c")
+#define FILEUTILS_CP_OPTSTR "pdRfilsLHarPvu" 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).
index a4be875d244d8e316808833f8a53c9dacc004ac8..23bcf2e828f03bcbc0654e115ae8d18131802ee8 100644 (file)
@@ -64,6 +64,11 @@ static int ask_and_unlink(const char *dest, int flags)
                bb_perror_msg("can't create '%s'", dest);
                return -1; /* error */
        }
+#if ENABLE_FEATURE_CP_LONG_OPTIONS
+       if (flags & FILEUTILS_RMDEST)
+               if (flags & FILEUTILS_VERBOSE)
+                       printf("removed '%s'\n", dest);
+#endif
        return 1; /* ok (to try again) */
 }
 
@@ -210,6 +215,22 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
                goto preserve_mode_ugid_time;
        }
 
+       if (dest_exists) {
+               if (flags & FILEUTILS_UPDATE) {
+                       if (source_stat.st_mtime <= dest_stat.st_mtime) {
+                               return 0; /* source file must be newer */
+                       }
+               }
+#if ENABLE_FEATURE_CP_LONG_OPTIONS
+               if (flags & FILEUTILS_RMDEST) {
+                       ovr = ask_and_unlink(dest, flags);
+                       if (ovr <= 0)
+                               return ovr;
+                       dest_exists = 0;
+               }
+#endif
+       }
+
        if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) {
                int (*lf)(const char *oldpath, const char *newpath);
  make_links: