Home | History | Annotate | Download | only in acp
      1 /*
      2  * Copyright 2005 The Android Open Source Project
      3  *
      4  * Android "cp" replacement.
      5  *
      6  * The GNU/Linux "cp" uses O_LARGEFILE in its open() calls, utimes() instead
      7  * of utime(), and getxattr()/setxattr() instead of chmod().  These are
      8  * probably "better", but are non-portable, and not necessary for our
      9  * purposes.
     10  */
     11 #include <stdlib.h>
     12 #include <stdio.h>
     13 #include <string.h>
     14 #include <unistd.h>
     15 #include <sys/types.h>
     16 #include <sys/stat.h>
     17 #include <getopt.h>
     18 #include <dirent.h>
     19 #include <fcntl.h>
     20 #include <utime.h>
     21 #include <limits.h>
     22 #include <errno.h>
     23 #include <assert.h>
     24 #include <host/CopyFile.h>
     25 
     26 /*#define DEBUG_MSGS*/
     27 #ifdef DEBUG_MSGS
     28 # define DBUG(x) printf x
     29 #else
     30 # define DBUG(x) ((void)0)
     31 #endif
     32 
     33 #define FSSEP '/'       /* filename separator char */
     34 
     35 
     36 /*
     37  * Process the command-line file arguments.
     38  *
     39  * Returns 0 on success.
     40  */
     41 int process(int argc, char* const argv[], unsigned int options)
     42 {
     43     int retVal = 0;
     44     int i;
     45     char* stripDest = NULL;
     46     int stripDestLen;
     47     bool destMustBeDir = false;
     48     struct stat sb;
     49 
     50     assert(argc >= 2);
     51 
     52     /*
     53      * Check for and trim a trailing slash on the last arg.
     54      *
     55      * It's useful to be able to say "cp foo bar/" when you want to copy
     56      * a single file into a directory.  If you say "cp foo bar", and "bar"
     57      * does not exist, it will create "bar", when what you really wanted
     58      * was for the cp command to fail with "directory does not exist".
     59      */
     60     stripDestLen = strlen(argv[argc-1]);
     61     stripDest = malloc(stripDestLen+1);
     62     memcpy(stripDest, argv[argc-1], stripDestLen+1);
     63     if (stripDest[stripDestLen-1] == FSSEP) {
     64         stripDest[--stripDestLen] = '\0';
     65         destMustBeDir = true;
     66     }
     67 
     68     if (argc > 2)
     69         destMustBeDir = true;
     70 
     71     /*
     72      * Start with a quick check to ensure that, if we're expecting to copy
     73      * to a directory, the target already exists and is actually a directory.
     74      * It's okay if it's a symlink to a directory.
     75      *
     76      * If it turns out to be a directory, go ahead and raise the
     77      * destMustBeDir flag so we do some path concatenation below.
     78      */
     79     if (stat(stripDest, &sb) < 0) {
     80         if (destMustBeDir) {
     81             if (errno == ENOENT)
     82                 fprintf(stderr,
     83                     "acp: destination directory '%s' does not exist\n",
     84                     stripDest);
     85             else
     86                 fprintf(stderr, "acp: unable to stat dest dir\n");
     87             retVal = 1;
     88             goto bail;
     89         }
     90     } else {
     91         if (S_ISDIR(sb.st_mode)) {
     92             DBUG(("--- dest exists and is a dir, setting flag\n"));
     93             destMustBeDir = true;
     94         } else if (destMustBeDir) {
     95             fprintf(stderr,
     96                 "acp: destination '%s' is not a directory\n",
     97                 stripDest);
     98             retVal = 1;
     99             goto bail;
    100         }
    101     }
    102 
    103     /*
    104      * Copying files.
    105      *
    106      * Strip trailing slashes off.  They shouldn't be there, but
    107      * sometimes file completion will put them in for directories.
    108      *
    109      * The observed behavior of GNU and BSD cp is that they print warnings
    110      * if something fails, but continue on.  If any part fails, the command
    111      * exits with an error status.
    112      */
    113     for (i = 0; i < argc-1; i++) {
    114         const char* srcName;
    115         char* src;
    116         char* dst;
    117         int copyResult;
    118         int srcLen;
    119 
    120         /* make a copy of the source name, and strip trailing '/' */
    121         srcLen = strlen(argv[i]);
    122         src = malloc(srcLen+1);
    123         memcpy(src, argv[i], srcLen+1);
    124 
    125         if (src[srcLen-1] == FSSEP)
    126             src[--srcLen] = '\0';
    127 
    128         /* find just the name part */
    129         srcName = strrchr(src, FSSEP);
    130         if (srcName == NULL) {
    131             srcName = src;
    132         } else {
    133             srcName++;
    134             assert(*srcName != '\0');
    135         }
    136 
    137         if (destMustBeDir) {
    138             /* concatenate dest dir and src name */
    139             int srcNameLen = strlen(srcName);
    140 
    141             dst = malloc(stripDestLen +1 + srcNameLen +1);
    142             memcpy(dst, stripDest, stripDestLen);
    143             dst[stripDestLen] = FSSEP;
    144             memcpy(dst + stripDestLen+1, srcName, srcNameLen+1);
    145         } else {
    146             /* simple */
    147             dst = stripDest;
    148         }
    149 
    150         /*
    151          * Copy the source to the destination.
    152          */
    153         copyResult = copyFile(src, dst, options);
    154 
    155         if (copyResult != 0)
    156             retVal = 1;
    157 
    158         free(src);
    159         if (dst != stripDest)
    160             free(dst);
    161     }
    162 
    163 bail:
    164     free(stripDest);
    165     return retVal;
    166 }
    167 
    168 /*
    169  * Set up the options.
    170  */
    171 int main(int argc, char* const argv[])
    172 {
    173     bool wantUsage;
    174     int ic, retVal;
    175     int verboseLevel;
    176     unsigned int options;
    177 
    178     verboseLevel = 0;
    179     options = 0;
    180     wantUsage = false;
    181 
    182     while (1) {
    183         ic = getopt(argc, argv, "defprtuv");
    184         if (ic < 0)
    185             break;
    186 
    187         switch (ic) {
    188             case 'd':
    189                 options |= COPY_NO_DEREFERENCE;
    190                 break;
    191             case 'e':
    192                 options |= COPY_TRY_EXE;
    193                 break;
    194             case 'f':
    195                 options |= COPY_FORCE;
    196                 break;
    197             case 'p':
    198                 options |= COPY_PERMISSIONS;
    199                 break;
    200             case 't':
    201                 options |= COPY_TIMESTAMPS;
    202                 break;
    203             case 'r':
    204                 options |= COPY_RECURSIVE;
    205                 break;
    206             case 'u':
    207                 options |= COPY_UPDATE_ONLY;
    208                 break;
    209             case 'v':
    210                 verboseLevel++;
    211                 break;
    212             default:
    213                 fprintf(stderr, "Unexpected arg -%c\n", ic);
    214                 wantUsage = true;
    215                 break;
    216         }
    217 
    218         if (wantUsage)
    219             break;
    220     }
    221 
    222     options |= verboseLevel & COPY_VERBOSE_MASK;
    223 
    224     if (optind == argc-1) {
    225         fprintf(stderr, "acp: missing destination file\n");
    226         return 2;
    227     } else if (optind+2 > argc)
    228         wantUsage = true;
    229 
    230     if (wantUsage) {
    231         fprintf(stderr, "Usage: acp [OPTION]... SOURCE DEST\n");
    232         fprintf(stderr, "  or:  acp [OPTION]... SOURCE... DIRECTORY\n");
    233         fprintf(stderr, "\nOptions:\n");
    234         fprintf(stderr, "  -d  never follow (dereference) symbolic links\n");
    235         fprintf(stderr, "  -e  if source file doesn't exist, try adding "
    236                         "'.exe' [Win32 only]\n");
    237         fprintf(stderr, "  -f  use force, removing existing file if it's "
    238                         "not writeable\n");
    239         fprintf(stderr, "  -p  preserve mode, ownership\n");
    240         fprintf(stderr, "  -r  recursive copy\n");
    241         fprintf(stderr, "  -t  preserve timestamps\n");
    242         fprintf(stderr, "  -u  update only: don't copy if dest is newer\n");
    243         fprintf(stderr, "  -v  verbose output (-vv is more verbose)\n");
    244         return 2;
    245     }
    246 
    247     retVal = process(argc-optind, argv+optind, options);
    248     DBUG(("EXIT: %d\n", retVal));
    249     return retVal;
    250 }
    251 
    252