diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | b6c1cf6de79035f58b512f4400db458c8401379a (patch) | |
tree | 68979db37c85b499bc384e4ac337ed1424baab51 /tools/acp | |
download | build-b6c1cf6de79035f58b512f4400db458c8401379a.zip build-b6c1cf6de79035f58b512f4400db458c8401379a.tar.gz build-b6c1cf6de79035f58b512f4400db458c8401379a.tar.bz2 |
Initial Contribution
Diffstat (limited to 'tools/acp')
-rw-r--r-- | tools/acp/Android.mk | 26 | ||||
-rw-r--r-- | tools/acp/README | 40 | ||||
-rw-r--r-- | tools/acp/acp.c | 252 |
3 files changed, 318 insertions, 0 deletions
diff --git a/tools/acp/Android.mk b/tools/acp/Android.mk new file mode 100644 index 0000000..5e0e2e4 --- /dev/null +++ b/tools/acp/Android.mk @@ -0,0 +1,26 @@ +# Copyright 2005 The Android Open Source Project +# +# Custom version of cp. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + acp.c + +ifeq ($(HOST_OS),cygwin) +LOCAL_CFLAGS += -DWIN32_EXE +endif +ifeq ($(HOST_OS),darwin) +LOCAL_CFLAGS += -DMACOSX_RSRC +endif +ifeq ($(HOST_OS),linux) +endif + +LOCAL_STATIC_LIBRARIES := libhost +LOCAL_C_INCLUDES := build/libs/host/include +LOCAL_MODULE := acp +LOCAL_ACP_UNAVAILABLE := true + +include $(BUILD_HOST_EXECUTABLE) + diff --git a/tools/acp/README b/tools/acp/README new file mode 100644 index 0000000..a1809d9 --- /dev/null +++ b/tools/acp/README @@ -0,0 +1,40 @@ +README for Android "acp" Command + +The "cp" command was judged and found wanting. The issues are: + +Mac OS X: + - Uses the BSD cp, not the fancy GNU cp. It lacks the "-u" flag, which + only copies files if they are newer than the destination. This can + slow the build when copying lots of content. + - Doesn't take the "-d" flag, which causes symlinks to be copied as + links. This is the default behavior, so it's not all bad, but it + complains if you supply "-d". + +MinGW/Cygwin: + - Gets really weird when copying a file called "foo.exe", failing with + "cp: skipping file 'foo.exe', as it was replaced while being copied". + This only seems to happen when the source file is on an NFS/Samba + volume. "cp" works okay copying from local disk. + +Linux: + - On some systems it's possible to have microsecond-accurate timestamps + on an NFS volume, and non-microsecond timestamps on a local volume. + If you copy from NFS to local disk, your NFS files will always be + newer, because the local disk time stamp is truncated rather than + rounded up. This foils the "-u" flag if you also supply the "-p" flag + to preserve timestamps. + - The Darwin linker insists that ranlib be current. If you copy the + library, the time stamp no longer matches. Preserving the time + stamp is essential, so simply turning the "-p" flag off doesn't work. + +Futzing around these in make with GNU make functions is awkward at best. +It's easier and more reliable to write a cp command that works properly. + + +The "acp" command takes most of the standard flags, following the GNU +conventions. It adds a "-e" flag, used when copying executables around. +On most systems it is ignored, but on MinGW/Cygwin it allows "cp foo bar" +to work when what is actually meant is "cp foo.exe bar.exe". Unlike the +default Cygwin cp, "acp foo bar" will not find foo.exe unless you add +the "-e" flag, avoiding potential ambiguity. + diff --git a/tools/acp/acp.c b/tools/acp/acp.c new file mode 100644 index 0000000..eb1de1f --- /dev/null +++ b/tools/acp/acp.c @@ -0,0 +1,252 @@ +/* + * Copyright 2005 The Android Open Source Project + * + * Android "cp" replacement. + * + * The GNU/Linux "cp" uses O_LARGEFILE in its open() calls, utimes() instead + * of utime(), and getxattr()/setxattr() instead of chmod(). These are + * probably "better", but are non-portable, and not necessary for our + * purposes. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <getopt.h> +#include <dirent.h> +#include <fcntl.h> +#include <utime.h> +#include <limits.h> +#include <errno.h> +#include <assert.h> +#include <host/CopyFile.h> + +/*#define DEBUG_MSGS*/ +#ifdef DEBUG_MSGS +# define DBUG(x) printf x +#else +# define DBUG(x) ((void)0) +#endif + +#define FSSEP '/' /* filename separator char */ + + +/* + * Process the command-line file arguments. + * + * Returns 0 on success. + */ +int process(int argc, char* const argv[], unsigned int options) +{ + int retVal = 0; + int i, cc; + char* stripDest = NULL; + int stripDestLen; + struct stat destStat; + bool destMustBeDir = false; + struct stat sb; + + assert(argc >= 2); + + /* + * Check for and trim a trailing slash on the last arg. + * + * It's useful to be able to say "cp foo bar/" when you want to copy + * a single file into a directory. If you say "cp foo bar", and "bar" + * does not exist, it will create "bar", when what you really wanted + * was for the cp command to fail with "directory does not exist". + */ + stripDestLen = strlen(argv[argc-1]); + stripDest = malloc(stripDestLen+1); + memcpy(stripDest, argv[argc-1], stripDestLen+1); + if (stripDest[stripDestLen-1] == FSSEP) { + stripDest[--stripDestLen] = '\0'; + destMustBeDir = true; + } + + if (argc > 2) + destMustBeDir = true; + + /* + * Start with a quick check to ensure that, if we're expecting to copy + * to a directory, the target already exists and is actually a directory. + * It's okay if it's a symlink to a directory. + * + * If it turns out to be a directory, go ahead and raise the + * destMustBeDir flag so we do some path concatenation below. + */ + if (stat(stripDest, &sb) < 0) { + if (destMustBeDir) { + if (errno == ENOENT) + fprintf(stderr, + "acp: destination directory '%s' does not exist\n", + stripDest); + else + fprintf(stderr, "acp: unable to stat dest dir\n"); + retVal = 1; + goto bail; + } + } else { + if (S_ISDIR(sb.st_mode)) { + DBUG(("--- dest exists and is a dir, setting flag\n")); + destMustBeDir = true; + } else if (destMustBeDir) { + fprintf(stderr, + "acp: destination '%s' is not a directory\n", + stripDest); + retVal = 1; + goto bail; + } + } + + /* + * Copying files. + * + * Strip trailing slashes off. They shouldn't be there, but + * sometimes file completion will put them in for directories. + * + * The observed behavior of GNU and BSD cp is that they print warnings + * if something fails, but continue on. If any part fails, the command + * exits with an error status. + */ + for (i = 0; i < argc-1; i++) { + const char* srcName; + char* src; + char* dst; + int copyResult; + int srcLen; + + /* make a copy of the source name, and strip trailing '/' */ + srcLen = strlen(argv[i]); + src = malloc(srcLen+1); + memcpy(src, argv[i], srcLen+1); + + if (src[srcLen-1] == FSSEP) + src[--srcLen] = '\0'; + + /* find just the name part */ + srcName = strrchr(src, FSSEP); + if (srcName == NULL) { + srcName = src; + } else { + srcName++; + assert(*srcName != '\0'); + } + + if (destMustBeDir) { + /* concatenate dest dir and src name */ + int srcNameLen = strlen(srcName); + + dst = malloc(stripDestLen +1 + srcNameLen +1); + memcpy(dst, stripDest, stripDestLen); + dst[stripDestLen] = FSSEP; + memcpy(dst + stripDestLen+1, srcName, srcNameLen+1); + } else { + /* simple */ + dst = stripDest; + } + + /* + * Copy the source to the destination. + */ + copyResult = copyFile(src, dst, options); + + if (copyResult != 0) + retVal = 1; + + free(src); + if (dst != stripDest) + free(dst); + } + +bail: + free(stripDest); + return retVal; +} + +/* + * Set up the options. + */ +int main(int argc, char* const argv[]) +{ + bool wantUsage; + int ic, retVal; + int verboseLevel; + unsigned int options; + + verboseLevel = 0; + options = 0; + wantUsage = false; + + while (1) { + ic = getopt(argc, argv, "defprtuv"); + if (ic < 0) + break; + + switch (ic) { + case 'd': + options |= COPY_NO_DEREFERENCE; + break; + case 'e': + options |= COPY_TRY_EXE; + break; + case 'f': + options |= COPY_FORCE; + break; + case 'p': + options |= COPY_PERMISSIONS; + break; + case 't': + options |= COPY_TIMESTAMPS; + break; + case 'r': + options |= COPY_RECURSIVE; + break; + case 'u': + options |= COPY_UPDATE_ONLY; + break; + case 'v': + verboseLevel++; + break; + default: + fprintf(stderr, "Unexpected arg -%c\n", ic); + wantUsage = true; + break; + } + + if (wantUsage) + break; + } + + options |= verboseLevel & COPY_VERBOSE_MASK; + + if (optind == argc-1) { + fprintf(stderr, "acp: missing destination file\n"); + return 2; + } else if (optind+2 > argc) + wantUsage = true; + + if (wantUsage) { + fprintf(stderr, "Usage: acp [OPTION]... SOURCE DEST\n"); + fprintf(stderr, " or: acp [OPTION]... SOURCE... DIRECTORY\n"); + fprintf(stderr, "\nOptions:\n"); + fprintf(stderr, " -d never follow (dereference) symbolic links\n"); + fprintf(stderr, " -e if source file doesn't exist, try adding " + "'.exe' [Win32 only]\n"); + fprintf(stderr, " -f use force, removing existing file if it's " + "not writeable\n"); + fprintf(stderr, " -p preserve mode, ownership\n"); + fprintf(stderr, " -r recursive copy\n"); + fprintf(stderr, " -t preserve timestamps\n"); + fprintf(stderr, " -u update only: don't copy if dest is newer\n"); + fprintf(stderr, " -v verbose output (-vv is more verbose)\n"); + return 2; + } + + retVal = process(argc-optind, argv+optind, options); + DBUG(("EXIT: %d\n", retVal)); + return retVal; +} + |