aboutsummaryrefslogtreecommitdiff
path: root/toys/posix/cp.c
diff options
context:
space:
mode:
Diffstat (limited to 'toys/posix/cp.c')
-rw-r--r--toys/posix/cp.c107
1 files changed, 55 insertions, 52 deletions
diff --git a/toys/posix/cp.c b/toys/posix/cp.c
index 232d8fab..b7834e86 100644
--- a/toys/posix/cp.c
+++ b/toys/posix/cp.c
@@ -6,53 +6,53 @@
*
* See http://opengroup.org/onlinepubs/9699919799/utilities/cp.html
*
- * "R+ra+d+p+r"
-USE_CP(NEWTOY(cp, "<2vslrRdpaHLPif", TOYFLAG_BIN))
+ * TODO: "R+ra+d+p+r" sHLPR
+
+USE_CP(NEWTOY(cp, "<2"USE_CP_MORE("rdavsl")"RHLPfip", TOYFLAG_BIN))
config CP
bool "cp (broken by dirtree changes)"
default n
help
- usage: cp -fiprdal SOURCE... DEST
+ usage: cp [-fipRHLP] SOURCE... DEST
- Copy files from SOURCE to DEST. If more than one SOURCE, DEST must
- be a directory.
+ Copy files from SOURCE to DEST. If more than one SOURCE, DEST must
+ be a directory.
-f force copy by deleting destination file
-i interactive, prompt before overwriting existing DEST
-p preserve timestamps, ownership, and permissions
- -r recurse into subdirectories (DEST must be a directory)
+ -R recurse into subdirectories (DEST must be a directory)
+ -H Follow symlinks listed on command line
+ -L Follow all symlinks
+ -P Do not follow symlinks [default]
+
+config CP_MORE
+ bool "cp -rdavsl options"
+ default y
+ depends on CP
+ help
+ usage: cp [-rdavsl]
+
+ -r synonym for -R
-d don't dereference symlinks
-a same as -dpr
- -l hard link instead of copying
+ -l hard link instead of copy
+ -s symlink instead of copy
-v verbose
*/
+#define FOR_cp
#include "toys.h"
-#define FLAG_f 1
-#define FLAG_i 2
-#define FLAG_P 4 // todo
-#define FLAG_L 8 // todo
-#define FLAG_H 16 // todo
-#define FLAG_a 32
-#define FLAG_p 64
-#define FLAG_d 128 // todo
-#define FLAG_R 256
-#define FLAG_r 512
-#define FLAG_l 1024 // todo
-#define FLAG_s 2048 // todo
-#define FLAG_v 4098
-
-DEFINE_GLOBALS(
+// TODO: PLHlsd
+
+GLOBALS(
char *destname;
int destisdir;
- int destisnew;
int keep_symlinks;
)
-#define TT this.cp
-
// Copy an individual file or directory to target.
void cp_file(char *src, char *dst, struct stat *srcst)
@@ -156,42 +156,36 @@ int cp_node(struct dirtree *node)
void cp_main(void)
{
- struct stat st;
+ char *dpath = NULL;
+ struct stat st, std;
int i;
- // Grab target argument. (Guaranteed to be there due to "<2" above.)
-
- TT.destname = toys.optargs[--toys.optc];
+ // Identify destination
- // If destination doesn't exist, are we ok with that?
+ if (!stat(TT.destname, &std) && S_ISDIR(std.st_mode)) TT.destisdir++;
+ else if (toys.optc>1) error_exit("'%s' not directory", TT.destname);
- if (stat(TT.destname, &st)) {
- if (toys.optc>1) goto error_notdir;
- TT.destisnew++;
+ // TODO: This is too early: we haven't created it yet if we need to
+ if (toys.optflags & (FLAG_R|FLAG_r|FLAG_a))
+ dpath = realpath(TT.destname = toys.optargs[--toys.optc], NULL);
- // If destination exists...
-
- } else {
- if (S_ISDIR(st.st_mode)) TT.destisdir++;
- else if (toys.optc > 1) goto error_notdir;
- }
-
- // Handle sources
+ // Loop through sources
for (i=0; i<toys.optc; i++) {
- char *src = toys.optargs[i];
- char *dst;
+ char *dst, *src = toys.optargs[i];
// Skip src==dest (TODO check inodes to catch "cp blah ./blah").
- if (!strcmp(src, TT.destname)) continue;
+ if (!strncmp(src, TT.destname)) continue;
// Skip nonexistent sources.
TT.keep_symlinks = toys.optflags & (FLAG_d|FLAG_a);
- if (TT.keep_symlinks ? lstat(src, &st) : stat(src, &st))
+ if (TT.keep_symlinks ? lstat(src, &st) : stat(src, &st)
+ || (st.st_dev = dst.st_dev && st.st_ino == dst.dst_ino))
{
- perror_msg("'%s'", src);
+objection:
+ perror_msg("bad '%s'", src);
toys.exitval = 1;
continue;
}
@@ -199,26 +193,35 @@ void cp_main(void)
// Copy directory or file.
if (TT.destisdir) {
+ char *s;
+
+ // Catch "cp -R .. ." and friends that would go on forever
+ if (dpath && (s = realpath(src, NULL)) {
+ int i = strlen(s);
+ i = (!strncmp(s, dst, i) && (!s[i] || s[i]=='/'));
+ free(s);
+
+ if (i) goto objection;
+ }
+
+ // Create destination filename within directory
dst = strrchr(src, '/');
if (dst) dst++;
else dst=src;
dst = xmsprintf("%s/%s", TT.destname, dst);
} else dst = TT.destname;
+
if (S_ISDIR(st.st_mode)) {
if (toys.optflags & (FLAG_r|FLAG_R|FLAG_a)) {
cp_file(src, dst, &st);
TT.keep_symlinks++;
- strncpy(toybuf, src, sizeof(toybuf)-1);
- toybuf[sizeof(toybuf)-1]=0;
- dirtree_read(toybuf, cp_node);
+ dirtree_read(src, cp_node);
} else error_msg("Skipped dir '%s'", src);
} else cp_file(src, dst, &st);
if (TT.destisdir) free(dst);
}
+ if (CFG_TOYBOX_FREE) free(dpath);
return;
-
-error_notdir:
- error_exit("'%s' isn't a directory", TT.destname);
}