summaryrefslogtreecommitdiff
path: root/appl/wm/ftree/cptree.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/wm/ftree/cptree.b')
-rw-r--r--appl/wm/ftree/cptree.b136
1 files changed, 136 insertions, 0 deletions
diff --git a/appl/wm/ftree/cptree.b b/appl/wm/ftree/cptree.b
new file mode 100644
index 00000000..5af59fff
--- /dev/null
+++ b/appl/wm/ftree/cptree.b
@@ -0,0 +1,136 @@
+implement Cptree;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "readdir.m";
+ readdir: Readdir;
+include "cptree.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ readdir = load Readdir Readdir->PATH;
+}
+
+Context: adt {
+ progressch: chan of string;
+ warningch: chan of (string, chan of int);
+ finishedch: chan of string;
+};
+
+# recursively copy file/directory f into directory d;
+# the name remains the same.
+copyproc(f, d: string, progressch: chan of string,
+ warningch: chan of (string, chan of int),
+ finishedch: chan of string)
+{
+ ctxt := ref Context(progressch, warningch, finishedch);
+ (fok, fstat) := sys->stat(f);
+ if (fok == -1)
+ error(ctxt, sys->sprint("cannot stat '%s': %r", f));
+ (dok, dstat) := sys->stat(d);
+ if (dok == -1)
+ error(ctxt, sys->sprint("cannot stat '%s': %r", d));
+ if ((dstat.mode & Sys->DMDIR) == 0)
+ error(ctxt, sys->sprint("'%s' is not a directory", d));
+ if (fstat.qid.path == dstat.qid.path)
+ error(ctxt, sys->sprint("'%s' and '%s' are identical", f, d));
+
+ c := d + "/" + fname(f);
+ (cok, cstat) := sys->stat(c);
+ if (cok == 0)
+ error(ctxt, sys->sprint("'%s' already exists", c));
+ rcopy(ctxt, f, ref fstat, c);
+ finishedch <-= nil;
+}
+
+rcopy(ctxt: ref Context, src: string, srcstat: ref Sys->Dir, dst: string)
+{
+ omode := Sys->OWRITE;
+ perm := srcstat.mode;
+ if (perm & Sys->DMDIR) {
+ omode = Sys->OREAD;
+ perm |= 8r300;
+ }
+
+ dstfd := sys->create(dst, omode, perm);
+ if (dstfd == nil) {
+ warning(ctxt, sys->sprint("cannot create '%s': %r", dst));
+ return;
+ }
+ if (srcstat.mode & Sys->DMDIR) {
+ (entries, n) := readdir->init(src, Readdir->NAME | Readdir->COMPACT);
+ if (n == -1)
+ warning(ctxt, sys->sprint("cannot read dir '%s': %r", src));
+ for (i := 0; i < len entries; i++) {
+ e := entries[i];
+ rcopy(ctxt, src + "/" + e.name, e, dst + "/" + e.name);
+ }
+ if (perm != srcstat.mode) {
+ (ok, nil) := sys->fstat(dstfd);
+ if (ok != -1) {
+ dststat := sys->nulldir;
+ dststat.mode = srcstat.mode;
+ sys->fwstat(dstfd, dststat);
+ }
+ }
+ } else {
+ srcfd := sys->open(src, Sys->OREAD);
+ if (srcfd == nil) {
+ sys->remove(dst);
+ warning(ctxt, sys->sprint("cannot open '%s': %r", src));
+ return;
+ }
+ ctxt.progressch <-= "copying " + src;
+ buf := array[Sys->ATOMICIO] of byte;
+ while ((n := sys->read(srcfd, buf, len buf)) > 0) {
+ if (sys->write(dstfd, buf, n) != n) {
+ sys->remove(dst);
+ warning(ctxt, sys->sprint("error writing '%s': %r", dst));
+ return;
+ }
+ }
+ if (n == -1) {
+ sys->remove(dst);
+ warning(ctxt, sys->sprint("error reading '%s': %r", src));
+ return;
+ }
+ }
+}
+
+warning(ctxt: ref Context, msg: string)
+{
+ r := chan of int;
+ ctxt.warningch <-= (msg, r);
+ if (!<-r)
+ exit;
+}
+
+error(ctxt: ref Context, msg: string)
+{
+ ctxt.finishedch <-= msg;
+ exit;
+}
+
+fname(f: string): string
+{
+ f = cleanname(f);
+ for (i := len f - 1; i >= 0; i--)
+ if (f[i] == '/')
+ break;
+ return f[i+1:];
+}
+
+cleanname(s: string): string
+{
+ t := "";
+ i := 0;
+ while (i < len s)
+ if ((t[len t] = s[i++]) == '/')
+ while (i < len s && s[i] == '/')
+ i++;
+ if (len t > 1 && t[len t - 1] == '/')
+ t = t[0:len t - 1];
+ return t;
+}