summaryrefslogtreecommitdiffstats
path: root/tools/acp
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commitb6c1cf6de79035f58b512f4400db458c8401379a (patch)
tree68979db37c85b499bc384e4ac337ed1424baab51 /tools/acp
downloadbuild-b6c1cf6de79035f58b512f4400db458c8401379a.zip
build-b6c1cf6de79035f58b512f4400db458c8401379a.tar.gz
build-b6c1cf6de79035f58b512f4400db458c8401379a.tar.bz2
Initial Contribution
Diffstat (limited to 'tools/acp')
-rw-r--r--tools/acp/Android.mk26
-rw-r--r--tools/acp/README40
-rw-r--r--tools/acp/acp.c252
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;
+}
+