# # Voice output, portable part. Uses mvoice.m for the host-dependent # implementation. # May be used as a prototype for file systems that have one or two # files as their interface, relying on underlying host commands. # Or perhaps provide a basic common module for such tools. implement Voice; include "sys.m"; sys: Sys; Dir, pctl, NEWPGRP, DMDIR, open, OREAD, FD, OWRITE, ORCLOSE, FORKFD, ORDWR, FORKNS, NEWFD, MREPL, MBEFORE, MAFTER, MCREATE, pipe, mount, fprint, sprint, create, pwrite, read, QTDIR, QTFILE, fildes, Qid: import sys; include "draw.m"; include "styx.m"; styx: Styx; Rmsg, Tmsg: import styx; include "error.m"; err: Error; checkload, stderr, panic, kill, error: import err; include "styxservers.m"; styxs: Styxservers; Styxserver, readbytes, readstr, Navigator, Fid: import styxs; nametree: Nametree; Tree: import nametree; include "arg.m"; arg: Arg; usage: import arg; include "netget.m"; netget: Netget; include "env.m"; env: Env; getenv: import env; include "string.m"; str: String; splitl: import str; include "mvoice.m"; mvoice: Mvoice; speak: import mvoice; Voice: module { init: fn(nil: ref Draw->Context, argv: list of string); }; Qroot, Qvoice, Qndb: con big iota; debug := 0; user: string; readall(fname: string) : string { fd := open(fname, OREAD); if (fd == nil) return "none"; max : con int 1024; data := array[max] of byte; tot := nr := 0; do { nr = read(fd, data[tot:], len data - tot); if (nr > 0) tot += nr; } while(nr > 0 && tot < len data); if (tot == 0) return "none"; return string data[0:tot]; } newdir(name: string, perm: int, qid: big): Dir { d := sys->zerodir; d.name = name; d.uid = user; d.gid = user; d.qid.path = qid; if (perm & DMDIR) d.qid.qtype = QTDIR; else d.qid.qtype = QTFILE; d.mode = perm; return d; } fsreq(srv: ref Styxserver, nil: ref Tree, req: ref Tmsg) : ref Rmsg { pick m := req { Read => (fid, e) := srv.canread(m); if (e != nil) return ref Rmsg.Error(m.tag, e); if (fid.qtype&QTDIR) return nil; if (fid.path != Qndb) return readstr(m, ""); data := netget->ndb(); return readstr(m, data); Write => (fid, e) := srv.canwrite(m); if (e != nil || fid.path != Qvoice) return ref Rmsg.Error(m.tag, e); s := string m.data; if (s != nil && s != ""){ (s1, nil) := splitl(s, "\n"); if (s1 != nil && s1 != "") speak(s1); } return ref Rmsg.Write(m.tag, len m.data); Wstat => return ref Rmsg.Wstat(m.tag); * => return nil; } } fs(pidc: chan of int, fd: ref FD) { styx->init(); styxs->init(styx); user = getenv("user"); if (user == nil) user = readall("/dev/user"); if (pidc != nil) pidc <-= pctl(FORKNS|NEWPGRP|NEWFD, list of {0,1,2,fd.fd}); else pctl(NEWPGRP, nil); stderr = fildes(2); netget->announce("voice", "path /voice"); (tree, navc) := nametree->start(); nav := Navigator.new(navc); (reqc, srv) := Styxserver.new(fd, nav, Qroot); tree.create(Qroot, newdir(".", DMDIR|8r775, Qroot)); tree.create(Qroot, newdir("speak", 8r660, Qvoice)); tree.create(Qroot, newdir("ndb", 8r440, Qndb)); for (;;) { req := <-reqc; if (req == nil) break; rep := fsreq(srv, tree, req); if (rep == nil) srv.default(req); else srv.reply(rep); } tree.quit(); netget->terminate(); kill(pctl(0, nil),"killgrp"); # be sure to quit } init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; err = load Error Error->PATH; err->init(); styx = checkload(load Styx Styx->PATH, Styx->PATH); styxs = checkload(load Styxservers Styxservers->PATH, Styxservers->PATH); nametree = checkload(load Nametree Nametree->PATH, Nametree->PATH); nametree->init(); env = checkload(load Env Env->PATH, Env->PATH); netget = checkload(load Netget Netget->PATH, Netget->PATH); mvoice = checkload(load Mvoice Mvoice->PATH, Mvoice->PATH); str = checkload(load String String->PATH, String->PATH); arg = checkload(load Arg Arg->PATH, Arg->PATH); arg->init(args); arg->setusage("voice [-abcd] [-m mnt]"); mnt: string; flag := MREPL|MCREATE; while((opt := arg->opt()) != 0) { case opt{ 'b' => flag = MBEFORE; 'a' => flag = MAFTER; 'c' => flag |= MCREATE; 'm' => mnt = arg->earg(); 'd' => debug = 1; styxs->traceset(1); * => usage(); } } if (len arg->argv() != 0) usage(); e := mvoice->init(); if (e != nil) error("voice: "+e); if (mnt == nil) fs(nil, fildes(0)); else { pfds := array[2] of ref FD; if (pipe(pfds) < 0) error(sprint("voice: pipe: %r")); pidc := chan of int; spawn fs(pidc, pfds[0]); <-pidc; if (mount(pfds[1], nil, mnt, flag, nil) < 0) error(sprint("voice: mount: %r")); pfds[0] = nil; } }