aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--toys/cp.c89
1 files changed, 47 insertions, 42 deletions
diff --git a/toys/cp.c b/toys/cp.c
index 4809a778..0e2d04ed 100644
--- a/toys/cp.c
+++ b/toys/cp.c
@@ -11,9 +11,9 @@ USE_CP(NEWTOY(cp, "<2slrR+rdpa+d+p+rHLPif", TOYFLAG_BIN))
config CP
bool "cp"
- default n
+ default y
help
- usage: cp -fpr SOURCE... DEST
+ usage: cp -fiprdal SOURCE... DEST
Copy files from SOURCE to DEST. If more than one SOURCE, DEST must
be a directory.
@@ -22,6 +22,9 @@ config CP
-i interactive, prompt before overwriting existing DEST
-p preserve timestamps, ownership, and permissions
-r recurse into subdirectories (DEST must be a directory)
+ -d don't dereference symlinks
+ -a same as -dpr
+ -l hard link instead of copying
*/
#include "toys.h"
@@ -36,35 +39,24 @@ config CP
#define FLAG_d 128 // todo
#define FLAG_R 256
#define FLAG_r 512
-#define FLAG_s 1024 // todo
-#define FLAG_l 2048 // todo
+#define FLAG_l 1024 // todo
+#define FLAG_s 2048 // todo
DEFINE_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, struct stat *srcst, int depth)
+void cp_file(char *src, char *dst, struct stat *srcst)
{
- char *s = NULL;
int fdout = -1;
- // Trim path from name if necessary.
-
- if (!depth) s = strrchr(src, '/');
- if (s) s++;
- else s=src;
-
- // Determine location to create new file/directory at.
-
- if (TT.destisdir || depth) s = xmsprintf("%s/%s", TT.destname, s);
- else s = xstrdup(TT.destname);
-
// Copy directory or file to destination.
if (S_ISDIR(srcst->st_mode)) {
@@ -77,31 +69,31 @@ void cp_file(char *src, struct stat *srcst, int depth)
// we created. The closest we can do to closing this is make sure
// that what we open _is_ a directory rather than something else.
- if (mkdir(s, srcst->st_mode | 0200) || 0>(fdout=open(s, 0))
+ if (mkdir(dst, srcst->st_mode | 0200) || 0>(fdout=open(dst, 0))
|| fstat(fdout, &st2) || !S_ISDIR(st2.st_mode))
{
- perror_exit("mkdir '%s'", s);
+ perror_exit("mkdir '%s'", dst);
}
- } else if ((depth || (toys.optflags & FLAG_d)) && S_ISLNK(srcst->st_mode)) {
- struct stat st2;
+ } else if (TT.keep_symlinks && S_ISLNK(srcst->st_mode)) {
char *link = xreadlink(src);
// Note: -p currently has no effect on symlinks. How do you get a
// filehandle to them? O_NOFOLLOW causes the open to fail.
- if (!link || symlink(link, s)) perror_msg("link '%s'",s);
+ if (!link || symlink(link, dst)) perror_msg("link '%s'", dst);
free(link);
} else if (toys.optflags & FLAG_l) {
- if (link(src, s)) perror_msg("link '%s'");
+ if (link(src, dst)) perror_msg("link '%s'");
+ return;
} else {
int fdin, i;
fdin = xopen(src, O_RDONLY);
for (i=2 ; i; i--) {
- fdout = open(s, O_RDWR|O_CREAT|O_TRUNC, srcst->st_mode);
+ fdout = open(dst, O_RDWR|O_CREAT|O_TRUNC, srcst->st_mode);
if (fdout>=0 || !(toys.optflags & FLAG_f)) break;
- unlink(s);
+ unlink(dst);
}
- if (fdout<0) perror_exit("%s", s);
+ if (fdout<0) perror_exit("%s", dst);
xsendfile(fdin, fdout);
close(fdin);
}
@@ -116,11 +108,10 @@ void cp_file(char *src, struct stat *srcst, int depth)
fchown(fdout,srcst->st_uid, srcst->st_gid);
ut.actime = srcst->st_atime;
ut.modtime = srcst->st_mtime;
- utime(s, &ut);
+ utime(dst, &ut);
umask(mask);
}
xclose(fdout);
- free(s);
}
// Callback from dirtree_read() for each file/directory under a source dir.
@@ -128,21 +119,22 @@ void cp_file(char *src, struct stat *srcst, int depth)
int cp_node(char *path, struct dirtree *node)
{
char *s = path+strlen(path);
- struct dirtree *n = node;
- int depth = 0;
+ struct dirtree *n;
// Find appropriate chunk of path for destination.
- for (;;) {
- if (*(--s) == '/') {
- depth++;
- if (!n->parent) break;
- n = n->parent;
+ for (n = node;;n = n->parent) {
+ while (s!=path) {
+ if (*(--s)=='/') break;
}
+ if (!n) break;
}
- s++;
-
- cp_file(s, &(node->st), depth);
+ if (s != path) s++;
+
+ s = xmsprintf("%s/%s", TT.destname, s);
+ cp_file(path, s, &(node->st));
+ free(s);
+
return 0;
}
@@ -172,27 +164,40 @@ void cp_main(void)
for (i=0; i<toys.optc; i++) {
char *src = toys.optargs[i];
+ char *dst;
- // Skip nonexistent sources, or src==dest.
+ // Skip src==dest (should check inodes to catch "cp blah ./blah").
if (!strcmp(src, TT.destname)) continue;
- if ((toys.optflags & FLAG_d) ? lstat(src, &st) : stat(src, &st))
+
+ // Skip nonexistent sources.
+
+ TT.keep_symlinks = toys.optflags & FLAG_d;
+ if (TT.keep_symlinks ? lstat(src, &st) : stat(src, &st))
{
perror_msg("'%s'", src);
toys.exitval = 1;
continue;
}
+ dst = strrchr(src, '/');
+ if (dst) dst++;
+ else dst=src;
+
// Copy directory or file.
+ if (TT.destisdir) dst = xmsprintf("%s/%s", TT.destname, dst);
if (S_ISDIR(st.st_mode)) {
if (toys.optflags & FLAG_r) {
- cp_file(src, &st, 0);
+ cp_file(src, dst, &st);
+
+ TT.keep_symlinks++;
strncpy(toybuf, src, sizeof(toybuf)-1);
toybuf[sizeof(toybuf)-1]=0;
dirtree_read(toybuf, NULL, cp_node);
} else error_msg("Skipped dir '%s'", src);
- } else cp_file(src, &st, 0);
+ } else cp_file(src, dst, &st);
+ if (TT.destisdir) free(dst);
}
return;