summaryrefslogtreecommitdiff
path: root/appl/cmd/cp.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/cmd/cp.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/cmd/cp.b')
-rw-r--r--appl/cmd/cp.b237
1 files changed, 237 insertions, 0 deletions
diff --git a/appl/cmd/cp.b b/appl/cmd/cp.b
new file mode 100644
index 00000000..23c49a15
--- /dev/null
+++ b/appl/cmd/cp.b
@@ -0,0 +1,237 @@
+implement Cp;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+include "arg.m";
+
+include "readdir.m";
+ readdir: Readdir;
+
+Cp: module
+{
+ init: fn(nil: ref Draw->Context, args: list of string);
+};
+
+stderr: ref Sys->FD;
+errors := 0;
+gflag := 0;
+uflag := 0;
+xflag := 0;
+
+init(nil: ref Draw->Context, args: list of string)
+{
+ sys = load Sys Sys->PATH;
+ stderr = sys->fildes(2);
+
+ arg := load Arg Arg->PATH;
+ recursive := 0;
+ arg->init(args);
+ arg->setusage("\tcp [-gux] src target\n\tcp [-r] [-gux] src ... directory");
+ while((opt := arg->opt()) != 0)
+ case opt {
+ 'r' => recursive = 1;
+ 'g' => gflag = 1;
+ 'u' => uflag = gflag = 1;
+ 'x' => xflag = 1;
+ * => arg->usage();
+ }
+ args = arg->argv();
+ argc := len args;
+ if(argc < 2)
+ arg->usage();
+ arg = nil;
+
+ dst: string;
+ for(t := args; t != nil; t = tl t)
+ dst = hd t;
+
+ (ok, dir) := sys->stat(dst);
+ todir := (ok != -1 && (dir.mode & Sys->DMDIR));
+ if(argc > 2 && !todir){
+ sys->fprint(stderr, "cp: %s not a directory\n", dst);
+ raise "fail:error";
+ }
+ if(recursive)
+ cpdir(args, dst);
+ else{
+ for(; tl args != nil; args = tl args){
+ if(todir)
+ cp(hd args, dst, basename(hd args));
+ else
+ cp(hd args, dst, nil);
+ }
+ }
+ if(errors)
+ raise "fail:error";
+}
+
+basename(s: string): string
+{
+ for((nil, ls) := sys->tokenize(s, "/"); ls != nil; ls = tl ls)
+ s = hd ls;
+ return s;
+}
+
+cp(src, dst: string, newname: string)
+{
+ dd: Sys->Dir;
+
+ if(newname != nil)
+ dst += "/" + newname;
+ (ok, ds) := sys->stat(src);
+ if(ok < 0){
+ warning(sys->sprint("%s: %r", src));
+ return;
+ }
+ if(ds.mode & Sys->DMDIR){
+ warning(src + " is a directory");
+ return;
+ }
+ (ok, dd) = sys->stat(dst);
+ if(ok != -1 && samefile(ds, dd)){
+ warning(src + " and " + dst + " are the same file");
+ return;
+ }
+ sfd := sys->open(src, Sys->OREAD);
+ if(sfd == nil){
+ warning(sys->sprint("cannot open %s: %r", src));
+ return;
+ }
+ dfd := sys->create(dst, Sys->OWRITE, ds.mode & 8r777);
+ if(dfd == nil){
+ warning(sys->sprint("cannot create %s: %r", dst));
+ return;
+ }
+ if(copy(sfd, dfd, src, dst)!=0)
+ return;
+ if(wstat(dfd, ds, 0) < 0)
+ warning(sys->sprint("can't wstat %s: %r", src));
+}
+
+copy(sfd, dfd: ref Sys->FD, src, dst: string): int
+{
+ buf := array[Sys->ATOMICIO] of byte;
+ while((r := sys->read(sfd, buf, len buf)) > 0){
+ if(sys->write(dfd, buf, r) != r){
+ warning(sys->sprint("error writing %s: %r", dst));
+ return -1;
+ }
+ }
+ if(r < 0){
+ warning(sys->sprint("error reading %s: %r", src));
+ return -1;
+ }
+ return 0;
+}
+
+cpdir(args: list of string, dst: string)
+{
+ readdir = load Readdir Readdir->PATH;
+ if(readdir == nil){
+ sys->fprint(stderr, "cp: cannot load %s: %r\n", Readdir->PATH);
+ raise "fail:bad module";
+ }
+ cache = array[NCACHE] of list of ref Sys->Dir;
+ dexists := 0;
+ (ok, dd) := sys->stat(dst);
+ # destination file exists
+ if(ok != -1){
+ if((dd.mode & Sys->DMDIR) == 0){
+ warning(dst + ": destination not a directory");
+ return;
+ }
+ dexists = 1;
+ }
+ for(; tl args != nil; args = tl args){
+ ds: Sys->Dir;
+ src := hd args;
+ (ok, ds) = sys->stat(src);
+ if(ok < 0){
+ warning(sys->sprint("can't stat %s: %r", src));
+ continue;
+ }
+ if((ds.mode & Sys->DMDIR) == 0){
+ cp(hd args, dst, basename(hd args));
+ } else if(dexists){
+ if(samefile(ds, dd)){
+ warning("cannot copy " + src + " into itself");
+ continue;
+ }
+ copydir(src, dst + "/" + basename(src), ds);
+ } else
+ copydir(src, dst, ds);
+ }
+}
+
+copydir(src, dst: string, srcd: Sys->Dir)
+{
+ (ok, nil) := sys->stat(dst);
+ if(ok != -1){
+ warning("cannot copy " + src + " onto another directory");
+ return;
+ }
+ tmode := srcd.mode | 8r777; # Fix for Nt
+ dfd := sys->create(dst, Sys->OREAD, Sys->DMDIR | tmode);
+ if(dfd == nil){
+ warning(sys->sprint("cannot make directory %s: %r", dst));
+ return;
+ }
+ (entries, n) := readdir->init(src, Readdir->COMPACT);
+ for(i := 0; i < n; i++){
+ e := entries[i];
+ path := src + "/" + e.name;
+ if((e.mode & Sys->DMDIR) == 0)
+ cp(path, dst, e.name);
+ else if(seen(e))
+ warning(path + ": directory loop found");
+ else
+ copydir(path, dst + "/" + e.name, *e);
+ }
+ if(wstat(dfd, srcd, 1) < 0)
+ warning(sys->sprint("can't wstat %s: %r", dst));
+}
+
+wstat(dfd: ref Sys->FD, ds: Sys->Dir, mflag: int): int
+{
+ if(!xflag && !gflag && !uflag && !mflag)
+ return 0;
+ d := sys->nulldir;
+ if(xflag)
+ d.mtime = ds.mtime;
+ if(xflag || mflag)
+ d.mode = ds.mode;
+ if(uflag)
+ d.uid = ds.uid;
+ if(gflag)
+ d.gid = ds.gid;
+ return sys->fwstat(dfd, d);
+}
+
+samefile(d1: Sys->Dir, d2: Sys->Dir): int
+{
+ return d1.dtype == d2.dtype && d1.dev == d2.dev &&
+ d1.qid.qtype == d2.qid.qtype && d1.qid.path == d2.qid.path &&
+ d1.qid.vers == d2.qid.vers;
+}
+
+# Avoid loops in tangled namespaces. (from du.b)
+NCACHE: con 64; # must be power of two
+cache: array of list of ref sys->Dir;
+
+seen(dir: ref sys->Dir): int
+{
+ savlist := cache[int dir.qid.path&(NCACHE-1)];
+ for(c := savlist; c!=nil; c = tl c)
+ if(samefile(*dir, *hd c))
+ return 1;
+ cache[int dir.qid.path&(NCACHE-1)] = dir :: savlist;
+ return 0;
+}
+
+warning(e: string)
+{
+ sys->fprint(stderr, "cp: %s\n", e);
+ errors++;
+}