summaryrefslogtreecommitdiff
path: root/appl/cmd/fcp.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/fcp.b')
-rw-r--r--appl/cmd/fcp.b312
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);
+}