diff options
Diffstat (limited to 'appl/cmd/fcp.b')
| -rw-r--r-- | appl/cmd/fcp.b | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/appl/cmd/fcp.b b/appl/cmd/fcp.b new file mode 100644 index 00000000..0bc520e4 --- /dev/null +++ b/appl/cmd/fcp.b @@ -0,0 +1,312 @@ +implement Fcp; + +include "sys.m"; + sys: Sys; +include "draw.m"; +include "arg.m"; +include "readdir.m"; + readdir: Readdir; + +Fcp: module +{ + init: fn(nil: ref Draw->Context, argv: list of string); +}; + +stderr: ref Sys->FD; +errors := 0; + +fdc: chan of (ref Sys->FD, ref Sys->FD); + +init(nil: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + stderr = sys->fildes(2); + + arg := load Arg Arg->PATH; + if (arg == nil) { + sys->fprint(stderr, "fcp: cannot load %s: %r\n", Arg->PATH); + raise "fail:bad module"; + } + recursive := 0; + nreaders := nwriters := 8; + arg->init(argv); + arg->setusage("\tfcp [-r] [-R nproc] [-W nproc] src target\n\tfcp [-r] [-R nproc] [-W nproc] src ... directory"); + while ((opt := arg->opt()) != 0) { + case opt { + 'R' => + nreaders = int arg->earg(); + 'W' => + nwriters = int arg->earg(); + 'r' => + recursive = 1; + * => + arg->usage(); + } + } + if(nreaders < 1 || nwriters < 1) + arg->usage(); + if(nreaders > 1 || nwriters > 1){ + fdc = chan of (ref Sys->FD, ref Sys->FD); + spawn mstream(fdc, Sys->ATOMICIO, nreaders, nwriters); + } + argv = arg->argv(); + argc := len argv; + if (argc < 2) + arg->usage(); + arg = nil; + + dst: string; + for (t := argv; 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, "fcp: %s not a directory\n", dst); + raise "fail:error"; + } + if (recursive) + cpdir(argv, dst); + else { + for (; tl argv != nil; argv = tl argv) { + if (todir) + cp(hd argv, dst, basename(hd argv)); + else + cp(hd argv, dst, nil); + } + } + if(fdc != nil) + fdc <-= (nil, 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) +{ + ok: int; + ds, 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 && + ds.qid.path == dd.qid.path && + ds.dev == dd.dev && + ds.dtype == dd.dtype) { + 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); + if (dfd == nil) { + warning(sys->sprint("cannot create %s: %r", dst)); + return; + } + copy(sfd, dfd, src, dst); +} + +mkdir(d: string, mode: int): int +{ + dfd := sys->create(d, sys->OREAD, sys->DMDIR | mode); + if (dfd == nil) { + warning(sys->sprint("cannot make directory %s: %r", d)); + return -1; + } + return 0; +} + +copy(sfd, dfd: ref Sys->FD, src, dst: string): int +{ + if(fdc != nil){ + fdc <-= (sfd, dfd); + return 0; + } + buf := array[Sys->ATOMICIO] of byte; + for (;;) { + r := sys->read(sfd, buf, Sys->ATOMICIO); + if (r < 0) { + warning(sys->sprint("error reading %s: %r", src)); + return -1; + } + if (r == 0) + return 0; + if (sys->write(dfd, buf, r) != r) { + warning(sys->sprint("error writing %s: %r", dst)); + return -1; + } + } +} + +cpdir(argv: list of string, dst: string) +{ + readdir = load Readdir Readdir->PATH; + if (readdir == nil) { + sys->fprint(stderr, "fcp: 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 argv != nil; argv = tl argv) { + ds: Sys->Dir; + src := hd argv; + (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 argv, dst, basename(hd argv)); + } else if (dexists) { + if (ds.qid.path==dd.qid.path && + ds.dev==dd.dev && + ds.dtype==dd.dtype) { + warning("cannot copy " + src + " into itself"); + continue; + } + copydir(src, dst + "/" + basename(src), ds.mode); + } else { + copydir(src, dst, ds.mode); + } + } +} + +copydir(src, dst: string, srcmode: int) +{ + (ok, nil) := sys->stat(dst); + if (ok != -1) { + warning("cannot copy " + src + " onto another directory"); + return; + } + tmode := srcmode | 8r777; # Fix for Nt + if (mkdir(dst, tmode) == -1) + 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.mode); + } + chmod(dst, srcmode); +} + +# 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){ + sav := hd c; + if(dir.qid.path==sav.qid.path && + dir.dtype==sav.dtype && dir.dev==sav.dev) + return 1; + } + cache[int dir.qid.path&(NCACHE-1)] = dir :: savlist; + return 0; +} + +warning(e: string) +{ + sys->fprint(stderr, "fcp: %s\n", e); + errors++; +} + +chmod(s: string, mode: int): int +{ + (ok, d) := sys->stat(s); + if (ok < 0) + return -1; + + if(d.mode == mode) + return 0; + d = sys->nulldir; + d.mode = mode; + if (sys->wstat(s, d) < 0) { + warning(sys->sprint("cannot wstat %s: %r", s)); + return -1; + } + return 0; +} + +mstream(fdc: chan of (ref Sys->FD, ref Sys->FD), bufsize: int, nin, nout: int) +{ + inc := chan of (ref Sys->FD, big, int, ref Sys->FD); + outc := chan of (ref Sys->FD, big, array of byte); + for(i := 0; i < nin; i++) + spawn readproc(inc, outc); + for(i = 0; i < nout; i++) + spawn writeproc(outc); + while(((src, dst) := <-fdc).t0 != nil){ + (ok, stat) := sys->fstat(src); + if(ok == -1) + continue; + tot := stat.length; + o := big 0; + while((n := tot - o) > big 0){ + if(n < big bufsize) + inc <-= (src, o, int n, dst); + else + inc <-= (src, o, bufsize, dst); + o += big bufsize; + } + } + for(i = 0; i < nin; i++) + inc <-= (nil, big 0, 0, nil); + for(i = 0; i < nout; i++) + outc <-= (nil, big 0, nil); +} + +readproc(inc: chan of (ref Sys->FD, big, int, ref Sys->FD), outc: chan of (ref Sys->FD, big, array of byte)) +{ + buf: array of byte; + while(((src, o, nb, dst) := <-inc).t0 != nil){ + if(len buf < nb) + buf = array[nb*2] of byte; + n := sys->pread(src, buf, nb, o); + if(n > 0){ + outc <-= (dst, o, buf[0:n]); + buf = buf[n:]; + } + } +} + +writeproc(outc: chan of (ref Sys->FD, big, array of byte)) +{ + while(((dst, o, buf) := <-outc).t0 != nil) + sys->pwrite(dst, buf, len buf, o); +} |
