shithub: purgatorio

ref: d540dcf6834b4bec8a2d21f2fe95eccf49f97b03
dir: /appl/wm/ftree/cptree.b/

View raw version
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;
}