changeset 262:70f36d9c5387

Add first pass at cp, totally untested, unlikely to work yet. :)
author Rob Landley <rob@landley.net>
date Wed, 20 Feb 2008 01:47:56 -0600
parents ae693c7bf2f7
children 7c53152a483b
files lib/dirtree.c lib/lib.h scripts/test/cp.test toys/cp.c
diffstat 4 files changed, 152 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/lib/dirtree.c	Mon Feb 18 03:32:17 2008 -0600
+++ b/lib/dirtree.c	Wed Feb 20 01:47:56 2008 -0600
@@ -50,7 +50,7 @@
 // structures after use, and return NULL.
 
 struct dirtree *dirtree_read(char *path, struct dirtree *parent,
-					int (*callback)(struct dirtree *node))
+					int (*callback)(struct dirtree *node, int after))
 {
 	struct dirtree *dt = NULL, **ddt = &dt;
 	DIR *dir;
@@ -72,9 +72,10 @@
 		*ddt = dirtree_add_node(path);
 		if (!*ddt) continue;
 		(*ddt)->parent = parent;
-		if (callback) callback(*ddt);
+		if (callback) callback(*ddt, 0);
 		if (entry->d_type == DT_DIR)
 			(*ddt)->child = dirtree_read(path, *ddt, callback);
+		if (callback) callback(*ddt, 1);
 		if (callback) free(*ddt);
 		else ddt = &((*ddt)->next);
 		path[len]=0;
--- a/lib/lib.h	Mon Feb 18 03:32:17 2008 -0600
+++ b/lib/lib.h	Wed Feb 20 01:47:56 2008 -0600
@@ -41,7 +41,7 @@
 
 struct dirtree *dirtree_add_node(char *path);
 struct dirtree *dirtree_read(char *path, struct dirtree *parent,
-                    int (*callback)(struct dirtree *node));
+                    int (*callback)(struct dirtree *node, int after));
 
 // lib.c
 void xstrcpy(char *dest, char *src, size_t size);
--- a/scripts/test/cp.test	Mon Feb 18 03:32:17 2008 -0600
+++ b/scripts/test/cp.test	Wed Feb 20 01:47:56 2008 -0600
@@ -72,5 +72,7 @@
 rm one two random
 rm -rf dir
 
+# cp -r ../source destdir
+
 # cp file1 file2 dir
 # cp file1 missing file2 -> dir
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/cp.c	Wed Feb 20 01:47:56 2008 -0600
@@ -0,0 +1,146 @@
+/* vi: set sw=4 ts=4:
+ *
+ * cp.c - Copy files.
+ *
+ * Copyright 2008 Rob Landley <rob@landley.net>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/cp.html
+ *
+ * "R+ra+d+p+r"
+USE_HELLO(NEWTOY(hello, "<2rR+rdpa+d+p+rHLPif", TOYFLAG_BIN|TOYFLAG_UMASK))
+
+config CP
+	bool "cp"
+	default n
+	help
+	  usage: cp -f SOURCE... DEST
+
+	  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)
+*/
+
+#include "toys.h"
+
+#define FLAG_f 1
+#define FLAG_i 2
+#define FLAG_P 4
+#define FLAG_L 8
+#define FLAG_H 16
+#define FLAG_a 32
+#define FLAG_p 64
+#define FLAG_d 128
+#define FLAG_R 256
+#define FLAG_r 512
+
+DEFINE_GLOBALS(
+	char *destname;
+	int destisdir;
+)
+
+#define TT this.cp
+
+// Copy an individual file or directory to target.
+
+void cp_file(char *src, struct stat *srcst, int topdir, int again)
+{
+	char *s = NULL;
+	int mode = (toys.optflags & FLAG_p) ? 0700 : 0777;
+
+	// The second time we're called, chmod data.  We can't do this on
+	// the first pass because we may copy files into a read-only directory.
+	if (again) {
+		if (toys.optflags & FLAG_p) {
+			struct utimbuf ut;
+
+			// Inability to set these isn't fatal, some require root access.
+			// Can't do fchmod() etc here because -p works on mkdir, too.
+			chown(s, srcst->st_uid, srcst->st_gid);
+			chmod(s, srcst->st_mode);
+			ut.actime = srcst->st_atime;
+			ut.modtime = srcst->st_mtime;
+			utime(s, &ut);
+		}
+		return;
+	}
+
+	// Trim path from name if necessary.
+	if (topdir) s = strrchr(src, '/');
+	if (!s) s=src;
+
+	// Determine location to create new file/directory at.
+	if (TT.destisdir) s = xmsprintf(toybuf, "%s/%s", TT.destname, s);
+	else s = xstrdup(TT.destname);
+
+	// Copy directory or file to destination.
+	if (S_ISDIR(srcst->st_mode)) {
+		if (mkdir(s, mode)) perror_exit("mkdir '%s'", s);
+	} else {
+		int fdin, fdout;
+		fdin = xopen(src, O_RDONLY);
+		fdout = xcreate(s, O_CREAT|O_TRUNC, mode);
+		xsendfile(fdin, fdout);
+		close(fdin);
+		xclose(fdout);
+	}
+}
+
+// Callback from dirtree_read() for each file/directory under a source dir.
+
+int cp_node(struct dirtree *node, int after)
+{
+	cp_file(node->name, &(node->st), 0, after);
+	return 0;
+}
+
+void cp_main(void)
+{
+	struct stat st;
+	int i;
+
+	// Grab target argument.  (Guaranteed to be there due to "<2" above.)
+
+	TT.destname = toys.optargs[--toys.optc];
+
+	// If destination doesn't exist, are we ok with that?
+	if (stat(TT.destname, &st)) {
+		if (toys.optc>1) goto error_notdir;
+
+	// If destination exists...
+	} else {
+		if (S_ISDIR(st.st_mode)) TT.destisdir++;
+		else if (toys.optc > 1) goto error_notdir;
+	}
+
+	// Handle sources
+	for (i=0; i<toys.optc; i++) {
+		char *src = toys.optargs[i];
+
+		// Skip nonexistent sources...
+		if (!((toys.optflags & FLAG_d) ? lstat(src, &st) : stat(src, &st))) {
+			perror_msg("'%s'", src);
+			toys.exitval = 1;
+			continue;
+		}
+
+		// Copy directory or file.
+		if (S_ISDIR(st.st_mode)) {
+			if (toys.optflags & FLAG_r) {
+				cp_file(src, &st, 1, 0);
+				dirtree_read(src, NULL, cp_node);
+				cp_file(src, &st, 1, 1);
+			} else error_msg("Skipped dir '%s'", src);
+		} else {
+			cp_file(src, &st, 1, 0);
+			cp_file(src, &st, 1, 1);
+		}
+	}
+	return;
+
+error_notdir:
+	error_exit("'%s' isn't a directory", TT.destname);
+}