changeset 269:7b3d9594bf9c

Fix -r logic, it needs both source and dest paths explicitly stated.
author Rob Landley <rob@landley.net>
date Mon, 24 Mar 2008 05:14:37 -0500
parents 56a29fb3c9f6
children e9f75ffd3200
files toys/cp.c
diffstat 1 files changed, 47 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- a/toys/cp.c	Mon Mar 24 00:32:25 2008 -0500
+++ b/toys/cp.c	Mon Mar 24 05:14:37 2008 -0500
@@ -11,9 +11,9 @@
 
 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 @@
 		-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 @@
 #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 @@
 		// 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 @@
 		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 @@
 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 @@
 
 	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;