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); }