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