shithub: mc

ref: 81faf1634871ae20739df2d5eb6525f47d14da77
dir: /lib/std/syswrap+plan9.myr/

View raw version
use sys

use "option"
use "types"
use "errno"
use "result"
use "cstrconv"
use "strfind"
use "getint"

pkg std =
	type fd		= sys.fd
	type pid	= sys.pid
	type fdopt	= sys.fdopt
	type whence = int64

	type sysinfo = struct
		system	: byte[:]
		version	: byte[:]
		release	: byte[:]
		arch	: byte[:]
	;;

	const Seekset	: whence = 0
	const Seekcur	: whence = 1
	const Seekend	: whence = 2

	const Failmem	: byte#	= -1 castto(byte#)

	const Ordonly  	: fdopt = sys.Ordonly	castto(fdopt)
	const Owronly  	: fdopt = sys.Owronly	castto(fdopt)
	const Ordwr    	: fdopt = sys.Ordwr	castto(fdopt)
	const Otrunc   	: fdopt = sys.Otrunc	castto(fdopt)
	const Ocreat   	: fdopt = 0x1000000	/* emulated by redirecting to creat(). */
	const Oappend  	: fdopt = 0x2000000	/* emulated by seeking to EOF */
	const Odir	: fdopt = 0x0	/* no-op on plan9 */

	/* fd stuff */
	const open	: (path : byte[:], opts : fdopt -> result(fd, errno))
	const openmode	: (path : byte[:], opts : fdopt, mode : int64 -> result(fd, errno))
	const close	: (fd : fd -> errno)
	const read	: (fd : fd, buf : byte[:] -> result(size, errno))
	const write	: (fd : fd, buf : byte[:] -> result(size, errno))
	const seek	: (fd : fd, delta : off, whence : whence -> result(off, errno))
	const pipe	: (fds : fd[2]# -> errno)
	const dup2	: (ofd : fd, nfd : fd -> result(fd, errno))

	/* useful/portable bits of stat */
	const fmtime	: (f : byte[:]	-> result(time, errno))
	const fsize	: (f : byte[:]	-> result(off, errno))
	const fexists	: (f : byte[:]	-> bool)
	const fisdir	: (f : byte[:]	-> bool)
	const fisreg	: (f : byte[:]	-> bool)

	/* the important bits that uname provides */
	const getsysinfo	: (si : sysinfo# -> void)

	/* path manipulation */
	const mkdir	: (path : byte[:], mode : int64 -> errno)
	const chdir	: (path : byte[:] -> bool)
	const remove	: (path : byte[:] -> bool)

	/* process stuff */
	const getpid	: ( -> pid)
	const suicide	: ( -> void)
	const fork	: (-> pid)
	const execv	: (cmd : byte[:], args : byte[:][:] -> errno)
	const execve	: (cmd : byte[:], args : byte[:][:], env : byte[:][:] -> errno)
	const nanosleep	: (nsecs : uint64 -> errno)

	pkglocal const Canunmap	: bool = true
	pkglocal const getmem	: (sz : size -> byte#)
	pkglocal const freemem	: (p : byte#, sz : size -> void)
	pkglocal const curtime	: (-> time)
	pkglocal const p9errstr	: (buf : byte[:] -> byte[:])

	/* statbuf offsets */
	pkglocal const Sizeoff	: int64 = 0
	pkglocal const Typeoff	: int64 = 2
	pkglocal const Devoff	: int64 = 4
	pkglocal const Qidtypeoff	: int64 =8
	pkglocal const Qidversoff	: int64 = 9
	pkglocal const Qidpathoff	: int64 = 13
	pkglocal const Modeoff	: int64 = 21
	pkglocal const Atimeoff	: int64 = 25
	pkglocal const Mtimeoff	: int64 = 29
	pkglocal const Lengthoff	: int64 = 31
	pkglocal const Stringsoff	: int64 = 39
;;

/* UGLY: circular dependency breaking... */
extern const getenvv : (name : byte[:], default : byte[:] -> byte[:])

/* fd stuff */
const open	= {path, opts;	-> check(sys.open(path, opts castto(sys.fdopt)))}
const openmode	= {path, opts, mode;
	var fd


	if opts & Ocreat != 0
		fd = sys.create(path, (opts & ~Ocreat) castto(sys.fdopt), mode castto(int))
	else
		fd = sys.open(path, opts castto(sys.fdopt))
	;;
	if opts & Oappend != 0
		sys.seek(fd, 0, 2)
	;;
	-> check(fd)
}


/* useful/portable bits of stat */
const fexists = {path
	var buf : byte[4]	/* big enough for size, nothing else. */

	-> sys.stat(path, buf[:]) >= 0
}

const fmtime = {path
	var buf	: byte[Stringsoff + 512]	/* enough space for some strings */

	if sys.stat(path, buf[:]) < Stringsoff
		-> `Fail Emisc
	;;
	-> `Ok (getle32(buf[Mtimeoff:Mtimeoff + 8]) castto(time))
}

const fsize = {path
	var buf	: byte[Stringsoff + 512]	/* enough space for some strings */

	if sys.stat(path, buf[:]) < Stringsoff
		-> `Fail Emisc
	;;
	-> `Ok (getle64(buf[Lengthoff:Lengthoff + 8]) castto(off))
}

extern const put : (fmt : byte[:], args : ... -> int64)
const fisdir = {path
	var buf	: byte[Stringsoff + 39]	/* enough space for qid type */

	if sys.stat(path, buf[:]) < Stringsoff
		-> false
	;;
	-> (getle32(buf[Modeoff:Modeoff + 4]) & sys.Dmdir) != 0
}

const fisreg = {path
	var buf	: byte[Stringsoff + 512]	/* enough space for some strings */
	var ty

	if sys.stat(path, buf[:]) < Stringsoff
		-> false
	;;

	ty = getle16(buf[Typeoff:Typeoff + 2])
	if (getle32(buf[Modeoff:Modeoff + 4]) & sys.Dmdir) != 0
		-> false
	/* ugh. maybe this call should just be '!fisdir()' */
	elif ty == '|' || ty == 's' || ty == 'm'
		-> false
	else
		-> true
	;;
}

const getsysinfo = {si
	si.system = getenvv("osname", "Plan9")
	si.release = "4"
	si.version = "0"
	si.arch = getenvv("objtype", "amd64")
}

const close	= {fd;		-> sys.close(fd castto(sys.fd)) castto(errno)}
const read	= {fd, buf;	-> check(sys.pread(fd castto(sys.fd), buf, -1))}
const write	= {fd, buf;	-> check(sys.pwrite(fd castto(sys.fd), buf, -1))}
const seek	= {fd, off, whence; 	-> check(sys.seek(fd castto(sys.fd), off castto(sys.off), whence castto(int64)))}
const pipe	= {fds;		-> sys.pipe(fds castto(sys.fd[2]#)) castto(errno)}
const dup2	= {ofd, nfd;	-> check(sys.dup(ofd castto(sys.fd), nfd castto(sys.fd)))}

/* path manipulation */
const remove	= {path;	-> sys.remove(path) == 0}
const chdir	= {path;	-> sys.chdir(path) == 0}
const mkdir	= {path, mode;
	var fd

	fd = sys.create(path, sys.Ordonly, sys.Dmdir | (mode castto(int)))
	if fd < 0
		-> lasterr()
	;;
	sys.close(fd)
	-> 0
}

/* process stuff */
const getpid	= {;	-> sys.tosptr.pid castto(pid)}
const suicide	= {;	(0 castto(byte#))#}	/* let's happy segfault!! t */
const fork	= {;		-> sys.rfork(sys.Rffdg | sys.Rfrend | sys.Rfproc) castto(pid)}
const execv	= {cmd, args;
	sys.exec(cmd, args)
	-> lasterr()
}

const execve	= {cmd, args, env;
	sys.exec(cmd, args)
	-> lasterr()
}

const nanosleep = {nsecs
	if sys.sleep((nsecs/1_000_000) castto(uint32)) < 0
		-> lasterr()
	;;
	-> 0
}

/* memory stuff */
const getmem	= {sz
	var endp, oldp

	oldp = sys.curbrk
	endp = (sys.curbrk castto(intptr)) + (sz castto(intptr))
	endp = (endp + 4095) & ~4095
	if sys.brk_(endp castto(byte#)) < 0
		-> Failmem
	;;
	sys.curbrk = endp castto(byte#)
	-> oldp
}
	
const freemem = {p, sz
	/* FIXME: we leak address space */
	sys.segfree(p, sz castto(sys.size))
}

const curtime = {
	-> sys.nsec()/1000 castto(time)
}

const p9errstr = {errbuf
	var i

	sys.errstr(errbuf)
	for i = 0; errbuf[i] != 0 && i < errbuf.len; i++
		continue
	;;
	-> errbuf[:i]
}

/* FIXME: will be needed when we resize stat bufs when statting.
const statsz = {buf
	-> (buf[0] castto(int64)) | ((buf[1] << 8) castto(int64))
}
*/

generic check = {e : @a::(integral, numeric) -> result(@b, errno)
	if e < 0
		-> `Fail lasterr()
	else
		-> `Ok e castto(@b)
	;;
}

const lasterr = {
	var errbuf	: byte[sys.Maxerr]
	var err

	sys.errstr(errbuf[:])
	err = cstrconv(errbuf[:])
	if strhas(err, "no error")
		-> Enone
	elif strhas(err, "already exists")
		-> Eexist
	elif strhas(err, "read or write too large")
		-> Erange
	elif strhas(err, "read or write too small")
		-> Erange
	elif strhas(err, "i/o error")
		-> Eio
	elif strhas(err, "fd out of range or not open")
		-> Ebadf
	else
		-> Emisc
	;;
}