shithub: mc

ref: d60e706a5f499863320b9359519438c62a4df303
dir: /lib/std/dial+plan9.myr/

View raw version
use sys

use "alloc.use"
use "die.use"
use "fmt.use"
use "option.use"
use "pathjoin.use"
use "result.use"
use "sleq.use"
use "strfind.use"
use "strstrip.use"
use "syswrap.use"
use "utf.use"


pkg std =
	const dial	: (dialstr : byte[:] -> result(fd, byte[:]))
;;

const Maxpath = 512

const dial = {str
	var netdir, proto, rem

	(netdir, proto, rem) = parsedial(str)
	if netdir.len != 0
		-> csdial(netdir, proto, rem)
	;;

	match csdial("/net", proto, rem)
	| `Ok fd:	-> `Ok fd
	| `Fail m:
		-> csdial("/net.alt", proto, rem)
	;;
}

const csdial = {netdir, proto, rem
	var dir, clone, addr, csaddr
	var ret, csfd, n
	var buf	: byte[Maxpath]

	/* Try using the connection server */
	dir = fmt("{}/cs", netdir)
	csfd = open(dir, Ordwr)
	if csfd < 0
		clone = fmt("{}/{}/clone", netdir, proto)
		ret = call(clone, rem, netdir)
		slfree(clone)
		if ret == -1
			-> `Fail "unable to dial without cs"
		else
			-> `Ok ret
		;;
	;;
	slfree(dir)

	csaddr = fmt("{}!{}", proto, rem)
	if write(csfd, csaddr) < 0
		close(csfd)
		-> `Fail "couldn't blah cs"
	;;
	slfree(csaddr)

	seek(csfd, 0, 0)
	while true
		n = read(csfd, buf[:])
		if n <= 0
			break
		;;

		match strfind(buf[:n], " ")
		| `None:	continue
		| `Some i:
			clone = buf[:i]
			addr = buf[i+1:n]
		;;

		ret = call(clone, addr, netdir)
		if ret >= 0
			break
		;;
	;;

	close(csfd)
	if ret < 0
		-> `Fail "unable to dial"
	;;
	-> `Ok ret
}

const call = {clone, addr, netdir
	var namebuf : byte[Maxpath]
	var databuf : byte[Maxpath]
	var name, base, dpath
	var cfd, datafd
	var c, n

	datafd = -1
	c = nsclonestr(clone, netdir)
	cfd = open(c, Ordwr)
	if cfd < 0
		goto cleanup
	;;

	n = read(cfd, namebuf[:])
	if n < 0
		goto cleanup
	;;
	fput(cfd, "connect {}", addr)
	name = strstrip(namebuf[:n])
	match strrfind(c, "/")
	| `None:	die("there should be a '/' here\n")
	| `Some i:	base = c[:i]
	;;
	dpath = bfmt(databuf[:], "{}/{}/data", base, name)
	datafd = open(dpath, Ordwr)
:cleanup
	close(cfd)
	slfree(c)
	-> datafd
}

const nsclonestr = {clone, netdir
	if decode(clone) == '#' || decode(clone) == '/'
		match std.strfind(clone[1:], "/")
		| `Some i:	clone = clone[i+1:]
		| `None:	/* nothing */
		;;
	;;
	-> pathcat(netdir, clone)
}

const parsedial = {str
	var netdir, proto, rem, hd, tl

	netdir=""
	proto = ""
	rem = ""
	match strfind(str, "!")
	| `None:
		proto = "net"
		rem = str
	| `Some sep:
		hd = str[:sep]
		tl = str[sep+1:]
		if decode(hd) == '#' || decode(hd) == '/'
			match strrfind(hd, "/")
			| `Some idx:
				netdir = hd[:idx]
				proto = hd[idx+1:]
			| `None:
				netdir = ""
				proto = hd
			;;
		else
			netdir = ""
			proto = hd

		;;
		rem = tl
	;;

	-> (netdir, proto, rem)
}